Annotation of embedaddon/strongswan/src/libcharon/plugins/bypass_lan/bypass_lan_listener.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * Copyright (C) 2016 Tobias Brunner
        !             3:  * HSR Hochschule fuer Technik Rapperswil
        !             4:  *
        !             5:  * This program is free software; you can redistribute it and/or modify it
        !             6:  * under the terms of the GNU General Public License as published by the
        !             7:  * Free Software Foundation; either version 2 of the License, or (at your
        !             8:  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
        !             9:  *
        !            10:  * This program is distributed in the hope that it will be useful, but
        !            11:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
        !            12:  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
        !            13:  * for more details.
        !            14:  */
        !            15: 
        !            16: #include "bypass_lan_listener.h"
        !            17: 
        !            18: #include <collections/hashtable.h>
        !            19: #include <collections/linked_list.h>
        !            20: #include <threading/mutex.h>
        !            21: #include <processing/jobs/callback_job.h>
        !            22: 
        !            23: #include <daemon.h>
        !            24: 
        !            25: typedef struct private_bypass_lan_listener_t private_bypass_lan_listener_t;
        !            26: 
        !            27: /**
        !            28:  * Private data
        !            29:  */
        !            30: struct private_bypass_lan_listener_t {
        !            31: 
        !            32:        /**
        !            33:         * Public interface.
        !            34:         */
        !            35:        bypass_lan_listener_t public;
        !            36: 
        !            37:        /**
        !            38:         * Currently installed bypass policies, bypass_policy_t*.
        !            39:         */
        !            40:        hashtable_t *policies;
        !            41: 
        !            42:        /**
        !            43:         * Mutex to access list of policies.
        !            44:         */
        !            45:        mutex_t *mutex;
        !            46: 
        !            47:        /**
        !            48:         * List of interface names to include or exclude (char*), NULL if interfaces
        !            49:         * are not filtered.
        !            50:         */
        !            51:        linked_list_t *ifaces_filter;
        !            52: 
        !            53:        /**
        !            54:         * TRUE to exclude interfaces listed in ifaces_filter, FALSE to consider
        !            55:         * only those listed there.
        !            56:         */
        !            57:        bool ifaces_exclude;
        !            58: };
        !            59: 
        !            60: /**
        !            61:  * Data for bypass policies
        !            62:  */
        !            63: typedef struct {
        !            64:        private_bypass_lan_listener_t *listener;
        !            65:        host_t *net;
        !            66:        uint8_t mask;
        !            67:        char *iface;
        !            68:        child_cfg_t *cfg;
        !            69: } bypass_policy_t;
        !            70: 
        !            71: /**
        !            72:  * Destroy a bypass policy
        !            73:  */
        !            74: static void bypass_policy_destroy(bypass_policy_t *this)
        !            75: {
        !            76:        traffic_selector_t *ts;
        !            77: 
        !            78:        if (this->cfg)
        !            79:        {
        !            80:                ts = traffic_selector_create_from_subnet(this->net->clone(this->net),
        !            81:                                                                                                 this->mask, 0, 0, 65535);
        !            82:                DBG1(DBG_IKE, "uninstalling bypass policy for %R", ts);
        !            83:                charon->shunts->uninstall(charon->shunts, "bypass-lan",
        !            84:                                                                  this->cfg->get_name(this->cfg));
        !            85:                this->cfg->destroy(this->cfg);
        !            86:                ts->destroy(ts);
        !            87:        }
        !            88:        this->net->destroy(this->net);
        !            89:        free(this->iface);
        !            90:        free(this);
        !            91: }
        !            92: 
        !            93: /**
        !            94:  * Hash a bypass policy
        !            95:  */
        !            96: static u_int policy_hash(bypass_policy_t *policy)
        !            97: {
        !            98:        return chunk_hash_inc(policy->net->get_address(policy->net),
        !            99:                                                  chunk_hash(chunk_from_thing(policy->mask)));
        !           100: }
        !           101: 
        !           102: /**
        !           103:  * Compare bypass policy
        !           104:  */
        !           105: static bool policy_equals(bypass_policy_t *a, bypass_policy_t *b)
        !           106: {
        !           107:        return a->mask == b->mask && a->net->equals(a->net, b->net);
        !           108: }
        !           109: 
        !           110: /**
        !           111:  * Check if an interface should be considered
        !           112:  */
        !           113: static bool consider_interface(private_bypass_lan_listener_t *this, char *iface)
        !           114: {
        !           115:        if (!iface || !this->ifaces_filter)
        !           116:        {
        !           117:                return TRUE;
        !           118:        }
        !           119:        return this->ifaces_filter->find_first(this->ifaces_filter,
        !           120:                                        linked_list_match_str, NULL, iface) != this->ifaces_exclude;
        !           121: }
        !           122: 
        !           123: /**
        !           124:  * Job updating bypass policies
        !           125:  */
        !           126: static job_requeue_t update_bypass(private_bypass_lan_listener_t *this)
        !           127: {
        !           128:        enumerator_t *enumerator;
        !           129:        hashtable_t *seen;
        !           130:        bypass_policy_t *found, *lookup;
        !           131:        traffic_selector_t *ts;
        !           132:        host_t *net;
        !           133:        uint8_t mask;
        !           134:        char *iface;
        !           135: 
        !           136:        seen = hashtable_create((hashtable_hash_t)policy_hash,
        !           137:                                                        (hashtable_equals_t)policy_equals, 4);
        !           138: 
        !           139:        this->mutex->lock(this->mutex);
        !           140: 
        !           141:        enumerator = charon->kernel->create_local_subnet_enumerator(charon->kernel);
        !           142:        while (enumerator->enumerate(enumerator, &net, &mask, &iface))
        !           143:        {
        !           144:                if (!consider_interface(this, iface))
        !           145:                {
        !           146:                        continue;
        !           147:                }
        !           148: 
        !           149:                INIT(lookup,
        !           150:                        .net = net->clone(net),
        !           151:                        .mask = mask,
        !           152:                        .iface = strdupnull(iface),
        !           153:                );
        !           154:                found = seen->put(seen, lookup, lookup);
        !           155:                if (found)
        !           156:                {       /* in case the same subnet is on multiple interfaces */
        !           157:                        bypass_policy_destroy(found);
        !           158:                }
        !           159: 
        !           160:                found = this->policies->get(this->policies, lookup);
        !           161:                if (!found)
        !           162:                {
        !           163:                        child_cfg_create_t child = {
        !           164:                                .mode = MODE_PASS,
        !           165:                        };
        !           166:                        child_cfg_t *cfg;
        !           167:                        char name[128];
        !           168: 
        !           169:                        ts = traffic_selector_create_from_subnet(net->clone(net), mask,
        !           170:                                                                                                         0, 0, 65535);
        !           171:                        snprintf(name, sizeof(name), "Bypass LAN %R", ts);
        !           172: 
        !           173:                        cfg = child_cfg_create(name, &child);
        !           174:                        cfg->add_traffic_selector(cfg, FALSE, ts->clone(ts));
        !           175:                        cfg->add_traffic_selector(cfg, TRUE, ts);
        !           176:                        charon->shunts->install(charon->shunts, "bypass-lan", cfg);
        !           177:                        DBG1(DBG_IKE, "installed bypass policy for %R", ts);
        !           178: 
        !           179:                        INIT(found,
        !           180:                                .net = net->clone(net),
        !           181:                                .mask = mask,
        !           182:                                .iface = strdupnull(iface),
        !           183:                                .cfg = cfg,
        !           184:                        );
        !           185:                        this->policies->put(this->policies, found, found);
        !           186:                }
        !           187:        }
        !           188:        enumerator->destroy(enumerator);
        !           189: 
        !           190:        enumerator = this->policies->create_enumerator(this->policies);
        !           191:        while (enumerator->enumerate(enumerator, NULL, &lookup))
        !           192:        {
        !           193:                found = seen->get(seen, lookup);
        !           194:                if (!found)
        !           195:                {
        !           196:                        this->policies->remove_at(this->policies, enumerator);
        !           197:                        bypass_policy_destroy(lookup);
        !           198:                }
        !           199:                else if (!streq(lookup->iface, found->iface))
        !           200:                {       /* if the subnet is on multiple interfaces, we only get the last
        !           201:                         * one (hopefully, they are enumerated in a consistent order) */
        !           202:                        ts = traffic_selector_create_from_subnet(
        !           203:                                                                                                lookup->net->clone(lookup->net),
        !           204:                                                                                                lookup->mask, 0, 0, 65535);
        !           205:                        DBG1(DBG_IKE, "interface change for bypass policy for %R (from %s "
        !           206:                                 "to %s)", ts, lookup->iface, found->iface);
        !           207:                        ts->destroy(ts);
        !           208:                        free(lookup->iface);
        !           209:                        lookup->iface = strdupnull(found->iface);
        !           210:                        /* there is currently no API to update shunts, so we remove and
        !           211:                         * reinstall it to update the route */
        !           212:                        charon->shunts->uninstall(charon->shunts, "bypass-lan",
        !           213:                                                                          lookup->cfg->get_name(lookup->cfg));
        !           214:                        charon->shunts->install(charon->shunts, "bypass-lan", lookup->cfg);
        !           215:                }
        !           216:        }
        !           217:        enumerator->destroy(enumerator);
        !           218:        this->mutex->unlock(this->mutex);
        !           219: 
        !           220:        seen->destroy_function(seen, (void*)bypass_policy_destroy);
        !           221:        return JOB_REQUEUE_NONE;
        !           222: }
        !           223: 
        !           224: METHOD(kernel_listener_t, roam, bool,
        !           225:        private_bypass_lan_listener_t *this, bool address)
        !           226: {
        !           227:        lib->processor->queue_job(lib->processor,
        !           228:                        (job_t*)callback_job_create((callback_job_cb_t)update_bypass, this,
        !           229:                                                                        NULL, (callback_job_cancel_t)return_false));
        !           230:        return TRUE;
        !           231: }
        !           232: 
        !           233: METHOD(bypass_lan_listener_t, reload_interfaces, void,
        !           234:        private_bypass_lan_listener_t *this)
        !           235: {
        !           236:        char *ifaces;
        !           237: 
        !           238:        this->mutex->lock(this->mutex);
        !           239:        DESTROY_FUNCTION_IF(this->ifaces_filter, (void*)free);
        !           240:        this->ifaces_filter = NULL;
        !           241:        this->ifaces_exclude = FALSE;
        !           242: 
        !           243:        ifaces = lib->settings->get_str(lib->settings,
        !           244:                                        "%s.plugins.bypass-lan.interfaces_use", NULL, lib->ns);
        !           245:        if (!ifaces)
        !           246:        {
        !           247:                this->ifaces_exclude = TRUE;
        !           248:                ifaces = lib->settings->get_str(lib->settings,
        !           249:                                        "%s.plugins.bypass-lan.interfaces_ignore", NULL, lib->ns);
        !           250:        }
        !           251:        if (ifaces)
        !           252:        {
        !           253:                enumerator_t *enumerator;
        !           254:                char *iface;
        !           255: 
        !           256:                enumerator = enumerator_create_token(ifaces, ",", " ");
        !           257:                while (enumerator->enumerate(enumerator, &iface))
        !           258:                {
        !           259:                        if (!this->ifaces_filter)
        !           260:                        {
        !           261:                                this->ifaces_filter = linked_list_create();
        !           262:                        }
        !           263:                        this->ifaces_filter->insert_last(this->ifaces_filter,
        !           264:                                                                                         strdup(iface));
        !           265:                }
        !           266:                enumerator->destroy(enumerator);
        !           267:        }
        !           268:        this->mutex->unlock(this->mutex);
        !           269:        lib->processor->queue_job(lib->processor,
        !           270:                        (job_t*)callback_job_create((callback_job_cb_t)update_bypass, this,
        !           271:                                                                        NULL, (callback_job_cancel_t)return_false));
        !           272: }
        !           273: 
        !           274: METHOD(bypass_lan_listener_t, destroy, void,
        !           275:        private_bypass_lan_listener_t *this)
        !           276: {
        !           277:        enumerator_t *enumerator;
        !           278:        bypass_policy_t *policy;
        !           279: 
        !           280:        enumerator = this->policies->create_enumerator(this->policies);
        !           281:        while (enumerator->enumerate(enumerator, NULL, &policy))
        !           282:        {
        !           283:                bypass_policy_destroy(policy);
        !           284:        }
        !           285:        enumerator->destroy(enumerator);
        !           286:        DESTROY_FUNCTION_IF(this->ifaces_filter, (void*)free);
        !           287:        this->policies->destroy(this->policies);
        !           288:        this->mutex->destroy(this->mutex);
        !           289:        free(this);
        !           290: }
        !           291: 
        !           292: /*
        !           293:  * See header
        !           294:  */
        !           295: bypass_lan_listener_t *bypass_lan_listener_create()
        !           296: {
        !           297:        private_bypass_lan_listener_t *this;
        !           298: 
        !           299:        INIT(this,
        !           300:                .public = {
        !           301:                        .listener = {
        !           302:                                .roam = _roam,
        !           303:                        },
        !           304:                        .reload_interfaces = _reload_interfaces,
        !           305:                        .destroy = _destroy,
        !           306:                },
        !           307:                .policies = hashtable_create((hashtable_hash_t)policy_hash,
        !           308:                                                                         (hashtable_equals_t)policy_equals, 4),
        !           309:                .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
        !           310:        );
        !           311: 
        !           312:        reload_interfaces(this);
        !           313:        return &this->public;
        !           314: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>