Annotation of embedaddon/strongswan/src/libipsec/ipsec_processor.c, revision 1.1.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>