Annotation of embedaddon/strongswan/src/libcharon/sa/shunt_manager.c, revision 1.1.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>