Annotation of embedaddon/strongswan/src/libcharon/sa/shunt_manager.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * Copyright (C) 2015-2017 Tobias Brunner
        !             3:  * Copyright (C) 2011-2016 Andreas Steffen
        !             4:  * HSR Hochschule fuer Technik Rapperswil
        !             5:  *
        !             6:  * This program is free software; you can redistribute it and/or modify it
        !             7:  * under the terms of the GNU General Public License as published by the
        !             8:  * Free Software Foundation; either version 2 of the License, or (at your
        !             9:  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
        !            10:  *
        !            11:  * This program is distributed in the hope that it will be useful, but
        !            12:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
        !            13:  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
        !            14:  * for more details.
        !            15:  */
        !            16: 
        !            17: #include "shunt_manager.h"
        !            18: 
        !            19: #include <daemon.h>
        !            20: #include <threading/rwlock.h>
        !            21: #include <threading/rwlock_condvar.h>
        !            22: #include <collections/linked_list.h>
        !            23: 
        !            24: #define INSTALL_DISABLED ((u_int)~0)
        !            25: 
        !            26: typedef struct private_shunt_manager_t private_shunt_manager_t;
        !            27: 
        !            28: /**
        !            29:  * Private data of an shunt_manager_t object.
        !            30:  */
        !            31: struct private_shunt_manager_t {
        !            32: 
        !            33:        /**
        !            34:         * Public shunt_manager_t interface.
        !            35:         */
        !            36:        shunt_manager_t public;
        !            37: 
        !            38:        /**
        !            39:         * Installed shunts, as entry_t
        !            40:         */
        !            41:        linked_list_t *shunts;
        !            42: 
        !            43:        /**
        !            44:         * Lock to safely access the list of shunts
        !            45:         */
        !            46:        rwlock_t *lock;
        !            47: 
        !            48:        /**
        !            49:         * Number of threads currently installing shunts, or INSTALL_DISABLED
        !            50:         */
        !            51:        u_int installing;
        !            52: 
        !            53:        /**
        !            54:         * Condvar to signal shunt installation
        !            55:         */
        !            56:        rwlock_condvar_t *condvar;
        !            57: };
        !            58: 
        !            59: /**
        !            60:  * Config entry for a shunt
        !            61:  */
        !            62: typedef struct {
        !            63:        /**
        !            64:         * Configured namespace
        !            65:         */
        !            66:        char *ns;
        !            67: 
        !            68:        /**
        !            69:         * Child config
        !            70:         */
        !            71:        child_cfg_t *cfg;
        !            72: 
        !            73: } entry_t;
        !            74: 
        !            75: /**
        !            76:  * Destroy a config entry
        !            77:  */
        !            78: static void entry_destroy(entry_t *this)
        !            79: {
        !            80:        this->cfg->destroy(this->cfg);
        !            81:        free(this->ns);
        !            82:        free(this);
        !            83: }
        !            84: 
        !            85: /**
        !            86:  * Install in and out shunt policies in the kernel
        !            87:  */
        !            88: static bool install_shunt_policy(child_cfg_t *child)
        !            89: {
        !            90:        enumerator_t *e_my_ts, *e_other_ts;
        !            91:        linked_list_t *my_ts_list, *other_ts_list, *hosts;
        !            92:        traffic_selector_t *my_ts, *other_ts;
        !            93:        host_t *host_any, *host_any6;
        !            94:        policy_type_t policy_type;
        !            95:        policy_priority_t policy_prio;
        !            96:        status_t status = SUCCESS;
        !            97:        uint32_t manual_prio;
        !            98:        char *interface;
        !            99:        bool fwd_out;
        !           100:        ipsec_sa_cfg_t sa = { .mode = MODE_TRANSPORT };
        !           101: 
        !           102:        switch (child->get_mode(child))
        !           103:        {
        !           104:                case MODE_PASS:
        !           105:                        policy_type = POLICY_PASS;
        !           106:                        policy_prio = POLICY_PRIORITY_PASS;
        !           107:                        break;
        !           108:                case MODE_DROP:
        !           109:                        policy_type = POLICY_DROP;
        !           110:                        policy_prio = POLICY_PRIORITY_FALLBACK;
        !           111:                        break;
        !           112:                default:
        !           113:                        return FALSE;
        !           114:        }
        !           115: 
        !           116:        host_any = host_create_any(AF_INET);
        !           117:        host_any6 = host_create_any(AF_INET6);
        !           118: 
        !           119:        hosts = linked_list_create_with_items(host_any, host_any6, NULL);
        !           120:        my_ts_list =    child->get_traffic_selectors(child, TRUE,  NULL, hosts,
        !           121:                                                                                                 FALSE);
        !           122:        other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts,
        !           123:                                                                                                 FALSE);
        !           124:        hosts->destroy(hosts);
        !           125: 
        !           126:        manual_prio = child->get_manual_prio(child);
        !           127:        interface = child->get_interface(child);
        !           128:        fwd_out = child->has_option(child, OPT_FWD_OUT_POLICIES);
        !           129: 
        !           130:        /* enumerate pairs of traffic selectors */
        !           131:        e_my_ts = my_ts_list->create_enumerator(my_ts_list);
        !           132:        while (e_my_ts->enumerate(e_my_ts, &my_ts))
        !           133:        {
        !           134:                e_other_ts = other_ts_list->create_enumerator(other_ts_list);
        !           135:                while (e_other_ts->enumerate(e_other_ts, &other_ts))
        !           136:                {
        !           137:                        if (my_ts->get_type(my_ts) != other_ts->get_type(other_ts))
        !           138:                        {
        !           139:                                continue;
        !           140:                        }
        !           141:                        if (my_ts->get_protocol(my_ts) &&
        !           142:                                other_ts->get_protocol(other_ts) &&
        !           143:                                my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts))
        !           144:                        {
        !           145:                                continue;
        !           146:                        }
        !           147:                        /* install out policy */
        !           148:                        kernel_ipsec_policy_id_t id = {
        !           149:                                .dir = POLICY_OUT,
        !           150:                                .src_ts = my_ts,
        !           151:                                .dst_ts = other_ts,
        !           152:                                .mark = child->get_mark(child, FALSE),
        !           153:                                .interface = interface,
        !           154:                        };
        !           155:                        kernel_ipsec_manage_policy_t policy = {
        !           156:                                .type = policy_type,
        !           157:                                .prio = policy_prio,
        !           158:                                .manual_prio = manual_prio,
        !           159:                                .src = host_any,
        !           160:                                .dst = host_any,
        !           161:                                .sa = &sa,
        !           162:                        };
        !           163:                        status |= charon->kernel->add_policy(charon->kernel, &id, &policy);
        !           164:                        if (fwd_out)
        !           165:                        {       /* install "outbound" forward policy */
        !           166:                                id.dir = POLICY_FWD;
        !           167:                                status |= charon->kernel->add_policy(charon->kernel, &id, &policy);
        !           168:                        }
        !           169:                        /* install in policy */
        !           170:                        id = (kernel_ipsec_policy_id_t){
        !           171:                                .dir = POLICY_IN,
        !           172:                                .src_ts = other_ts,
        !           173:                                .dst_ts = my_ts,
        !           174:                                .mark = child->get_mark(child, TRUE),
        !           175:                                .interface = interface,
        !           176:                        };
        !           177:                        status |= charon->kernel->add_policy(charon->kernel, &id, &policy);
        !           178:                        /* install "inbound" forward policy */
        !           179:                        id.dir = POLICY_FWD;
        !           180:                        status |= charon->kernel->add_policy(charon->kernel, &id, &policy);
        !           181:                }
        !           182:                e_other_ts->destroy(e_other_ts);
        !           183:        }
        !           184:        e_my_ts->destroy(e_my_ts);
        !           185: 
        !           186:        my_ts_list->destroy_offset(my_ts_list,
        !           187:                                                           offsetof(traffic_selector_t, destroy));
        !           188:        other_ts_list->destroy_offset(other_ts_list,
        !           189:                                                           offsetof(traffic_selector_t, destroy));
        !           190:        host_any6->destroy(host_any6);
        !           191:        host_any->destroy(host_any);
        !           192: 
        !           193:        return status == SUCCESS;
        !           194: }
        !           195: 
        !           196: METHOD(shunt_manager_t, install, bool,
        !           197:        private_shunt_manager_t *this, char *ns, child_cfg_t *cfg)
        !           198: {
        !           199:        enumerator_t *enumerator;
        !           200:        entry_t *entry;
        !           201:        bool found = FALSE, success;
        !           202: 
        !           203:        if (!ns)
        !           204:        {
        !           205:                DBG1(DBG_CFG, "missing namespace for shunt policy '%s'",
        !           206:                         cfg->get_name(cfg));
        !           207:                return FALSE;
        !           208:        }
        !           209: 
        !           210:        /* check if not already installed */
        !           211:        this->lock->write_lock(this->lock);
        !           212:        if (this->installing == INSTALL_DISABLED)
        !           213:        {       /* flush() has been called */
        !           214:                this->lock->unlock(this->lock);
        !           215:                return FALSE;
        !           216:        }
        !           217:        enumerator = this->shunts->create_enumerator(this->shunts);
        !           218:        while (enumerator->enumerate(enumerator, &entry))
        !           219:        {
        !           220:                if (streq(ns, entry->ns) &&
        !           221:                        streq(cfg->get_name(cfg), entry->cfg->get_name(entry->cfg)))
        !           222:                {
        !           223:                        found = TRUE;
        !           224:                        break;
        !           225:                }
        !           226:        }
        !           227:        enumerator->destroy(enumerator);
        !           228:        if (found)
        !           229:        {
        !           230:                DBG1(DBG_CFG, "shunt %N policy '%s' already installed",
        !           231:                         ipsec_mode_names, cfg->get_mode(cfg), cfg->get_name(cfg));
        !           232:                this->lock->unlock(this->lock);
        !           233:                return TRUE;
        !           234:        }
        !           235:        INIT(entry,
        !           236:                .ns = strdup(ns),
        !           237:                .cfg = cfg->get_ref(cfg),
        !           238:        );
        !           239:        this->shunts->insert_last(this->shunts, entry);
        !           240:        this->installing++;
        !           241:        this->lock->unlock(this->lock);
        !           242: 
        !           243:        success = install_shunt_policy(cfg);
        !           244: 
        !           245:        this->lock->write_lock(this->lock);
        !           246:        if (!success)
        !           247:        {
        !           248:                this->shunts->remove(this->shunts, entry, NULL);
        !           249:                entry_destroy(entry);
        !           250:        }
        !           251:        this->installing--;
        !           252:        this->condvar->signal(this->condvar);
        !           253:        this->lock->unlock(this->lock);
        !           254:        return success;
        !           255: }
        !           256: 
        !           257: /**
        !           258:  * Uninstall in and out shunt policies in the kernel
        !           259:  */
        !           260: static void uninstall_shunt_policy(child_cfg_t *child)
        !           261: {
        !           262:        enumerator_t *e_my_ts, *e_other_ts;
        !           263:        linked_list_t *my_ts_list, *other_ts_list, *hosts;
        !           264:        traffic_selector_t *my_ts, *other_ts;
        !           265:        host_t *host_any, *host_any6;
        !           266:        policy_type_t policy_type;
        !           267:        policy_priority_t policy_prio;
        !           268:        status_t status = SUCCESS;
        !           269:        uint32_t manual_prio;
        !           270:        char *interface;
        !           271:        bool fwd_out;
        !           272:        ipsec_sa_cfg_t sa = { .mode = MODE_TRANSPORT };
        !           273: 
        !           274:        switch (child->get_mode(child))
        !           275:        {
        !           276:                case MODE_PASS:
        !           277:                        policy_type = POLICY_PASS;
        !           278:                        policy_prio = POLICY_PRIORITY_PASS;
        !           279:                        break;
        !           280:                case MODE_DROP:
        !           281:                        policy_type = POLICY_DROP;
        !           282:                        policy_prio = POLICY_PRIORITY_FALLBACK;
        !           283:                        break;
        !           284:                default:
        !           285:                        return;
        !           286:        }
        !           287: 
        !           288:        host_any = host_create_any(AF_INET);
        !           289:        host_any6 = host_create_any(AF_INET6);
        !           290: 
        !           291:        hosts = linked_list_create_with_items(host_any, host_any6, NULL);
        !           292:        my_ts_list =    child->get_traffic_selectors(child, TRUE,  NULL, hosts,
        !           293:                                                                                                 FALSE);
        !           294:        other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts,
        !           295:                                                                                                 FALSE);
        !           296:        hosts->destroy(hosts);
        !           297: 
        !           298:        manual_prio = child->get_manual_prio(child);
        !           299:        interface = child->get_interface(child);
        !           300:        fwd_out = child->has_option(child, OPT_FWD_OUT_POLICIES);
        !           301: 
        !           302:        /* enumerate pairs of traffic selectors */
        !           303:        e_my_ts = my_ts_list->create_enumerator(my_ts_list);
        !           304:        while (e_my_ts->enumerate(e_my_ts, &my_ts))
        !           305:        {
        !           306:                e_other_ts = other_ts_list->create_enumerator(other_ts_list);
        !           307:                while (e_other_ts->enumerate(e_other_ts, &other_ts))
        !           308:                {
        !           309:                        if (my_ts->get_type(my_ts) != other_ts->get_type(other_ts))
        !           310:                        {
        !           311:                                continue;
        !           312:                        }
        !           313:                        if (my_ts->get_protocol(my_ts) &&
        !           314:                                other_ts->get_protocol(other_ts) &&
        !           315:                                my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts))
        !           316:                        {
        !           317:                                continue;
        !           318:                        }
        !           319:                        /* uninstall out policy */
        !           320:                        kernel_ipsec_policy_id_t id = {
        !           321:                                .dir = POLICY_OUT,
        !           322:                                .src_ts = my_ts,
        !           323:                                .dst_ts = other_ts,
        !           324:                                .mark = child->get_mark(child, FALSE),
        !           325:                                .interface = interface,
        !           326:                        };
        !           327:                        kernel_ipsec_manage_policy_t policy = {
        !           328:                                .type = policy_type,
        !           329:                                .prio = policy_prio,
        !           330:                                .manual_prio = manual_prio,
        !           331:                                .src = host_any,
        !           332:                                .dst = host_any,
        !           333:                                .sa = &sa,
        !           334:                        };
        !           335:                        status |= charon->kernel->del_policy(charon->kernel, &id, &policy);
        !           336:                        if (fwd_out)
        !           337:                        {
        !           338:                                /* uninstall "outbound" forward policy */
        !           339:                                id.dir = POLICY_FWD;
        !           340:                                status |= charon->kernel->del_policy(charon->kernel, &id, &policy);
        !           341:                        }
        !           342:                        /* uninstall in policy */
        !           343:                        id = (kernel_ipsec_policy_id_t){
        !           344:                                .dir = POLICY_IN,
        !           345:                                .src_ts = other_ts,
        !           346:                                .dst_ts = my_ts,
        !           347:                                .mark = child->get_mark(child, TRUE),
        !           348:                                .interface = interface,
        !           349:                        };
        !           350:                        status |= charon->kernel->del_policy(charon->kernel, &id, &policy);
        !           351:                        /* uninstall "inbound" forward policy */
        !           352:                        id.dir = POLICY_FWD;
        !           353:                        status |= charon->kernel->del_policy(charon->kernel, &id, &policy);
        !           354:                }
        !           355:                e_other_ts->destroy(e_other_ts);
        !           356:        }
        !           357:        e_my_ts->destroy(e_my_ts);
        !           358: 
        !           359:        my_ts_list->destroy_offset(my_ts_list,
        !           360:                                                           offsetof(traffic_selector_t, destroy));
        !           361:        other_ts_list->destroy_offset(other_ts_list,
        !           362:                                                           offsetof(traffic_selector_t, destroy));
        !           363:        host_any6->destroy(host_any6);
        !           364:        host_any->destroy(host_any);
        !           365: 
        !           366:        if (status != SUCCESS)
        !           367:        {
        !           368:                DBG1(DBG_CFG, "uninstalling shunt %N 'policy %s' failed",
        !           369:                         ipsec_mode_names, child->get_mode(child), child->get_name(child));
        !           370:        }
        !           371: }
        !           372: 
        !           373: METHOD(shunt_manager_t, uninstall, bool,
        !           374:        private_shunt_manager_t *this, char *ns, char *name)
        !           375: {
        !           376:        enumerator_t *enumerator;
        !           377:        entry_t *entry, *found = NULL;
        !           378: 
        !           379:        this->lock->write_lock(this->lock);
        !           380:        enumerator = this->shunts->create_enumerator(this->shunts);
        !           381:        while (enumerator->enumerate(enumerator, &entry))
        !           382:        {
        !           383:                if ((!ns || streq(ns, entry->ns)) &&
        !           384:                        streq(name, entry->cfg->get_name(entry->cfg)))
        !           385:                {
        !           386:                        this->shunts->remove_at(this->shunts, enumerator);
        !           387:                        found = entry;
        !           388:                        break;
        !           389:                }
        !           390:        }
        !           391:        enumerator->destroy(enumerator);
        !           392:        this->lock->unlock(this->lock);
        !           393: 
        !           394:        if (!found)
        !           395:        {
        !           396:                return FALSE;
        !           397:        }
        !           398:        uninstall_shunt_policy(found->cfg);
        !           399:        entry_destroy(found);
        !           400:        return TRUE;
        !           401: }
        !           402: 
        !           403: CALLBACK(filter_entries, bool,
        !           404:        void *unused, enumerator_t *orig, va_list args)
        !           405: {
        !           406:        entry_t *entry;
        !           407:        child_cfg_t **cfg;
        !           408:        char **ns;
        !           409: 
        !           410:        VA_ARGS_VGET(args, ns, cfg);
        !           411: 
        !           412:        if (orig->enumerate(orig, &entry))
        !           413:        {
        !           414:                if (ns)
        !           415:                {
        !           416:                        *ns = entry->ns;
        !           417:                }
        !           418:                *cfg = entry->cfg;
        !           419:                return TRUE;
        !           420:        }
        !           421:        return FALSE;
        !           422: }
        !           423: 
        !           424: METHOD(shunt_manager_t, create_enumerator, enumerator_t*,
        !           425:        private_shunt_manager_t *this)
        !           426: {
        !           427:        this->lock->read_lock(this->lock);
        !           428:        return enumerator_create_filter(
        !           429:                                                        this->shunts->create_enumerator(this->shunts),
        !           430:                                                        filter_entries, this->lock,
        !           431:                                                        (void*)this->lock->unlock);
        !           432: }
        !           433: 
        !           434: METHOD(shunt_manager_t, flush, void,
        !           435:        private_shunt_manager_t *this)
        !           436: {
        !           437:        entry_t *entry;
        !           438: 
        !           439:        this->lock->write_lock(this->lock);
        !           440:        while (this->installing)
        !           441:        {
        !           442:                this->condvar->wait(this->condvar, this->lock);
        !           443:        }
        !           444:        while (this->shunts->remove_last(this->shunts, (void**)&entry) == SUCCESS)
        !           445:        {
        !           446:                uninstall_shunt_policy(entry->cfg);
        !           447:                entry_destroy(entry);
        !           448:        }
        !           449:        this->installing = INSTALL_DISABLED;
        !           450:        this->lock->unlock(this->lock);
        !           451: }
        !           452: 
        !           453: METHOD(shunt_manager_t, destroy, void,
        !           454:        private_shunt_manager_t *this)
        !           455: {
        !           456:        this->shunts->destroy_offset(this->shunts, offsetof(child_cfg_t, destroy));
        !           457:        this->lock->destroy(this->lock);
        !           458:        this->condvar->destroy(this->condvar);
        !           459:        free(this);
        !           460: }
        !           461: 
        !           462: /**
        !           463:  * See header
        !           464:  */
        !           465: shunt_manager_t *shunt_manager_create()
        !           466: {
        !           467:        private_shunt_manager_t *this;
        !           468: 
        !           469:        INIT(this,
        !           470:                .public = {
        !           471:                        .install = _install,
        !           472:                        .uninstall = _uninstall,
        !           473:                        .create_enumerator = _create_enumerator,
        !           474:                        .flush = _flush,
        !           475:                        .destroy = _destroy,
        !           476:                },
        !           477:                .shunts = linked_list_create(),
        !           478:                .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
        !           479:                .condvar = rwlock_condvar_create(),
        !           480:        );
        !           481: 
        !           482:        return &this->public;
        !           483: }

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