123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- /*
- * Copyright (C) 2012 Tobias Brunner
- * HSR Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
- #include "ipsec.h"
- #include "ipsec_processor.h"
- #include <utils/debug.h>
- #include <library.h>
- #include <threading/rwlock.h>
- #include <collections/blocking_queue.h>
- #include <processing/jobs/callback_job.h>
- typedef struct private_ipsec_processor_t private_ipsec_processor_t;
- /**
- * Private additions to ipsec_processor_t.
- */
- struct private_ipsec_processor_t {
- /**
- * Public members
- */
- ipsec_processor_t public;
- /**
- * Queue for inbound packets (esp_packet_t*)
- */
- blocking_queue_t *inbound_queue;
- /**
- * Queue for outbound packets (ip_packet_t*)
- */
- blocking_queue_t *outbound_queue;
- /**
- * Registered inbound callback
- */
- struct {
- ipsec_inbound_cb_t cb;
- void *data;
- } inbound;
- /**
- * Registered outbound callback
- */
- struct {
- ipsec_outbound_cb_t cb;
- void *data;
- } outbound;
- /**
- * Lock used to synchronize access to the callbacks
- */
- rwlock_t *lock;
- };
- /**
- * Deliver an inbound IP packet to the registered listener
- */
- static void deliver_inbound(private_ipsec_processor_t *this,
- esp_packet_t *packet)
- {
- this->lock->read_lock(this->lock);
- if (this->inbound.cb)
- {
- this->inbound.cb(this->inbound.data, packet->extract_payload(packet));
- }
- else
- {
- DBG2(DBG_ESP, "no inbound callback registered, dropping packet");
- }
- packet->destroy(packet);
- this->lock->unlock(this->lock);
- }
- /**
- * Processes inbound packets
- */
- static job_requeue_t process_inbound(private_ipsec_processor_t *this)
- {
- esp_packet_t *packet;
- ip_packet_t *ip_packet;
- ipsec_sa_t *sa;
- uint8_t next_header;
- uint32_t spi, reqid;
- packet = (esp_packet_t*)this->inbound_queue->dequeue(this->inbound_queue);
- if (!packet->parse_header(packet, &spi))
- {
- packet->destroy(packet);
- return JOB_REQUEUE_DIRECT;
- }
- sa = ipsec->sas->checkout_by_spi(ipsec->sas, spi,
- packet->get_destination(packet));
- if (!sa)
- {
- DBG2(DBG_ESP, "inbound ESP packet does not belong to an installed SA");
- packet->destroy(packet);
- return JOB_REQUEUE_DIRECT;
- }
- if (!sa->is_inbound(sa))
- {
- DBG1(DBG_ESP, "error: IPsec SA is not inbound");
- packet->destroy(packet);
- ipsec->sas->checkin(ipsec->sas, sa);
- return JOB_REQUEUE_DIRECT;
- }
- if (packet->decrypt(packet, sa->get_esp_context(sa)) != SUCCESS)
- {
- ipsec->sas->checkin(ipsec->sas, sa);
- packet->destroy(packet);
- return JOB_REQUEUE_DIRECT;
- }
- ip_packet = packet->get_payload(packet);
- sa->update_usestats(sa, ip_packet->get_encoding(ip_packet).len);
- reqid = sa->get_reqid(sa);
- ipsec->sas->checkin(ipsec->sas, sa);
- next_header = packet->get_next_header(packet);
- switch (next_header)
- {
- case IPPROTO_IPIP:
- case IPPROTO_IPV6:
- {
- ipsec_policy_t *policy;
- policy = ipsec->policies->find_by_packet(ipsec->policies,
- ip_packet, TRUE, reqid);
- if (policy)
- {
- deliver_inbound(this, packet);
- policy->destroy(policy);
- break;
- }
- DBG1(DBG_ESP, "discarding inbound IP packet %#H == %#H [%hhu] due "
- "to policy", ip_packet->get_source(ip_packet),
- ip_packet->get_destination(ip_packet),
- ip_packet->get_next_header(ip_packet));
- /* no matching policy found, fall-through */
- }
- case IPPROTO_NONE:
- /* discard dummy packets */
- /* fall-through */
- default:
- packet->destroy(packet);
- break;
- }
- return JOB_REQUEUE_DIRECT;
- }
- /**
- * Send an ESP packet using the registered outbound callback
- */
- static void send_outbound(private_ipsec_processor_t *this,
- esp_packet_t *packet)
- {
- this->lock->read_lock(this->lock);
- if (this->outbound.cb)
- {
- this->outbound.cb(this->outbound.data, packet);
- }
- else
- {
- DBG2(DBG_ESP, "no outbound callback registered, dropping packet");
- packet->destroy(packet);
- }
- this->lock->unlock(this->lock);
- }
- /**
- * Processes outbound packets
- */
- static job_requeue_t process_outbound(private_ipsec_processor_t *this)
- {
- ipsec_policy_t *policy;
- esp_packet_t *esp_packet;
- ip_packet_t *packet;
- ipsec_sa_t *sa;
- host_t *src, *dst;
- packet = (ip_packet_t*)this->outbound_queue->dequeue(this->outbound_queue);
- policy = ipsec->policies->find_by_packet(ipsec->policies, packet, FALSE, 0);
- if (!policy)
- {
- DBG2(DBG_ESP, "no matching outbound IPsec policy for %#H == %#H [%hhu]",
- packet->get_source(packet), packet->get_destination(packet),
- packet->get_next_header(packet));
- packet->destroy(packet);
- return JOB_REQUEUE_DIRECT;
- }
- sa = ipsec->sas->checkout_by_reqid(ipsec->sas, policy->get_reqid(policy),
- FALSE);
- if (!sa)
- { /* TODO-IPSEC: send an acquire to uppper layer */
- DBG1(DBG_ESP, "could not find an outbound IPsec SA for reqid {%u}, "
- "dropping packet", policy->get_reqid(policy));
- packet->destroy(packet);
- policy->destroy(policy);
- return JOB_REQUEUE_DIRECT;
- }
- src = sa->get_source(sa);
- dst = sa->get_destination(sa);
- esp_packet = esp_packet_create_from_payload(src->clone(src),
- dst->clone(dst), packet);
- if (esp_packet->encrypt(esp_packet, sa->get_esp_context(sa),
- sa->get_spi(sa)) != SUCCESS)
- {
- ipsec->sas->checkin(ipsec->sas, sa);
- esp_packet->destroy(esp_packet);
- policy->destroy(policy);
- return JOB_REQUEUE_DIRECT;
- }
- sa->update_usestats(sa, packet->get_encoding(packet).len);
- ipsec->sas->checkin(ipsec->sas, sa);
- policy->destroy(policy);
- send_outbound(this, esp_packet);
- return JOB_REQUEUE_DIRECT;
- }
- METHOD(ipsec_processor_t, queue_inbound, void,
- private_ipsec_processor_t *this, esp_packet_t *packet)
- {
- this->inbound_queue->enqueue(this->inbound_queue, packet);
- }
- METHOD(ipsec_processor_t, queue_outbound, void,
- private_ipsec_processor_t *this, ip_packet_t *packet)
- {
- this->outbound_queue->enqueue(this->outbound_queue, packet);
- }
- METHOD(ipsec_processor_t, register_inbound, void,
- private_ipsec_processor_t *this, ipsec_inbound_cb_t cb, void *data)
- {
- this->lock->write_lock(this->lock);
- this->inbound.cb = cb;
- this->inbound.data = data;
- this->lock->unlock(this->lock);
- }
- METHOD(ipsec_processor_t, unregister_inbound, void,
- private_ipsec_processor_t *this, ipsec_inbound_cb_t cb)
- {
- this->lock->write_lock(this->lock);
- if (this->inbound.cb == cb)
- {
- this->inbound.cb = NULL;
- }
- this->lock->unlock(this->lock);
- }
- METHOD(ipsec_processor_t, register_outbound, void,
- private_ipsec_processor_t *this, ipsec_outbound_cb_t cb, void *data)
- {
- this->lock->write_lock(this->lock);
- this->outbound.cb = cb;
- this->outbound.data = data;
- this->lock->unlock(this->lock);
- }
- METHOD(ipsec_processor_t, unregister_outbound, void,
- private_ipsec_processor_t *this, ipsec_outbound_cb_t cb)
- {
- this->lock->write_lock(this->lock);
- if (this->outbound.cb == cb)
- {
- this->outbound.cb = NULL;
- }
- this->lock->unlock(this->lock);
- }
- METHOD(ipsec_processor_t, destroy, void,
- private_ipsec_processor_t *this)
- {
- this->inbound_queue->destroy_offset(this->inbound_queue,
- offsetof(esp_packet_t, destroy));
- this->outbound_queue->destroy_offset(this->outbound_queue,
- offsetof(ip_packet_t, destroy));
- this->lock->destroy(this->lock);
- free(this);
- }
- /**
- * Described in header.
- */
- ipsec_processor_t *ipsec_processor_create()
- {
- private_ipsec_processor_t *this;
- INIT(this,
- .public = {
- .queue_inbound = _queue_inbound,
- .queue_outbound = _queue_outbound,
- .register_inbound = _register_inbound,
- .unregister_inbound = _unregister_inbound,
- .register_outbound = _register_outbound,
- .unregister_outbound = _unregister_outbound,
- .destroy = _destroy,
- },
- .inbound_queue = blocking_queue_create(),
- .outbound_queue = blocking_queue_create(),
- .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
- );
- lib->processor->queue_job(lib->processor,
- (job_t*)callback_job_create((callback_job_cb_t)process_inbound, this,
- NULL, (callback_job_cancel_t)return_false));
- lib->processor->queue_job(lib->processor,
- (job_t*)callback_job_create((callback_job_cb_t)process_outbound, this,
- NULL, (callback_job_cancel_t)return_false));
- return &this->public;
- }
|