kernel-netlink: Optionally trigger roam events on routing rule changes
authorTobias Brunner <tobias@strongswan.org>
Mon, 29 Jan 2018 14:26:17 +0000 (15:26 +0100)
committerTobias Brunner <tobias@strongswan.org>
Fri, 9 Feb 2018 14:51:28 +0000 (15:51 +0100)
This can be useful if routing rules (instead of e.g. route metrics) are used
to switch from one to another interface (i.e. from one to another
routing table).  Since we currently don't evaluate routing rules when
doing the route lookup this is only useful if the kernel-based route
lookup is used.

Resolves strongswan/strongswan#88.

conf/plugins/kernel-netlink.opt
src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c

index 3d9c4a7..211add8 100644 (file)
@@ -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.
 
index e4ded32..c3f92f5 100644 (file)
@@ -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",
@@ -473,6 +476,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
         */
        bool roam_events;
@@ -1452,6 +1460,45 @@ static void process_route(private_kernel_netlink_net_t *this, struct nlmsghdr *h
 }
 
 /**
+ * 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
  */
 static bool receive_events(private_kernel_netlink_net_t *this, int fd,
@@ -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)",