e99d07428fe5008f35e3016a4086122ff589fbf1
[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 * virtual ip
46 */
47 host_t *virtual_ip;
48
49 /**
50 * list of attributes requested and its handler, entry_t
51 */
52 linked_list_t *requested;
53 };
54
55 /**
56 * Entry for a requested attribute and the requesting handler
57 */
58 typedef struct {
59 /** attribute requested */
60 configuration_attribute_type_t type;
61 /** handler requesting this attribute */
62 attribute_handler_t *handler;
63 } entry_t;
64
65 /**
66 * build INTERNAL_IPV4/6_ADDRESS attribute from virtual ip
67 */
68 static configuration_attribute_t *build_vip(host_t *vip)
69 {
70 configuration_attribute_type_t type;
71 chunk_t chunk, prefix;
72
73 if (vip->get_family(vip) == AF_INET)
74 {
75 type = INTERNAL_IP4_ADDRESS;
76 if (vip->is_anyaddr(vip))
77 {
78 chunk = chunk_empty;
79 }
80 else
81 {
82 chunk = vip->get_address(vip);
83 }
84 }
85 else
86 {
87 type = INTERNAL_IP6_ADDRESS;
88 if (vip->is_anyaddr(vip))
89 {
90 chunk = chunk_empty;
91 }
92 else
93 {
94 prefix = chunk_alloca(1);
95 *prefix.ptr = 64;
96 chunk = vip->get_address(vip);
97 chunk = chunk_cata("cc", chunk, prefix);
98 }
99 }
100 return configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1,
101 type, chunk);
102 }
103
104 /**
105 * Handle a received attribute as initiator
106 */
107 static void handle_attribute(private_mode_config_t *this,
108 configuration_attribute_t *ca)
109 {
110 attribute_handler_t *handler = NULL;
111 enumerator_t *enumerator;
112 entry_t *entry;
113
114 /* find the handler which requested this attribute */
115 enumerator = this->requested->create_enumerator(this->requested);
116 while (enumerator->enumerate(enumerator, &entry))
117 {
118 if (entry->type == ca->get_type(ca))
119 {
120 handler = entry->handler;
121 this->requested->remove_at(this->requested, enumerator);
122 free(entry);
123 break;
124 }
125 }
126 enumerator->destroy(enumerator);
127
128 /* and pass it to the handle function */
129 handler = hydra->attributes->handle(hydra->attributes,
130 this->ike_sa->get_other_id(this->ike_sa), handler,
131 ca->get_type(ca), ca->get_chunk(ca));
132 if (handler)
133 {
134 this->ike_sa->add_configuration_attribute(this->ike_sa,
135 handler, ca->get_type(ca), ca->get_chunk(ca));
136 }
137 }
138
139 /**
140 * process a single configuration attribute
141 */
142 static void process_attribute(private_mode_config_t *this,
143 configuration_attribute_t *ca)
144 {
145 host_t *ip;
146 chunk_t addr;
147 int family = AF_INET6;
148
149 switch (ca->get_type(ca))
150 {
151 case INTERNAL_IP4_ADDRESS:
152 family = AF_INET;
153 /* fall */
154 case INTERNAL_IP6_ADDRESS:
155 {
156 addr = ca->get_chunk(ca);
157 if (addr.len == 0)
158 {
159 ip = host_create_any(family);
160 }
161 else
162 {
163 /* skip prefix byte in IPv6 payload*/
164 if (family == AF_INET6)
165 {
166 addr.len--;
167 }
168 ip = host_create_from_chunk(family, addr, 0);
169 }
170 if (ip)
171 {
172 DESTROY_IF(this->virtual_ip);
173 this->virtual_ip = ip;
174 }
175 break;
176 }
177 default:
178 {
179 if (this->initiator)
180 {
181 handle_attribute(this, ca);
182 }
183 }
184 }
185 }
186
187 /**
188 * Scan for configuration payloads and attributes
189 */
190 static void process_payloads(private_mode_config_t *this, message_t *message)
191 {
192 enumerator_t *enumerator, *attributes;
193 payload_t *payload;
194
195 enumerator = message->create_payload_enumerator(message);
196 while (enumerator->enumerate(enumerator, &payload))
197 {
198 if (payload->get_type(payload) == CONFIGURATION_V1)
199 {
200 cp_payload_t *cp = (cp_payload_t*)payload;
201 configuration_attribute_t *ca;
202
203 switch (cp->get_type(cp))
204 {
205 case CFG_REQUEST:
206 case CFG_REPLY:
207 attributes = cp->create_attribute_enumerator(cp);
208 while (attributes->enumerate(attributes, &ca))
209 {
210 DBG2(DBG_IKE, "processing %N attribute",
211 configuration_attribute_type_names, ca->get_type(ca));
212 process_attribute(this, ca);
213 }
214 attributes->destroy(attributes);
215 break;
216 default:
217 DBG1(DBG_IKE, "ignoring %N config payload",
218 config_type_names, cp->get_type(cp));
219 break;
220 }
221 }
222 }
223 enumerator->destroy(enumerator);
224 }
225
226 METHOD(task_t, build_i, status_t,
227 private_mode_config_t *this, message_t *message)
228 {
229 cp_payload_t *cp = NULL;
230 enumerator_t *enumerator;
231 attribute_handler_t *handler;
232 peer_cfg_t *config;
233 configuration_attribute_type_t type;
234 chunk_t data;
235 host_t *vip;
236
237 /* reuse virtual IP if we already have one */
238 vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE);
239 if (!vip)
240 {
241 config = this->ike_sa->get_peer_cfg(this->ike_sa);
242 vip = config->get_virtual_ip(config);
243 }
244 if (vip)
245 {
246 cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REQUEST);
247 cp->add_attribute(cp, build_vip(vip));
248 }
249
250 enumerator = hydra->attributes->create_initiator_enumerator(hydra->attributes,
251 this->ike_sa->get_other_id(this->ike_sa), vip);
252 while (enumerator->enumerate(enumerator, &handler, &type, &data))
253 {
254 configuration_attribute_t *ca;
255 entry_t *entry;
256
257 DBG2(DBG_IKE, "building %N attribute",
258 configuration_attribute_type_names, type);
259 ca = configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1,
260 type, data);
261 if (!cp)
262 {
263 cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REQUEST);
264 }
265 cp->add_attribute(cp, ca);
266
267 INIT(entry,
268 .type = type,
269 .handler = handler,
270 );
271 this->requested->insert_last(this->requested, entry);
272 }
273 enumerator->destroy(enumerator);
274
275 if (cp)
276 {
277 message->add_payload(message, (payload_t*)cp);
278 }
279 return NEED_MORE;
280 }
281
282 METHOD(task_t, process_r, status_t,
283 private_mode_config_t *this, message_t *message)
284 {
285 process_payloads(this, message);
286 return NEED_MORE;
287 }
288
289 METHOD(task_t, build_r, status_t,
290 private_mode_config_t *this, message_t *message)
291 {
292 enumerator_t *enumerator;
293 configuration_attribute_type_t type;
294 chunk_t value;
295 host_t *vip = NULL;
296 cp_payload_t *cp = NULL;
297 peer_cfg_t *config;
298 identification_t *id;
299
300 id = this->ike_sa->get_other_eap_id(this->ike_sa);
301
302 config = this->ike_sa->get_peer_cfg(this->ike_sa);
303 if (this->virtual_ip)
304 {
305 DBG1(DBG_IKE, "peer requested virtual IP %H", this->virtual_ip);
306 if (config->get_pool(config))
307 {
308 vip = hydra->attributes->acquire_address(hydra->attributes,
309 config->get_pool(config), id, this->virtual_ip);
310 }
311 cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REPLY);
312 if (vip)
313 {
314 DBG1(DBG_IKE, "assigning virtual IP %H to peer '%Y'", vip, id);
315 this->ike_sa->set_virtual_ip(this->ike_sa, FALSE, vip);
316 cp->add_attribute(cp, build_vip(vip));
317 }
318 else
319 {
320 DBG1(DBG_IKE, "no virtual IP found, sending empty config payload");
321 }
322 }
323 /* query registered providers for additional attributes to include */
324 enumerator = hydra->attributes->create_responder_enumerator(
325 hydra->attributes, config->get_pool(config), id, vip);
326 while (enumerator->enumerate(enumerator, &type, &value))
327 {
328 if (!cp)
329 {
330 cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REPLY);
331 }
332 DBG2(DBG_IKE, "building %N attribute",
333 configuration_attribute_type_names, type);
334 cp->add_attribute(cp,
335 configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1,
336 type, value));
337 }
338 enumerator->destroy(enumerator);
339
340 if (cp)
341 {
342 message->add_payload(message, (payload_t*)cp);
343 }
344 DESTROY_IF(vip);
345 return SUCCESS;
346 }
347
348 METHOD(task_t, process_i, status_t,
349 private_mode_config_t *this, message_t *message)
350 {
351 process_payloads(this, message);
352
353 if (this->virtual_ip)
354 {
355 this->ike_sa->set_virtual_ip(this->ike_sa, TRUE, this->virtual_ip);
356 }
357 return SUCCESS;
358 }
359
360 METHOD(task_t, get_type, task_type_t,
361 private_mode_config_t *this)
362 {
363 return TASK_MODE_CONFIG;
364 }
365
366 METHOD(task_t, migrate, void,
367 private_mode_config_t *this, ike_sa_t *ike_sa)
368 {
369 DESTROY_IF(this->virtual_ip);
370
371 this->ike_sa = ike_sa;
372 this->virtual_ip = NULL;
373 this->requested->destroy_function(this->requested, free);
374 this->requested = linked_list_create();
375 }
376
377 METHOD(task_t, destroy, void,
378 private_mode_config_t *this)
379 {
380 DESTROY_IF(this->virtual_ip);
381 this->requested->destroy_function(this->requested, free);
382 free(this);
383 }
384
385 /*
386 * Described in header.
387 */
388 mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator)
389 {
390 private_mode_config_t *this;
391
392 INIT(this,
393 .public = {
394 .task = {
395 .get_type = _get_type,
396 .migrate = _migrate,
397 .destroy = _destroy,
398 },
399 },
400 .initiator = initiator,
401 .ike_sa = ike_sa,
402 .requested = linked_list_create(),
403 );
404
405 if (initiator)
406 {
407 this->public.task.build = _build_i;
408 this->public.task.process = _process_i;
409 }
410 else
411 {
412 this->public.task.build = _build_r;
413 this->public.task.process = _process_r;
414 }
415
416 return &this->public;
417 }