Annotation of embedaddon/strongswan/src/libcharon/plugins/vici/vici_attribute.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (C) 2014-2016 Tobias Brunner
3: * HSR Hochschule fuer Technik Rapperswil
4: *
5: * Copyright (C) 2014 Martin Willi
6: * Copyright (C) 2014 revosec AG
7: *
8: * This program is free software; you can redistribute it and/or modify it
9: * under the terms of the GNU General Public License as published by the
10: * Free Software Foundation; either version 2 of the License, or (at your
11: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12: *
13: * This program is distributed in the hope that it will be useful, but
14: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16: * for more details.
17: */
18:
19: #include "vici_attribute.h"
20: #include "vici_builder.h"
21:
22: #include <daemon.h>
23: #include <collections/hashtable.h>
24: #include <collections/array.h>
25: #include <threading/rwlock.h>
26: #include <attributes/mem_pool.h>
27:
28: typedef struct private_vici_attribute_t private_vici_attribute_t;
29:
30: /**
31: * private data of vici_attribute
32: */
33: struct private_vici_attribute_t {
34:
35: /**
36: * public functions
37: */
38: vici_attribute_t public;
39:
40: /**
41: * vici connection dispatcher
42: */
43: vici_dispatcher_t *dispatcher;
44:
45: /**
46: * Configured pools, as char* => pool_t
47: */
48: hashtable_t *pools;
49:
50: /**
51: * rwlock to lock access to pools
52: */
53: rwlock_t *lock;
54: };
55:
56: /**
57: * Single configuration attribute with type
58: */
59: typedef struct {
60: /** type of attribute */
61: configuration_attribute_type_t type;
62: /** attribute value */
63: chunk_t value;
64: } attribute_t;
65:
66: /**
67: * Clean up an attribute
68: */
69: static void attribute_destroy(attribute_t *attr)
70: {
71: free(attr->value.ptr);
72: free(attr);
73: }
74:
75: /**
76: * Pool instances with associated attributes
77: */
78: typedef struct {
79: /** in-memory virtual IP pool */
80: mem_pool_t *vips;
81: /** configuration attributes, as attribute_t */
82: array_t *attrs;
83: } pool_t;
84:
85: /**
86: * Clean up a pool instance
87: */
88: static void pool_destroy(pool_t *pool)
89: {
90: DESTROY_IF(pool->vips);
91: array_destroy_function(pool->attrs, (void*)attribute_destroy, NULL);
92: free(pool);
93: }
94:
95: /**
96: * Find an existing or not yet existing lease
97: */
98: static host_t *find_addr(private_vici_attribute_t *this, linked_list_t *pools,
99: identification_t *id, host_t *requested,
100: mem_pool_op_t op, host_t *peer)
101: {
102: enumerator_t *enumerator;
103: host_t *addr = NULL;
104: pool_t *pool;
105: char *name;
106:
107: enumerator = pools->create_enumerator(pools);
108: while (enumerator->enumerate(enumerator, &name))
109: {
110: pool = this->pools->get(this->pools, name);
111: if (pool)
112: {
113: addr = pool->vips->acquire_address(pool->vips, id, requested,
114: op, peer);
115: if (addr)
116: {
117: break;
118: }
119: }
120: }
121: enumerator->destroy(enumerator);
122:
123: return addr;
124: }
125:
126: METHOD(attribute_provider_t, acquire_address, host_t*,
127: private_vici_attribute_t *this, linked_list_t *pools, ike_sa_t *ike_sa,
128: host_t *requested)
129: {
130: identification_t *id;
131: host_t *addr, *peer;
132:
133: id = ike_sa->get_other_eap_id(ike_sa);
134: peer = ike_sa->get_other_host(ike_sa);
135:
136: this->lock->read_lock(this->lock);
137:
138: addr = find_addr(this, pools, id, requested, MEM_POOL_EXISTING, peer);
139: if (!addr)
140: {
141: addr = find_addr(this, pools, id, requested, MEM_POOL_NEW, peer);
142: if (!addr)
143: {
144: addr = find_addr(this, pools, id, requested, MEM_POOL_REASSIGN, peer);
145: }
146: }
147:
148: this->lock->unlock(this->lock);
149:
150: return addr;
151: }
152:
153: METHOD(attribute_provider_t, release_address, bool,
154: private_vici_attribute_t *this, linked_list_t *pools, host_t *address,
155: ike_sa_t *ike_sa)
156: {
157: enumerator_t *enumerator;
158: identification_t *id;
159: bool found = FALSE;
160: pool_t *pool;
161: char *name;
162:
163: id = ike_sa->get_other_eap_id(ike_sa);
164:
165: this->lock->read_lock(this->lock);
166:
167: enumerator = pools->create_enumerator(pools);
168: while (enumerator->enumerate(enumerator, &name))
169: {
170: pool = this->pools->get(this->pools, name);
171: if (pool)
172: {
173: found = pool->vips->release_address(pool->vips, address, id);
174: if (found)
175: {
176: break;
177: }
178: }
179: }
180: enumerator->destroy(enumerator);
181:
182: this->lock->unlock(this->lock);
183:
184: return found;
185: }
186:
187: CALLBACK(attr_filter, bool,
188: void *data, enumerator_t *orig, va_list args)
189: {
190: attribute_t *attr;
191: configuration_attribute_type_t *type;
192: chunk_t *value;
193:
194: VA_ARGS_VGET(args, type, value);
195:
196: if (orig->enumerate(orig, &attr))
197: {
198: *type = attr->type;
199: *value = attr->value;
200: return TRUE;
201: }
202: return FALSE;
203: }
204:
205: /**
206: * Create nested inner enumerator over pool attributes
207: */
208: CALLBACK(create_nested, enumerator_t*,
209: pool_t *pool, void *this)
210: {
211: return enumerator_create_filter(array_create_enumerator(pool->attrs),
212: attr_filter, NULL, NULL);
213: }
214:
215: /**
216: * Data associated to nested enumerator cleanup
217: */
218: typedef struct {
219: private_vici_attribute_t *this;
220: linked_list_t *list;
221: } nested_data_t;
222:
223: /**
224: * Clean up nested enumerator data
225: */
226: CALLBACK(nested_cleanup, void,
227: nested_data_t *data)
228: {
229: data->this->lock->unlock(data->this->lock);
230: data->list->destroy(data->list);
231: free(data);
232: }
233:
234: /**
235: * Check if any of vips is from pool
236: */
237: static bool have_vips_from_pool(mem_pool_t *pool, linked_list_t *vips)
238: {
239: enumerator_t *enumerator;
240: host_t *host;
241: chunk_t start, end, current;
242: uint32_t size;
243: bool found = FALSE;
244:
245: host = pool->get_base(pool);
246: start = host->get_address(host);
247:
248: if (start.len >= sizeof(size))
249: {
250: end = chunk_clone(start);
251:
252: /* mem_pool is currently limited to 2^31 addresses, so 32-bit
253: * calculations should be sufficient. */
254: size = untoh32(start.ptr + start.len - sizeof(size));
255: htoun32(end.ptr + end.len - sizeof(size), size + pool->get_size(pool));
256:
257: enumerator = vips->create_enumerator(vips);
258: while (enumerator->enumerate(enumerator, &host))
259: {
260: current = host->get_address(host);
261: if (chunk_compare(current, start) >= 0 &&
262: chunk_compare(current, end) < 0)
263: {
264: found = TRUE;
265: break;
266: }
267: }
268: enumerator->destroy(enumerator);
269:
270: free(end.ptr);
271: }
272: return found;
273: }
274:
275: METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*,
276: private_vici_attribute_t *this, linked_list_t *pools,
277: ike_sa_t *ike_sa, linked_list_t *vips)
278: {
279: enumerator_t *enumerator;
280: nested_data_t *data;
281: pool_t *pool;
282: char *name;
283:
284: INIT(data,
285: .this = this,
286: .list = linked_list_create(),
287: );
288:
289: this->lock->read_lock(this->lock);
290:
291: enumerator = pools->create_enumerator(pools);
292: while (enumerator->enumerate(enumerator, &name))
293: {
294: pool = this->pools->get(this->pools, name);
295: if (pool && have_vips_from_pool(pool->vips, vips))
296: {
297: data->list->insert_last(data->list, pool);
298: }
299: }
300: enumerator->destroy(enumerator);
301:
302: return enumerator_create_nested(data->list->create_enumerator(data->list),
303: create_nested, data, nested_cleanup);
304: }
305:
306: /**
307: * Merge a pool configuration with existing ones
308: */
309: static bool merge_pool(private_vici_attribute_t *this, pool_t *new)
310: {
311: mem_pool_t *tmp;
312: host_t *base;
313: pool_t *old;
314: const char *name;
315: u_int size;
316:
317: name = new->vips->get_name(new->vips);
318: base = new->vips->get_base(new->vips);
319: size = new->vips->get_size(new->vips);
320:
321: old = this->pools->remove(this->pools, name);
322: if (!old)
323: {
324: this->pools->put(this->pools, name, new);
325: DBG1(DBG_CFG, "added vici pool %s: %H, %u entries", name, base, size);
326: return TRUE;
327: }
328:
329: if (base->ip_equals(base, old->vips->get_base(old->vips)) &&
330: size == old->vips->get_size(old->vips))
331: {
332: /* no changes in pool, so keep existing, but use new attributes */
333: DBG1(DBG_CFG, "updated vici pool %s: %H, %u entries", name, base, size);
334: tmp = new->vips;
335: new->vips = old->vips;
336: old->vips = tmp;
337: this->pools->put(this->pools, new->vips->get_name(new->vips), new);
338: pool_destroy(old);
339: return TRUE;
340: }
341: if (old->vips->get_online(old->vips) == 0)
342: {
343: /* can replace old pool, no online leases */
344: DBG1(DBG_CFG, "replaced vici pool %s: %H, %u entries", name, base, size);
345: this->pools->put(this->pools, name, new);
346: pool_destroy(old);
347: return TRUE;
348: }
349: /* have online leases, unable to replace, TODO: migrate leases? */
350: DBG1(DBG_CFG, "vici pool %s has %u online leases, unable to replace",
351: name, old->vips->get_online(old->vips));
352: this->pools->put(this->pools, old->vips->get_name(old->vips), old);
353: return FALSE;
354: }
355:
356: /**
357: * Create a (error) reply message
358: */
359: static vici_message_t* create_reply(char *fmt, ...)
360: {
361: vici_builder_t *builder;
362: va_list args;
363:
364: builder = vici_builder_create();
365: builder->add_kv(builder, "success", fmt ? "no" : "yes");
366: if (fmt)
367: {
368: va_start(args, fmt);
369: builder->vadd_kv(builder, "errmsg", fmt, args);
370: va_end(args);
371: }
372: return builder->finalize(builder);
373: }
374:
375: /**
376: * Parse a range definition of an address pool
377: */
378: static mem_pool_t *create_pool_range(char *name, char *buf)
379: {
380: mem_pool_t *pool;
381: host_t *from, *to;
382:
383: if (!host_create_from_range(buf, &from, &to))
384: {
385: return NULL;
386: }
387: pool = mem_pool_create_range(name, from, to);
388: from->destroy(from);
389: to->destroy(to);
390: return pool;
391: }
392:
393: /**
394: * Parse callback data, passed to each callback
395: */
396: typedef struct {
397: private_vici_attribute_t *this;
398: vici_message_t *reply;
399: } request_data_t;
400:
401: /**
402: * Data associated to a pool load
403: */
404: typedef struct {
405: request_data_t *request;
406: char *name;
407: pool_t *pool;
408: } load_data_t;
409:
410: CALLBACK(pool_li, bool,
411: load_data_t *data, vici_message_t *message, char *name, chunk_t value)
412: {
413: struct {
414: char *name;
415: configuration_attribute_type_t v4;
416: configuration_attribute_type_t v6;
417: } keys[] = {
418: {"address", INTERNAL_IP4_ADDRESS, INTERNAL_IP6_ADDRESS },
419: {"dns", INTERNAL_IP4_DNS, INTERNAL_IP6_DNS },
420: {"nbns", INTERNAL_IP4_NBNS, INTERNAL_IP6_NBNS },
421: {"dhcp", INTERNAL_IP4_DHCP, INTERNAL_IP6_DHCP },
422: {"netmask", INTERNAL_IP4_NETMASK, INTERNAL_IP6_NETMASK },
423: {"server", INTERNAL_IP4_SERVER, INTERNAL_IP6_SERVER },
424: {"subnet", INTERNAL_IP4_SUBNET, INTERNAL_IP6_SUBNET },
425: {"split_include", UNITY_SPLIT_INCLUDE, UNITY_SPLIT_INCLUDE },
426: {"split_exclude", UNITY_LOCAL_LAN, UNITY_LOCAL_LAN },
427: };
428: char buf[256];
429: int i, index = -1, mask = -1, type = 0;
430: chunk_t encoding;
431: attribute_t *attr;
432: host_t *host = NULL;
433:
434: for (i = 0; i < countof(keys); i++)
435: {
436: if (streq(name, keys[i].name))
437: {
438: index = i;
439: break;
440: }
441: }
442: if (index == -1)
443: {
444: type = atoi(name);
445: if (!type)
446: {
447: data->request->reply = create_reply("invalid attribute: %s", name);
448: return FALSE;
449: }
450: }
451:
452: if (vici_stringify(value, buf, sizeof(buf)))
453: {
454: if (strchr(buf, '/'))
455: {
456: host = host_create_from_subnet(buf, &mask);
457: }
458: else
459: {
460: host = host_create_from_string(buf, 0);
461: }
462: }
463: if (host)
464: {
465: if (index != -1)
466: {
467: switch (host->get_family(host))
468: {
469: case AF_INET:
470: type = keys[index].v4;
471: break;
472: case AF_INET6:
473: default:
474: type = keys[index].v6;
475: break;
476: }
477: }
478: if (mask == -1)
479: {
480: encoding = chunk_clone(host->get_address(host));
481: }
482: else
483: {
484: if (host->get_family(host) == AF_INET)
485: { /* IPv4 attributes contain a subnet mask */
486: uint32_t netmask = 0;
487:
488: if (mask)
489: { /* shifting uint32_t by 32 or more is undefined */
490: mask = 32 - mask;
491: netmask = htonl((0xFFFFFFFF >> mask) << mask);
492: }
493: encoding = chunk_cat("cc", host->get_address(host),
494: chunk_from_thing(netmask));
495: }
496: else
497: { /* IPv6 addresses the prefix only */
498: encoding = chunk_cat("cc", host->get_address(host),
499: chunk_from_chars(mask));
500: }
501: }
502: host->destroy(host);
503: }
504: else
505: {
506: if (index != -1)
507: {
508: data->request->reply = create_reply("invalid attribute value "
509: "for %s", name);
510: return FALSE;
511: }
512: /* use raw binary data for numbered attributes */
513: encoding = chunk_clone(value);
514: }
515: INIT(attr,
516: .type = type,
517: .value = encoding,
518: );
519: array_insert_create(&data->pool->attrs, ARRAY_TAIL, attr);
520: return TRUE;
521: }
522:
523: CALLBACK(pool_kv, bool,
524: load_data_t *data, vici_message_t *message, char *name, chunk_t value)
525: {
526: if (streq(name, "addrs"))
527: {
528: char buf[128];
529: mem_pool_t *pool;
530: host_t *base = NULL;
531: int bits;
532:
533: if (data->pool->vips)
534: {
535: data->request->reply = create_reply("multiple addrs defined");
536: return FALSE;
537: }
538: if (!vici_stringify(value, buf, sizeof(buf)))
539: {
540: data->request->reply = create_reply("invalid addrs value");
541: return FALSE;
542: }
543: pool = create_pool_range(data->name, buf);
544: if (!pool)
545: {
546: base = host_create_from_subnet(buf, &bits);
547: if (base)
548: {
549: pool = mem_pool_create(data->name, base, bits);
550: base->destroy(base);
551: }
552: }
553: if (!pool)
554: {
555: data->request->reply = create_reply("invalid addrs value: %s", buf);
556: return FALSE;
557: }
558: data->pool->vips = pool;
559: return TRUE;
560: }
561: data->request->reply = create_reply("invalid attribute: %s", name);
562: return FALSE;
563: }
564:
565: CALLBACK(pool_sn, bool,
566: request_data_t *request, vici_message_t *message,
567: vici_parse_context_t *ctx, char *name)
568: {
569: load_data_t data = {
570: .request = request,
571: .name = name,
572: };
573: bool merged;
574:
575: INIT(data.pool);
576:
577: if (!message->parse(message, ctx, NULL, pool_kv, pool_li, &data))
578: {
579: pool_destroy(data.pool);
580: return FALSE;
581: }
582:
583: if (!data.pool->vips)
584: {
585: request->reply = create_reply("missing addrs for pool '%s'", name);
586: pool_destroy(data.pool);
587: return FALSE;
588: }
589:
590: request->this->lock->write_lock(request->this->lock);
591: merged = merge_pool(request->this, data.pool);
592: request->this->lock->unlock(request->this->lock);
593:
594: if (!merged)
595: {
596: request->reply = create_reply("vici pool %s has online leases, "
597: "unable to replace", name);
598: pool_destroy(data.pool);
599: }
600: return merged;
601: }
602:
603: CALLBACK(load_pool, vici_message_t*,
604: private_vici_attribute_t *this, char *name, u_int id,
605: vici_message_t *message)
606: {
607: request_data_t request = {
608: .this = this,
609: };
610:
611: if (!message->parse(message, NULL, pool_sn, NULL, NULL, &request))
612: {
613: if (request.reply)
614: {
615: return request.reply;
616: }
617: return create_reply("parsing request failed");
618: }
619: return create_reply(NULL);
620: }
621:
622: CALLBACK(unload_pool, vici_message_t*,
623: private_vici_attribute_t *this, char *name, u_int id,
624: vici_message_t *message)
625: {
626: vici_message_t *reply;
627: u_int online;
628: pool_t *pool;
629:
630: name = message->get_str(message, NULL, "name");
631: if (!name)
632: {
633: return create_reply("missing pool name to unload");
634: }
635:
636: this->lock->write_lock(this->lock);
637:
638: pool = this->pools->remove(this->pools, name);
639: if (pool)
640: {
641: online = pool->vips->get_online(pool->vips);
642: if (online)
643: {
644: DBG1(DBG_CFG, "vici pool %s has %u online leases, unable to unload",
645: name, online);
646: reply = create_reply("%s has online leases, unable to unload", name);
647: this->pools->put(this->pools, pool->vips->get_name(pool->vips), pool);
648: }
649: else
650: {
651: DBG1(DBG_CFG, "unloaded vici pool %s", name);
652: reply = create_reply(NULL);
653: pool_destroy(pool);
654: }
655: }
656: else
657: {
658: reply = create_reply("%s not found", name);
659: }
660:
661: this->lock->unlock(this->lock);
662:
663: return reply;
664: }
665:
666: CALLBACK(get_pools, vici_message_t*,
667: private_vici_attribute_t *this, char *name, u_int id,
668: vici_message_t *message)
669: {
670: vici_builder_t *builder;
671: enumerator_t *enumerator, *leases;
672: mem_pool_t *vips;
673: pool_t *pool;
674: identification_t *uid;
675: host_t *lease;
676: bool list_leases, on;
677: char buf[32], *filter;
678: int i;
679:
680: list_leases = message->get_bool(message, FALSE, "leases");
681: filter = message->get_str(message, NULL, "name");
682:
683: builder = vici_builder_create();
684:
685: this->lock->read_lock(this->lock);
686: enumerator = this->pools->create_enumerator(this->pools);
687: while (enumerator->enumerate(enumerator, &name, &pool))
688: {
689: if (filter && !streq(name, filter))
690: {
691: continue;
692: }
693:
694: vips = pool->vips;
695:
696: builder->begin_section(builder, name);
697:
698: builder->add_kv(builder, "base", "%H", vips->get_base(vips));
699: builder->add_kv(builder, "size", "%u", vips->get_size(vips));
700: builder->add_kv(builder, "online", "%u", vips->get_online(vips));
701: builder->add_kv(builder, "offline", "%u", vips->get_offline(vips));
702:
703: if (list_leases)
704: {
705: i = 0;
706: builder->begin_section(builder, "leases");
707: leases = vips->create_lease_enumerator(vips);
708: while (leases->enumerate(leases, &uid, &lease, &on))
709: {
710: snprintf(buf, sizeof(buf), "%d", i++);
711: builder->begin_section(builder, buf);
712: builder->add_kv(builder, "address", "%H", lease);
713: builder->add_kv(builder, "identity", "%Y", uid);
714: builder->add_kv(builder, "status", on ? "online" : "offline");
715: builder->end_section(builder);
716: }
717: leases->destroy(leases);
718: builder->end_section(builder);
719: }
720: builder->end_section(builder);
721: }
722: enumerator->destroy(enumerator);
723: this->lock->unlock(this->lock);
724:
725: return builder->finalize(builder);
726: }
727:
728: static void manage_command(private_vici_attribute_t *this,
729: char *name, vici_command_cb_t cb, bool reg)
730: {
731: this->dispatcher->manage_command(this->dispatcher, name,
732: reg ? cb : NULL, this);
733: }
734:
735: /**
736: * (Un-)register dispatcher functions
737: */
738: static void manage_commands(private_vici_attribute_t *this, bool reg)
739: {
740: manage_command(this, "load-pool", load_pool, reg);
741: manage_command(this, "unload-pool", unload_pool, reg);
742: manage_command(this, "get-pools", get_pools, reg);
743: }
744:
745: METHOD(vici_attribute_t, destroy, void,
746: private_vici_attribute_t *this)
747: {
748: enumerator_t *enumerator;
749: pool_t *pool;
750:
751: manage_commands(this, FALSE);
752:
753: enumerator = this->pools->create_enumerator(this->pools);
754: while (enumerator->enumerate(enumerator, NULL, &pool))
755: {
756: pool_destroy(pool);
757: }
758: enumerator->destroy(enumerator);
759: this->pools->destroy(this->pools);
760: this->lock->destroy(this->lock);
761: free(this);
762: }
763:
764: /*
765: * see header file
766: */
767: vici_attribute_t *vici_attribute_create(vici_dispatcher_t *dispatcher)
768: {
769: private_vici_attribute_t *this;
770:
771: INIT(this,
772: .public = {
773: .provider = {
774: .acquire_address = _acquire_address,
775: .release_address = _release_address,
776: .create_attribute_enumerator = _create_attribute_enumerator,
777: },
778: .destroy = _destroy,
779: },
780: .dispatcher = dispatcher,
781: .pools = hashtable_create(hashtable_hash_str, hashtable_equals_str, 4),
782: .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
783: );
784:
785: manage_commands(this, TRUE);
786:
787: return &this->public;
788: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>