chiark / gitweb /
Import release 0.1.1
[secnet.git] / netlink.c
index 0b1ebe411291b7f4b8f9bb59490693b5e94084ea..dea05f2e912f0294189244603897b25037d1abee 100644 (file)
--- a/netlink.c
+++ b/netlink.c
@@ -1,33 +1,30 @@
 /* User-kernel network link */
 
-/* We will eventually support a variety of methods for extracting
-   packets from the kernel: userv-ipif, ipif on its own (when we run
-   as root), the kernel TUN driver, SLIP to a pty, an external netlink
-   daemon. There is a performance/security tradeoff. */
+/* We support a variety of methods for extracting packets from the
+   kernel: userv-ipif, ipif on its own (when we run as root), the
+   kernel TUN driver.  Possible future methods: SLIP to a pty, an
+   external netlink daemon.  There is a performance/security
+   tradeoff. */
 
 /* When dealing with SLIP (to a pty, or ipif) we have separate rx, tx
-   and client buffers. When receiving we may read() any amount, not
-   just whole packets. When transmitting we need to bytestuff anyway,
+   and client buffers.  When receiving we may read() any amount, not
+   just whole packets.  When transmitting we need to bytestuff anyway,
    and may be part-way through receiving. */
 
-/* Each netlink device is actually a router, with its own IP
-   address. We do things like decreasing the TTL and recalculating the
-   header checksum, generating ICMP, responding to pings, etc. */
+/* Each netlink device is actually a router, with its own IP address.
+   We do things like decreasing the TTL and recalculating the header
+   checksum, generating ICMP, responding to pings, etc. */
 
 /* This is where we have the anti-spoofing paranoia - before sending a
    packet to the kernel we check that the tunnel it came over could
    reasonably have produced it. */
 
-/* XXX now implement TUN. Kernel needs recompiling. */
-
+#include "secnet.h"
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
-
-#include "config.h"
-#include "secnet.h"
 #include "util.h"
 
 #ifdef HAVE_LINUX_IF_H
@@ -35,7 +32,7 @@
 #include <linux/if_tun.h>
 #endif
 
-/* XXX where do we find if_tun on other architectures? */
+/* XXX where do we find if_tun on other platforms? */
 
 #define DEFAULT_BUFSIZE 2048
 #define DEFAULT_MTU 1000
@@ -51,10 +48,15 @@ struct netlink_client {
     netlink_deliver_fn *deliver;
     void *dst;
     string_t name;
-    bool_t can_deliver;
+    uint32_t link_quality;
     struct netlink_client *next;
 };
 
+struct netlink_route {
+    struct subnet net;
+    struct netlink_client *c;
+};
+
 /* Netlink provides one function to the device driver, to call to deliver
    a packet from the device. The device driver provides one function to
    netlink, for it to call to deliver a packet to the device. */
@@ -67,12 +69,15 @@ struct netlink {
     uint32_t max_start_pad;
     uint32_t max_end_pad;
     struct subnet_list networks;
+    struct subnet_list exclude_remote_networks;
     uint32_t local_address; /* host interface address */
     uint32_t secnet_address; /* our own address */
     uint32_t mtu;
     struct netlink_client *clients;
     netlink_deliver_fn *deliver_to_host; /* Provided by driver */
     struct buffer_if icmp; /* Buffer for assembly of outgoing ICMP */
+    uint32_t n_routes; /* How many routes do we know about? */
+    struct netlink_route *routes;
 };
 
 /* Generic IP checksum routine */
@@ -176,7 +181,9 @@ struct icmphdr {
     } d;
 };
     
-static void netlink_packet_deliver(struct netlink *st, struct buffer_if *buf);
+static void netlink_packet_deliver(struct netlink *st,
+                                  struct netlink_client *client,
+                                  struct buffer_if *buf);
 
 static struct icmphdr *netlink_icmp_tmpl(struct netlink *st,
                                         uint32_t dest,uint16_t len)
@@ -266,7 +273,10 @@ static uint16_t netlink_icmp_reply_len(struct buffer_if *buf)
     return (hlen>plen?plen:hlen);
 }
 
