Add enum names for HA message types
[strongswan.git] / src / libcharon / plugins / ha / ha_message.c
1 /*
2 * Copyright (C) 2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
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 #define _GNU_SOURCE
17 #include <string.h>
18 #include <arpa/inet.h>
19
20 #include "ha_message.h"
21
22 #include <daemon.h>
23
24 #define ALLOCATION_BLOCK 64
25
26 typedef struct private_ha_message_t private_ha_message_t;
27
28 /**
29 * Private data of an ha_message_t object.
30 */
31 struct private_ha_message_t {
32
33 /**
34 * Public ha_message_t interface.
35 */
36 ha_message_t public;
37
38 /**
39 * Allocated size of buf
40 */
41 size_t allocated;
42
43 /**
44 * Buffer containing encoded data
45 */
46 chunk_t buf;
47 };
48
49 ENUM(ha_message_type_names, HA_IKE_ADD, HA_RESYNC,
50 "IKE_ADD",
51 "IKE_UPDATE",
52 "IKE_MID_INITIATOR",
53 "IKE_MID_RESPONDER",
54 "IKE_DELETE",
55 "CHILD_ADD",
56 "CHILD_DELETE",
57 "SEGMENT_DROP",
58 "SEGMENT_TAKE",
59 "STATUS",
60 "RESYNC",
61 );
62
63 typedef struct ike_sa_id_encoding_t ike_sa_id_encoding_t;
64
65 /**
66 * Encoding if an ike_sa_id_t
67 */
68 struct ike_sa_id_encoding_t {
69 u_int64_t initiator_spi;
70 u_int64_t responder_spi;
71 u_int8_t initiator;
72 } __attribute__((packed));
73
74 typedef struct identification_encoding_t identification_encoding_t;
75
76 /**
77 * Encoding of a identification_t
78 */
79 struct identification_encoding_t {
80 u_int8_t type;
81 u_int8_t len;
82 char encoding[];
83 } __attribute__((packed));
84
85 typedef struct host_encoding_t host_encoding_t;
86
87 /**
88 * encoding of a host_t
89 */
90 struct host_encoding_t {
91 u_int16_t port;
92 u_int8_t family;
93 char encoding[];
94 } __attribute__((packed));
95
96 typedef struct ts_encoding_t ts_encoding_t;
97
98 /**
99 * encoding of a traffic_selector_t
100 */
101 struct ts_encoding_t {
102 u_int8_t type;
103 u_int8_t protocol;
104 u_int16_t from_port;
105 u_int16_t to_port;
106 u_int8_t dynamic;
107 char encoding[];
108 } __attribute__((packed));
109
110 METHOD(ha_message_t, get_type, ha_message_type_t,
111 private_ha_message_t *this)
112 {
113 return this->buf.ptr[1];
114 }
115
116 /**
117 * check for space in buffer, increase if necessary
118 */
119 static void check_buf(private_ha_message_t *this, size_t len)
120 {
121 int increased = 0;
122
123 while (this->buf.len + len > this->allocated)
124 { /* double size */
125 this->allocated += ALLOCATION_BLOCK;
126 increased++;
127 }
128 if (increased)
129 {
130 this->buf.ptr = realloc(this->buf.ptr, this->allocated);
131 }
132 }
133
134 METHOD(ha_message_t, add_attribute, void,
135 private_ha_message_t *this, ha_message_attribute_t attribute, ...)
136 {
137 size_t len;
138 va_list args;
139
140 check_buf(this, sizeof(u_int8_t));
141 this->buf.ptr[this->buf.len] = attribute;
142 this->buf.len += sizeof(u_int8_t);
143
144 va_start(args, attribute);
145 switch (attribute)
146 {
147 /* ike_sa_id_t* */
148 case HA_IKE_ID:
149 case HA_IKE_REKEY_ID:
150 {
151 ike_sa_id_encoding_t *enc;
152 ike_sa_id_t *id;
153
154 id = va_arg(args, ike_sa_id_t*);
155 check_buf(this, sizeof(ike_sa_id_encoding_t));
156 enc = (ike_sa_id_encoding_t*)(this->buf.ptr + this->buf.len);
157 this->buf.len += sizeof(ike_sa_id_encoding_t);
158 enc->initiator = id->is_initiator(id);
159 enc->initiator_spi = id->get_initiator_spi(id);
160 enc->responder_spi = id->get_responder_spi(id);
161 break;
162 }
163 /* identification_t* */
164 case HA_LOCAL_ID:
165 case HA_REMOTE_ID:
166 {
167 identification_encoding_t *enc;
168 identification_t *id;
169 chunk_t data;
170
171 id = va_arg(args, identification_t*);
172 data = id->get_encoding(id);
173 check_buf(this, sizeof(identification_encoding_t) + data.len);
174 enc = (identification_encoding_t*)(this->buf.ptr + this->buf.len);
175 this->buf.len += sizeof(identification_encoding_t) + data.len;
176 enc->type = id->get_type(id);
177 enc->len = data.len;
178 memcpy(enc->encoding, data.ptr, data.len);
179 break;
180 }
181 /* host_t* */
182 case HA_LOCAL_ADDR:
183 case HA_REMOTE_ADDR:
184 case HA_LOCAL_VIP:
185 case HA_REMOTE_VIP:
186 case HA_ADDITIONAL_ADDR:
187 {
188 host_encoding_t *enc;
189 host_t *host;
190 chunk_t data;
191
192 host = va_arg(args, host_t*);
193 data = host->get_address(host);
194 check_buf(this, sizeof(host_encoding_t) + data.len);
195 enc = (host_encoding_t*)(this->buf.ptr + this->buf.len);
196 this->buf.len += sizeof(host_encoding_t) + data.len;
197 enc->family = host->get_family(host);
198 enc->port = htons(host->get_port(host));
199 memcpy(enc->encoding, data.ptr, data.len);
200 break;
201 }
202 /* char* */
203 case HA_CONFIG_NAME:
204 {
205 char *str;
206
207 str = va_arg(args, char*);
208 len = strlen(str) + 1;
209 check_buf(this, len);
210 memcpy(this->buf.ptr + this->buf.len, str, len);
211 this->buf.len += len;
212 break;
213 }
214 /* u_int8_t */
215 case HA_IPSEC_MODE:
216 case HA_IPCOMP:
217 {
218 u_int8_t val;
219
220 val = va_arg(args, u_int);
221 check_buf(this, sizeof(val));
222 this->buf.ptr[this->buf.len] = val;
223 this->buf.len += sizeof(val);
224 break;
225 }
226 /* u_int16_t */
227 case HA_ALG_PRF:
228 case HA_ALG_OLD_PRF:
229 case HA_ALG_ENCR:
230 case HA_ALG_ENCR_LEN:
231 case HA_ALG_INTEG:
232 case HA_INBOUND_CPI:
233 case HA_OUTBOUND_CPI:
234 case HA_SEGMENT:
235 {
236 u_int16_t val;
237
238 val = va_arg(args, u_int);
239 check_buf(this, sizeof(val));
240 *(u_int16_t*)(this->buf.ptr + this->buf.len) = htons(val);
241 this->buf.len += sizeof(val);
242 break;
243 }
244 /** u_int32_t */
245 case HA_CONDITIONS:
246 case HA_EXTENSIONS:
247 case HA_INBOUND_SPI:
248 case HA_OUTBOUND_SPI:
249 case HA_MID:
250 {
251 u_int32_t val;
252
253 val = va_arg(args, u_int);
254 check_buf(this, sizeof(val));
255 *(u_int32_t*)(this->buf.ptr + this->buf.len) = htonl(val);
256 this->buf.len += sizeof(val);
257 break;
258 }
259 /** chunk_t */
260 case HA_NONCE_I:
261 case HA_NONCE_R:
262 case HA_SECRET:
263 case HA_OLD_SKD:
264 {
265 chunk_t chunk;
266
267 chunk = va_arg(args, chunk_t);
268 check_buf(this, chunk.len + sizeof(u_int16_t));
269 *(u_int16_t*)(this->buf.ptr + this->buf.len) = htons(chunk.len);
270 memcpy(this->buf.ptr + this->buf.len + sizeof(u_int16_t),
271 chunk.ptr, chunk.len);
272 this->buf.len += chunk.len + sizeof(u_int16_t);;
273 break;
274 }
275 /** traffic_selector_t */
276 case HA_LOCAL_TS:
277 case HA_REMOTE_TS:
278 {
279 ts_encoding_t *enc;
280 traffic_selector_t *ts;
281 chunk_t data;
282
283 ts = va_arg(args, traffic_selector_t*);
284 data = chunk_cata("cc", ts->get_from_address(ts),
285 ts->get_to_address(ts));
286 check_buf(this, sizeof(ts_encoding_t) + data.len);
287 enc = (ts_encoding_t*)(this->buf.ptr + this->buf.len);
288 this->buf.len += sizeof(ts_encoding_t) + data.len;
289 enc->type = ts->get_type(ts);
290 enc->protocol = ts->get_protocol(ts);
291 enc->from_port = htons(ts->get_from_port(ts));
292 enc->to_port = htons(ts->get_to_port(ts));
293 enc->dynamic = ts->is_dynamic(ts);
294 memcpy(enc->encoding, data.ptr, data.len);
295 break;
296 }
297 default:
298 {
299 DBG1(DBG_CFG, "unable to encode, attribute %d unknown", attribute);
300 this->buf.len -= sizeof(u_int8_t);
301 break;
302 }
303 }
304 va_end(args);
305 }
306
307 /**
308 * Attribute enumerator implementation
309 */
310 typedef struct {
311 /** implementes enumerator_t */
312 enumerator_t public;
313 /** position in message */
314 chunk_t buf;
315 /** cleanup handler of current element, if any */
316 void (*cleanup)(void* data);
317 /** data to pass to cleanup handler */
318 void *cleanup_data;
319 } attribute_enumerator_t;
320
321 METHOD(enumerator_t, attribute_enumerate, bool,
322 attribute_enumerator_t *this, ha_message_attribute_t *attr_out,
323 ha_message_value_t *value)
324 {
325 ha_message_attribute_t attr;
326
327 if (this->cleanup)
328 {
329 this->cleanup(this->cleanup_data);
330 this->cleanup = NULL;
331 }
332 if (this->buf.len < 1)
333 {
334 return FALSE;
335 }
336 attr = this->buf.ptr[0];
337 this->buf = chunk_skip(this->buf, 1);
338 switch (attr)
339 {
340 /* ike_sa_id_t* */
341 case HA_IKE_ID:
342 case HA_IKE_REKEY_ID:
343 {
344 ike_sa_id_encoding_t *enc;
345
346 if (this->buf.len < sizeof(ike_sa_id_encoding_t))
347 {
348 return FALSE;
349 }
350 enc = (ike_sa_id_encoding_t*)(this->buf.ptr);
351 value->ike_sa_id = ike_sa_id_create(enc->initiator_spi,
352 enc->responder_spi, enc->initiator);
353 *attr_out = attr;
354 this->cleanup = (void*)value->ike_sa_id->destroy;
355 this->cleanup_data = value->ike_sa_id;
356 this->buf = chunk_skip(this->buf, sizeof(ike_sa_id_encoding_t));
357 return TRUE;
358 }
359 /* identification_t* */
360 case HA_LOCAL_ID:
361 case HA_REMOTE_ID:
362 {
363 identification_encoding_t *enc;
364
365 enc = (identification_encoding_t*)(this->buf.ptr);
366 if (this->buf.len < sizeof(identification_encoding_t) ||
367 this->buf.len < sizeof(identification_encoding_t) + enc->len)
368 {
369 return FALSE;
370 }
371 value->id = identification_create_from_encoding(enc->type,
372 chunk_create(enc->encoding, enc->len));
373 *attr_out = attr;
374 this->cleanup = (void*)value->id->destroy;
375 this->cleanup_data = value->id;
376 this->buf = chunk_skip(this->buf,
377 sizeof(identification_encoding_t) + enc->len);
378 return TRUE;
379 }
380 /* host_t* */
381 case HA_LOCAL_ADDR:
382 case HA_REMOTE_ADDR:
383 case HA_LOCAL_VIP:
384 case HA_REMOTE_VIP:
385 case HA_ADDITIONAL_ADDR:
386 {
387 host_encoding_t *enc;
388
389 enc = (host_encoding_t*)(this->buf.ptr);
390 if (this->buf.len < sizeof(host_encoding_t))
391 {
392 return FALSE;
393 }
394 value->host = host_create_from_chunk(enc->family,
395 chunk_create(enc->encoding,
396 this->buf.len - sizeof(host_encoding_t)),
397 ntohs(enc->port));
398 if (!value->host)
399 {
400 return FALSE;
401 }
402 *attr_out = attr;
403 this->cleanup = (void*)value->host->destroy;
404 this->cleanup_data = value->host;
405 this->buf = chunk_skip(this->buf, sizeof(host_encoding_t) +
406 value->host->get_address(value->host).len);
407 return TRUE;
408 }
409 /* char* */
410 case HA_CONFIG_NAME:
411 {
412 size_t len;
413
414 len = strnlen(this->buf.ptr, this->buf.len);
415 if (len >= this->buf.len)
416 {
417 return FALSE;
418 }
419 value->str = this->buf.ptr;
420 *attr_out = attr;
421 this->buf = chunk_skip(this->buf, len + 1);
422 return TRUE;
423 }
424 /* u_int8_t */
425 case HA_IPSEC_MODE:
426 case HA_IPCOMP:
427 {
428 if (this->buf.len < sizeof(u_int8_t))
429 {
430 return FALSE;
431 }
432 value->u8 = *(u_int8_t*)this->buf.ptr;
433 *attr_out = attr;
434 this->buf = chunk_skip(this->buf, sizeof(u_int8_t));
435 return TRUE;
436 }
437 /** u_int16_t */
438 case HA_ALG_PRF:
439 case HA_ALG_OLD_PRF:
440 case HA_ALG_ENCR:
441 case HA_ALG_ENCR_LEN:
442 case HA_ALG_INTEG:
443 case HA_INBOUND_CPI:
444 case HA_OUTBOUND_CPI:
445 case HA_SEGMENT:
446 {
447 if (this->buf.len < sizeof(u_int16_t))
448 {
449 return FALSE;
450 }
451 value->u16 = ntohs(*(u_int16_t*)this->buf.ptr);
452 *attr_out = attr;
453 this->buf = chunk_skip(this->buf, sizeof(u_int16_t));
454 return TRUE;
455 }
456 /** u_int32_t */
457 case HA_CONDITIONS:
458 case HA_EXTENSIONS:
459 case HA_INBOUND_SPI:
460 case HA_OUTBOUND_SPI:
461 case HA_MID:
462 {
463 if (this->buf.len < sizeof(u_int32_t))
464 {
465 return FALSE;
466 }
467 value->u32 = ntohl(*(u_int32_t*)this->buf.ptr);
468 *attr_out = attr;
469 this->buf = chunk_skip(this->buf, sizeof(u_int32_t));
470 return TRUE;
471 }
472 /** chunk_t */
473 case HA_NONCE_I:
474 case HA_NONCE_R:
475 case HA_SECRET:
476 case HA_OLD_SKD:
477 {
478 size_t len;
479
480 if (this->buf.len < sizeof(u_int16_t))
481 {
482 return FALSE;
483 }
484 len = ntohs(*(u_int16_t*)this->buf.ptr);
485 this->buf = chunk_skip(this->buf, sizeof(u_int16_t));
486 if (this->buf.len < len)
487 {
488 return FALSE;
489 }
490 value->chunk.len = len;
491 value->chunk.ptr = this->buf.ptr;
492 *attr_out = attr;
493 this->buf = chunk_skip(this->buf, len);
494 return TRUE;
495 }
496 case HA_LOCAL_TS:
497 case HA_REMOTE_TS:
498 {
499 ts_encoding_t *enc;
500 host_t *host;
501 int addr_len;
502
503 enc = (ts_encoding_t*)(this->buf.ptr);
504 if (this->buf.len < sizeof(ts_encoding_t))
505 {
506 return FALSE;
507 }
508 switch (enc->type)
509 {
510 case TS_IPV4_ADDR_RANGE:
511 addr_len = 4;
512 if (this->buf.len < sizeof(ts_encoding_t) + 2 * addr_len)
513 {
514 return FALSE;
515 }
516 break;
517 case TS_IPV6_ADDR_RANGE:
518 addr_len = 16;
519 if (this->buf.len < sizeof(ts_encoding_t) + 2 * addr_len)
520 {
521 return FALSE;
522 }
523 break;
524 default:
525 return FALSE;
526 }
527 if (enc->dynamic)
528 {
529 host = host_create_from_chunk(0,
530 chunk_create(enc->encoding, addr_len), 0);
531 if (!host)
532 {
533 return FALSE;
534 }
535 value->ts = traffic_selector_create_dynamic(enc->protocol,
536 ntohs(enc->from_port), ntohs(enc->to_port));
537 value->ts->set_address(value->ts, host);
538 host->destroy(host);
539 }
540 else
541 {
542 value->ts = traffic_selector_create_from_bytes(enc->protocol,
543 enc->type, chunk_create(enc->encoding, addr_len),
544 ntohs(enc->from_port),
545 chunk_create(enc->encoding + addr_len, addr_len),
546 ntohs(enc->to_port));
547 if (!value->ts)
548 {
549 return FALSE;
550 }
551 }
552 *attr_out = attr;
553 this->cleanup = (void*)value->ts->destroy;
554 this->cleanup_data = value->ts;
555 this->buf = chunk_skip(this->buf, sizeof(ts_encoding_t)
556 + addr_len * 2);
557 return TRUE;
558 }
559 default:
560 {
561 return FALSE;
562 }
563 }
564 }
565
566 METHOD(enumerator_t, enum_destroy, void,
567 attribute_enumerator_t *this)
568 {
569 if (this->cleanup)
570 {
571 this->cleanup(this->cleanup_data);
572 }
573 free(this);
574 }
575
576 METHOD(ha_message_t, create_attribute_enumerator, enumerator_t*,
577 private_ha_message_t *this)
578 {
579 attribute_enumerator_t *e;
580
581 INIT(e,
582 .public = {
583 .enumerate = (void*)_attribute_enumerate,
584 .destroy = _enum_destroy,
585 },
586 .buf = chunk_skip(this->buf, 2),
587 );
588
589 return &e->public;
590 }
591
592 METHOD(ha_message_t, get_encoding, chunk_t,
593 private_ha_message_t *this)
594 {
595 return this->buf;
596 }
597
598 METHOD(ha_message_t, destroy, void,
599 private_ha_message_t *this)
600 {
601 free(this->buf.ptr);
602 free(this);
603 }
604
605
606 static private_ha_message_t *ha_message_create_generic()
607 {
608 private_ha_message_t *this;
609
610 INIT(this,
611 .public = {
612 .get_type = _get_type,
613 .add_attribute = _add_attribute,
614 .create_attribute_enumerator = _create_attribute_enumerator,
615 .get_encoding = _get_encoding,
616 .destroy = _destroy,
617 },
618 );
619 return this;
620 }
621
622 /**
623 * See header
624 */
625 ha_message_t *ha_message_create(ha_message_type_t type)
626 {
627 private_ha_message_t *this = ha_message_create_generic();
628
629 this->allocated = ALLOCATION_BLOCK;
630 this->buf.ptr = malloc(this->allocated);
631 this->buf.len = 2;
632 this->buf.ptr[0] = HA_MESSAGE_VERSION;
633 this->buf.ptr[1] = type;
634
635 return &this->public;
636 }
637
638 /**
639 * See header
640 */
641 ha_message_t *ha_message_parse(chunk_t data)
642 {
643 private_ha_message_t *this;
644
645 if (data.len < 2)
646 {
647 DBG1(DBG_CFG, "HA message too short");
648 return NULL;
649 }
650 if (data.ptr[0] != HA_MESSAGE_VERSION)
651 {
652 DBG1(DBG_CFG, "HA message has version %d, expected %d",
653 data.ptr[0], HA_MESSAGE_VERSION);
654 return NULL;
655 }
656
657 this = ha_message_create_generic();
658 this->buf = chunk_clone(data);
659 this->allocated = this->buf.len;
660
661 return &this->public;
662 }
663