summaryrefslogtreecommitdiff
path: root/src/libhydra/plugins/kernel_netlink
diff options
context:
space:
mode:
Diffstat (limited to 'src/libhydra/plugins/kernel_netlink')
-rw-r--r--src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c53
-rw-r--r--src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c40
-rw-r--r--src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c16
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");