X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;f=netlink.c;h=dea05f2e912f0294189244603897b25037d1abee;hb=refs%2Ftags%2Fv0.1.1;hp=0b1ebe411291b7f4b8f9bb59490693b5e94084ea;hpb=4efd681a66c15bc6f81eefc69396669e165e5e0f;p=secnet.git diff --git a/netlink.c b/netlink.c index 0b1ebe4..dea05f2 100644 --- 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 #include #include #include #include - -#include "config.h" -#include "secnet.h" #include "util.h" #ifdef HAVE_LINUX_IF_H @@ -35,7 +32,7 @@ #include #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; in_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.lennet.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; jnetworks->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; in_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; inetworks.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); }