diff --git a/conf/plugins/kernel-netlink.opt b/conf/plugins/kernel-netlink.opt index 3d9c4a7a9..211add85c 100644 --- a/conf/plugins/kernel-netlink.opt +++ b/conf/plugins/kernel-netlink.opt @@ -47,6 +47,13 @@ charon.plugins.kernel-netlink.port_bypass = no port based policies use global XFRM bypass policies for the used IKE UDP ports. +charon.plugins.kernel-netlink.process_rules = no + Whether to process changes in routing rules to trigger roam events. + + Whether to process changes in routing rules to trigger roam events. This is + currently only useful if the kernel based route lookup is used (i.e. if + route installation is disabled or an inverted fwmark match is configured). + charon.plugins.kernel-netlink.receive_buffer_size = 0 Maximum Netlink socket receive buffer in bytes. diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c index e4ded3225..c3f92f500 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2016 Tobias Brunner + * Copyright (C) 2008-2018 Tobias Brunner * Copyright (C) 2005-2008 Martin Willi * HSR Hochschule fuer Technik Rapperswil * @@ -78,6 +78,9 @@ #define ROUTING_TABLE_PRIO 0 #endif +/** multicast groups (for groups > 31 setsockopt has to be used) */ +#define nl_group(group) (1 << (group - 1)) + ENUM(rt_msg_names, RTM_NEWLINK, RTM_GETRULE, "RTM_NEWLINK", "RTM_DELLINK", @@ -472,6 +475,11 @@ struct private_kernel_netlink_net_t { */ bool process_route; + /** + * whether to react to RTM_NEWRULE or RTM_DELRULE events + */ + bool process_rules; + /** * whether to trigger roam events */ @@ -1451,6 +1459,45 @@ static void process_route(private_kernel_netlink_net_t *this, struct nlmsghdr *h host->destroy(host); } +/** + * process RTM_NEW|DELRULE from kernel + */ +static void process_rule(private_kernel_netlink_net_t *this, struct nlmsghdr *hdr) +{ +#ifdef HAVE_LINUX_FIB_RULES_H + struct rtmsg* msg = NLMSG_DATA(hdr); + struct rtattr *rta = RTM_RTA(msg); + size_t rtasize = RTM_PAYLOAD(hdr); + uint32_t table = 0; + + /* ignore rules added by us or in the local routing table (local addrs) */ + if (msg->rtm_table && (msg->rtm_table == this->routing_table || + msg->rtm_table == RT_TABLE_LOCAL)) + { + return; + } + + while (RTA_OK(rta, rtasize)) + { + switch (rta->rta_type) + { + case FRA_TABLE: + if (RTA_PAYLOAD(rta) == sizeof(table)) + { + table = *(uint32_t*)RTA_DATA(rta); + } + break; + } + rta = RTA_NEXT(rta, rtasize); + } + if (table && table == this->routing_table) + { /* also check against extended table ID */ + return; + } + fire_roam_event(this, FALSE); +#endif +} + /** * Receives events from kernel */ @@ -1508,6 +1555,13 @@ static bool receive_events(private_kernel_netlink_net_t *this, int fd, process_route(this, hdr); } break; + case RTM_NEWRULE: + case RTM_DELRULE: + if (this->process_rules) + { + process_rule(this, hdr); + } + break; default: break; } @@ -2985,6 +3039,8 @@ kernel_netlink_net_t *kernel_netlink_net_create() "%s.prefer_temporary_addrs", FALSE, lib->ns), .roam_events = lib->settings->get_bool(lib->settings, "%s.plugins.kernel-netlink.roam_events", TRUE, lib->ns), + .process_rules = lib->settings->get_bool(lib->settings, + "%s.plugins.kernel-netlink.process_rules", FALSE, lib->ns), .mtu = lib->settings->get_int(lib->settings, "%s.plugins.kernel-netlink.mtu", 0, lib->ns), .mss = lib->settings->get_int(lib->settings, @@ -3037,8 +3093,19 @@ kernel_netlink_net_t *kernel_netlink_net_create() destroy(this); return NULL; } - addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | - RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_LINK; + addr.nl_groups = nl_group(RTNLGRP_IPV4_IFADDR) | + nl_group(RTNLGRP_IPV6_IFADDR) | + nl_group(RTNLGRP_LINK); + if (this->process_route) + { + addr.nl_groups |= nl_group(RTNLGRP_IPV4_ROUTE) | + nl_group(RTNLGRP_IPV6_ROUTE); + } + if (this->process_rules) + { + addr.nl_groups |= nl_group(RTNLGRP_IPV4_RULE) | + nl_group(RTNLGRP_IPV6_RULE); + } if (bind(this->socket_events, (struct sockaddr*)&addr, sizeof(addr))) { DBG1(DBG_KNL, "unable to bind RT event socket: %s (%d)",