+/* client indicates where the packet we're constructing a response to
+   comes from. NULL indicates the host. */
 static void netlink_icmp_simple(struct netlink *st, struct buffer_if *buf,
+                               struct netlink_client *client,
                                uint8_t type, uint8_t code)
 {
     struct iphdr *iph=(struct iphdr *)buf->start;
@@ -279,7 +289,7 @@ static void netlink_icmp_simple(struct netlink *st, struct buffer_if *buf,
        h->type=type; h->code=code;
        memcpy(buf_append(&st->icmp,len),buf->start,len);
        netlink_icmp_csum(h);
-       netlink_packet_deliver(st,&st->icmp);
+       netlink_packet_deliver(st,NULL,&st->icmp);
        BUF_ASSERT_FREE(&st->icmp);
     }
 }
@@ -323,11 +333,19 @@ static bool_t netlink_check(struct netlink *st, struct buffer_if *buf)
     return True;
 }
 
-static void netlink_packet_deliver(struct netlink *st, struct buffer_if *buf)
+/* Deliver a packet. "client" points to the _origin_ of the packet, not
+   its destination. (May be used when sending ICMP response - avoid
+   asymmetric routing.) */
+static void netlink_packet_deliver(struct netlink *st,
+                                  struct netlink_client *client,
+                                  struct buffer_if *buf)
 {
     struct iphdr *iph=(struct iphdr *)buf->start;
     uint32_t dest=ntohl(iph->daddr);
-    struct netlink_client *c;
+    uint32_t source=ntohl(iph->saddr);
+    uint32_t best_quality;
+    int best_match;
+    int i;
 
     BUF_ASSERT_USED(buf);
 
@@ -337,30 +355,69 @@ static void netlink_packet_deliver(struct netlink *st, struct buffer_if *buf)
        return;
     }
     
