chiark / gitweb /
tun: add hard routes even if they are currently down.
authorSimon Tatham <anakin@pobox.com>
Tue, 27 Nov 2012 19:10:24 +0000 (19:10 +0000)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Wed, 3 Jul 2013 21:58:53 +0000 (22:58 +0100)
If a netlink is configured with OPT_SOFTROUTE, we expect to be adding
and removing the kernel routing table entry as the remote site appears
and disappears, so tun_set_route should bring the route up or down
depending on route->up. However, if it isn't, then we won't be doing
that during PHASE_RUN, and therefore we should bring the route up once
and for all at startup.

Previously the state was as follows:

secnet's 'tun' netlink will add and remove kernel routing table
entries during PHASE_RUN if the OPT_SOFTROUTE option is set.
However, at startup during PHASE_GETRESOURCES it will set up routes
for a site only if that site lists an address. So if you have a site
in your sites file with no address but also haven't enabled
OPT_SOFTROUTE (e.g. because you run secnet so that it drops privs),
then no route for that site will _ever_ be set up.

These two policies don't match. We should bring a site's route(s) up
at startup in any situation where we will not be prepared to do so
dynamically during run time. In other words, routes should be added
at startup time not only if they have a fixed address parameter, but
also if they do not have OPT_SOFTROUTE set.

(See also 04f92904ea6c41517ff7154910c16ef4c3bc646b, which seems to be
fixing the analogous bug for userv-ipif.  [
bdd4351ff2fc6dc8b1dad689f751ac46347636cf which seems to be the
relevant later commit is in fact empty -iwj. ]

This commit implements this fix, and causes my home secnet
implementation to be able to route to my laptop successfully
(sgo/resolution/resolution). In order to do this I've had to move the
definition of OPT_SOFTROUTE out of netlink.c into netlink.h so that
tun_set_route can see it, which suggests a possible layering
violation, but on the other hand since the netlink_client structure is
visible outside netlink.c it seems only reasonable that the bit flags
used in its 'options' field should be visible too.

Signed-off-by: Simon Tatham <anakin@pobox.com>
netlink.c
netlink.h
tun.c

index 486df6fd1515f24ce4204fb3229a4ec22ac8f925..b2bd2248d632d90b5faefe91e9d664527d710a3a 100644 (file)
--- a/netlink.c
+++ b/netlink.c
@@ -106,9 +106,6 @@ their use.
 #include "netlink.h"
 #include "process.h"
 
-#define OPT_SOFTROUTE   1
-#define OPT_ALLOWROUTE  2
-
 #define ICMP_TYPE_ECHO_REPLY             0
 
 #define ICMP_TYPE_UNREACHABLE            3
index ffefd8082ef67f2b662f5391b9baf628080c9e16..a7bc9afd31b70aa09ae5e8be00920992ab4ab4d0 100644 (file)
--- a/netlink.h
+++ b/netlink.h
@@ -31,6 +31,10 @@ struct netlink_client {
     struct netlink_client *next;
 };
 
+/* options field in 'struct netlink_client' */
+#define OPT_SOFTROUTE   1
+#define OPT_ALLOWROUTE  2
+
 typedef bool_t netlink_route_fn(void *cst, struct netlink_client *routes);
 
 /* Netlink provides one function to the device driver, to call to deliver
diff --git a/tun.c b/tun.c
index 0baaf12226101cf83af5099cfac99e2fc6e464ae..b0bde38ea7405057fe448ac84e4a2d5e593c9bbc 100644 (file)
--- a/tun.c
+++ b/tun.c
@@ -166,8 +166,14 @@ static bool_t tun_set_route(void *sst, struct netlink_client *routes)
     struct subnet_list *nets;
     int32_t i;
     int fd=-1;
+    bool_t up;
 
-    if (routes->up == routes->kup) return False;
+    if (routes->options & OPT_SOFTROUTE)
+        up = routes->up;
+    else
+        up = routes->link_quality > LINK_QUALITY_UNUSED;
+
+    if (up == routes->kup) return False;
     if (st->route_type==TUN_CONFIG_IOCTL) {
        if (st->tun_flavour==TUN_FLAVOUR_STREAMS) {
            fd=open(st->ip_path,O_RDWR);
@@ -187,20 +193,20 @@ static bool_t tun_set_route(void *sst, struct netlink_client *routes)
        network=ipaddr_to_string(nets->list[i].prefix);
        mask=ipaddr_to_string(nets->list[i].mask);
        Message(M_INFO,"%s: %s route %s/%d %s kernel routing table\n",
-               st->nl.name,routes->up?"adding":"deleting",network,
-               nets->list[i].len,routes->up?"to":"from");
+               st->nl.name,up?"adding":"deleting",network,
+               nets->list[i].len,up?"to":"from");
        switch (st->route_type) {
        case TUN_CONFIG_LINUX:
-           sys_cmd(st->route_path,"route",routes->up?"add":"del",
+           sys_cmd(st->route_path,"route",up?"add":"del",
                    "-net",network,"netmask",mask,
                    "gw",secnetaddr,(char *)0);
            break;
        case TUN_CONFIG_BSD:
-           sys_cmd(st->route_path,"route",routes->up?"add":"del",
+           sys_cmd(st->route_path,"route",up?"add":"del",
                    "-net",network,secnetaddr,mask,(char *)0);
            break;
        case TUN_CONFIG_SOLARIS25:
-           sys_cmd(st->route_path,"route",routes->up?"add":"del",
+           sys_cmd(st->route_path,"route",up?"add":"del",
                    network,secnetaddr,(char *)0);
            break;
        case TUN_CONFIG_IOCTL:
@@ -222,7 +228,7 @@ static bool_t tun_set_route(void *sst, struct netlink_client *routes)
            sa->sin_family=AF_INET;
            sa->sin_addr.s_addr=htonl(st->nl.secnet_address);
            rt.rt_flags=RTF_UP|RTF_GATEWAY;
-           action=routes->up?SIOCADDRT:SIOCDELRT;
+           action=up?SIOCADDRT:SIOCDELRT;
            if (ioctl(fd,action,&rt)<0) {
                fatal_perror("tun_set_route: ioctl()");
            }
@@ -241,7 +247,7 @@ static bool_t tun_set_route(void *sst, struct netlink_client *routes)
     if (st->route_type==TUN_CONFIG_IOCTL) {
        close(fd);
     }
-    routes->kup=routes->up;
+    routes->kup=up;
     return True;
 }