Return to vici_attribute.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / strongswan / src / libcharon / plugins / vici |
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: }