diff options
Diffstat (limited to 'src/libhydra/plugins/kernel_netlink')
3 files changed, 85 insertions, 24 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"); |