Clear virtual IPs before storing assigned ones on the IKE_SA
[strongswan.git] / src / libcharon / sa / ikev1 / tasks / mode_config.c
1 /*
2 * Copyright (C) 2011 Martin Willi
3 * Copyright (C) 2011 revosec AG
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 "mode_config.h"
17
18 #include <daemon.h>
19 #include <hydra.h>
20 #include <encoding/payloads/cp_payload.h>
21
22 typedef struct private_mode_config_t private_mode_config_t;
23
24 /**
25 * Private members of a mode_config_t task.
26 */
27 struct private_mode_config_t {
28
29 /**
30 * Public methods and task_t interface.
31 */
32 mode_config_t public;
33
34 /**
35 * Assigned IKE_SA.
36 */
37 ike_sa_t *ike_sa;
38
39 /**
40 * Are we the initiator?
41 */
42 bool initiator;
43
44 /**
45 * Received list of virtual IPs, host_t*
46 */
47 linked_list_t *vips;
48
49 /**
50 * list of attributes requested and its handler, entry_t
51 */
52 linked_list_t *requested;
53
54 /**
55 * Identifier to include in response
56 */
57 u_int16_t identifier;
58 };
59
60 /**
61 * Entry for a requested attribute and the requesting handler
62 */
63 typedef struct {
64 /** attribute requested */
65 configuration_attribute_type_t type;
66 /** handler requesting this attribute */
67 attribute_handler_t *handler;
68 } entry_t;
69
70 /**
71 * build INTERNAL_IPV4/6_ADDRESS attribute from virtual ip
72 */
73 static configuration_attribute_t *build_vip(host_t *vip)
74 {
75 configuration_attribute_type_t type;
76 chunk_t chunk, prefix;
77
78 if (vip->get_family(vip) == AF_INET)
79 {
80 type = INTERNAL_IP4_ADDRESS;
81 if (vip->is_anyaddr(vip))
82 {
83 chunk = chunk_empty;
84 }
85 else
86 {
87 chunk = vip->get_address(vip);
88 }
89 }
90 else
91 {
92 type = INTERNAL_IP6_ADDRESS;
93 if (vip->is_anyaddr(vip))
94 {
95 chunk = chunk_empty;
96 }
97 else
98 {
99 prefix = chunk_alloca(1);
100 *prefix.ptr = 64;
101 chunk = vip->get_address(vip);
102 chunk = chunk_cata("cc", chunk, prefix);
103 }
104 }
105 return configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1,
106 type, chunk);
107 }
108
109 /**
110 * Handle a received attribute as initiator
111 */
112 static void handle_attribute(private_mode_config_t *this,
113 configuration_attribute_t *ca)
114 {
115 attribute_handler_t *handler = NULL;
116 enumerator_t *enumerator;
117 entry_t *entry;
118
119 /* find the handler which requested this attribute */
120 enumerator = this->requested->create_enumerator(this->requested);
121 while (enumerator->enumerate(enumerator, &entry))
122 {
123 if (entry->type == ca->get_type(ca))
124 {
125 handler = entry->handler;
126 this->requested->remove_at(this->requested, enumerator);
127 free(entry);
128 break;
129 }
130 }
131 enumerator->destroy(enumerator);
132
133 /* and pass it to the handle function */
134 handler = hydra->attributes->handle(hydra->attributes,
135 this->ike_sa->get_other_id(this->ike_sa), handler,
136 ca->get_type(ca), ca->get_chunk(ca));
137 if (handler)
138 {
139 this->ike_sa->add_configuration_attribute(this->ike_sa,
140 handler, ca->get_type(ca), ca->get_chunk(ca));
141 }
142 }
143
144 /**
145 * process a single configuration attribute
146 */
147 static void process_attribute(private_mode_config_t *this,
148 configuration_attribute_t *ca)
149 {
150 host_t *ip;
151 chunk_t addr;
152 int family = AF_INET6;
153
154 switch (ca->get_type(ca))
155 {
156 case INTERNAL_IP4_ADDRESS:
157 family = AF_INET;
158 /* fall */
159 case INTERNAL_IP6_ADDRESS:
160 {
161 addr = ca->get_chunk(ca);
162 if (addr.len == 0)
163 {
164 ip = host_create_any(family);
165 }
166 else
167 {
168 /* skip prefix byte in IPv6 payload*/
169 if (family == AF_INET6)
170 {
171 addr.len--;
172 }
173 ip = host_create_from_chunk(family, addr, 0);
174 }
175 if (ip)
176 {
177 this->vips->insert_last(this->vips, ip);
178 }
179 break;
180 }
181 default:
182 {
183 if (this->initiator)
184 {
185 handle_attribute(this, ca);
186 }
187 }
188 }
189 }
190
191 /**
192 * Scan for configuration payloads and attributes
193 */
194 static void process_payloads(private_mode_config_t *this, message_t *message)
195 {
196 enumerator_t *enumerator, *attributes;
197 payload_t *payload;
198
199 enumerator = message->create_payload_enumerator(message);
200 while (enumerator->enumerate(enumerator, &payload))
201 {
202 if (payload->get_type(payload) == CONFIGURATION_V1)
203 {
204 cp_payload_t *cp = (cp_payload_t*)payload;
205 configuration_attribute_t *ca;
206
207 switch (cp->get_type(cp))
208 {
209 case CFG_REQUEST:
210 this->identifier = cp->get_identifier(cp);
211 /* FALL */
212 case CFG_REPLY:
213 attributes = cp->create_attribute_enumerator(cp);
214 while (attributes->enumerate(attributes, &ca))
215 {
216 DBG2(DBG_IKE, "processing %N attribute",
217 configuration_attribute_type_names, ca->get_type(ca));
218 process_attribute(this, ca);
219 }
220 attributes->destroy(attributes);
221 break;
222 default:
223 DBG1(DBG_IKE, "ignoring %N config payload",
224 config_type_names, cp->get_type(cp));
225 break;
226 }
227 }
228 }
229 enumerator->destroy(enumerator);
230 }
231
232 METHOD(task_t, build_i, status_t,
233 private_mode_config_t *this, message_t *message)
234 {
235 cp_payload_t *cp = NULL;
236 enumerator_t *enumerator;
237 attribute_handler_t *handler;
238 peer_cfg_t *config;
239 configuration_attribute_type_t type;
240 chunk_t data;
241 linked_list_t *vips;
242 host_t *host;
243
244 vips = linked_list_create();
245
246 /* reuse virtual IP if we already have one */
247 enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa, TRUE);
248 while (enumerator->enumerate(enumerator, &host))
249 {
250 vips->insert_last(vips, host);
251 }
252 enumerator->destroy(enumerator);
253
254 if (vips->get_count(vips) == 0)
255 {
256 config = this->ike_sa->get_peer_cfg(this->ike_sa);
257 enumerator = config->create_virtual_ip_enumerator(config);
258 while (enumerator->enumerate(enumerator, &host))
259 {
260 vips->insert_last(vips, host);
261 }
262 enumerator->destroy(enumerator);
263 }
264
265 if (vips->get_count(vips))
266 {
267 cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REQUEST);
268 enumerator = vips->create_enumerator(vips);
269 while (enumerator->enumerate(enumerator, &host))
270 {
271 cp->add_attribute(cp, build_vip(host));
272 }
273 enumerator->destroy(enumerator);
274 }
275
276 enumerator = hydra->attributes->create_initiator_enumerator(
277 hydra->attributes,
278 this->ike_sa->get_other_id(this->ike_sa), vips);
279 while (enumerator->enumerate(enumerator, &handler, &type, &data))
280 {
281 configuration_attribute_t *ca;
282 entry_t *entry;
283
284 DBG2(DBG_IKE, "building %N attribute",
285 configuration_attribute_type_names, type);
286 ca = configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1,
287 type, data);
288 if (!cp)
289 {
290 cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REQUEST);
291 }
292 cp->add_attribute(cp, ca);
293
294 INIT(entry,
295 .type = type,
296 .handler = handler,
297 );
298 this->requested->insert_last(this->requested, entry);
299 }
300 enumerator->destroy(enumerator);
301
302 vips->destroy(vips);
303
304 if (cp)
305 {
306 message->add_payload(message, (payload_t*)cp);
307 }
308 return NEED_MORE;
309 }
310
311 METHOD(task_t, process_r, status_t,
312 private_mode_config_t *this, message_t *message)
313 {
314 process_payloads(this, message);
315 return NEED_MORE;
316 }
317
318 METHOD(task_t, build_r, status_t,
319 private_mode_config_t *this, message_t *message)
320 {
321 enumerator_t *enumerator;
322 configuration_attribute_type_t type;
323 chunk_t value;
324 cp_payload_t *cp = NULL;
325 peer_cfg_t *config;
326 identification_t *id;
327 linked_list_t *vips, *pools;
328 host_t *requested;
329
330 id = this->ike_sa->get_other_eap_id(this->ike_sa);
331 config = this->ike_sa->get_peer_cfg(this->ike_sa);
332 vips = linked_list_create();
333 pools = linked_list_create();
334
335 this->ike_sa->clear_virtual_ips(this->ike_sa, FALSE);
336
337 enumerator = this->vips->create_enumerator(this->vips);
338 while (enumerator->enumerate(enumerator, &requested))
339 {
340 enumerator_t *poolenum;
341 char *pool;
342 host_t *found = NULL;
343
344 /* query all pools until we get an address */
345 DBG1(DBG_IKE, "peer requested virtual IP %H", requested);
346
347 poolenum = config->create_pool_enumerator(config);
348 while (poolenum->enumerate(poolenum, &pool))
349 {
350 found = hydra->attributes->acquire_address(hydra->attributes,
351 pool, id, requested);
352 if (!found)
353 {
354 continue;
355 }
356 DBG1(DBG_IKE, "assigning virtual IP %H to peer '%Y'", found, id);
357 this->ike_sa->add_virtual_ip(this->ike_sa, FALSE, found);
358 if (!cp)
359 {
360 cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REPLY);
361 }
362 cp->add_attribute(cp, build_vip(found));
363 /* use pool to request other attributes */
364 if (pools->find_first(pools, NULL, (void**)&pool) == NOT_FOUND)
365 {
366 pools->insert_last(pools, pool);
367 }
368 vips->insert_last(vips, found);
369 break;
370 }
371 poolenum->destroy(poolenum);
372
373 if (!found)
374 {
375 DBG1(DBG_IKE, "no virtual IP found for %H requested by '%Y'",
376 requested, id);
377 }
378 }
379 enumerator->destroy(enumerator);
380
381 /* query registered providers for additional attributes to include */
382 enumerator = hydra->attributes->create_responder_enumerator(
383 hydra->attributes, pools, id, vips);
384 while (enumerator->enumerate(enumerator, &type, &value))
385 {
386 if (!cp)
387 {
388 cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REPLY);
389 }
390 DBG2(DBG_IKE, "building %N attribute",
391 configuration_attribute_type_names, type);
392 cp->add_attribute(cp,
393 configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1,
394 type, value));
395 }
396 enumerator->destroy(enumerator);
397 vips->destroy_offset(vips, offsetof(host_t, destroy));
398 pools->destroy(pools);
399
400 if (cp)
401 {
402 cp->set_identifier(cp, this->identifier);
403 message->add_payload(message, (payload_t*)cp);
404 }
405 return SUCCESS;
406 }
407
408 METHOD(task_t, process_i, status_t,
409 private_mode_config_t *this, message_t *message)
410 {
411 enumerator_t *enumerator;
412 host_t *host;
413
414 process_payloads(this, message);
415
416 this->ike_sa->clear_virtual_ips(this->ike_sa, TRUE);
417
418 enumerator = this->vips->create_enumerator(this->vips);
419 while (enumerator->enumerate(enumerator, &host))
420 {
421 if (!host->is_anyaddr(host))
422 {
423 this->ike_sa->add_virtual_ip(this->ike_sa, TRUE, host);
424 }
425 }
426 enumerator->destroy(enumerator);
427
428 return SUCCESS;
429 }
430
431 METHOD(task_t, get_type, task_type_t,
432 private_mode_config_t *this)
433 {
434 return TASK_MODE_CONFIG;
435 }
436
437 METHOD(task_t, migrate, void,
438 private_mode_config_t *this, ike_sa_t *ike_sa)
439 {
440 this->ike_sa = ike_sa;
441 this->vips->destroy_offset(this->vips, offsetof(host_t, destroy));
442 this->vips = linked_list_create();
443 this->requested->destroy_function(this->requested, free);
444 this->requested = linked_list_create();
445 }
446
447 METHOD(task_t, destroy, void,
448 private_mode_config_t *this)
449 {
450 this->vips->destroy_offset(this->vips, offsetof(host_t, destroy));
451 this->requested->destroy_function(this->requested, free);
452 free(this);
453 }
454
455 /*
456 * Described in header.
457 */
458 mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator)
459 {
460 private_mode_config_t *this;
461
462 INIT(this,
463 .public = {
464 .task = {
465 .get_type = _get_type,
466 .migrate = _migrate,
467 .destroy = _destroy,
468 },
469 },
470 .initiator = initiator,
471 .ike_sa = ike_sa,
472 .requested = linked_list_create(),
473 .vips = linked_list_create(),
474 );
475
476 if (initiator)
477 {
478 this->public.task.build = _build_i;
479 this->public.task.process = _process_i;
480 }
481 else
482 {
483 this->public.task.build = _build_r;
484 this->public.task.process = _process_r;
485 }
486
487 return &this->public;
488 }