-    for (c=st->clients; c; c=c->next) {
-       if (subnet_match(c->networks,dest)) {
-           if (c->can_deliver) {
-               c->deliver(c->dst,c,buf);
+    if (!client) {
+       /* Origin of packet is host or secnet. Might be for a tunnel. */
+       best_quality=0;
+       best_match=-1;
+       for (i=0; i<st->n_routes; i++) {
+           if (subnet_match(&st->routes[i].net,dest)) {
+               if (st->routes[i].c->link_quality>best_quality
+                   || best_quality==0) {
+                   best_quality=st->routes[i].c->link_quality;
+                   best_match=i;
+                   /* If quality isn't perfect we may wish to
+                      consider kicking the tunnel with a 0-length
+                      packet to prompt it to perform a key setup.
+                      Then it'll eventually decide it's up or
+                      down. */
+                   /* If quality is perfect we don't need to search
+                       any more. */
+                   if (best_quality>=MAXIMUM_LINK_QUALITY) break;
+               }
+           }
+       }
+       if (best_match==-1) {
+           /* Not going down a tunnel. Might be for the host. 
+              XXX think about this - only situation should be if we're
+              sending ICMP. */
+           if (source!=st->secnet_address) {
+               Message(M_ERROR,"netlink_packet_deliver: outgoing packet "
+                       "from host that won't fit down any of our tunnels!\n");
+               BUF_FREE(buf);
+           } else {
+               st->deliver_to_host(st->dst,NULL,buf);
+               BUF_ASSERT_FREE(buf);
+           }
+       } else {
+           if (best_quality>0) {
+               st->routes[best_match].c->deliver(
+                   st->routes[best_match].c->dst,
+                   st->routes[best_match].c, buf);
                BUF_ASSERT_FREE(buf);
            } else {
                /* Generate ICMP destination unreachable */
-               netlink_icmp_simple(st,buf,3,0);
+               netlink_icmp_simple(st,buf,client,3,0); /* client==NULL */
                BUF_FREE(buf);
            }
-           return;
+       }
+    } else { /* client is set */
+       /* We know the origin is a tunnel - packet must be for the host */
+       if (subnet_matches_list(&st->networks,dest)) {
+           st->deliver_to_host(st->dst,NULL,buf);
+           BUF_ASSERT_FREE(buf);
+       } else {
+           Message(M_ERROR,"%s: packet from tunnel %s can't be delivered "
+                   "to the host\n",st->name,client->name);
+           netlink_icmp_simple(st,buf,client,3,0);
+           BUF_FREE(buf);
        }
     }
-    if (subnet_match(&st->networks,dest)) {
-       st->deliver_to_host(st->dst,NULL,buf);
-       BUF_ASSERT_FREE(buf);
-       return;
-    }
-    Message(M_ERROR,"%s: failed to deliver a packet (bad destination address)"
-           "\nXXX make this message clearer\n");
-    BUF_FREE(buf);
+    BUF_ASSERT_FREE(buf);
 }
 
-static void netlink_packet_forward(struct netlink *st, struct buffer_if *buf)
+static void netlink_packet_forward(struct netlink *st, 
+                                  struct netlink_client *client,
+                                  struct buffer_if *buf)
 {
     struct iphdr *iph=(struct iphdr *)buf->start;
     
@@ -369,7 +426,7 @@ static void netlink_packet_forward(struct netlink *st, struct buffer_if *buf)
     /* Packet has already been checked */
     if (iph->ttl<=1) {
        /* Generate ICMP time exceeded */
-       netlink_icmp_simple(st,buf,11,0);
+       netlink_icmp_simple(st,buf,client,11,0);
        BUF_FREE(buf);
        return;
     }
@@ -377,13 +434,15 @@ static void netlink_packet_forward(struct netlink *st, struct buffer_if *buf)
     iph->check=0;
     iph->check=ip_fast_csum((uint8_t *)iph,iph->ihl);
 
-    netlink_packet_deliver(st,buf);
+    netlink_packet_deliver(st,client,buf);
     BUF_ASSERT_FREE(buf);
 }
 
 /* Someone has been foolish enough to address a packet to us. I
    suppose we should reply to it, just to be polite. */
-static void netlink_packet_local(struct netlink *st, struct buffer_if *buf)
+static void netlink_packet_local(struct netlink *st,
+                                struct netlink_client *client,
+                                struct buffer_if *buf)
 {
     struct icmphdr *h;
 
@@ -407,13 +466,13 @@ static void netlink_packet_local(struct netlink *st, struct buffer_if *buf)
            h->iph.check=0;
            h->iph.check=ip_fast_csum((uint8_t *)h,h->iph.ihl);
            netlink_icmp_csum(h);
-           netlink_packet_deliver(st,buf);
+           netlink_packet_deliver(st,NULL,buf);
            return;
        }
        Message(M_WARNING,"%s: unknown incoming ICMP\n",st->name);
     } else {
        /* Send ICMP protocol unreachable */
-       netlink_icmp_simple(st,buf,3,2);
+       netlink_icmp_simple(st,buf,client,3,2);
        BUF_FREE(buf);
        return;
     }
@@ -443,8 +502,8 @@ static void netlink_from_tunnel(void *sst, void *cst, struct buffer_if *buf)
     dest=ntohl(iph->daddr);
 
     /* Check that the packet source is in 'nets' and its destination is
-       in client->networks */
-    if (!subnet_match(client->networks,source)) {
+       in st->networks */
+    if (!subnet_matches_list(client->networks,source)) {
        string_t s,d;
        s=ipaddr_to_string(source);
        d=ipaddr_to_string(dest);
@@ -458,11 +517,11 @@ static void netlink_from_tunnel(void *sst, void *cst, struct buffer_if *buf)
        st->networks because secnet's IP address may not be in the
        range the host is willing to deal with) */
     if (dest==st->secnet_address) {
-        netlink_packet_local(st,buf);
+        netlink_packet_local(st,client,buf);
        BUF_ASSERT_FREE(buf);
        return;
     }
-    if (!subnet_match(&st->networks,dest)) {
+    if (!subnet_matches_list(&st->networks,dest)) {
        string_t s,d;
        s=ipaddr_to_string(source);
        d=ipaddr_to_string(dest);
@@ -474,7 +533,7 @@ static void netlink_from_tunnel(void *sst, void *cst, struct buffer_if *buf)
        return;
     }
 
-    netlink_packet_forward(st,buf);
+    netlink_packet_forward(st,client,buf);
 
     BUF_ASSERT_FREE(buf);
 }
@@ -500,7 +559,7 @@ static void netlink_from_host(void *sst, void *cid, struct buffer_if *buf)
     source=ntohl(iph->saddr);
     dest=ntohl(iph->daddr);
 
-    if (!subnet_match(&st->networks,source)) {
+    if (!subnet_matches_list(&st->networks,source)) {
        string_t s,d;
        s=ipaddr_to_string(source);
        d=ipaddr_to_string(dest);
@@ -511,19 +570,19 @@ static void netlink_from_host(void *sst, void *cid, struct buffer_if *buf)
        return;
     }
     if (dest==st->secnet_address) {
-       netlink_packet_local(st,buf);
+       netlink_packet_local(st,NULL,buf);
        BUF_ASSERT_FREE(buf);
        return;
     }
-    netlink_packet_forward(st,buf);
+    netlink_packet_forward(st,NULL,buf);
     BUF_ASSERT_FREE(buf);
 }
 
-static void netlink_set_delivery(void *sst, void *cid, bool_t can_deliver)
+static void netlink_set_quality(void *sst, void *cid, uint32_t quality)
 {
     struct netlink_client *c=cid;
 
-    c->can_deliver=can_deliver;
+    c->link_quality=quality;
 }
 
 static void *netlink_regnets(void *sst, struct subnet_list *nets,
@@ -538,20 +597,90 @@ static void *netlink_regnets(void *sst, struct subnet_list *nets,
            "max_start_pad=%d, max_end_pad=%d\n",
            nets->entries,max_start_pad,max_end_pad);
 
+    /* Check that nets does not intersect with st->networks or
+       st->exclude_remote_networks; refuse to register if it does. */
+    if (subnet_lists_intersect(&st->networks,nets)) {
+       Message(M_ERROR,"%s: site %s specifies networks that "
+               "intersect with our local networks\n",st->name,client_name);
+       return False;
+    }
+    if (subnet_lists_intersect(&st->exclude_remote_networks,nets)) {
+       Message(M_ERROR,"%s: site %s specifies networks that "
+               "intersect with the explicitly excluded remote networks\n",
+               st->name,client_name);
+       return False;
+    }
+
     c=safe_malloc(sizeof(*c),"netlink_regnets");
     c->networks=nets;
     c->deliver=deliver;
     c->dst=dst;
     c->name=client_name; /* XXX copy it? */
-    c->can_deliver=False;
+    c->link_quality=LINK_QUALITY_DOWN;
     c->next=st->clients;
     st->clients=c;
     if (max_start_pad > st->max_start_pad) st->max_start_pad=max_start_pad;
     if (max_end_pad > st->max_end_pad) st->max_end_pad=max_end_pad;
+    st->n_routes+=nets->entries;
 
     return c;
 }
 
+static int netlink_compare_route_specificity(const void *ap, const void *bp)
+{
+    const struct netlink_route *a=ap;
+    const struct netlink_route *b=bp;
+
+    if (a->net.len==b->net.len) return 0;
+    if (a->net.len<b->net.len) return 1;
+    return -1;
+}
+
+static void netlink_phase_hook(void *sst, uint32_t new_phase)
+{
+    struct netlink *st=sst;
+    struct netlink_client *c;
+    uint32_t i,j;
+
+    /* All the networks serviced by the various tunnels should now
+     * have been registered.  We build a routing table by sorting the
+     * routes into most-specific-first order.  */
+    st->routes=safe_malloc(st->n_routes*sizeof(*st->routes),
+                          "netlink_phase_hook");
+    /* Fill the table */
+    i=0;
+    for (c=st->clients; c; c=c->next) {
+       for (j=0; j<c->networks->entries; j++) {
+           st->routes[i].net=c->networks->list[j];
+           st->routes[i].c=c;
+           i++;
+       }
+    }
+    /* ASSERT i==st->n_routes */
+    if (i!=st->n_routes) {
+       fatal("netlink: route count error: expected %d got %d\n",
+             st->n_routes,i);
+    }
+    /* Sort the table in descending order of specificity */
+    qsort(st->routes,st->n_routes,sizeof(*st->routes),
+         netlink_compare_route_specificity);
+    Message(M_INFO,"%s: routing table:\n",st->name);
+    for (i=0; i<st->n_routes; i++) {
+       string_t net;
+       net=subnet_to_string(&st->routes[i].net);
+       Message(M_INFO,"%s -> tunnel %s\n",net,st->routes[i].c->name);
+       free(net);
+    }
+    Message(M_INFO,"%s/32 -> netlink \"%s\"\n",
+           ipaddr_to_string(st->secnet_address),st->name);
+    for (i=0; i<st->networks.entries; i++) {
+       string_t net;
+       net=subnet_to_string(&st->networks.list[i]);
+       Message(M_INFO,"%s -> host\n",net);
+       free(net);
+    }
+}
+
 static netlink_deliver_fn *netlink_init(struct netlink *st,
                                        void *dst, struct cloc loc,
                                        dict_t *dict, string_t description,
@@ -565,7 +694,7 @@ static netlink_deliver_fn *netlink_init(struct netlink *st,
     st->ops.st=st;
     st->ops.regnets=netlink_regnets;
     st->ops.deliver=netlink_from_tunnel;
-    st->ops.set_delivery=netlink_set_delivery;
+    st->ops.set_quality=netlink_set_quality;
     st->max_start_pad=0;
     st->max_end_pad=0;
     st->clients=NULL;
@@ -575,15 +704,21 @@ static netlink_deliver_fn *netlink_init(struct netlink *st,
     if (!st->name) st->name=description;
     dict_read_subnet_list(dict, "networks", True, "netlink", loc,
                          &st->networks);
+    dict_read_subnet_list(dict, "exclude-remote-networks", False, "netlink",
+                         loc, &st->exclude_remote_networks);
+    /* local-address and secnet-address do not have to be in local-networks;
+       however, they should be advertised in the 'sites' file for the
+       local site. */
     st->local_address=string_to_ipaddr(
        dict_find_item(dict,"local-address", True, "netlink", loc),"netlink");
     st->secnet_address=string_to_ipaddr(
        dict_find_item(dict,"secnet-address", True, "netlink", loc),"netlink");
-    if (!subnet_match(&st->networks,st->local_address)) {
-       cfgfatal(loc,"netlink","local-address must be in local networks\n");
-    }
     st->mtu=dict_read_number(dict, "mtu", False, "netlink", loc, DEFAULT_MTU);
     buffer_new(&st->icmp,ICMP_BUFSIZE);
+    st->n_routes=0;
+    st->routes=NULL;
+
+    add_hook(PHASE_SETUP,netlink_phase_hook,st);
 
     return netlink_from_host;
 }
@@ -846,6 +981,8 @@ struct tun {
     string_t interface_name;
     string_t ifconfig_path;
     string_t route_path;
+    bool_t tun_old;
+    bool_t search_for_if; /* Applies to tun-old only */
     struct buffer_if *buff; /* We receive packets into here
                               and send them to the netlink code. */
     netlink_deliver_fn *netlink_to_tunnel;
@@ -910,6 +1047,66 @@ static void tun_phase_hook(void *sst, uint32_t newphase)
     struct netlink_client *c;
     int i;
 
+    if (st->tun_old) {
+       if (st->search_for_if) {
+           string_t dname;
+           int i;
+
+           /* ASSERT st->interface_name */
+           dname=safe_malloc(strlen(st->device_path)+4,"tun_old_apply");
+           st->interface_name=safe_malloc(8,"tun_phase_hook");
+       
+           for (i=0; i<255; i++) {
+               sprintf(dname,"%s%d",st->device_path,i);
+               if ((st->fd=open(dname,O_RDWR))>0) {
+                   sprintf(st->interface_name,"tun%d",i);
+                   Message(M_INFO,"%s: allocated network interface %s "
+                           "through %s\n",st->nl.name,st->interface_name,
+                           dname);
+                   break;
+               }
+           }
+           if (st->fd==-1) {
+               fatal("%s: unable to open any TUN device (%s...)\n",
+                     st->nl.name,st->device_path);
+           }
+       } else {
+           st->fd=open(st->device_path,O_RDWR);
+           if (st->fd==-1) {
+               fatal_perror("%s: unable to open TUN device file %s",
+                            st->nl.name,st->device_path);
+           }
+       }
+    } else {
+#ifdef HAVE_LINUX_IF_H
+       struct ifreq ifr;
+
+       /* New TUN interface: open the device, then do ioctl TUNSETIFF
+          to set or find out the network interface name. */
+       st->fd=open(st->device_path,O_RDWR);
+       if (st->fd==-1) {
+           fatal_perror("%s: can't open device file %s",st->nl.name,
+                        st->device_path);
+       }
+       memset(&ifr,0,sizeof(ifr));
+       ifr.ifr_flags = IFF_TUN | IFF_NO_PI; /* Just send/receive IP packets,
+                                               no extra headers */
+       if (st->interface_name)
+           strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ);
+       Message(M_INFO,"%s: about to ioctl(TUNSETIFF)...\n",st->nl.name);
+       if (ioctl(st->fd,TUNSETIFF,&ifr)<0) {
+           fatal_perror("%s: ioctl(TUNSETIFF)",st->nl.name);
+       }
+       if (!st->interface_name) {
+           st->interface_name=safe_malloc(strlen(ifr.ifr_name)+1,"tun_apply");
+           strcpy(st->interface_name,ifr.ifr_name);
+           Message(M_INFO,"%s: allocated network interface %s\n",st->nl.name,
+                   st->interface_name);
+       }
+#else
+       fatal("netlink.c:tun_phase_hook:!tun_old unexpected\n");
+#endif /* HAVE_LINUX_IF_H */
+    }
     /* All the networks we'll be using have been registered. Invoke ifconfig
        to set the TUN device's address, and route to add routes to all
        our networks. */
@@ -943,7 +1140,6 @@ static list_t *tun_apply(closure_t *self, struct cloc loc, dict_t *context,
     struct tun *st;
     item_t *item;
     dict_t *dict;
-    struct ifreq ifr;
 
     st=safe_malloc(sizeof(*st),"tun_apply");
 
@@ -958,40 +1154,21 @@ static list_t *tun_apply(closure_t *self, struct cloc loc, dict_t *context,
        netlink_init(&st->nl,st,loc,dict,
                     "netlink-tun",tun_deliver_to_kernel);
 
+    st->tun_old=False;
     st->device_path=dict_read_string(dict,"device",False,"tun-netlink",loc);
     st->interface_name=dict_read_string(dict,"interface",False,
                                        "tun-netlink",loc);
-    st->ifconfig_path=dict_read_string(dict,"device",False,"tun-netlink",loc);
-    st->route_path=dict_read_string(dict,"device",False,"tun-netlink",loc);
+    st->ifconfig_path=dict_read_string(dict,"ifconfig-path",
+                                      False,"tun-netlink",loc);
+    st->route_path=dict_read_string(dict,"route-path",
+                                   False,"tun-netlink",loc);
 
     if (!st->device_path) st->device_path="/dev/net/tun";
     if (!st->ifconfig_path) st->ifconfig_path="ifconfig";
     if (!st->route_path) st->route_path="route";
     st->buff=find_cl_if(dict,"buffer",CL_BUFFER,True,"tun-netlink",loc);
 
-    /* New TUN interface: open the device, then do ioctl TUNSETIFF
-       to set or find out the network interface name. */
-    st->fd=open(st->device_path,O_RDWR);
-    if (st->fd==-1) {
-       fatal_perror("%s: can't open device file %s",st->nl.name,
-                    st->device_path);
-    }
-    memset(&ifr,0,sizeof(ifr));
-    ifr.ifr_flags = IFF_TUN | IFF_NO_PI; /* Just send/receive IP packets,
-                                           no extra headers */
-    if (st->interface_name)
-       strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ);
-    if (ioctl(st->fd,TUNSETIFF,&ifr)<0) {
-       fatal_perror("%s: ioctl(TUNSETIFF)",st->nl.name);
-    }
-    if (!st->interface_name) {
-       st->interface_name=safe_malloc(strlen(ifr.ifr_name)+1,"tun_apply");
-       strcpy(st->interface_name,ifr.ifr_name);
-       Message(M_INFO,"%s: allocated network interface %s\n",st->nl.name,
-               st->interface_name);
-    }
-
-    add_hook(PHASE_DROPPRIV,tun_phase_hook,st);
+    add_hook(PHASE_GETRESOURCES,tun_phase_hook,st);
 
     return new_closure(&st->nl.cl);
 }
@@ -1003,7 +1180,6 @@ static list_t *tun_old_apply(closure_t *self, struct cloc loc, dict_t *context,
     struct tun *st;
     item_t *item;
     dict_t *dict;
-    bool_t search_for_if;
 
     st=safe_malloc(sizeof(*st),"tun_old_apply");
 
@@ -1021,13 +1197,15 @@ static list_t *tun_old_apply(closure_t *self, struct cloc loc, dict_t *context,
        netlink_init(&st->nl,st,loc,dict,
                     "netlink-tun",tun_deliver_to_kernel);
 
+    st->tun_old=True;
     st->device_path=dict_read_string(dict,"device",False,"tun-netlink",loc);
     st->interface_name=dict_read_string(dict,"interface",False,
                                        "tun-netlink",loc);
-    search_for_if=dict_read_bool(dict,"interface-search",False,"tun-netlink",
-                                loc,st->device_path==NULL);
-    st->ifconfig_path=dict_read_string(dict,"device",False,"tun-netlink",loc);
-    st->route_path=dict_read_string(dict,"device",False,"tun-netlink",loc);
+    st->search_for_if=dict_read_bool(dict,"interface-search",False,
+                                    "tun-netlink",loc,st->device_path==NULL);
+    st->ifconfig_path=dict_read_string(dict,"ifconfig-path",False,
+                                      "tun-netlink",loc);
+    st->route_path=dict_read_string(dict,"route-path",False,"tun-netlink",loc);
 
     if (!st->device_path) st->device_path="/dev/tun";
     if (!st->ifconfig_path) st->ifconfig_path="ifconfig";
@@ -1039,43 +1217,17 @@ static list_t *tun_old_apply(closure_t *self, struct cloc loc, dict_t *context,
        'device' as the prefix and try numbers from 0--255. If it's set
        to false, treat 'device' as the whole name, and require than an
        appropriate interface name be specified. */
-    if (search_for_if) {
-       string_t dname;
-       int i;
-
-       if (st->interface_name) {
-           cfgfatal(loc,"tun-old","you may not specify an interface name "
-                    "in interface-search mode\n");
-       }
-       dname=safe_malloc(strlen(st->device_path)+4,"tun_old_apply");
-       st->interface_name=safe_malloc(8,"tun_old_apply");
-       
-       for (i=0; i<255; i++) {
-           sprintf(dname,"%s%d",st->device_path,i);
-           if ((st->fd=open(dname,O_RDWR))>0) {
-               sprintf(st->interface_name,"tun%d",i);
-               Message(M_INFO,"%s: allocated network interface %s "
-                       "through %s\n",st->nl.name,st->interface_name,dname);
-               continue;
-           }
-       }
-       if (st->fd==-1) {
-           fatal("%s: unable to open any TUN device (%s...)\n",
-                 st->nl.name,st->device_path);
-       }
-    } else {
-       if (!st->interface_name) {
-           cfgfatal(loc,"tun-old","you must specify an interface name "
-                    "when you explicitly specify a TUN device file\n");
-       }
-       st->fd=open(st->device_path,O_RDWR);
-       if (st->fd==-1) {
-           fatal_perror("%s: unable to open TUN device file %s",
-                        st->nl.name,st->device_path);
-       }
+    if (st->search_for_if && st->interface_name) {
+       cfgfatal(loc,"tun-old","you may not specify an interface name "
+                "in interface-search mode\n");
     }
+    if (!st->search_for_if && !st->interface_name) {
+       cfgfatal(loc,"tun-old","you must specify an interface name "
+                "when you explicitly specify a TUN device file\n");
+    }
+
 
-    add_hook(PHASE_DROPPRIV,tun_phase_hook,st);
+    add_hook(PHASE_GETRESOURCES,tun_phase_hook,st);
 
     return new_closure(&st->nl.cl);
 }