start and route connections defined in an SQL database via start_action field and...
[strongswan.git] / src / libcharon / config / child_cfg.c
1 /*
2 * Copyright (C) 2008-2009 Tobias Brunner
3 * Copyright (C) 2005-2007 Martin Willi
4 * Copyright (C) 2005 Jan Hutter
5 * Hochschule fuer Technik Rapperswil
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 #include "child_cfg.h"
19
20 #include <stdint.h>
21
22 #include <daemon.h>
23
24 ENUM(action_names, ACTION_NONE, ACTION_RESTART,
25 "clear",
26 "hold",
27 "restart",
28 );
29
30 typedef struct private_child_cfg_t private_child_cfg_t;
31
32 /**
33 * Private data of an child_cfg_t object
34 */
35 struct private_child_cfg_t {
36
37 /**
38 * Public part
39 */
40 child_cfg_t public;
41
42 /**
43 * Number of references hold by others to this child_cfg
44 */
45 refcount_t refcount;
46
47 /**
48 * Name of the child_cfg, used to query it
49 */
50 char *name;
51
52 /**
53 * list for all proposals
54 */
55 linked_list_t *proposals;
56
57 /**
58 * list for traffic selectors for my site
59 */
60 linked_list_t *my_ts;
61
62 /**
63 * list for traffic selectors for others site
64 */
65 linked_list_t *other_ts;
66
67 /**
68 * updown script
69 */
70 char *updown;
71
72 /**
73 * allow host access
74 */
75 bool hostaccess;
76
77 /**
78 * Mode to propose for a initiated CHILD: tunnel/transport
79 */
80 ipsec_mode_t mode;
81
82 /**
83 * action to take to start CHILD_SA
84 */
85 action_t start_action;
86
87 /**
88 * action to take on DPD
89 */
90 action_t dpd_action;
91
92 /**
93 * action to take on CHILD_SA close
94 */
95 action_t close_action;
96
97 /**
98 * CHILD_SA lifetime config
99 */
100 lifetime_cfg_t lifetime;
101
102 /**
103 * enable IPComp
104 */
105 bool use_ipcomp;
106
107 /**
108 * Inactivity timeout
109 */
110 u_int32_t inactivity;
111
112 /**
113 * Reqid to install CHILD_SA with
114 */
115 u_int32_t reqid;
116
117 /**
118 * Optional mark to install inbound CHILD_SA with
119 */
120 mark_t mark_in;
121
122 /**
123 * Optional mark to install outbound CHILD_SA with
124 */
125 mark_t mark_out;
126 /**
127 * set up IPsec transport SA in MIPv6 proxy mode
128 */
129 bool proxy_mode;
130
131 /**
132 * enable installation and removal of kernel IPsec policies
133 */
134 bool install_policy;
135 };
136
137 METHOD(child_cfg_t, get_name, char*,
138 private_child_cfg_t *this)
139 {
140 return this->name;
141 }
142
143 METHOD(child_cfg_t, add_proposal, void,
144 private_child_cfg_t *this, proposal_t *proposal)
145 {
146 this->proposals->insert_last(this->proposals, proposal);
147 }
148
149 METHOD(child_cfg_t, get_proposals, linked_list_t*,
150 private_child_cfg_t *this, bool strip_dh)
151 {
152 enumerator_t *enumerator;
153 proposal_t *current;
154 linked_list_t *proposals = linked_list_create();
155
156 enumerator = this->proposals->create_enumerator(this->proposals);
157 while (enumerator->enumerate(enumerator, &current))
158 {
159 current = current->clone(current);
160 if (strip_dh)
161 {
162 current->strip_dh(current);
163 }
164 proposals->insert_last(proposals, current);
165 }
166 enumerator->destroy(enumerator);
167
168 return proposals;
169 }
170
171 METHOD(child_cfg_t, select_proposal, proposal_t*,
172 private_child_cfg_t*this, linked_list_t *proposals, bool strip_dh,
173 bool private)
174 {
175 enumerator_t *stored_enum, *supplied_enum;
176 proposal_t *stored, *supplied, *selected = NULL;
177
178 stored_enum = this->proposals->create_enumerator(this->proposals);
179 supplied_enum = proposals->create_enumerator(proposals);
180
181 /* compare all stored proposals with all supplied. Stored ones are preferred. */
182 while (stored_enum->enumerate(stored_enum, &stored))
183 {
184 stored = stored->clone(stored);
185 while (supplied_enum->enumerate(supplied_enum, &supplied))
186 {
187 if (strip_dh)
188 {
189 stored->strip_dh(stored);
190 }
191 selected = stored->select(stored, supplied, private);
192 if (selected)
193 {
194 DBG2(DBG_CFG, "received proposals: %#P", proposals);
195 DBG2(DBG_CFG, "configured proposals: %#P", this->proposals);
196 DBG2(DBG_CFG, "selected proposal: %P", selected);
197 break;
198 }
199 }
200 stored->destroy(stored);
201 if (selected)
202 {
203 break;
204 }
205 supplied_enum->destroy(supplied_enum);
206 supplied_enum = proposals->create_enumerator(proposals);
207 }
208 stored_enum->destroy(stored_enum);
209 supplied_enum->destroy(supplied_enum);
210 if (selected == NULL)
211 {
212 DBG1(DBG_CFG, "received proposals: %#P", proposals);
213 DBG1(DBG_CFG, "configured proposals: %#P", this->proposals);
214 }
215 return selected;
216 }
217
218 METHOD(child_cfg_t, add_traffic_selector, void,
219 private_child_cfg_t *this, bool local, traffic_selector_t *ts)
220 {
221 if (local)
222 {
223 this->my_ts->insert_last(this->my_ts, ts);
224 }
225 else
226 {
227 this->other_ts->insert_last(this->other_ts, ts);
228 }
229 }
230
231 METHOD(child_cfg_t, get_traffic_selectors, linked_list_t*,
232 private_child_cfg_t *this, bool local, linked_list_t *supplied, host_t *host)
233 {
234 enumerator_t *e1, *e2;
235 traffic_selector_t *ts1, *ts2, *selected;
236 linked_list_t *result = linked_list_create();
237
238 if (local)
239 {
240 e1 = this->my_ts->create_enumerator(this->my_ts);
241 }
242 else
243 {
244 e1 = this->other_ts->create_enumerator(this->other_ts);
245 }
246
247 /* no list supplied, just fetch the stored traffic selectors */
248 if (supplied == NULL)
249 {
250 DBG2(DBG_CFG, "proposing traffic selectors for %s:",
251 local ? "us" : "other");
252 while (e1->enumerate(e1, &ts1))
253 {
254 /* we make a copy of the TS, this allows us to update dynamic TS' */
255 selected = ts1->clone(ts1);
256 if (host)
257 {
258 selected->set_address(selected, host);
259 }
260 DBG2(DBG_CFG, " %R (derived from %R)", selected, ts1);
261 result->insert_last(result, selected);
262 }
263 e1->destroy(e1);
264 }
265 else
266 {
267 DBG2(DBG_CFG, "selecting traffic selectors for %s:",
268 local ? "us" : "other");
269 e2 = supplied->create_enumerator(supplied);
270 /* iterate over all stored selectors */
271 while (e1->enumerate(e1, &ts1))
272 {
273 /* we make a copy of the TS, as we have to update dynamic TS' */
274 ts1 = ts1->clone(ts1);
275 if (host)
276 {
277 ts1->set_address(ts1, host);
278 }
279
280 /* iterate over all supplied traffic selectors */
281 while (e2->enumerate(e2, &ts2))
282 {
283 selected = ts1->get_subset(ts1, ts2);
284 if (selected)
285 {
286 DBG2(DBG_CFG, " config: %R, received: %R => match: %R",
287 ts1, ts2, selected);
288 result->insert_last(result, selected);
289 }
290 else
291 {
292 DBG2(DBG_CFG, " config: %R, received: %R => no match",
293 ts1, ts2);
294 }
295 }
296 e2->destroy(e2);
297 e2 = supplied->create_enumerator(supplied);
298 ts1->destroy(ts1);
299 }
300 e1->destroy(e1);
301 e2->destroy(e2);
302 }
303
304 /* remove any redundant traffic selectors in the list */
305 e1 = result->create_enumerator(result);
306 e2 = result->create_enumerator(result);
307 while (e1->enumerate(e1, &ts1))
308 {
309 while (e2->enumerate(e2, &ts2))
310 {
311 if (ts1 != ts2)
312 {
313 if (ts2->is_contained_in(ts2, ts1))
314 {
315 result->remove_at(result, e2);
316 ts2->destroy(ts2);
317 e1->destroy(e1);
318 e1 = result->create_enumerator(result);
319 break;
320 }
321 if (ts1->is_contained_in(ts1, ts2))
322 {
323 result->remove_at(result, e1);
324 ts1->destroy(ts1);
325 e2->destroy(e2);
326 e2 = result->create_enumerator(result);
327 break;
328 }
329 }
330 }
331 }
332 e1->destroy(e1);
333 e2->destroy(e2);
334
335 return result;
336 }
337
338 METHOD(child_cfg_t, get_updown, char*,
339 private_child_cfg_t *this)
340 {
341 return this->updown;
342 }
343
344 METHOD(child_cfg_t, get_hostaccess, bool,
345 private_child_cfg_t *this)
346 {
347 return this->hostaccess;
348 }
349
350 /**
351 * Applies jitter to the rekey value. Returns the new rekey value.
352 * Note: The distribution of random values is not perfect, but it
353 * should get the job done.
354 */
355 static u_int64_t apply_jitter(u_int64_t rekey, u_int64_t jitter)
356 {
357 if (jitter == 0)
358 {
359 return rekey;
360 }
361 jitter = (jitter == UINT64_MAX) ? jitter : jitter + 1;
362 return rekey - jitter * (random() / (RAND_MAX + 1.0));
363 }
364 #define APPLY_JITTER(l) l.rekey = apply_jitter(l.rekey, l.jitter)
365
366 METHOD(child_cfg_t, get_lifetime, lifetime_cfg_t*,
367 private_child_cfg_t *this)
368 {
369 lifetime_cfg_t *lft = malloc_thing(lifetime_cfg_t);
370 memcpy(lft, &this->lifetime, sizeof(lifetime_cfg_t));
371 APPLY_JITTER(lft->time);
372 APPLY_JITTER(lft->bytes);
373 APPLY_JITTER(lft->packets);
374 return lft;
375 }
376
377 METHOD(child_cfg_t, get_mode, ipsec_mode_t,
378 private_child_cfg_t *this)
379 {
380 return this->mode;
381 }
382
383 METHOD(child_cfg_t, get_start_action, action_t,
384 private_child_cfg_t *this)
385 {
386 return this->start_action;
387 }
388
389 METHOD(child_cfg_t, get_dpd_action, action_t,
390 private_child_cfg_t *this)
391 {
392 return this->dpd_action;
393 }
394
395 METHOD(child_cfg_t, get_close_action, action_t,
396 private_child_cfg_t *this)
397 {
398 return this->close_action;
399 }
400
401 METHOD(child_cfg_t, get_dh_group, diffie_hellman_group_t,
402 private_child_cfg_t *this)
403 {
404 enumerator_t *enumerator;
405 proposal_t *proposal;
406 u_int16_t dh_group = MODP_NONE;
407
408 enumerator = this->proposals->create_enumerator(this->proposals);
409 while (enumerator->enumerate(enumerator, &proposal))
410 {
411 if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, &dh_group, NULL))
412 {
413 break;
414 }
415 }
416 enumerator->destroy(enumerator);
417 return dh_group;
418 }
419
420 METHOD(child_cfg_t, use_ipcomp, bool,
421 private_child_cfg_t *this)
422 {
423 return this->use_ipcomp;
424 }
425
426 METHOD(child_cfg_t, get_inactivity, u_int32_t,
427 private_child_cfg_t *this)
428 {
429 return this->inactivity;
430 }
431
432 METHOD(child_cfg_t, get_reqid, u_int32_t,
433 private_child_cfg_t *this)
434 {
435 return this->reqid;
436 }
437
438 METHOD(child_cfg_t, get_mark, mark_t,
439 private_child_cfg_t *this, bool inbound)
440 {
441 return inbound ? this->mark_in : this->mark_out;
442 }
443
444 METHOD(child_cfg_t, set_mipv6_options, void,
445 private_child_cfg_t *this, bool proxy_mode, bool install_policy)
446 {
447 this->proxy_mode = proxy_mode;
448 this->install_policy = install_policy;
449 }
450
451 METHOD(child_cfg_t, use_proxy_mode, bool,
452 private_child_cfg_t *this)
453 {
454 return this->proxy_mode;
455 }
456
457 METHOD(child_cfg_t, install_policy, bool,
458 private_child_cfg_t *this)
459 {
460 return this->install_policy;
461 }
462
463 METHOD(child_cfg_t, get_ref, child_cfg_t*,
464 private_child_cfg_t *this)
465 {
466 ref_get(&this->refcount);
467 return &this->public;
468 }
469
470 METHOD(child_cfg_t, destroy, void,
471 private_child_cfg_t *this)
472 {
473 if (ref_put(&this->refcount))
474 {
475 this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
476 this->my_ts->destroy_offset(this->my_ts, offsetof(traffic_selector_t, destroy));
477 this->other_ts->destroy_offset(this->other_ts, offsetof(traffic_selector_t, destroy));
478 if (this->updown)
479 {
480 free(this->updown);
481 }
482 free(this->name);
483 free(this);
484 }
485 }
486
487 /*
488 * Described in header-file
489 */
490 child_cfg_t *child_cfg_create(char *name, lifetime_cfg_t *lifetime,
491 char *updown, bool hostaccess,
492 ipsec_mode_t mode, action_t start_action,
493 action_t dpd_action, action_t close_action,
494 bool ipcomp, u_int32_t inactivity, u_int32_t reqid,
495 mark_t *mark_in, mark_t *mark_out)
496 {
497 private_child_cfg_t *this;
498
499 INIT(this,
500 .public = {
501 .get_name = _get_name,
502 .add_traffic_selector = _add_traffic_selector,
503 .get_traffic_selectors = _get_traffic_selectors,
504 .add_proposal = _add_proposal,
505 .get_proposals = _get_proposals,
506 .select_proposal = _select_proposal,
507 .get_updown = _get_updown,
508 .get_hostaccess = _get_hostaccess,
509 .get_mode = _get_mode,
510 .get_start_action = _get_start_action,
511 .get_dpd_action = _get_dpd_action,
512 .get_close_action = _get_close_action,
513 .get_lifetime = _get_lifetime,
514 .get_dh_group = _get_dh_group,
515 .set_mipv6_options = _set_mipv6_options,
516 .use_ipcomp = _use_ipcomp,
517 .get_inactivity = _get_inactivity,
518 .get_reqid = _get_reqid,
519 .get_mark = _get_mark,
520 .use_proxy_mode = _use_proxy_mode,
521 .install_policy = _install_policy,
522 .get_ref = _get_ref,
523 .destroy = _destroy,
524 },
525 .name = strdup(name),
526 .updown = updown ? strdup(updown) : NULL,
527 .hostaccess = hostaccess,
528 .mode = mode,
529 .start_action = start_action,
530 .dpd_action = dpd_action,
531 .close_action = close_action,
532 .use_ipcomp = ipcomp,
533 .inactivity = inactivity,
534 .reqid = reqid,
535 .proxy_mode = FALSE,
536 .install_policy = TRUE,
537 .refcount = 1,
538 .proposals = linked_list_create(),
539 .my_ts = linked_list_create(),
540 .other_ts = linked_list_create(),
541 );
542
543 if (mark_in)
544 {
545 this->mark_in = *mark_in;
546 }
547 if (mark_out)
548 {
549 this->mark_out = *mark_out;
550 }
551 memcpy(&this->lifetime, lifetime, sizeof(lifetime_cfg_t));
552
553 return &this->public;
554 }
555