e904b44e99d8fdd2b625cbe166c4edddd02c9a95
[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 on DPD
84 */
85 action_t dpd_action;
86
87 /**
88 * action to take on CHILD_SA close
89 */
90 action_t close_action;
91
92 /**
93 * CHILD_SA lifetime config
94 */
95 lifetime_cfg_t lifetime;
96
97 /**
98 * enable IPComp
99 */
100 bool use_ipcomp;
101
102 /**
103 * Inactivity timeout
104 */
105 u_int32_t inactivity;
106
107 /**
108 * Reqid to install CHILD_SA with
109 */
110 u_int32_t reqid;
111
112 /**
113 * Optional mark to install inbound CHILD_SA with
114 */
115 mark_t mark_in;
116
117 /**
118 * Optional mark to install outbound CHILD_SA with
119 */
120 mark_t mark_out;
121 /**
122 * set up IPsec transport SA in MIPv6 proxy mode
123 */
124 bool proxy_mode;
125
126 /**
127 * enable installation and removal of kernel IPsec policies
128 */
129 bool install_policy;
130 };
131
132 METHOD(child_cfg_t, get_name, char*,
133 private_child_cfg_t *this)
134 {
135 return this->name;
136 }
137
138 METHOD(child_cfg_t, add_proposal, void,
139 private_child_cfg_t *this, proposal_t *proposal)
140 {
141 this->proposals->insert_last(this->proposals, proposal);
142 }
143
144 METHOD(child_cfg_t, get_proposals, linked_list_t*,
145 private_child_cfg_t *this, bool strip_dh)
146 {
147 enumerator_t *enumerator;
148 proposal_t *current;
149 linked_list_t *proposals = linked_list_create();
150
151 enumerator = this->proposals->create_enumerator(this->proposals);
152 while (enumerator->enumerate(enumerator, &current))
153 {
154 current = current->clone(current);
155 if (strip_dh)
156 {
157 current->strip_dh(current);
158 }
159 proposals->insert_last(proposals, current);
160 }
161 enumerator->destroy(enumerator);
162
163 return proposals;
164 }
165
166 METHOD(child_cfg_t, select_proposal, proposal_t*,
167 private_child_cfg_t*this, linked_list_t *proposals, bool strip_dh,
168 bool private)
169 {
170 enumerator_t *stored_enum, *supplied_enum;
171 proposal_t *stored, *supplied, *selected = NULL;
172
173 stored_enum = this->proposals->create_enumerator(this->proposals);
174 supplied_enum = proposals->create_enumerator(proposals);
175
176 /* compare all stored proposals with all supplied. Stored ones are preferred. */
177 while (stored_enum->enumerate(stored_enum, &stored))
178 {
179 stored = stored->clone(stored);
180 while (supplied_enum->enumerate(supplied_enum, &supplied))
181 {
182 if (strip_dh)
183 {
184 stored->strip_dh(stored);
185 }
186 selected = stored->select(stored, supplied, private);
187 if (selected)
188 {
189 DBG2(DBG_CFG, "received proposals: %#P", proposals);
190 DBG2(DBG_CFG, "configured proposals: %#P", this->proposals);
191 DBG2(DBG_CFG, "selected proposal: %P", selected);
192 break;
193 }
194 }
195 stored->destroy(stored);
196 if (selected)
197 {
198 break;
199 }
200 supplied_enum->destroy(supplied_enum);
201 supplied_enum = proposals->create_enumerator(proposals);
202 }
203 stored_enum->destroy(stored_enum);
204 supplied_enum->destroy(supplied_enum);
205 if (selected == NULL)
206 {
207 DBG1(DBG_CFG, "received proposals: %#P", proposals);
208 DBG1(DBG_CFG, "configured proposals: %#P", this->proposals);
209 }
210 return selected;
211 }
212
213 METHOD(child_cfg_t, add_traffic_selector, void,
214 private_child_cfg_t *this, bool local, traffic_selector_t *ts)
215 {
216 if (local)
217 {
218 this->my_ts->insert_last(this->my_ts, ts);
219 }
220 else
221 {
222 this->other_ts->insert_last(this->other_ts, ts);
223 }
224 }
225
226 METHOD(child_cfg_t, get_traffic_selectors, linked_list_t*,
227 private_child_cfg_t *this, bool local, linked_list_t *supplied, host_t *host)
228 {
229 enumerator_t *e1, *e2;
230 traffic_selector_t *ts1, *ts2, *selected;
231 linked_list_t *result = linked_list_create();
232
233 if (local)
234 {
235 e1 = this->my_ts->create_enumerator(this->my_ts);
236 }
237 else
238 {
239 e1 = this->other_ts->create_enumerator(this->other_ts);
240 }
241
242 /* no list supplied, just fetch the stored traffic selectors */
243 if (supplied == NULL)
244 {
245 DBG2(DBG_CFG, "proposing traffic selectors for %s:",
246 local ? "us" : "other");
247 while (e1->enumerate(e1, &ts1))
248 {
249 /* we make a copy of the TS, this allows us to update dynamic TS' */
250 selected = ts1->clone(ts1);
251 if (host)
252 {
253 selected->set_address(selected, host);
254 }
255 DBG2(DBG_CFG, " %R (derived from %R)", selected, ts1);
256 result->insert_last(result, selected);
257 }
258 e1->destroy(e1);
259 }
260 else
261 {
262 DBG2(DBG_CFG, "selecting traffic selectors for %s:",
263 local ? "us" : "other");
264 e2 = supplied->create_enumerator(supplied);
265 /* iterate over all stored selectors */
266 while (e1->enumerate(e1, &ts1))
267 {
268 /* we make a copy of the TS, as we have to update dynamic TS' */
269 ts1 = ts1->clone(ts1);
270 if (host)
271 {
272 ts1->set_address(ts1, host);
273 }
274
275 /* iterate over all supplied traffic selectors */
276 while (e2->enumerate(e2, &ts2))
277 {
278 selected = ts1->get_subset(ts1, ts2);
279 if (selected)
280 {
281 DBG2(DBG_CFG, " config: %R, received: %R => match: %R",
282 ts1, ts2, selected);
283 result->insert_last(result, selected);
284 }
285 else
286 {
287 DBG2(DBG_CFG, " config: %R, received: %R => no match",
288 ts1, ts2);
289 }
290 }
291 e2->destroy(e2);
292 e2 = supplied->create_enumerator(supplied);
293 ts1->destroy(ts1);
294 }
295 e1->destroy(e1);
296 e2->destroy(e2);
297 }
298
299 /* remove any redundant traffic selectors in the list */
300 e1 = result->create_enumerator(result);
301 e2 = result->create_enumerator(result);
302 while (e1->enumerate(e1, &ts1))
303 {
304 while (e2->enumerate(e2, &ts2))
305 {
306 if (ts1 != ts2)
307 {
308 if (ts2->is_contained_in(ts2, ts1))
309 {
310 result->remove_at(result, e2);
311 ts2->destroy(ts2);
312 e1->destroy(e1);
313 e1 = result->create_enumerator(result);
314 break;
315 }
316 if (ts1->is_contained_in(ts1, ts2))
317 {
318 result->remove_at(result, e1);
319 ts1->destroy(ts1);
320 e2->destroy(e2);
321 e2 = result->create_enumerator(result);
322 break;
323 }
324 }
325 }
326 }
327 e1->destroy(e1);
328 e2->destroy(e2);
329
330 return result;
331 }
332
333 METHOD(child_cfg_t, get_updown, char*,
334 private_child_cfg_t *this)
335 {
336 return this->updown;
337 }
338
339 METHOD(child_cfg_t, get_hostaccess, bool,
340 private_child_cfg_t *this)
341 {
342 return this->hostaccess;
343 }
344
345 /**
346 * Applies jitter to the rekey value. Returns the new rekey value.
347 * Note: The distribution of random values is not perfect, but it
348 * should get the job done.
349 */
350 static u_int64_t apply_jitter(u_int64_t rekey, u_int64_t jitter)
351 {
352 if (jitter == 0)
353 {
354 return rekey;
355 }
356 jitter = (jitter == UINT64_MAX) ? jitter : jitter + 1;
357 return rekey - jitter * (random() / (RAND_MAX + 1.0));
358 }
359 #define APPLY_JITTER(l) l.rekey = apply_jitter(l.rekey, l.jitter)
360
361 METHOD(child_cfg_t, get_lifetime, lifetime_cfg_t*,
362 private_child_cfg_t *this)
363 {
364 lifetime_cfg_t *lft = malloc_thing(lifetime_cfg_t);
365 memcpy(lft, &this->lifetime, sizeof(lifetime_cfg_t));
366 APPLY_JITTER(lft->time);
367 APPLY_JITTER(lft->bytes);
368 APPLY_JITTER(lft->packets);
369 return lft;
370 }
371
372 METHOD(child_cfg_t, get_mode, ipsec_mode_t,
373 private_child_cfg_t *this)
374 {
375 return this->mode;
376 }
377
378 METHOD(child_cfg_t, get_dpd_action, action_t,
379 private_child_cfg_t *this)
380 {
381 return this->dpd_action;
382 }
383
384 METHOD(child_cfg_t, get_close_action, action_t,
385 private_child_cfg_t *this)
386 {
387 return this->close_action;
388 }
389
390 METHOD(child_cfg_t, get_dh_group, diffie_hellman_group_t,
391 private_child_cfg_t *this)
392 {
393 enumerator_t *enumerator;
394 proposal_t *proposal;
395 u_int16_t dh_group = MODP_NONE;
396
397 enumerator = this->proposals->create_enumerator(this->proposals);
398 while (enumerator->enumerate(enumerator, &proposal))
399 {
400 if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, &dh_group, NULL))
401 {
402 break;
403 }
404 }
405 enumerator->destroy(enumerator);
406 return dh_group;
407 }
408
409 METHOD(child_cfg_t, use_ipcomp, bool,
410 private_child_cfg_t *this)
411 {
412 return this->use_ipcomp;
413 }
414
415 METHOD(child_cfg_t, get_inactivity, u_int32_t,
416 private_child_cfg_t *this)
417 {
418 return this->inactivity;
419 }
420
421 METHOD(child_cfg_t, get_reqid, u_int32_t,
422 private_child_cfg_t *this)
423 {
424 return this->reqid;
425 }
426
427 METHOD(child_cfg_t, get_mark, mark_t,
428 private_child_cfg_t *this, bool inbound)
429 {
430 return inbound ? this->mark_in : this->mark_out;
431 }
432
433 METHOD(child_cfg_t, set_mipv6_options, void,
434 private_child_cfg_t *this, bool proxy_mode, bool install_policy)
435 {
436 this->proxy_mode = proxy_mode;
437 this->install_policy = install_policy;
438 }
439
440 METHOD(child_cfg_t, use_proxy_mode, bool,
441 private_child_cfg_t *this)
442 {
443 return this->proxy_mode;
444 }
445
446 METHOD(child_cfg_t, install_policy, bool,
447 private_child_cfg_t *this)
448 {
449 return this->install_policy;
450 }
451
452 METHOD(child_cfg_t, get_ref, child_cfg_t*,
453 private_child_cfg_t *this)
454 {
455 ref_get(&this->refcount);
456 return &this->public;
457 }
458
459 METHOD(child_cfg_t, destroy, void,
460 private_child_cfg_t *this)
461 {
462 if (ref_put(&this->refcount))
463 {
464 this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
465 this->my_ts->destroy_offset(this->my_ts, offsetof(traffic_selector_t, destroy));
466 this->other_ts->destroy_offset(this->other_ts, offsetof(traffic_selector_t, destroy));
467 if (this->updown)
468 {
469 free(this->updown);
470 }
471 free(this->name);
472 free(this);
473 }
474 }
475
476 /*
477 * Described in header-file
478 */
479 child_cfg_t *child_cfg_create(char *name, lifetime_cfg_t *lifetime,
480 char *updown, bool hostaccess,
481 ipsec_mode_t mode, action_t dpd_action,
482 action_t close_action, bool ipcomp,
483 u_int32_t inactivity, u_int32_t reqid,
484 mark_t *mark_in, mark_t *mark_out)
485 {
486 private_child_cfg_t *this;
487
488 INIT(this,
489 .public = {
490 .get_name = _get_name,
491 .add_traffic_selector = _add_traffic_selector,
492 .get_traffic_selectors = _get_traffic_selectors,
493 .add_proposal = _add_proposal,
494 .get_proposals = _get_proposals,
495 .select_proposal = _select_proposal,
496 .get_updown = _get_updown,
497 .get_hostaccess = _get_hostaccess,
498 .get_mode = _get_mode,
499 .get_dpd_action = _get_dpd_action,
500 .get_close_action = _get_close_action,
501 .get_lifetime = _get_lifetime,
502 .get_dh_group = _get_dh_group,
503 .set_mipv6_options = _set_mipv6_options,
504 .use_ipcomp = _use_ipcomp,
505 .get_inactivity = _get_inactivity,
506 .get_reqid = _get_reqid,
507 .get_mark = _get_mark,
508 .use_proxy_mode = _use_proxy_mode,
509 .install_policy = _install_policy,
510 .get_ref = _get_ref,
511 .destroy = _destroy,
512 },
513 .name = strdup(name),
514 .updown = updown ? strdup(updown) : NULL,
515 .hostaccess = hostaccess,
516 .mode = mode,
517 .start_action = start_action,
518 .dpd_action = dpd_action,
519 .close_action = close_action,
520 .use_ipcomp = ipcomp,
521 .inactivity = inactivity,
522 .reqid = reqid,
523 .proxy_mode = FALSE,
524 .install_policy = TRUE,
525 .refcount = 1,
526 .proposals = linked_list_create(),
527 .my_ts = linked_list_create(),
528 .other_ts = linked_list_create(),
529 );
530
531 if (mark_in)
532 {
533 this->mark_in = *mark_in;
534 }
535 if (mark_out)
536 {
537 this->mark_out = *mark_out;
538 }
539 memcpy(&this->lifetime, lifetime, sizeof(lifetime_cfg_t));
540
541 return &this->public;
542 }
543