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