diff options
author | Yves-Alexis Perez <corsac@debian.org> | 2015-10-22 11:43:58 +0200 |
---|---|---|
committer | Yves-Alexis Perez <corsac@debian.org> | 2015-10-22 11:43:58 +0200 |
commit | 5dca9ea0e2931f0e2a056c7964d311bcc30a01b8 (patch) | |
tree | 037f1ec5bb860846938ddcf29771c24e9c529be0 /src/libhydra | |
parent | b238cf34df3fe4476ae6b7012e7cb3e9769d4d51 (diff) | |
download | vyos-strongswan-5dca9ea0e2931f0e2a056c7964d311bcc30a01b8.tar.gz vyos-strongswan-5dca9ea0e2931f0e2a056c7964d311bcc30a01b8.zip |
Imported Upstream version 5.3.3
Diffstat (limited to 'src/libhydra')
6 files changed, 171 insertions, 77 deletions
diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c index f22e07d95..605476ef1 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -199,6 +199,7 @@ static kernel_algorithm_t encryption_algs[] = { /* {ENCR_CAMELLIA_CCM_ICV16, "***" }, */ {ENCR_SERPENT_CBC, "serpent" }, {ENCR_TWOFISH_CBC, "twofish" }, + {ENCR_CHACHA20_POLY1305, "rfc7539esp(chacha20,poly1305)"}, }; /** @@ -734,6 +735,7 @@ static struct xfrm_selector ts2selector(traffic_selector_t *src, traffic_selector_t *dst) { struct xfrm_selector sel; + u_int16_t port; memset(&sel, 0, sizeof(sel)); sel.family = (src->get_type(src) == TS_IPV4_ADDR_RANGE) ? AF_INET : AF_INET6; @@ -746,13 +748,13 @@ static struct xfrm_selector ts2selector(traffic_selector_t *src, if ((sel.proto == IPPROTO_ICMP || sel.proto == IPPROTO_ICMPV6) && (sel.dport || sel.sport)) { - /* the ICMP type is encoded in the most significant 8 bits and the ICMP - * code in the least significant 8 bits of the port. via XFRM we have - * to pass the ICMP type and code in the source and destination port - * fields, respectively. the port is in network byte order. */ - u_int16_t port = max(sel.dport, sel.sport); - sel.sport = htons(port & 0xff); - sel.dport = htons(port >> 8); + /* the kernel expects the ICMP type and code in the source and + * destination port fields, respectively. */ + port = ntohs(max(sel.dport, sel.sport)); + sel.sport = htons(traffic_selector_icmp_type(port)); + sel.sport_mask = sel.sport ? ~0 : 0; + sel.dport = htons(traffic_selector_icmp_code(port)); + sel.dport_mask = sel.dport ? ~0 : 0; } sel.ifindex = 0; sel.user = 0; @@ -1291,6 +1293,7 @@ METHOD(kernel_ipsec_t, add_sa, status_t, case ENCR_AES_GCM_ICV16: case ENCR_NULL_AUTH_AES_GMAC: case ENCR_CAMELLIA_CCM_ICV16: + case ENCR_CHACHA20_POLY1305: icv_size += 32; /* FALL */ case ENCR_AES_CCM_ICV12: @@ -2022,23 +2025,36 @@ METHOD(kernel_ipsec_t, flush_sas, status_t, netlink_buf_t request; struct nlmsghdr *hdr; struct xfrm_usersa_flush *flush; + struct { + u_int8_t proto; + char *name; + } protos[] = { + { IPPROTO_AH, "AH" }, + { IPPROTO_ESP, "ESP" }, + { IPPROTO_COMP, "IPComp" }, + }; + int i; memset(&request, 0, sizeof(request)); - DBG2(DBG_KNL, "flushing all SAD entries"); - hdr = &request.hdr; hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; hdr->nlmsg_type = XFRM_MSG_FLUSHSA; hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_flush)); flush = NLMSG_DATA(hdr); - flush->proto = IPSEC_PROTO_ANY; - if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS) + for (i = 0; i < countof(protos); i++) { - DBG1(DBG_KNL, "unable to flush SAD entries"); - return FAILED; + DBG2(DBG_KNL, "flushing all %s SAD entries", protos[i].name); + + flush->proto = protos[i].proto; + + if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS) + { + DBG1(DBG_KNL, "unable to flush %s SAD entries", protos[i].name); + return FAILED; + } } return SUCCESS; } @@ -2057,6 +2073,7 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, ipsec_sa_t *ipsec = mapping->sa; struct xfrm_userpolicy_info *policy_info; struct nlmsghdr *hdr; + status_t status; int i; /* clone the policy so we are able to check it out again later */ @@ -2151,7 +2168,14 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, } this->mutex->unlock(this->mutex); - if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS) + status = this->socket_xfrm->send_ack(this->socket_xfrm, hdr); + if (status == ALREADY_DONE && !update) + { + DBG1(DBG_KNL, "policy already exists, try to update it"); + hdr->nlmsg_type = XFRM_MSG_UPDPOLICY; + status = this->socket_xfrm->send_ack(this->socket_xfrm, hdr); + } + if (status != SUCCESS) { return FAILED; } @@ -2560,6 +2584,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t, if (!add_mark(hdr, sizeof(request), mark)) { + this->mutex->unlock(this->mutex); return FAILED; } diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c index 1515b01cc..4e5e02d07 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c @@ -491,6 +491,16 @@ struct private_kernel_netlink_net_t { bool rta_prefsrc_for_ipv6; /** + * whether marks can be used in route lookups + */ + bool rta_mark; + + /** + * the mark excluded from the routing rule used for virtual IPs + */ + mark_t routing_mark; + + /** * whether to prefer temporary IPv6 addresses over public ones */ bool prefer_temporary_addrs; @@ -1676,18 +1686,25 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest, family = dest->get_family(dest); hdr = &request.hdr; hdr->nlmsg_flags = NLM_F_REQUEST; - if (family == AF_INET || this->rta_prefsrc_for_ipv6 || - this->routing_table || match_net) - { /* kernels prior to 3.0 do not support RTA_PREFSRC for IPv6 routes. - * as we want to ignore routes with virtual IPs we cannot use DUMP - * if these routes are not installed in a separate table */ - hdr->nlmsg_flags |= NLM_F_DUMP; - } hdr->nlmsg_type = RTM_GETROUTE; hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); msg = NLMSG_DATA(hdr); msg->rtm_family = family; + if (!match_net && this->rta_mark && this->routing_mark.value) + { + /* if our routing rule excludes packets with a certain mark we can + * get the preferred route without having to dump all routes */ + chunk = chunk_from_thing(this->routing_mark.value); + netlink_add_attribute(hdr, RTA_MARK, chunk, sizeof(request)); + } + else if (family == AF_INET || this->rta_prefsrc_for_ipv6 || + this->routing_table || match_net) + { /* kernels prior to 3.0 do not support RTA_PREFSRC for IPv6 routes. + * as we want to ignore routes with virtual IPs we cannot use DUMP + * if these routes are not installed in a separate table */ + hdr->nlmsg_flags |= NLM_F_DUMP; + } if (candidate) { chunk = candidate->get_address(candidate); @@ -2412,6 +2429,10 @@ static status_t manage_rule(private_kernel_netlink_net_t *this, int nlmsg_type, netlink_add_attribute(hdr, FRA_FWMARK, chunk, sizeof(request)); chunk = chunk_from_thing(mark.mask); netlink_add_attribute(hdr, FRA_FWMASK, chunk, sizeof(request)); + if (msg->rtm_flags & FIB_RULE_INVERT) + { + this->routing_mark = mark; + } } #else DBG1(DBG_KNL, "setting firewall mark on routing rule is not supported"); @@ -2435,6 +2456,10 @@ static void check_kernel_features(private_kernel_netlink_net_t *this) case 3: if (a == 2) { + if (b == 6 && c >= 36) + { + this->rta_mark = TRUE; + } DBG2(DBG_KNL, "detected Linux %d.%d.%d, no support for " "RTA_PREFSRC for IPv6 routes", a, b, c); break; @@ -2443,6 +2468,7 @@ static void check_kernel_features(private_kernel_netlink_net_t *this) case 2: /* only 3.x+ uses two part version numbers */ this->rta_prefsrc_for_ipv6 = TRUE; + this->rta_mark = TRUE; break; default: break; diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c index b0e3103d3..f7ce992a3 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c @@ -185,8 +185,8 @@ static ssize_t read_msg(private_netlink_socket_t *this, return -1; } } - len = recv(this->socket, buf, buflen, block ? 0 : MSG_DONTWAIT); - if (len == buflen) + len = recv(this->socket, buf, buflen, MSG_TRUNC|(block ? 0 : MSG_DONTWAIT)); + if (len > buflen) { DBG1(DBG_KNL, "netlink response exceeds buffer size"); return 0; @@ -571,7 +571,7 @@ netlink_socket_t *netlink_socket_create(int protocol, enum_name_t *names, .protocol = protocol, .names = names, .buflen = lib->settings->get_int(lib->settings, - "%s.plugins.kernel-netlink.buflen", 4096, lib->ns), + "%s.plugins.kernel-netlink.buflen", 0, lib->ns), .timeout = lib->settings->get_int(lib->settings, "%s.plugins.kernel-netlink.timeout", 0, lib->ns), .retries = lib->settings->get_int(lib->settings, @@ -582,6 +582,16 @@ netlink_socket_t *netlink_socket_create(int protocol, enum_name_t *names, .parallel = parallel, ); + if (!this->buflen) + { + long pagesize = sysconf(_SC_PAGESIZE); + if (pagesize == -1) + { + pagesize = 4096; + } + /* base this on NLMSG_GOODSIZE */ + this->buflen = min(pagesize, 8192); + } if (this->socket == -1) { DBG1(DBG_KNL, "unable to create netlink socket"); diff --git a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index 3b32ba553..5027e1759 100644 --- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -106,6 +106,12 @@ #define SADB_X_EALG_CASTCBC SADB_X_EALG_CAST128CBC #endif +#if !defined(SADB_X_EALG_AES_GCM_ICV8) && defined(SADB_X_EALG_AESGCM8) +#define SADB_X_EALG_AES_GCM_ICV8 SADB_X_EALG_AESGCM8 +#define SADB_X_EALG_AES_GCM_ICV12 SADB_X_EALG_AESGCM12 +#define SADB_X_EALG_AES_GCM_ICV16 SADB_X_EALG_AESGCM16 +#endif + #ifndef SOL_IP #define SOL_IP IPPROTO_IP #define SOL_IPV6 IPPROTO_IPV6 @@ -508,15 +514,30 @@ static policy_entry_t *create_policy_entry(traffic_selector_t *src_ts, INIT(policy, .direction = dir, ); + u_int16_t port; + u_int8_t proto; src_ts->to_subnet(src_ts, &policy->src.net, &policy->src.mask); dst_ts->to_subnet(dst_ts, &policy->dst.net, &policy->dst.mask); /* src or dest proto may be "any" (0), use more restrictive one */ - policy->src.proto = max(src_ts->get_protocol(src_ts), - dst_ts->get_protocol(dst_ts)); - policy->src.proto = policy->src.proto ? policy->src.proto : IPSEC_PROTO_ANY; - policy->dst.proto = policy->src.proto; + proto = max(src_ts->get_protocol(src_ts), dst_ts->get_protocol(dst_ts)); + /* map the ports to ICMP type/code how the Linux kernel expects them, that + * is, type in src, code in dst */ + if (proto == IPPROTO_ICMP || proto == IPPROTO_ICMPV6) + { + port = max(policy->src.net->get_port(policy->src.net), + policy->dst.net->get_port(policy->dst.net)); + policy->src.net->set_port(policy->src.net, + traffic_selector_icmp_type(port)); + policy->dst.net->set_port(policy->dst.net, + traffic_selector_icmp_code(port)); + } + else if (!proto) + { + proto = IPSEC_PROTO_ANY; + } + policy->src.proto = policy->dst.proto = proto; return policy; } @@ -826,9 +847,11 @@ static kernel_algorithm_t encryption_algs[] = { /* {ENCR_AES_CCM_ICV8, SADB_X_EALG_AES_CCM_ICV8 }, */ /* {ENCR_AES_CCM_ICV12, SADB_X_EALG_AES_CCM_ICV12 }, */ /* {ENCR_AES_CCM_ICV16, SADB_X_EALG_AES_CCM_ICV16 }, */ -/* {ENCR_AES_GCM_ICV8, SADB_X_EALG_AES_GCM_ICV8 }, */ -/* {ENCR_AES_GCM_ICV12, SADB_X_EALG_AES_GCM_ICV12 }, */ -/* {ENCR_AES_GCM_ICV16, SADB_X_EALG_AES_GCM_ICV16 }, */ +#ifdef SADB_X_EALG_AES_GCM_ICV8 /* assume the others are defined too */ + {ENCR_AES_GCM_ICV8, SADB_X_EALG_AES_GCM_ICV8 }, + {ENCR_AES_GCM_ICV12, SADB_X_EALG_AES_GCM_ICV12 }, + {ENCR_AES_GCM_ICV16, SADB_X_EALG_AES_GCM_ICV16 }, +#endif {END_OF_LIST, 0 }, }; @@ -942,28 +965,6 @@ static size_t hostcpy(void *dest, host_t *host, bool include_port) } /** - * Copy a host_t as sockaddr_t to the given memory location and map the port to - * ICMP/ICMPv6 message type/code as the Linux kernel expects it, that is, the - * type in the source and the code in the destination address. - * @return the number of bytes copied - */ -static size_t hostcpy_icmp(void *dest, host_t *host, u_int16_t type) -{ - size_t len; - - len = hostcpy(dest, host, TRUE); - if (type == SADB_EXT_ADDRESS_SRC) - { - set_port(dest, traffic_selector_icmp_type(host->get_port(host))); - } - else - { - set_port(dest, traffic_selector_icmp_code(host->get_port(host))); - } - return len; -} - -/** * add a host to the given sadb_msg */ static void add_addr_ext(struct sadb_msg *msg, host_t *host, u_int16_t type, @@ -975,14 +976,7 @@ static void add_addr_ext(struct sadb_msg *msg, host_t *host, u_int16_t type, addr->sadb_address_exttype = type; addr->sadb_address_proto = proto; addr->sadb_address_prefixlen = prefixlen; - if (proto == IPPROTO_ICMP || proto == IPPROTO_ICMPV6) - { - len = hostcpy_icmp(addr + 1, host, type); - } - else - { - len = hostcpy(addr + 1, host, include_port); - } + len = hostcpy(addr + 1, host, include_port); addr->sadb_address_len = PFKEY_LEN(sizeof(*addr) + len); PFKEY_EXT_ADD(msg, addr); } @@ -2078,31 +2072,44 @@ METHOD(kernel_ipsec_t, flush_sas, status_t, { unsigned char request[PFKEY_BUFFER_SIZE]; struct sadb_msg *msg, *out; + struct { + u_int8_t proto; + char *name; + } protos[] = { + { SADB_SATYPE_AH, "AH" }, + { SADB_SATYPE_ESP, "ESP" }, + { SADB_X_SATYPE_IPCOMP, "IPComp" }, + }; size_t len; + int i; memset(&request, 0, sizeof(request)); - DBG2(DBG_KNL, "flushing all SAD entries"); - msg = (struct sadb_msg*)request; msg->sadb_msg_version = PF_KEY_V2; msg->sadb_msg_type = SADB_FLUSH; - msg->sadb_msg_satype = SADB_SATYPE_UNSPEC; msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg)); - if (pfkey_send(this, msg, &out, &len) != SUCCESS) - { - DBG1(DBG_KNL, "unable to flush SAD entries"); - return FAILED; - } - else if (out->sadb_msg_errno) + for (i = 0; i < countof(protos); i++) { - DBG1(DBG_KNL, "unable to flush SAD entries: %s (%d)", - strerror(out->sadb_msg_errno), out->sadb_msg_errno); + DBG2(DBG_KNL, "flushing all %s SAD entries", protos[i].name); + + msg->sadb_msg_satype = protos[i].proto; + if (pfkey_send(this, msg, &out, &len) != SUCCESS) + { + DBG1(DBG_KNL, "unable to flush %s SAD entries", protos[i].name); + return FAILED; + } + else if (out->sadb_msg_errno) + { + DBG1(DBG_KNL, "unable to flush %s SAD entries: %s (%d)", + protos[i].name, strerror(out->sadb_msg_errno), + out->sadb_msg_errno); + free(out); + return FAILED; + } free(out); - return FAILED; } - free(out); return SUCCESS; } @@ -2357,6 +2364,7 @@ static status_t add_policy_internal(private_kernel_pfkey_ipsec_t *this, pfkey_msg_t response; size_t len; ipsec_mode_t proto_mode; + status_t status; memset(&request, 0, sizeof(request)); @@ -2444,7 +2452,15 @@ static status_t add_policy_internal(private_kernel_pfkey_ipsec_t *this, this->mutex->unlock(this->mutex); - if (pfkey_send(this, msg, &out, &len) != SUCCESS) + status = pfkey_send(this, msg, &out, &len); + if (status == SUCCESS && !update && out->sadb_msg_errno == EEXIST) + { + DBG1(DBG_KNL, "policy already exists, try to update it"); + free(out); + msg->sadb_msg_type = SADB_X_SPDUPDATE; + status = pfkey_send(this, msg, &out, &len); + } + if (status != SUCCESS) { return FAILED; } diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c index 0f7802270..df80c29b8 100644 --- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -408,6 +408,11 @@ struct private_kernel_pfroute_net_t * Time in ms to wait for IP addresses to appear/disappear */ int vip_wait; + + /** + * whether to actually install virtual IPs + */ + bool install_virtual_ip; }; @@ -1197,6 +1202,11 @@ METHOD(kernel_net_t, add_ip, status_t, tun_device_t *tun; bool timeout = FALSE; + if (!this->install_virtual_ip) + { /* disabled by config */ + return SUCCESS; + } + tun = tun_device_create(NULL); if (!tun) { @@ -1271,6 +1281,11 @@ METHOD(kernel_net_t, del_ip, status_t, host_t *addr; bool timeout = FALSE, found = FALSE; + if (!this->install_virtual_ip) + { /* disabled by config */ + return SUCCESS; + } + this->lock->write_lock(this->lock); enumerator = this->tuns->create_enumerator(this->tuns); while (enumerator->enumerate(enumerator, &tun)) @@ -1848,6 +1863,8 @@ kernel_pfroute_net_t *kernel_pfroute_net_create() .roam_lock = spinlock_create(), .vip_wait = lib->settings->get_int(lib->settings, "%s.plugins.kernel-pfroute.vip_wait", 1000, lib->ns), + .install_virtual_ip = lib->settings->get_bool(lib->settings, + "%s.install_virtual_ip", TRUE, lib->ns), ); timerclear(&this->last_route_reinstall); timerclear(&this->next_roam); diff --git a/src/libhydra/tests/hydra_tests.c b/src/libhydra/tests/hydra_tests.c index 90abd8369..0d6387be7 100644 --- a/src/libhydra/tests/hydra_tests.c +++ b/src/libhydra/tests/hydra_tests.c @@ -26,8 +26,8 @@ static test_configuration_t tests[] = { #define TEST_SUITE(x) \ { .suite = x, }, -#define TEST_SUITE_DEPEND(x, type, args) \ - { .suite = x, .feature = PLUGIN_DEPENDS(type, args) }, +#define TEST_SUITE_DEPEND(x, type, ...) \ + { .suite = x, .feature = PLUGIN_DEPENDS(type, __VA_ARGS__) }, #include "hydra_tests.h" { .suite = NULL, } }; |