Annotation of embedaddon/strongswan/src/libipsec/ipsec_sa_mgr.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * Copyright (C) 2012-2017 Tobias Brunner
        !             3:  * Copyright (C) 2012 Giuliano Grassi
        !             4:  * Copyright (C) 2012 Ralf Sager
        !             5:  * HSR Hochschule fuer Technik Rapperswil
        !             6:  *
        !             7:  * This program is free software; you can redistribute it and/or modify it
        !             8:  * under the terms of the GNU General Public License as published by the
        !             9:  * Free Software Foundation; either version 2 of the License, or (at your
        !            10:  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
        !            11:  *
        !            12:  * This program is distributed in the hope that it will be useful, but
        !            13:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
        !            14:  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
        !            15:  * for more details.
        !            16:  */
        !            17: 
        !            18: #include "ipsec.h"
        !            19: #include "ipsec_sa_mgr.h"
        !            20: 
        !            21: #include <utils/debug.h>
        !            22: #include <library.h>
        !            23: #include <processing/jobs/callback_job.h>
        !            24: #include <threading/condvar.h>
        !            25: #include <threading/mutex.h>
        !            26: #include <collections/hashtable.h>
        !            27: #include <collections/linked_list.h>
        !            28: 
        !            29: typedef struct private_ipsec_sa_mgr_t private_ipsec_sa_mgr_t;
        !            30: 
        !            31: /**
        !            32:  * Private additions to ipsec_sa_mgr_t.
        !            33:  */
        !            34: struct private_ipsec_sa_mgr_t {
        !            35: 
        !            36:        /**
        !            37:         * Public members of ipsec_sa_mgr_t.
        !            38:         */
        !            39:        ipsec_sa_mgr_t public;
        !            40: 
        !            41:        /**
        !            42:         * Installed SAs
        !            43:         */
        !            44:        linked_list_t *sas;
        !            45: 
        !            46:        /**
        !            47:         * SPIs allocated using get_spi()
        !            48:         */
        !            49:        hashtable_t *allocated_spis;
        !            50: 
        !            51:        /**
        !            52:         * Mutex used to synchronize access to the SA manager
        !            53:         */
        !            54:        mutex_t *mutex;
        !            55: 
        !            56:        /**
        !            57:         * RNG used to generate SPIs
        !            58:         */
        !            59:        rng_t *rng;
        !            60: };
        !            61: 
        !            62: /**
        !            63:  * Struct to keep track of locked IPsec SAs
        !            64:  */
        !            65: typedef struct {
        !            66: 
        !            67:        /**
        !            68:         * IPsec SA
        !            69:         */
        !            70:        ipsec_sa_t *sa;
        !            71: 
        !            72:        /**
        !            73:         * Set if this SA is currently in use by a thread
        !            74:         */
        !            75:        bool locked;
        !            76: 
        !            77:        /**
        !            78:         * Condvar used by threads to wait for this entry
        !            79:         */
        !            80:        condvar_t *condvar;
        !            81: 
        !            82:        /**
        !            83:         * Number of threads waiting for this entry
        !            84:         */
        !            85:        u_int waiting_threads;
        !            86: 
        !            87:        /**
        !            88:         * Set if this entry is awaiting deletion
        !            89:         */
        !            90:        bool awaits_deletion;
        !            91: 
        !            92: }  ipsec_sa_entry_t;
        !            93: 
        !            94: /**
        !            95:  * Helper struct for expiration events
        !            96:  */
        !            97: typedef struct {
        !            98: 
        !            99:        /**
        !           100:         * IPsec SA manager
        !           101:         */
        !           102:        private_ipsec_sa_mgr_t *manager;
        !           103: 
        !           104:        /**
        !           105:         * Entry that expired
        !           106:         */
        !           107:        ipsec_sa_entry_t *entry;
        !           108: 
        !           109:        /**
        !           110:         * SPI of the expired entry
        !           111:         */
        !           112:        uint32_t spi;
        !           113: 
        !           114:        /**
        !           115:         * 0 if this is a hard expire, otherwise the offset in s (soft->hard)
        !           116:         */
        !           117:        uint32_t hard_offset;
        !           118: 
        !           119: } ipsec_sa_expired_t;
        !           120: 
        !           121: /*
        !           122:  * Used for the hash table of allocated SPIs
        !           123:  */
        !           124: static bool spi_equals(uint32_t *spi, uint32_t *other_spi)
        !           125: {
        !           126:        return *spi == *other_spi;
        !           127: }
        !           128: 
        !           129: static u_int spi_hash(uint32_t *spi)
        !           130: {
        !           131:        return chunk_hash(chunk_from_thing(*spi));
        !           132: }
        !           133: 
        !           134: /**
        !           135:  * Create an SA entry
        !           136:  */
        !           137: static ipsec_sa_entry_t *create_entry(ipsec_sa_t *sa)
        !           138: {
        !           139:        ipsec_sa_entry_t *this;
        !           140: 
        !           141:        INIT(this,
        !           142:                .condvar = condvar_create(CONDVAR_TYPE_DEFAULT),
        !           143:                .sa = sa,
        !           144:        );
        !           145:        return this;
        !           146: }
        !           147: 
        !           148: /**
        !           149:  * Destroy an SA entry
        !           150:  */
        !           151: static void destroy_entry(ipsec_sa_entry_t *entry)
        !           152: {
        !           153:        entry->condvar->destroy(entry->condvar);
        !           154:        entry->sa->destroy(entry->sa);
        !           155:        free(entry);
        !           156: }
        !           157: 
        !           158: /**
        !           159:  * Makes sure an entry is safe to remove
        !           160:  * Must be called with this->mutex held.
        !           161:  *
        !           162:  * @return                     TRUE if entry can be removed, FALSE if entry is already
        !           163: *                                      being removed by another thread
        !           164:  */
        !           165: static bool wait_remove_entry(private_ipsec_sa_mgr_t *this,
        !           166:                                                          ipsec_sa_entry_t *entry)
        !           167: {
        !           168:        if (entry->awaits_deletion)
        !           169:        {
        !           170:                /* this will be deleted by another thread already */
        !           171:                return FALSE;
        !           172:        }
        !           173:        entry->awaits_deletion = TRUE;
        !           174:        while (entry->locked)
        !           175:        {
        !           176:                entry->condvar->wait(entry->condvar, this->mutex);
        !           177:        }
        !           178:        while (entry->waiting_threads > 0)
        !           179:        {
        !           180:                entry->condvar->broadcast(entry->condvar);
        !           181:                entry->condvar->wait(entry->condvar, this->mutex);
        !           182:        }
        !           183:        return TRUE;
        !           184: }
        !           185: 
        !           186: /**
        !           187:  * Waits until an is available and then locks it.
        !           188:  * Must only be called with this->mutex held
        !           189:  */
        !           190: static bool wait_for_entry(private_ipsec_sa_mgr_t *this,
        !           191:                                                   ipsec_sa_entry_t *entry)
        !           192: {
        !           193:        while (entry->locked && !entry->awaits_deletion)
        !           194:        {
        !           195:                entry->waiting_threads++;
        !           196:                entry->condvar->wait(entry->condvar, this->mutex);
        !           197:                entry->waiting_threads--;
        !           198:        }
        !           199:        if (entry->awaits_deletion)
        !           200:        {
        !           201:                /* others may still be waiting, */
        !           202:                entry->condvar->signal(entry->condvar);
        !           203:                return FALSE;
        !           204:        }
        !           205:        entry->locked = TRUE;
        !           206:        return TRUE;
        !           207: }
        !           208: 
        !           209: /**
        !           210:  * Flushes all entries
        !           211:  * Must be called with this->mutex held.
        !           212:  */
        !           213: static void flush_entries(private_ipsec_sa_mgr_t *this)
        !           214: {
        !           215:        ipsec_sa_entry_t *current;
        !           216:        enumerator_t *enumerator;
        !           217: 
        !           218:        DBG2(DBG_ESP, "flushing SAD");
        !           219: 
        !           220:        enumerator = this->sas->create_enumerator(this->sas);
        !           221:        while (enumerator->enumerate(enumerator, (void**)&current))
        !           222:        {
        !           223:                if (wait_remove_entry(this, current))
        !           224:                {
        !           225:                        this->sas->remove_at(this->sas, enumerator);
        !           226:                        destroy_entry(current);
        !           227:                }
        !           228:        }
        !           229:        enumerator->destroy(enumerator);
        !           230: }
        !           231: 
        !           232: CALLBACK(match_entry_by_sa_ptr, bool,
        !           233:        ipsec_sa_entry_t *item, va_list args)
        !           234: {
        !           235:        ipsec_sa_t *sa;
        !           236: 
        !           237:        VA_ARGS_VGET(args, sa);
        !           238:        return item->sa == sa;
        !           239: }
        !           240: 
        !           241: CALLBACK(match_entry_by_spi_inbound, bool,
        !           242:        ipsec_sa_entry_t *item, va_list args)
        !           243: {
        !           244:        uint32_t spi;
        !           245:        int inbound;
        !           246: 
        !           247:        VA_ARGS_VGET(args, spi, inbound);
        !           248:        return item->sa->get_spi(item->sa) == spi &&
        !           249:                   item->sa->is_inbound(item->sa) == inbound;
        !           250: }
        !           251: 
        !           252: static bool match_entry_by_spi_src_dst(ipsec_sa_entry_t *item, uint32_t spi,
        !           253:                                                                           host_t *src, host_t *dst)
        !           254: {
        !           255:        return item->sa->match_by_spi_src_dst(item->sa, spi, src, dst);
        !           256: }
        !           257: 
        !           258: CALLBACK(match_entry_by_spi_src_dst_cb, bool,
        !           259:        ipsec_sa_entry_t *item, va_list args)
        !           260: {
        !           261:        host_t *src, *dst;
        !           262:        uint32_t spi;
        !           263: 
        !           264:        VA_ARGS_VGET(args, spi, src, dst);
        !           265:        return match_entry_by_spi_src_dst(item, spi, src, dst);
        !           266: }
        !           267: 
        !           268: CALLBACK(match_entry_by_reqid_inbound, bool,
        !           269:        ipsec_sa_entry_t *item, va_list args)
        !           270: {
        !           271:        uint32_t reqid;
        !           272:        int inbound;
        !           273: 
        !           274:        VA_ARGS_VGET(args, reqid, inbound);
        !           275:        return item->sa->match_by_reqid(item->sa, reqid, inbound);
        !           276: }
        !           277: 
        !           278: CALLBACK(match_entry_by_spi_dst, bool,
        !           279:        ipsec_sa_entry_t *item, va_list args)
        !           280: {
        !           281:        host_t *dst;
        !           282:        uint32_t spi;
        !           283: 
        !           284:        VA_ARGS_VGET(args, spi, dst);
        !           285:        return item->sa->match_by_spi_dst(item->sa, spi, dst);
        !           286: }
        !           287: 
        !           288: /**
        !           289:  * Remove an entry
        !           290:  */
        !           291: static bool remove_entry(private_ipsec_sa_mgr_t *this, ipsec_sa_entry_t *entry)
        !           292: {
        !           293:        ipsec_sa_entry_t *current;
        !           294:        enumerator_t *enumerator;
        !           295:        bool removed = FALSE;
        !           296: 
        !           297:        enumerator = this->sas->create_enumerator(this->sas);
        !           298:        while (enumerator->enumerate(enumerator, (void**)&current))
        !           299:        {
        !           300:                if (current == entry)
        !           301:                {
        !           302:                        if (wait_remove_entry(this, current))
        !           303:                        {
        !           304:                                this->sas->remove_at(this->sas, enumerator);
        !           305:                                removed = TRUE;
        !           306:                        }
        !           307:                        break;
        !           308:                }
        !           309:        }
        !           310:        enumerator->destroy(enumerator);
        !           311:        return removed;
        !           312: }
        !           313: 
        !           314: /**
        !           315:  * Callback for expiration events
        !           316:  */
        !           317: static job_requeue_t sa_expired(ipsec_sa_expired_t *expired)
        !           318: {
        !           319:        private_ipsec_sa_mgr_t *this = expired->manager;
        !           320: 
        !           321:        this->mutex->lock(this->mutex);
        !           322:        if (this->sas->find_first(this->sas, NULL, (void**)&expired->entry) &&
        !           323:                expired->spi == expired->entry->sa->get_spi(expired->entry->sa))
        !           324:        {       /* only if we find the right SA at this pointer location */
        !           325:                uint32_t hard_offset;
        !           326: 
        !           327:                hard_offset = expired->hard_offset;
        !           328:                expired->entry->sa->expire(expired->entry->sa, hard_offset == 0);
        !           329:                if (hard_offset)
        !           330:                {       /* soft limit reached, schedule hard expire */
        !           331:                        expired->hard_offset = 0;
        !           332:                        this->mutex->unlock(this->mutex);
        !           333:                        return JOB_RESCHEDULE(hard_offset);
        !           334:                }
        !           335:                /* hard limit reached */
        !           336:                if (remove_entry(this, expired->entry))
        !           337:                {
        !           338:                        destroy_entry(expired->entry);
        !           339:                }
        !           340:        }
        !           341:        this->mutex->unlock(this->mutex);
        !           342:        return JOB_REQUEUE_NONE;
        !           343: }
        !           344: 
        !           345: /**
        !           346:  * Schedule a job to handle IPsec SA expiration
        !           347:  */
        !           348: static void schedule_expiration(private_ipsec_sa_mgr_t *this,
        !           349:                                                                ipsec_sa_entry_t *entry)
        !           350: {
        !           351:        lifetime_cfg_t *lifetime = entry->sa->get_lifetime(entry->sa);
        !           352:        ipsec_sa_expired_t *expired;
        !           353:        callback_job_t *job;
        !           354:        uint32_t timeout;
        !           355: 
        !           356:        if (!lifetime->time.life)
        !           357:        {       /* no expiration at all */
        !           358:                return;
        !           359:        }
        !           360: 
        !           361:        INIT(expired,
        !           362:                .manager = this,
        !           363:                .entry = entry,
        !           364:                .spi = entry->sa->get_spi(entry->sa),
        !           365:        );
        !           366: 
        !           367:        /* schedule a rekey first, a hard timeout will be scheduled then, if any */
        !           368:        expired->hard_offset = lifetime->time.life - lifetime->time.rekey;
        !           369:        timeout = lifetime->time.rekey;
        !           370: 
        !           371:        if (lifetime->time.life <= lifetime->time.rekey ||
        !           372:                lifetime->time.rekey == 0)
        !           373:        {       /* no rekey, schedule hard timeout */
        !           374:                expired->hard_offset = 0;
        !           375:                timeout = lifetime->time.life;
        !           376:        }
        !           377: 
        !           378:        job = callback_job_create((callback_job_cb_t)sa_expired, expired,
        !           379:                                                          (callback_job_cleanup_t)free, NULL);
        !           380:        lib->scheduler->schedule_job(lib->scheduler, (job_t*)job, timeout);
        !           381: }
        !           382: 
        !           383: /**
        !           384:  * Remove all allocated SPIs
        !           385:  */
        !           386: static void flush_allocated_spis(private_ipsec_sa_mgr_t *this)
        !           387: {
        !           388:        enumerator_t *enumerator;
        !           389:        uint32_t *current;
        !           390: 
        !           391:        DBG2(DBG_ESP, "flushing allocated SPIs");
        !           392:        enumerator = this->allocated_spis->create_enumerator(this->allocated_spis);
        !           393:        while (enumerator->enumerate(enumerator, NULL, (void**)&current))
        !           394:        {
        !           395:                this->allocated_spis->remove_at(this->allocated_spis, enumerator);
        !           396:                DBG2(DBG_ESP, "  removed allocated SPI %.8x", ntohl(*current));
        !           397:                free(current);
        !           398:        }
        !           399:        enumerator->destroy(enumerator);
        !           400: }
        !           401: 
        !           402: /**
        !           403:  * Pre-allocate an SPI for an inbound SA
        !           404:  */
        !           405: static bool allocate_spi(private_ipsec_sa_mgr_t *this, uint32_t spi)
        !           406: {
        !           407:        uint32_t *spi_alloc;
        !           408: 
        !           409:        if (this->allocated_spis->get(this->allocated_spis, &spi) ||
        !           410:                this->sas->find_first(this->sas, match_entry_by_spi_inbound,
        !           411:                                                          NULL, spi, TRUE))
        !           412:        {
        !           413:                return FALSE;
        !           414:        }
        !           415:        spi_alloc = malloc_thing(uint32_t);
        !           416:        *spi_alloc = spi;
        !           417:        this->allocated_spis->put(this->allocated_spis, spi_alloc, spi_alloc);
        !           418:        return TRUE;
        !           419: }
        !           420: 
        !           421: METHOD(ipsec_sa_mgr_t, get_spi, status_t,
        !           422:        private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, uint8_t protocol,
        !           423:        uint32_t *spi)
        !           424: {
        !           425:        uint32_t spi_min, spi_max, spi_new;
        !           426: 
        !           427:        spi_min = lib->settings->get_int(lib->settings, "%s.spi_min",
        !           428:                                                                         0x00000100, lib->ns);
        !           429:        spi_max = lib->settings->get_int(lib->settings, "%s.spi_max",
        !           430:                                                                         0xffffffff, lib->ns);
        !           431:        if (spi_min > spi_max)
        !           432:        {
        !           433:                spi_new = spi_min;
        !           434:                spi_min = spi_max;
        !           435:                spi_max = spi_new;
        !           436:        }
        !           437:        /* make sure the SPI is valid (not in range 0-255) */
        !           438:        spi_min = max(spi_min, 0x00000100);
        !           439:        spi_max = max(spi_max, 0x00000100);
        !           440: 
        !           441:        this->mutex->lock(this->mutex);
        !           442:        if (!this->rng)
        !           443:        {
        !           444:                this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
        !           445:                if (!this->rng)
        !           446:                {
        !           447:                        this->mutex->unlock(this->mutex);
        !           448:                        DBG1(DBG_ESP, "failed to create RNG for SPI generation");
        !           449:                        return FAILED;
        !           450:                }
        !           451:        }
        !           452: 
        !           453:        do
        !           454:        {
        !           455:                if (!this->rng->get_bytes(this->rng, sizeof(spi_new),
        !           456:                                                                 (uint8_t*)&spi_new))
        !           457:                {
        !           458:                        this->mutex->unlock(this->mutex);
        !           459:                        DBG1(DBG_ESP, "failed to allocate SPI");
        !           460:                        return FAILED;
        !           461:                }
        !           462:                spi_new = spi_min + spi_new % (spi_max - spi_min + 1);
        !           463:                spi_new = htonl(spi_new);
        !           464:        }
        !           465:        while (!allocate_spi(this, spi_new));
        !           466:        this->mutex->unlock(this->mutex);
        !           467: 
        !           468:        *spi = spi_new;
        !           469: 
        !           470:        DBG2(DBG_ESP, "allocated SPI %.8x", ntohl(*spi));
        !           471:        return SUCCESS;
        !           472: }
        !           473: 
        !           474: METHOD(ipsec_sa_mgr_t, add_sa, status_t,
        !           475:        private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, uint32_t spi,
        !           476:        uint8_t protocol, uint32_t reqid,       mark_t mark, uint32_t tfc,
        !           477:        lifetime_cfg_t *lifetime, uint16_t enc_alg, chunk_t enc_key,
        !           478:        uint16_t int_alg, chunk_t int_key, ipsec_mode_t mode, uint16_t ipcomp,
        !           479:        uint16_t cpi, bool initiator, bool encap, bool esn, bool inbound,
        !           480:        bool update)
        !           481: {
        !           482:        ipsec_sa_entry_t *entry;
        !           483:        ipsec_sa_t *sa_new;
        !           484: 
        !           485:        DBG2(DBG_ESP, "adding SAD entry with SPI %.8x and reqid {%u}",
        !           486:                 ntohl(spi), reqid);
        !           487:        DBG2(DBG_ESP, "  using encryption algorithm %N with key size %d",
        !           488:                 encryption_algorithm_names, enc_alg, enc_key.len * 8);
        !           489:        DBG2(DBG_ESP, "  using integrity algorithm %N with key size %d",
        !           490:                 integrity_algorithm_names, int_alg, int_key.len * 8);
        !           491: 
        !           492:        sa_new = ipsec_sa_create(spi, src, dst, protocol, reqid, mark, tfc,
        !           493:                                                         lifetime, enc_alg, enc_key, int_alg, int_key, mode,
        !           494:                                                         ipcomp, cpi, encap, esn, inbound);
        !           495:        if (!sa_new)
        !           496:        {
        !           497:                DBG1(DBG_ESP, "failed to create SAD entry");
        !           498:                return FAILED;
        !           499:        }
        !           500: 
        !           501:        this->mutex->lock(this->mutex);
        !           502: 
        !           503:        if (update)
        !           504:        {       /* remove any pre-allocated SPIs */
        !           505:                uint32_t *spi_alloc;
        !           506: 
        !           507:                spi_alloc = this->allocated_spis->remove(this->allocated_spis, &spi);
        !           508:                free(spi_alloc);
        !           509:        }
        !           510: 
        !           511:        if (this->sas->find_first(this->sas, match_entry_by_spi_src_dst_cb, NULL,
        !           512:                                                          spi, src, dst))
        !           513:        {
        !           514:                this->mutex->unlock(this->mutex);
        !           515:                DBG1(DBG_ESP, "failed to install SAD entry: already installed");
        !           516:                sa_new->destroy(sa_new);
        !           517:                return FAILED;
        !           518:        }
        !           519: 
        !           520:        entry = create_entry(sa_new);
        !           521:        schedule_expiration(this, entry);
        !           522:        this->sas->insert_first(this->sas, entry);
        !           523: 
        !           524:        this->mutex->unlock(this->mutex);
        !           525:        return SUCCESS;
        !           526: }
        !           527: 
        !           528: METHOD(ipsec_sa_mgr_t, update_sa, status_t,
        !           529:        private_ipsec_sa_mgr_t *this, uint32_t spi, uint8_t protocol,
        !           530:        uint16_t cpi, host_t *src, host_t *dst, host_t *new_src, host_t *new_dst,
        !           531:        bool encap, bool new_encap, mark_t mark)
        !           532: {
        !           533:        ipsec_sa_entry_t *entry = NULL;
        !           534: 
        !           535:        DBG2(DBG_ESP, "updating SAD entry with SPI %.8x from %#H..%#H to %#H..%#H",
        !           536:                 ntohl(spi), src, dst, new_src, new_dst);
        !           537: 
        !           538:        if (!new_encap)
        !           539:        {
        !           540:                DBG1(DBG_ESP, "failed to update SAD entry: can't deactivate UDP "
        !           541:                         "encapsulation");
        !           542:                return NOT_SUPPORTED;
        !           543:        }
        !           544: 
        !           545:        this->mutex->lock(this->mutex);
        !           546:        if (this->sas->find_first(this->sas, match_entry_by_spi_src_dst_cb,
        !           547:                                                         (void**)&entry, spi, src, dst) &&
        !           548:                wait_for_entry(this, entry))
        !           549:        {
        !           550:                entry->sa->set_source(entry->sa, new_src);
        !           551:                entry->sa->set_destination(entry->sa, new_dst);
        !           552:                /* checkin the entry */
        !           553:                entry->locked = FALSE;
        !           554:                entry->condvar->signal(entry->condvar);
        !           555:        }
        !           556:        this->mutex->unlock(this->mutex);
        !           557: 
        !           558:        if (!entry)
        !           559:        {
        !           560:                DBG1(DBG_ESP, "failed to update SAD entry: not found");
        !           561:                return FAILED;
        !           562:        }
        !           563:        return SUCCESS;
        !           564: }
        !           565: 
        !           566: METHOD(ipsec_sa_mgr_t, query_sa, status_t,
        !           567:        private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst,
        !           568:        uint32_t spi, uint8_t protocol, mark_t mark,
        !           569:        uint64_t *bytes, uint64_t *packets, time_t *time)
        !           570: {
        !           571:        ipsec_sa_entry_t *entry = NULL;
        !           572: 
        !           573:        this->mutex->lock(this->mutex);
        !           574:        if (this->sas->find_first(this->sas, match_entry_by_spi_src_dst_cb,
        !           575:                                                         (void**)&entry, spi, src, dst) &&
        !           576:                wait_for_entry(this, entry))
        !           577:        {
        !           578:                entry->sa->get_usestats(entry->sa, bytes, packets, time);
        !           579:                /* checkin the entry */
        !           580:                entry->locked = FALSE;
        !           581:                entry->condvar->signal(entry->condvar);
        !           582:        }
        !           583:        this->mutex->unlock(this->mutex);
        !           584: 
        !           585:        return entry ? SUCCESS : NOT_FOUND;
        !           586: }
        !           587: 
        !           588: METHOD(ipsec_sa_mgr_t, del_sa, status_t,
        !           589:        private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, uint32_t spi,
        !           590:        uint8_t protocol, uint16_t cpi, mark_t mark)
        !           591: {
        !           592:        ipsec_sa_entry_t *current, *found = NULL;
        !           593:        enumerator_t *enumerator;
        !           594: 
        !           595:        this->mutex->lock(this->mutex);
        !           596:        enumerator = this->sas->create_enumerator(this->sas);
        !           597:        while (enumerator->enumerate(enumerator, (void**)&current))
        !           598:        {
        !           599:                if (match_entry_by_spi_src_dst(current, spi, src, dst))
        !           600:                {
        !           601:                        if (wait_remove_entry(this, current))
        !           602:                        {
        !           603:                                this->sas->remove_at(this->sas, enumerator);
        !           604:                                found = current;
        !           605:                        }
        !           606:                        break;
        !           607:                }
        !           608:        }
        !           609:        enumerator->destroy(enumerator);
        !           610:        this->mutex->unlock(this->mutex);
        !           611: 
        !           612:        if (found)
        !           613:        {
        !           614:                DBG2(DBG_ESP, "deleted %sbound SAD entry with SPI %.8x",
        !           615:                         found->sa->is_inbound(found->sa) ? "in" : "out", ntohl(spi));
        !           616:                destroy_entry(found);
        !           617:                return SUCCESS;
        !           618:        }
        !           619:        return FAILED;
        !           620: }
        !           621: 
        !           622: METHOD(ipsec_sa_mgr_t, checkout_by_reqid, ipsec_sa_t*,
        !           623:        private_ipsec_sa_mgr_t *this, uint32_t reqid, bool inbound)
        !           624: {
        !           625:        ipsec_sa_entry_t *entry;
        !           626:        ipsec_sa_t *sa = NULL;
        !           627: 
        !           628:        this->mutex->lock(this->mutex);
        !           629:        if (this->sas->find_first(this->sas, match_entry_by_reqid_inbound,
        !           630:                                                         (void**)&entry, reqid, inbound) &&
        !           631:                wait_for_entry(this, entry))
        !           632:        {
        !           633:                sa = entry->sa;
        !           634:        }
        !           635:        this->mutex->unlock(this->mutex);
        !           636:        return sa;
        !           637: }
        !           638: 
        !           639: METHOD(ipsec_sa_mgr_t, checkout_by_spi, ipsec_sa_t*,
        !           640:        private_ipsec_sa_mgr_t *this, uint32_t spi, host_t *dst)
        !           641: {
        !           642:        ipsec_sa_entry_t *entry;
        !           643:        ipsec_sa_t *sa = NULL;
        !           644: 
        !           645:        this->mutex->lock(this->mutex);
        !           646:        if (this->sas->find_first(this->sas, match_entry_by_spi_dst,
        !           647:                                                         (void**)&entry, spi, dst) &&
        !           648:                wait_for_entry(this, entry))
        !           649:        {
        !           650:                sa = entry->sa;
        !           651:        }
        !           652:        this->mutex->unlock(this->mutex);
        !           653:        return sa;
        !           654: }
        !           655: 
        !           656: METHOD(ipsec_sa_mgr_t, checkin, void,
        !           657:        private_ipsec_sa_mgr_t *this, ipsec_sa_t *sa)
        !           658: {
        !           659:        ipsec_sa_entry_t *entry;
        !           660: 
        !           661:        this->mutex->lock(this->mutex);
        !           662:        if (this->sas->find_first(this->sas, match_entry_by_sa_ptr,
        !           663:                                                         (void**)&entry, sa))
        !           664:        {
        !           665:                if (entry->locked)
        !           666:                {
        !           667:                        entry->locked = FALSE;
        !           668:                        entry->condvar->signal(entry->condvar);
        !           669:                }
        !           670:        }
        !           671:        this->mutex->unlock(this->mutex);
        !           672: }
        !           673: 
        !           674: METHOD(ipsec_sa_mgr_t, flush_sas, status_t,
        !           675:        private_ipsec_sa_mgr_t *this)
        !           676: {
        !           677:        this->mutex->lock(this->mutex);
        !           678:        flush_entries(this);
        !           679:        this->mutex->unlock(this->mutex);
        !           680:        return SUCCESS;
        !           681: }
        !           682: 
        !           683: METHOD(ipsec_sa_mgr_t, destroy, void,
        !           684:        private_ipsec_sa_mgr_t *this)
        !           685: {
        !           686:        this->mutex->lock(this->mutex);
        !           687:        flush_entries(this);
        !           688:        flush_allocated_spis(this);
        !           689:        this->mutex->unlock(this->mutex);
        !           690: 
        !           691:        this->allocated_spis->destroy(this->allocated_spis);
        !           692:        this->sas->destroy(this->sas);
        !           693: 
        !           694:        this->mutex->destroy(this->mutex);
        !           695:        DESTROY_IF(this->rng);
        !           696:        free(this);
        !           697: }
        !           698: 
        !           699: /**
        !           700:  * Described in header.
        !           701:  */
        !           702: ipsec_sa_mgr_t *ipsec_sa_mgr_create()
        !           703: {
        !           704:        private_ipsec_sa_mgr_t *this;
        !           705: 
        !           706:        INIT(this,
        !           707:                .public = {
        !           708:                        .get_spi = _get_spi,
        !           709:                        .add_sa = _add_sa,
        !           710:                        .update_sa = _update_sa,
        !           711:                        .query_sa = _query_sa,
        !           712:                        .del_sa = _del_sa,
        !           713:                        .checkout_by_spi = _checkout_by_spi,
        !           714:                        .checkout_by_reqid = _checkout_by_reqid,
        !           715:                        .checkin = _checkin,
        !           716:                        .flush_sas = _flush_sas,
        !           717:                        .destroy = _destroy,
        !           718:                },
        !           719:                .sas = linked_list_create(),
        !           720:                .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
        !           721:                .allocated_spis = hashtable_create((hashtable_hash_t)spi_hash,
        !           722:                                                                                   (hashtable_equals_t)spi_equals, 16),
        !           723:        );
        !           724: 
        !           725:        return &this->public;
        !           726: }

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