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