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

1.1     ! misho       1: /*
        !             2:  * Copyright (C) 2012 Tobias Brunner
        !             3:  * HSR Hochschule fuer Technik Rapperswil
        !             4:  *
        !             5:  * This program is free software; you can redistribute it and/or modify it
        !             6:  * under the terms of the GNU General Public License as published by the
        !             7:  * Free Software Foundation; either version 2 of the License, or (at your
        !             8:  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
        !             9:  *
        !            10:  * This program is distributed in the hope that it will be useful, but
        !            11:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
        !            12:  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
        !            13:  * for more details.
        !            14:  */
        !            15: 
        !            16: #include "ipsec.h"
        !            17: #include "ipsec_processor.h"
        !            18: 
        !            19: #include <utils/debug.h>
        !            20: #include <library.h>
        !            21: #include <threading/rwlock.h>
        !            22: #include <collections/blocking_queue.h>
        !            23: #include <processing/jobs/callback_job.h>
        !            24: 
        !            25: typedef struct private_ipsec_processor_t private_ipsec_processor_t;
        !            26: 
        !            27: /**
        !            28:  * Private additions to ipsec_processor_t.
        !            29:  */
        !            30: struct private_ipsec_processor_t {
        !            31: 
        !            32:        /**
        !            33:         * Public members
        !            34:         */
        !            35:        ipsec_processor_t public;
        !            36: 
        !            37:        /**
        !            38:         * Queue for inbound packets (esp_packet_t*)
        !            39:         */
        !            40:        blocking_queue_t *inbound_queue;
        !            41: 
        !            42:        /**
        !            43:         * Queue for outbound packets (ip_packet_t*)
        !            44:         */
        !            45:        blocking_queue_t *outbound_queue;
        !            46: 
        !            47:        /**
        !            48:         * Registered inbound callback
        !            49:         */
        !            50:        struct {
        !            51:                ipsec_inbound_cb_t cb;
        !            52:                void *data;
        !            53:        } inbound;
        !            54: 
        !            55:        /**
        !            56:         * Registered outbound callback
        !            57:         */
        !            58:        struct {
        !            59:                ipsec_outbound_cb_t cb;
        !            60:                void *data;
        !            61:        } outbound;
        !            62: 
        !            63:        /**
        !            64:         * Lock used to synchronize access to the callbacks
        !            65:         */
        !            66:        rwlock_t *lock;
        !            67: };
        !            68: 
        !            69: /**
        !            70:  * Deliver an inbound IP packet to the registered listener
        !            71:  */
        !            72: static void deliver_inbound(private_ipsec_processor_t *this,
        !            73:                                                        esp_packet_t *packet)
        !            74: {
        !            75:        this->lock->read_lock(this->lock);
        !            76:        if (this->inbound.cb)
        !            77:        {
        !            78:                this->inbound.cb(this->inbound.data, packet->extract_payload(packet));
        !            79:        }
        !            80:        else
        !            81:        {
        !            82:                DBG2(DBG_ESP, "no inbound callback registered, dropping packet");
        !            83:        }
        !            84:        packet->destroy(packet);
        !            85:        this->lock->unlock(this->lock);
        !            86: }
        !            87: 
        !            88: /**
        !            89:  * Processes inbound packets
        !            90:  */
        !            91: static job_requeue_t process_inbound(private_ipsec_processor_t *this)
        !            92: {
        !            93:        esp_packet_t *packet;
        !            94:        ip_packet_t *ip_packet;
        !            95:        ipsec_sa_t *sa;
        !            96:        uint8_t next_header;
        !            97:        uint32_t spi, reqid;
        !            98: 
        !            99:        packet = (esp_packet_t*)this->inbound_queue->dequeue(this->inbound_queue);
        !           100: 
        !           101:        if (!packet->parse_header(packet, &spi))
        !           102:        {
        !           103:                packet->destroy(packet);
        !           104:                return JOB_REQUEUE_DIRECT;
        !           105:        }
        !           106: 
        !           107:        sa = ipsec->sas->checkout_by_spi(ipsec->sas, spi,
        !           108:                                                                         packet->get_destination(packet));
        !           109:        if (!sa)
        !           110:        {
        !           111:                DBG2(DBG_ESP, "inbound ESP packet does not belong to an installed SA");
        !           112:                packet->destroy(packet);
        !           113:                return JOB_REQUEUE_DIRECT;
        !           114:        }
        !           115: 
        !           116:        if (!sa->is_inbound(sa))
        !           117:        {
        !           118:                DBG1(DBG_ESP, "error: IPsec SA is not inbound");
        !           119:                packet->destroy(packet);
        !           120:                ipsec->sas->checkin(ipsec->sas, sa);
        !           121:                return JOB_REQUEUE_DIRECT;
        !           122:        }
        !           123: 
        !           124:        if (packet->decrypt(packet, sa->get_esp_context(sa)) != SUCCESS)
        !           125:        {
        !           126:                ipsec->sas->checkin(ipsec->sas, sa);
        !           127:                packet->destroy(packet);
        !           128:                return JOB_REQUEUE_DIRECT;
        !           129:        }
        !           130:        ip_packet = packet->get_payload(packet);
        !           131:        sa->update_usestats(sa, ip_packet->get_encoding(ip_packet).len);
        !           132:        reqid = sa->get_reqid(sa);
        !           133:        ipsec->sas->checkin(ipsec->sas, sa);
        !           134: 
        !           135:        next_header = packet->get_next_header(packet);
        !           136:        switch (next_header)
        !           137:        {
        !           138:                case IPPROTO_IPIP:
        !           139:                case IPPROTO_IPV6:
        !           140:                {
        !           141:                        ipsec_policy_t *policy;
        !           142: 
        !           143:                        policy = ipsec->policies->find_by_packet(ipsec->policies,
        !           144:                                                                                                         ip_packet, TRUE, reqid);
        !           145:                        if (policy)
        !           146:                        {
        !           147:                                deliver_inbound(this, packet);
        !           148:                                policy->destroy(policy);
        !           149:                                break;
        !           150:                        }
        !           151:                        DBG1(DBG_ESP, "discarding inbound IP packet %#H == %#H [%hhu] due "
        !           152:                                 "to policy", ip_packet->get_source(ip_packet),
        !           153:                                 ip_packet->get_destination(ip_packet),
        !           154:                                 ip_packet->get_next_header(ip_packet));
        !           155:                        /* no matching policy found, fall-through */
        !           156:                }
        !           157:                case IPPROTO_NONE:
        !           158:                        /* discard dummy packets */
        !           159:                        /* fall-through */
        !           160:                default:
        !           161:                        packet->destroy(packet);
        !           162:                        break;
        !           163:        }
        !           164:        return JOB_REQUEUE_DIRECT;
        !           165: }
        !           166: 
        !           167: /**
        !           168:  * Send an ESP packet using the registered outbound callback
        !           169:  */
        !           170: static void send_outbound(private_ipsec_processor_t *this,
        !           171:                                                  esp_packet_t *packet)
        !           172: {
        !           173:        this->lock->read_lock(this->lock);
        !           174:        if (this->outbound.cb)
        !           175:        {
        !           176:                this->outbound.cb(this->outbound.data, packet);
        !           177:        }
        !           178:        else
        !           179:        {
        !           180:                DBG2(DBG_ESP, "no outbound callback registered, dropping packet");
        !           181:                packet->destroy(packet);
        !           182:        }
        !           183:        this->lock->unlock(this->lock);
        !           184: }
        !           185: 
        !           186: /**
        !           187:  * Processes outbound packets
        !           188:  */
        !           189: static job_requeue_t process_outbound(private_ipsec_processor_t *this)
        !           190: {
        !           191:        ipsec_policy_t *policy;
        !           192:        esp_packet_t *esp_packet;
        !           193:        ip_packet_t *packet;
        !           194:        ipsec_sa_t *sa;
        !           195:        host_t *src, *dst;
        !           196: 
        !           197:        packet = (ip_packet_t*)this->outbound_queue->dequeue(this->outbound_queue);
        !           198: 
        !           199:        policy = ipsec->policies->find_by_packet(ipsec->policies, packet, FALSE, 0);
        !           200:        if (!policy)
        !           201:        {
        !           202:                DBG2(DBG_ESP, "no matching outbound IPsec policy for %#H == %#H [%hhu]",
        !           203:                         packet->get_source(packet), packet->get_destination(packet),
        !           204:                         packet->get_next_header(packet));
        !           205:                packet->destroy(packet);
        !           206:                return JOB_REQUEUE_DIRECT;
        !           207:        }
        !           208: 
        !           209:        sa = ipsec->sas->checkout_by_reqid(ipsec->sas, policy->get_reqid(policy),
        !           210:                                                                           FALSE);
        !           211:        if (!sa)
        !           212:        {       /* TODO-IPSEC: send an acquire to upper layer */
        !           213:                DBG1(DBG_ESP, "could not find an outbound IPsec SA for reqid {%u}, "
        !           214:                         "dropping packet", policy->get_reqid(policy));
        !           215:                packet->destroy(packet);
        !           216:                policy->destroy(policy);
        !           217:                return JOB_REQUEUE_DIRECT;
        !           218:        }
        !           219:        src = sa->get_source(sa);
        !           220:        dst = sa->get_destination(sa);
        !           221:        esp_packet = esp_packet_create_from_payload(src->clone(src),
        !           222:                                                                                                dst->clone(dst), packet);
        !           223:        if (esp_packet->encrypt(esp_packet, sa->get_esp_context(sa),
        !           224:                                                        sa->get_spi(sa)) != SUCCESS)
        !           225:        {
        !           226:                ipsec->sas->checkin(ipsec->sas, sa);
        !           227:                esp_packet->destroy(esp_packet);
        !           228:                policy->destroy(policy);
        !           229:                return JOB_REQUEUE_DIRECT;
        !           230:        }
        !           231:        sa->update_usestats(sa, packet->get_encoding(packet).len);
        !           232:        ipsec->sas->checkin(ipsec->sas, sa);
        !           233:        policy->destroy(policy);
        !           234:        send_outbound(this, esp_packet);
        !           235:        return JOB_REQUEUE_DIRECT;
        !           236: }
        !           237: 
        !           238: METHOD(ipsec_processor_t, queue_inbound, void,
        !           239:        private_ipsec_processor_t *this, esp_packet_t *packet)
        !           240: {
        !           241:        this->inbound_queue->enqueue(this->inbound_queue, packet);
        !           242: }
        !           243: 
        !           244: METHOD(ipsec_processor_t, queue_outbound, void,
        !           245:        private_ipsec_processor_t *this, ip_packet_t *packet)
        !           246: {
        !           247:        this->outbound_queue->enqueue(this->outbound_queue, packet);
        !           248: }
        !           249: 
        !           250: METHOD(ipsec_processor_t, register_inbound, void,
        !           251:        private_ipsec_processor_t *this, ipsec_inbound_cb_t cb, void *data)
        !           252: {
        !           253:        this->lock->write_lock(this->lock);
        !           254:        this->inbound.cb = cb;
        !           255:        this->inbound.data = data;
        !           256:        this->lock->unlock(this->lock);
        !           257: }
        !           258: 
        !           259: METHOD(ipsec_processor_t, unregister_inbound, void,
        !           260:        private_ipsec_processor_t *this, ipsec_inbound_cb_t cb)
        !           261: {
        !           262:        this->lock->write_lock(this->lock);
        !           263:        if (this->inbound.cb == cb)
        !           264:        {
        !           265:                this->inbound.cb = NULL;
        !           266:        }
        !           267:        this->lock->unlock(this->lock);
        !           268: }
        !           269: 
        !           270: METHOD(ipsec_processor_t, register_outbound, void,
        !           271:        private_ipsec_processor_t *this, ipsec_outbound_cb_t cb, void *data)
        !           272: {
        !           273:        this->lock->write_lock(this->lock);
        !           274:        this->outbound.cb = cb;
        !           275:        this->outbound.data = data;
        !           276:        this->lock->unlock(this->lock);
        !           277: }
        !           278: 
        !           279: METHOD(ipsec_processor_t, unregister_outbound, void,
        !           280:        private_ipsec_processor_t *this, ipsec_outbound_cb_t cb)
        !           281: {
        !           282:        this->lock->write_lock(this->lock);
        !           283:        if (this->outbound.cb == cb)
        !           284:        {
        !           285:                this->outbound.cb = NULL;
        !           286:        }
        !           287:        this->lock->unlock(this->lock);
        !           288: }
        !           289: 
        !           290: METHOD(ipsec_processor_t, destroy, void,
        !           291:        private_ipsec_processor_t *this)
        !           292: {
        !           293:        this->inbound_queue->destroy_offset(this->inbound_queue,
        !           294:                                                                                offsetof(esp_packet_t, destroy));
        !           295:        this->outbound_queue->destroy_offset(this->outbound_queue,
        !           296:                                                                                 offsetof(ip_packet_t, destroy));
        !           297:        this->lock->destroy(this->lock);
        !           298:        free(this);
        !           299: }
        !           300: 
        !           301: /**
        !           302:  * Described in header.
        !           303:  */
        !           304: ipsec_processor_t *ipsec_processor_create()
        !           305: {
        !           306:        private_ipsec_processor_t *this;
        !           307: 
        !           308:        INIT(this,
        !           309:                .public = {
        !           310:                        .queue_inbound = _queue_inbound,
        !           311:                        .queue_outbound = _queue_outbound,
        !           312:                        .register_inbound = _register_inbound,
        !           313:                        .unregister_inbound = _unregister_inbound,
        !           314:                        .register_outbound = _register_outbound,
        !           315:                        .unregister_outbound = _unregister_outbound,
        !           316:                        .destroy = _destroy,
        !           317:                },
        !           318:                .inbound_queue = blocking_queue_create(),
        !           319:                .outbound_queue = blocking_queue_create(),
        !           320:                .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
        !           321:        );
        !           322: 
        !           323:        lib->processor->queue_job(lib->processor,
        !           324:                (job_t*)callback_job_create((callback_job_cb_t)process_inbound, this,
        !           325:                                                                        NULL, (callback_job_cancel_t)return_false));
        !           326:        lib->processor->queue_job(lib->processor,
        !           327:                (job_t*)callback_job_create((callback_job_cb_t)process_outbound, this,
        !           328:                                                                        NULL, (callback_job_cancel_t)return_false));
        !           329:        return &this->public;
        !           330: }

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