ipsec: Increased log level for message in case no outbound policy is found
[strongswan.git] / src / libipsec / ipsec_processor.c
1 /*
2 * Copyright (C) 2012 Tobias Brunner
3 * 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 ipsec_sa_t *sa;
95 u_int8_t next_header;
96 u_int32_t spi;
97
98 packet = (esp_packet_t*)this->inbound_queue->dequeue(this->inbound_queue);
99
100 if (!packet->parse_header(packet, &spi))
101 {
102 packet->destroy(packet);
103 return JOB_REQUEUE_DIRECT;
104 }
105
106 sa = ipsec->sas->checkout_by_spi(ipsec->sas, spi,
107 packet->get_destination(packet));
108 if (!sa)
109 {
110 DBG2(DBG_ESP, "inbound ESP packet does not belong to an installed SA");
111 packet->destroy(packet);
112 return JOB_REQUEUE_DIRECT;
113 }
114
115 if (!sa->is_inbound(sa))
116 {
117 DBG1(DBG_ESP, "error: IPsec SA is not inbound");
118 packet->destroy(packet);
119 ipsec->sas->checkin(ipsec->sas, sa);
120 return JOB_REQUEUE_DIRECT;
121 }
122
123 if (packet->decrypt(packet, sa->get_esp_context(sa)) != SUCCESS)
124 {
125 ipsec->sas->checkin(ipsec->sas, sa);
126 packet->destroy(packet);
127 return JOB_REQUEUE_DIRECT;
128 }
129 ipsec->sas->checkin(ipsec->sas, sa);
130
131 next_header = packet->get_next_header(packet);
132 switch (next_header)
133 {
134 case IPPROTO_IPIP:
135 case IPPROTO_IPV6:
136 {
137 ipsec_policy_t *policy;
138 ip_packet_t *ip_packet;
139
140 ip_packet = packet->get_payload(packet);
141 policy = ipsec->policies->find_by_packet(ipsec->policies,
142 ip_packet, TRUE);
143 if (policy)
144 { /* TODO-IPSEC: update policy/sa stats? */
145 deliver_inbound(this, packet);
146 policy->destroy(policy);
147 break;
148 }
149 DBG1(DBG_ESP, "discarding inbound IP packet %H == %H due to "
150 "policy", ip_packet->get_source(ip_packet),
151 ip_packet->get_destination(ip_packet));
152 /* no matching policy found, fall-through */
153 }
154 case IPPROTO_NONE:
155 /* discard dummy packets */
156 /* fall-through */
157 default:
158 packet->destroy(packet);
159 break;
160 }
161 return JOB_REQUEUE_DIRECT;
162 }
163
164 /**
165 * Send an ESP packet using the registered outbound callback
166 */
167 static void send_outbound(private_ipsec_processor_t *this,
168 esp_packet_t *packet)
169 {
170 this->lock->read_lock(this->lock);
171 if (this->outbound.cb)
172 {
173 this->outbound.cb(this->outbound.data, packet);
174 }
175 else
176 {
177 DBG2(DBG_ESP, "no outbound callback registered, dropping packet");
178 packet->destroy(packet);
179 }
180 this->lock->unlock(this->lock);
181 }
182
183 /**
184 * Processes outbound packets
185 */
186 static job_requeue_t process_outbound(private_ipsec_processor_t *this)
187 {
188 ipsec_policy_t *policy;
189 esp_packet_t *esp_packet;
190 ip_packet_t *packet;
191 ipsec_sa_t *sa;
192 host_t *src, *dst;
193
194 packet = (ip_packet_t*)this->outbound_queue->dequeue(this->outbound_queue);
195
196 policy = ipsec->policies->find_by_packet(ipsec->policies, packet, FALSE);
197 if (!policy)
198 {
199 DBG2(DBG_ESP, "no matching outbound IPsec policy for %H == %H",
200 packet->get_source(packet), packet->get_destination(packet));
201 packet->destroy(packet);
202 return JOB_REQUEUE_DIRECT;
203 }
204
205 sa = ipsec->sas->checkout_by_reqid(ipsec->sas, policy->get_reqid(policy),
206 FALSE);
207 if (!sa)
208 { /* TODO-IPSEC: send an acquire to uppper layer */
209 DBG1(DBG_ESP, "could not find an outbound IPsec SA for reqid {%u}, "
210 "dropping packet", policy->get_reqid(policy));
211 packet->destroy(packet);
212 policy->destroy(policy);
213 return JOB_REQUEUE_DIRECT;
214 }
215 src = sa->get_source(sa);
216 dst = sa->get_destination(sa);
217 esp_packet = esp_packet_create_from_payload(src->clone(src),
218 dst->clone(dst), packet);
219 if (esp_packet->encrypt(esp_packet, sa->get_esp_context(sa),
220 sa->get_spi(sa)) != SUCCESS)
221 {
222 ipsec->sas->checkin(ipsec->sas, sa);
223 esp_packet->destroy(esp_packet);
224 policy->destroy(policy);
225 return JOB_REQUEUE_DIRECT;
226 }
227 /* TODO-IPSEC: update policy/sa counters? */
228 ipsec->sas->checkin(ipsec->sas, sa);
229 policy->destroy(policy);
230 send_outbound(this, esp_packet);
231 return JOB_REQUEUE_DIRECT;
232 }
233
234 METHOD(ipsec_processor_t, queue_inbound, void,
235 private_ipsec_processor_t *this, esp_packet_t *packet)
236 {
237 this->inbound_queue->enqueue(this->inbound_queue, packet);
238 }
239
240 METHOD(ipsec_processor_t, queue_outbound, void,
241 private_ipsec_processor_t *this, ip_packet_t *packet)
242 {
243 this->outbound_queue->enqueue(this->outbound_queue, packet);
244 }
245
246 METHOD(ipsec_processor_t, register_inbound, void,
247 private_ipsec_processor_t *this, ipsec_inbound_cb_t cb, void *data)
248 {
249 this->lock->write_lock(this->lock);
250 this->inbound.cb = cb;
251 this->inbound.data = data;
252 this->lock->unlock(this->lock);
253 }
254
255 METHOD(ipsec_processor_t, unregister_inbound, void,
256 private_ipsec_processor_t *this, ipsec_inbound_cb_t cb)
257 {
258 this->lock->write_lock(this->lock);
259 if (this->inbound.cb == cb)
260 {
261 this->inbound.cb = NULL;
262 }
263 this->lock->unlock(this->lock);
264 }
265
266 METHOD(ipsec_processor_t, register_outbound, void,
267 private_ipsec_processor_t *this, ipsec_outbound_cb_t cb, void *data)
268 {
269 this->lock->write_lock(this->lock);
270 this->outbound.cb = cb;
271 this->outbound.data = data;
272 this->lock->unlock(this->lock);
273 }
274
275 METHOD(ipsec_processor_t, unregister_outbound, void,
276 private_ipsec_processor_t *this, ipsec_outbound_cb_t cb)
277 {
278 this->lock->write_lock(this->lock);
279 if (this->outbound.cb == cb)
280 {
281 this->outbound.cb = NULL;
282 }
283 this->lock->unlock(this->lock);
284 }
285
286 METHOD(ipsec_processor_t, destroy, void,
287 private_ipsec_processor_t *this)
288 {
289 this->inbound_queue->destroy_offset(this->inbound_queue,
290 offsetof(esp_packet_t, destroy));
291 this->outbound_queue->destroy_offset(this->outbound_queue,
292 offsetof(ip_packet_t, destroy));
293 this->lock->destroy(this->lock);
294 free(this);
295 }
296
297 /**
298 * Described in header.
299 */
300 ipsec_processor_t *ipsec_processor_create()
301 {
302 private_ipsec_processor_t *this;
303
304 INIT(this,
305 .public = {
306 .queue_inbound = _queue_inbound,
307 .queue_outbound = _queue_outbound,
308 .register_inbound = _register_inbound,
309 .unregister_inbound = _unregister_inbound,
310 .register_outbound = _register_outbound,
311 .unregister_outbound = _unregister_outbound,
312 .destroy = _destroy,
313 },
314 .inbound_queue = blocking_queue_create(),
315 .outbound_queue = blocking_queue_create(),
316 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
317 );
318
319 lib->processor->queue_job(lib->processor,
320 (job_t*)callback_job_create((callback_job_cb_t)process_inbound, this,
321 NULL, (callback_job_cancel_t)return_false));
322 lib->processor->queue_job(lib->processor,
323 (job_t*)callback_job_create((callback_job_cb_t)process_outbound, this,
324 NULL, (callback_job_cancel_t)return_false));
325 return &this->public;
326 }