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>