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>