Return to kernel_libipsec_ipsec.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / strongswan / src / libcharon / plugins / kernel_libipsec |
1.1 misho 1: /* 2: * Copyright (C) 2012-2013 Tobias Brunner 3: * HSR 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: * This program is distributed in the hope that it will be useful, but 10: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12: * for more details. 13: */ 14: 15: #include "kernel_libipsec_ipsec.h" 16: #include "kernel_libipsec_router.h" 17: 18: #include <library.h> 19: #include <ipsec.h> 20: #include <daemon.h> 21: #include <networking/tun_device.h> 22: #include <threading/mutex.h> 23: #include <utils/debug.h> 24: 25: typedef struct private_kernel_libipsec_ipsec_t private_kernel_libipsec_ipsec_t; 26: 27: struct private_kernel_libipsec_ipsec_t { 28: 29: /** 30: * Public libipsec_ipsec interface 31: */ 32: kernel_libipsec_ipsec_t public; 33: 34: /** 35: * Listener for lifetime expire events 36: */ 37: ipsec_event_listener_t ipsec_listener; 38: 39: /** 40: * Mutex to lock access to various lists 41: */ 42: mutex_t *mutex; 43: 44: /** 45: * List of installed policies (policy_entry_t) 46: */ 47: linked_list_t *policies; 48: 49: /** 50: * List of exclude routes (exclude_route_t) 51: */ 52: linked_list_t *excludes; 53: 54: /** 55: * Whether the remote TS may equal the IKE peer 56: */ 57: bool allow_peer_ts; 58: }; 59: 60: typedef struct exclude_route_t exclude_route_t; 61: 62: /** 63: * Exclude route definition 64: */ 65: struct exclude_route_t { 66: /** Destination address to exclude */ 67: host_t *dst; 68: /** Source address for route */ 69: host_t *src; 70: /** Nexthop exclude has been installed */ 71: host_t *gtw; 72: /** References to this route */ 73: int refs; 74: }; 75: 76: /** 77: * Clean up an exclude route entry 78: */ 79: static void exclude_route_destroy(exclude_route_t *this) 80: { 81: this->dst->destroy(this->dst); 82: this->src->destroy(this->src); 83: this->gtw->destroy(this->gtw); 84: free(this); 85: } 86: 87: CALLBACK(exclude_route_match, bool, 88: exclude_route_t *current, va_list args) 89: { 90: host_t *dst; 91: 92: VA_ARGS_VGET(args, dst); 93: return dst->ip_equals(dst, current->dst); 94: } 95: 96: typedef struct route_entry_t route_entry_t; 97: 98: /** 99: * Installed routing entry 100: */ 101: struct route_entry_t { 102: /** Name of the interface the route is bound to */ 103: char *if_name; 104: /** Source IP of the route */ 105: host_t *src_ip; 106: /** Gateway of the route */ 107: host_t *gateway; 108: /** Destination net */ 109: chunk_t dst_net; 110: /** Destination net prefixlen */ 111: uint8_t prefixlen; 112: /** Reference to exclude route, if any */ 113: exclude_route_t *exclude; 114: }; 115: 116: /** 117: * Destroy a route_entry_t object 118: */ 119: static void route_entry_destroy(route_entry_t *this) 120: { 121: free(this->if_name); 122: DESTROY_IF(this->src_ip); 123: DESTROY_IF(this->gateway); 124: chunk_free(&this->dst_net); 125: free(this); 126: } 127: 128: /** 129: * Compare two route_entry_t objects 130: */ 131: static bool route_entry_equals(route_entry_t *a, route_entry_t *b) 132: { 133: if ((!a->src_ip && !b->src_ip) || (a->src_ip && b->src_ip && 134: a->src_ip->ip_equals(a->src_ip, b->src_ip))) 135: { 136: if ((!a->gateway && !b->gateway) || (a->gateway && b->gateway && 137: a->gateway->ip_equals(a->gateway, b->gateway))) 138: { 139: return a->if_name && b->if_name && streq(a->if_name, b->if_name) && 140: chunk_equals(a->dst_net, b->dst_net) && 141: a->prefixlen == b->prefixlen; 142: } 143: } 144: return FALSE; 145: } 146: 147: typedef struct policy_entry_t policy_entry_t; 148: 149: /** 150: * Installed policy 151: */ 152: struct policy_entry_t { 153: /** Direction of this policy: in, out, forward */ 154: uint8_t direction; 155: /** Parameters of installed policy */ 156: struct { 157: /** Subnet and port */ 158: host_t *net; 159: /** Subnet mask */ 160: uint8_t mask; 161: /** Protocol */ 162: uint8_t proto; 163: } src, dst; 164: /** Associated route installed for this policy */ 165: route_entry_t *route; 166: /** References to this policy */ 167: int refs; 168: }; 169: 170: /** 171: * Create a policy_entry_t object 172: */ 173: static policy_entry_t *create_policy_entry(traffic_selector_t *src_ts, 174: traffic_selector_t *dst_ts, 175: policy_dir_t dir) 176: { 177: policy_entry_t *this; 178: INIT(this, 179: .direction = dir, 180: ); 181: 182: src_ts->to_subnet(src_ts, &this->src.net, &this->src.mask); 183: dst_ts->to_subnet(dst_ts, &this->dst.net, &this->dst.mask); 184: 185: /* src or dest proto may be "any" (0), use more restrictive one */ 186: this->src.proto = max(src_ts->get_protocol(src_ts), 187: dst_ts->get_protocol(dst_ts)); 188: this->src.proto = this->src.proto ? this->src.proto : 0; 189: this->dst.proto = this->src.proto; 190: return this; 191: } 192: 193: /** 194: * Destroy a policy_entry_t object 195: */ 196: static void policy_entry_destroy(policy_entry_t *this) 197: { 198: if (this->route) 199: { 200: route_entry_destroy(this->route); 201: } 202: DESTROY_IF(this->src.net); 203: DESTROY_IF(this->dst.net); 204: free(this); 205: } 206: 207: CALLBACK(policy_entry_equals, bool, 208: policy_entry_t *a, va_list args) 209: { 210: policy_entry_t *b; 211: 212: VA_ARGS_VGET(args, b); 213: return a->direction == b->direction && 214: a->src.proto == b->src.proto && 215: a->dst.proto == b->dst.proto && 216: a->src.mask == b->src.mask && 217: a->dst.mask == b->dst.mask && 218: a->src.net->equals(a->src.net, b->src.net) && 219: a->dst.net->equals(a->dst.net, b->dst.net); 220: } 221: 222: /** 223: * Expiration callback 224: */ 225: static void expire(uint8_t protocol, uint32_t spi, host_t *dst, bool hard) 226: { 227: charon->kernel->expire(charon->kernel, protocol, spi, dst, hard); 228: } 229: 230: METHOD(kernel_ipsec_t, get_features, kernel_feature_t, 231: private_kernel_libipsec_ipsec_t *this) 232: { 233: return KERNEL_REQUIRE_UDP_ENCAPSULATION | KERNEL_ESP_V3_TFC; 234: } 235: 236: METHOD(kernel_ipsec_t, get_spi, status_t, 237: private_kernel_libipsec_ipsec_t *this, host_t *src, host_t *dst, 238: uint8_t protocol, uint32_t *spi) 239: { 240: return ipsec->sas->get_spi(ipsec->sas, src, dst, protocol, spi); 241: } 242: 243: METHOD(kernel_ipsec_t, get_cpi, status_t, 244: private_kernel_libipsec_ipsec_t *this, host_t *src, host_t *dst, 245: uint16_t *cpi) 246: { 247: return NOT_SUPPORTED; 248: } 249: 250: METHOD(kernel_ipsec_t, add_sa, status_t, 251: private_kernel_libipsec_ipsec_t *this, kernel_ipsec_sa_id_t *id, 252: kernel_ipsec_add_sa_t *data) 253: { 254: return ipsec->sas->add_sa(ipsec->sas, id->src, id->dst, id->spi, id->proto, 255: data->reqid, id->mark, data->tfc, data->lifetime, 256: data->enc_alg, data->enc_key, data->int_alg, data->int_key, 257: data->mode, data->ipcomp, data->cpi, data->initiator, 258: data->encap, data->esn, data->inbound, data->update); 259: } 260: 261: METHOD(kernel_ipsec_t, update_sa, status_t, 262: private_kernel_libipsec_ipsec_t *this, kernel_ipsec_sa_id_t *id, 263: kernel_ipsec_update_sa_t *data) 264: { 265: return NOT_SUPPORTED; 266: } 267: 268: METHOD(kernel_ipsec_t, query_sa, status_t, 269: private_kernel_libipsec_ipsec_t *this, kernel_ipsec_sa_id_t *id, 270: kernel_ipsec_query_sa_t *data, uint64_t *bytes, uint64_t *packets, 271: time_t *time) 272: { 273: return ipsec->sas->query_sa(ipsec->sas, id->src, id->dst, id->spi, 274: id->proto, id->mark, bytes, packets, time); 275: } 276: 277: METHOD(kernel_ipsec_t, del_sa, status_t, 278: private_kernel_libipsec_ipsec_t *this, kernel_ipsec_sa_id_t *id, 279: kernel_ipsec_del_sa_t *data) 280: { 281: return ipsec->sas->del_sa(ipsec->sas, id->src, id->dst, id->spi, id->proto, 282: data->cpi, id->mark); 283: } 284: 285: METHOD(kernel_ipsec_t, flush_sas, status_t, 286: private_kernel_libipsec_ipsec_t *this) 287: { 288: return ipsec->sas->flush_sas(ipsec->sas); 289: } 290: 291: /** 292: * Add an explicit exclude route to a routing entry 293: */ 294: static void add_exclude_route(private_kernel_libipsec_ipsec_t *this, 295: route_entry_t *route, host_t *src, host_t *dst) 296: { 297: exclude_route_t *exclude; 298: host_t *gtw; 299: 300: if (this->excludes->find_first(this->excludes, exclude_route_match, 301: (void**)&exclude, dst)) 302: { 303: route->exclude = exclude; 304: exclude->refs++; 305: } 306: 307: if (!route->exclude) 308: { 309: DBG2(DBG_KNL, "installing new exclude route for %H src %H", dst, src); 310: gtw = charon->kernel->get_nexthop(charon->kernel, dst, -1, NULL, NULL); 311: if (gtw) 312: { 313: char *if_name = NULL; 314: 315: if (charon->kernel->get_interface(charon->kernel, src, &if_name) && 316: charon->kernel->add_route(charon->kernel, dst->get_address(dst), 317: dst->get_family(dst) == AF_INET ? 32 : 128, 318: gtw, src, if_name, TRUE) == SUCCESS) 319: { 320: INIT(exclude, 321: .dst = dst->clone(dst), 322: .src = src->clone(src), 323: .gtw = gtw->clone(gtw), 324: .refs = 1, 325: ); 326: route->exclude = exclude; 327: this->excludes->insert_last(this->excludes, exclude); 328: } 329: else 330: { 331: DBG1(DBG_KNL, "installing exclude route for %H failed", dst); 332: } 333: gtw->destroy(gtw); 334: free(if_name); 335: } 336: else 337: { 338: DBG1(DBG_KNL, "gateway lookup for %H failed", dst); 339: } 340: } 341: } 342: 343: /** 344: * Remove an exclude route attached to a routing entry 345: */ 346: static void remove_exclude_route(private_kernel_libipsec_ipsec_t *this, 347: route_entry_t *route) 348: { 349: char *if_name = NULL; 350: host_t *dst; 351: 352: if (!route->exclude || --route->exclude->refs > 0) 353: { 354: return; 355: } 356: this->excludes->remove(this->excludes, route->exclude, NULL); 357: 358: dst = route->exclude->dst; 359: DBG2(DBG_KNL, "uninstalling exclude route for %H src %H", 360: dst, route->exclude->src); 361: if (charon->kernel->get_interface(charon->kernel, route->exclude->src, 362: &if_name) && 363: charon->kernel->del_route(charon->kernel, dst->get_address(dst), 364: dst->get_family(dst) == AF_INET ? 32 : 128, 365: route->exclude->gtw, route->exclude->src, 366: if_name, TRUE) != SUCCESS) 367: { 368: DBG1(DBG_KNL, "uninstalling exclude route for %H failed", dst); 369: } 370: exclude_route_destroy(route->exclude); 371: route->exclude = NULL; 372: free(if_name); 373: } 374: 375: /** 376: * Install a route for the given policy 377: * 378: * this->mutex is released by this function 379: */ 380: static bool install_route(private_kernel_libipsec_ipsec_t *this, 381: host_t *src, host_t *dst, traffic_selector_t *src_ts, 382: traffic_selector_t *dst_ts, policy_entry_t *policy) 383: { 384: route_entry_t *route, *old; 385: host_t *src_ip; 386: bool is_virtual; 387: 388: if (policy->direction != POLICY_OUT) 389: { 390: this->mutex->unlock(this->mutex); 391: return TRUE; 392: } 393: 394: if (charon->kernel->get_address_by_ts(charon->kernel, src_ts, &src_ip, 395: &is_virtual) != SUCCESS) 396: { 397: traffic_selector_t *multicast, *broadcast = NULL; 398: bool ignore = FALSE; 399: 400: this->mutex->unlock(this->mutex); 401: switch (src_ts->get_type(src_ts)) 402: { 403: case TS_IPV4_ADDR_RANGE: 404: multicast = traffic_selector_create_from_cidr("224.0.0.0/4", 405: 0, 0, 0xffff); 406: broadcast = traffic_selector_create_from_cidr("255.255.255.255/32", 407: 0, 0, 0xffff); 408: break; 409: case TS_IPV6_ADDR_RANGE: 410: multicast = traffic_selector_create_from_cidr("ff00::/8", 411: 0, 0, 0xffff); 412: break; 413: default: 414: return FALSE; 415: } 416: ignore = src_ts->is_contained_in(src_ts, multicast); 417: ignore |= broadcast && src_ts->is_contained_in(src_ts, broadcast); 418: multicast->destroy(multicast); 419: DESTROY_IF(broadcast); 420: if (!ignore) 421: { 422: DBG1(DBG_KNL, "error installing route with policy %R === %R %N", 423: src_ts, dst_ts, policy_dir_names, policy->direction); 424: } 425: return ignore; 426: } 427: 428: INIT(route, 429: .if_name = router->get_tun_name(router, is_virtual ? src_ip : NULL), 430: .src_ip = src_ip, 431: .dst_net = chunk_clone(policy->dst.net->get_address(policy->dst.net)), 432: .prefixlen = policy->dst.mask, 433: ); 434: #ifndef __linux__ 435: /* on Linux we can't install a gateway */ 436: route->gateway = charon->kernel->get_nexthop(charon->kernel, dst, -1, src, 437: NULL); 438: #endif 439: 440: if (policy->route) 441: { 442: old = policy->route; 443: 444: if (route_entry_equals(old, route)) 445: { /* such a route already exists */ 446: route_entry_destroy(route); 447: this->mutex->unlock(this->mutex); 448: return TRUE; 449: } 450: /* uninstall previously installed route */ 451: if (charon->kernel->del_route(charon->kernel, old->dst_net, 452: old->prefixlen, old->gateway, old->src_ip, 453: old->if_name, FALSE) != SUCCESS) 454: { 455: DBG1(DBG_KNL, "error uninstalling route installed with policy " 456: "%R === %R %N", src_ts, dst_ts, policy_dir_names, 457: policy->direction); 458: } 459: route_entry_destroy(old); 460: policy->route = NULL; 461: } 462: 463: if (!this->allow_peer_ts && dst_ts->is_host(dst_ts, dst)) 464: { 465: DBG1(DBG_KNL, "can't install route for %R === %R %N, conflicts with " 466: "IKE traffic", src_ts, dst_ts, policy_dir_names, 467: policy->direction); 468: route_entry_destroy(route); 469: this->mutex->unlock(this->mutex); 470: return FALSE; 471: } 472: /* if remote traffic selector covers the IKE peer, add an exclude route */ 473: if (!this->allow_peer_ts && dst_ts->includes(dst_ts, dst)) 474: { 475: /* add exclude route for peer */ 476: add_exclude_route(this, route, src, dst); 477: } 478: 479: DBG2(DBG_KNL, "installing route: %R src %H dev %s", 480: dst_ts, route->src_ip, route->if_name); 481: 482: switch (charon->kernel->add_route(charon->kernel, route->dst_net, 483: route->prefixlen, route->gateway, 484: route->src_ip, route->if_name, FALSE)) 485: { 486: case ALREADY_DONE: 487: /* route exists, do not uninstall */ 488: remove_exclude_route(this, route); 489: route_entry_destroy(route); 490: this->mutex->unlock(this->mutex); 491: return TRUE; 492: case SUCCESS: 493: /* cache the installed route */ 494: policy->route = route; 495: this->mutex->unlock(this->mutex); 496: return TRUE; 497: default: 498: DBG1(DBG_KNL, "installing route failed: %R src %H dev %s", 499: dst_ts, route->src_ip, route->if_name); 500: remove_exclude_route(this, route); 501: route_entry_destroy(route); 502: this->mutex->unlock(this->mutex); 503: return FALSE; 504: } 505: } 506: 507: METHOD(kernel_ipsec_t, add_policy, status_t, 508: private_kernel_libipsec_ipsec_t *this, kernel_ipsec_policy_id_t *id, 509: kernel_ipsec_manage_policy_t *data) 510: { 511: policy_entry_t *policy, *found = NULL; 512: status_t status; 513: 514: status = ipsec->policies->add_policy(ipsec->policies, data->src, data->dst, 515: id->src_ts, id->dst_ts, id->dir, 516: data->type, data->sa, id->mark, 517: data->prio); 518: if (status != SUCCESS) 519: { 520: return status; 521: } 522: /* we track policies in order to install routes */ 523: policy = create_policy_entry(id->src_ts, id->dst_ts, id->dir); 524: 525: this->mutex->lock(this->mutex); 526: if (this->policies->find_first(this->policies, policy_entry_equals, 527: (void**)&found, policy)) 528: { 529: policy_entry_destroy(policy); 530: policy = found; 531: } 532: else 533: { /* use the new one, if we have no such policy */ 534: this->policies->insert_last(this->policies, policy); 535: } 536: policy->refs++; 537: 538: if (!install_route(this, data->src, data->dst, id->src_ts, id->dst_ts, 539: policy)) 540: { 541: return FAILED; 542: } 543: return SUCCESS; 544: } 545: 546: METHOD(kernel_ipsec_t, query_policy, status_t, 547: private_kernel_libipsec_ipsec_t *this, kernel_ipsec_policy_id_t *id, 548: kernel_ipsec_query_policy_t *data, time_t *use_time) 549: { 550: return NOT_SUPPORTED; 551: } 552: 553: METHOD(kernel_ipsec_t, del_policy, status_t, 554: private_kernel_libipsec_ipsec_t *this, kernel_ipsec_policy_id_t *id, 555: kernel_ipsec_manage_policy_t *data) 556: { 557: policy_entry_t *policy, *found = NULL; 558: status_t status; 559: 560: status = ipsec->policies->del_policy(ipsec->policies, data->src, data->dst, 561: id->src_ts, id->dst_ts, id->dir, 562: data->type, data->sa, id->mark, 563: data->prio); 564: 565: policy = create_policy_entry(id->src_ts, id->dst_ts, id->dir); 566: 567: this->mutex->lock(this->mutex); 568: if (!this->policies->find_first(this->policies, policy_entry_equals, 569: (void**)&found, policy)) 570: { 571: policy_entry_destroy(policy); 572: this->mutex->unlock(this->mutex); 573: return status; 574: } 575: policy_entry_destroy(policy); 576: policy = found; 577: 578: if (--policy->refs > 0) 579: { /* policy is still in use */ 580: this->mutex->unlock(this->mutex); 581: return status; 582: } 583: 584: if (policy->route) 585: { 586: route_entry_t *route = policy->route; 587: 588: if (charon->kernel->del_route(charon->kernel, route->dst_net, 589: route->prefixlen, route->gateway, route->src_ip, 590: route->if_name, FALSE) != SUCCESS) 591: { 592: DBG1(DBG_KNL, "error uninstalling route installed with " 593: "policy %R === %R %N", id->src_ts, id->dst_ts, 594: policy_dir_names, id->dir); 595: } 596: remove_exclude_route(this, route); 597: } 598: this->policies->remove(this->policies, policy, NULL); 599: policy_entry_destroy(policy); 600: this->mutex->unlock(this->mutex); 601: return status; 602: } 603: 604: METHOD(kernel_ipsec_t, flush_policies, status_t, 605: private_kernel_libipsec_ipsec_t *this) 606: { 607: policy_entry_t *pol; 608: status_t status; 609: 610: status = ipsec->policies->flush_policies(ipsec->policies); 611: 612: this->mutex->lock(this->mutex); 613: while (this->policies->remove_first(this->policies, (void*)&pol) == SUCCESS) 614: { 615: if (pol->route) 616: { 617: route_entry_t *route = pol->route; 618: 619: charon->kernel->del_route(charon->kernel, route->dst_net, 620: route->prefixlen, route->gateway, 621: route->src_ip, route->if_name, FALSE); 622: remove_exclude_route(this, route); 623: } 624: policy_entry_destroy(pol); 625: } 626: this->mutex->unlock(this->mutex); 627: return status; 628: } 629: 630: METHOD(kernel_ipsec_t, bypass_socket, bool, 631: private_kernel_libipsec_ipsec_t *this, int fd, int family) 632: { 633: /* we use exclude routes for this */ 634: return NOT_SUPPORTED; 635: } 636: 637: METHOD(kernel_ipsec_t, enable_udp_decap, bool, 638: private_kernel_libipsec_ipsec_t *this, int fd, int family, uint16_t port) 639: { 640: return NOT_SUPPORTED; 641: } 642: 643: METHOD(kernel_ipsec_t, destroy, void, 644: private_kernel_libipsec_ipsec_t *this) 645: { 646: ipsec->events->unregister_listener(ipsec->events, &this->ipsec_listener); 647: this->policies->destroy_function(this->policies, (void*)policy_entry_destroy); 648: this->excludes->destroy(this->excludes); 649: this->mutex->destroy(this->mutex); 650: free(this); 651: } 652: 653: /* 654: * Described in header. 655: */ 656: kernel_libipsec_ipsec_t *kernel_libipsec_ipsec_create() 657: { 658: private_kernel_libipsec_ipsec_t *this; 659: 660: INIT(this, 661: .public = { 662: .interface = { 663: .get_features = _get_features, 664: .get_spi = _get_spi, 665: .get_cpi = _get_cpi, 666: .add_sa = _add_sa, 667: .update_sa = _update_sa, 668: .query_sa = _query_sa, 669: .del_sa = _del_sa, 670: .flush_sas = _flush_sas, 671: .add_policy = _add_policy, 672: .query_policy = _query_policy, 673: .del_policy = _del_policy, 674: .flush_policies = _flush_policies, 675: .bypass_socket = _bypass_socket, 676: .enable_udp_decap = _enable_udp_decap, 677: .destroy = _destroy, 678: }, 679: }, 680: .ipsec_listener = { 681: .expire = expire, 682: }, 683: .mutex = mutex_create(MUTEX_TYPE_DEFAULT), 684: .policies = linked_list_create(), 685: .excludes = linked_list_create(), 686: .allow_peer_ts = lib->settings->get_bool(lib->settings, 687: "%s.plugins.kernel-libipsec.allow_peer_ts", FALSE, lib->ns), 688: ); 689: 690: ipsec->events->register_listener(ipsec->events, &this->ipsec_listener); 691: 692: return &this->public; 693: };