chiark / gitweb /
changelog: start 0.6.8
[secnet.git] / tun.c
1 /*
2  * This file is part of secnet.
3  * See README for full list of copyright holders.
4  *
5  * secnet is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  * 
10  * secnet is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * version 3 along with secnet; if not, see
17  * https://www.gnu.org/licenses/gpl.html.
18  */
19
20 #include "secnet.h"
21 #include "util.h"
22 #include "netlink.h"
23 #include <stdio.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <sys/ioctl.h>
29 #include <sys/utsname.h>
30 #include <sys/socket.h>
31
32 #ifdef HAVE_NET_IF_H
33 #include <net/if.h>
34 #ifdef HAVE_LINUX_IF_TUN_H
35 #include <linux/if_tun.h>
36 #define LINUX_TUN_SUPPORTED
37 #endif
38 #endif
39
40 #ifdef HAVE_NET_ROUTE_H
41 #include <net/route.h>
42 #endif
43
44 #if defined(HAVE_STROPTS_H) && defined(HAVE_SYS_SOCKIO_H) && \
45 defined(HAVE_NET_IF_TUN_H)
46 #define HAVE_TUN_STREAMS
47 #endif
48
49 #ifdef HAVE_TUN_STREAMS
50 #include <stropts.h>
51 #include <sys/sockio.h>
52 #include <net/if_tun.h>
53 #endif
54
55 #define TUN_FLAVOUR_GUESS   0
56 #define TUN_FLAVOUR_BSD     1
57 #define TUN_FLAVOUR_LINUX   2
58 #define TUN_FLAVOUR_STREAMS 3
59
60 static struct flagstr flavours[]={
61     {"guess", TUN_FLAVOUR_GUESS},
62     {"bsd", TUN_FLAVOUR_BSD},
63     {"BSD", TUN_FLAVOUR_BSD},
64     {"linux", TUN_FLAVOUR_LINUX},
65     {"streams", TUN_FLAVOUR_STREAMS},
66     {"STREAMS", TUN_FLAVOUR_STREAMS},
67     {NULL, 0}
68 };
69
70 #define TUN_CONFIG_GUESS      0
71 #define TUN_CONFIG_IOCTL      1
72 #define TUN_CONFIG_BSD        2
73 #define TUN_CONFIG_LINUX      3
74 #define TUN_CONFIG_SOLARIS25  4
75
76 static struct flagstr config_types[]={
77     {"guess", TUN_CONFIG_GUESS},
78     {"ioctl", TUN_CONFIG_IOCTL},
79     {"bsd", TUN_CONFIG_BSD},
80     {"BSD", TUN_CONFIG_BSD},
81     {"linux", TUN_CONFIG_LINUX},
82     {"solaris-2.5", TUN_CONFIG_SOLARIS25},
83     {NULL, 0}
84 };
85
86 /* Connection to the kernel through the universal TUN/TAP driver */
87
88 struct tun {
89     struct netlink nl;
90     int fd;
91     cstring_t device_path;
92     cstring_t ip_path;
93     string_t interface_name;
94     cstring_t ifconfig_path;
95     uint32_t ifconfig_type;
96     cstring_t route_path;
97     uint32_t route_type;
98     uint32_t tun_flavour;
99     bool_t search_for_if; /* Applies to tun-BSD only */
100     struct buffer_if *buff; /* We receive packets into here
101                                and send them to the netlink code. */
102     netlink_deliver_fn *netlink_to_tunnel;
103 };
104
105 static cstring_t tun_flavour_str(uint32_t flavour)
106 {
107     switch (flavour) {
108     case TUN_FLAVOUR_GUESS: return "guess";
109     case TUN_FLAVOUR_BSD: return "BSD";
110     case TUN_FLAVOUR_LINUX: return "linux";
111     case TUN_FLAVOUR_STREAMS: return "STREAMS";
112     default: return "unknown";
113     }
114 }
115
116 static int tun_beforepoll(void *sst, struct pollfd *fds, int *nfds_io,
117                           int *timeout_io)
118 {
119     struct tun *st=sst;
120     BEFOREPOLL_WANT_FDS(1);
121     fds[0].fd=st->fd;
122     fds[0].events=POLLIN;
123     return 0;
124 }
125
126 static void tun_afterpoll(void *sst, struct pollfd *fds, int nfds)
127 {
128     struct tun *st=sst;
129     int l;
130
131     if (nfds==0) return;
132     if (fds[0].revents&POLLERR) {
133         printf("tun_afterpoll: hup!\n");
134     }
135     if (fds[0].revents&POLLIN) {
136         BUF_ALLOC(st->buff,"tun_afterpoll");
137         buffer_init(st->buff,calculate_max_start_pad());
138         l=read(st->fd, st->buff->start, buf_remaining_space(st->buff));
139         if (l<0) {
140             if (errno==EINTR || iswouldblock(errno)) return;
141             fatal_perror("tun_afterpoll: read()");
142         }
143         if (l==0) {
144             fatal("tun_afterpoll: read()=0; device gone away?");
145         }
146         if (l>0) {
147             st->buff->size=l;
148             st->netlink_to_tunnel(&st->nl,st->buff);
149             BUF_ASSERT_FREE(st->buff);
150         }
151     }
152 }
153
154 static void tun_deliver_to_kernel(void *sst, struct buffer_if *buf)
155 {
156     struct tun *st=sst;
157     ssize_t rc;
158
159     BUF_ASSERT_USED(buf);
160     
161     /* Log errors, so we can tell what's going on, but only once a
162        minute, so we don't flood the logs.  Short writes count as
163        errors. */
164     rc = write(st->fd,buf->start,buf->size);
165     if(rc != buf->size) {
166         static struct timeval last_report;
167         if(tv_now_global.tv_sec >= last_report.tv_sec + 60) {
168             if(rc < 0)
169                 Message(M_WARNING,
170                         "failed to deliver packet to tun device: %s\n",
171                         strerror(errno));
172             else
173                 Message(M_WARNING,
174                         "truncated packet delivered to tun device\n");
175             last_report = tv_now_global;
176         }
177     }
178     BUF_FREE(buf);
179 }
180
181 static bool_t tun_set_route(void *sst, struct netlink_client *routes)
182 {
183     struct tun *st=sst;
184     string_t network, mask, secnetaddr;
185     struct subnet_list *nets;
186     int32_t i;
187     int fd=-1;
188     bool_t up;
189
190     if (routes->options & OPT_SOFTROUTE)
191         up = routes->up;
192     else
193         up = routes->link_quality > LINK_QUALITY_UNUSED;
194
195     if (up == routes->kup) return False;
196     if (st->route_type==TUN_CONFIG_IOCTL) {
197         if (st->tun_flavour==TUN_FLAVOUR_STREAMS) {
198             fd=open(st->ip_path,O_RDWR);
199             if (fd<0) {
200                 fatal_perror("tun_set_route: can't open %s",st->ip_path);
201             }
202         } else {
203             fd=socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
204             if (fd<0) {
205                 fatal_perror("tun_set_route: socket()");
206             }
207         }
208     }
209     nets=routes->subnets;
210     secnetaddr=ipaddr_to_string(st->nl.secnet_address);
211     for (i=0; i<nets->entries; i++) {
212         network=ipaddr_to_string(nets->list[i].prefix);
213         mask=ipaddr_to_string(nets->list[i].mask);
214         Message(M_INFO,"%s: %s route %s/%d %s kernel routing table\n",
215                 st->nl.name,up?"adding":"deleting",network,
216                 nets->list[i].len,up?"to":"from");
217         switch (st->route_type) {
218         case TUN_CONFIG_LINUX:
219             sys_cmd(st->route_path,"route",up?"add":"del",
220                     "-net",network,"netmask",mask,
221                     "gw",secnetaddr,(char *)0);
222             break;
223         case TUN_CONFIG_BSD:
224             sys_cmd(st->route_path,"route",up?"add":"del",
225                     "-net",network,secnetaddr,mask,(char *)0);
226             break;
227         case TUN_CONFIG_SOLARIS25:
228             sys_cmd(st->route_path,"route",up?"add":"del",
229                     network,secnetaddr,(char *)0);
230             break;
231         case TUN_CONFIG_IOCTL:
232         {
233           /* darwin rtentry has a different format, use /sbin/route instead */
234 #if HAVE_NET_ROUTE_H && ! __APPLE__
235             struct rtentry rt;
236             struct sockaddr_in *sa;
237             int action;
238             
239             FILLZERO(rt);
240             sa=(struct sockaddr_in *)&rt.rt_dst;
241             sa->sin_family=AF_INET;
242             sa->sin_addr.s_addr=htonl(nets->list[i].prefix);
243             sa=(struct sockaddr_in *)&rt.rt_genmask;
244             sa->sin_family=AF_INET;
245             sa->sin_addr.s_addr=htonl(nets->list[i].mask);
246             sa=(struct sockaddr_in *)&rt.rt_gateway;
247             sa->sin_family=AF_INET;
248             sa->sin_addr.s_addr=htonl(st->nl.secnet_address);
249             rt.rt_flags=RTF_UP|RTF_GATEWAY;
250             action=up?SIOCADDRT:SIOCDELRT;
251             if (ioctl(fd,action,&rt)<0) {
252                 fatal_perror("tun_set_route: ioctl()");
253             }
254 #else
255             fatal("tun_set_route: ioctl method not supported");
256 #endif
257         }
258         break;
259         default:
260             fatal("tun_set_route: unsupported route command type");
261             break;
262         }
263     }
264     if (fd >= 0) {
265         close(fd);
266     }
267     routes->kup=up;
268     return True;
269 }
270
271 static void tun_phase_hook(void *sst, uint32_t newphase)
272 {
273     struct tun *st=sst;
274     string_t hostaddr,secnetaddr;
275     char mtu[6];
276     struct netlink_client *r;
277
278     if (st->tun_flavour==TUN_FLAVOUR_BSD) {
279         if (st->search_for_if) {
280             string_t dname;
281             int i;
282
283             dname=safe_malloc(strlen(st->device_path)+4,"tun_old_apply");
284             st->interface_name=safe_malloc(8,"tun_phase_hook");
285         
286             for (i=0; i<255; i++) {
287                 sprintf(dname,"%s%d",st->device_path,i);
288                 if ((st->fd=open(dname,O_RDWR))>0) {
289                     sprintf(st->interface_name,"tun%d",i);
290                     Message(M_INFO,"%s: allocated network interface %s "
291                             "through %s\n",st->nl.name,st->interface_name,
292                             dname);
293                     break;
294                 }
295             }
296             if (st->fd==-1) {
297                 fatal("%s: unable to open any TUN device (%s...)",
298                       st->nl.name,st->device_path);
299             }
300         } else {
301             st->fd=open(st->device_path,O_RDWR);
302             if (st->fd==-1) {
303                 fatal_perror("%s: unable to open TUN device file %s",
304                              st->nl.name,st->device_path);
305             }
306         }
307     } else if (st->tun_flavour==TUN_FLAVOUR_LINUX) {
308 #ifdef LINUX_TUN_SUPPORTED
309         struct ifreq ifr;
310
311         /* New TUN interface: open the device, then do ioctl TUNSETIFF
312            to set or find out the network interface name. */
313         st->fd=open(st->device_path,O_RDWR);
314         if (st->fd==-1) {
315             fatal_perror("%s: can't open device file %s",st->nl.name,
316                          st->device_path);
317         }
318         FILLZERO(ifr);
319         ifr.ifr_flags = IFF_TUN | IFF_NO_PI; /* Just send/receive IP packets,
320                                                 no extra headers */
321         if (st->interface_name)
322             strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ);
323         if (ioctl(st->fd,TUNSETIFF,&ifr)<0) {
324             fatal_perror("%s: ioctl(TUNSETIFF)",st->nl.name);
325         }
326         if (!st->interface_name) {
327             st->interface_name=safe_malloc(strlen(ifr.ifr_name)+1,"tun_apply");
328             strcpy(st->interface_name,ifr.ifr_name);
329             Message(M_INFO,"%s: allocated network interface %s\n",st->nl.name,
330                     st->interface_name);
331         }
332 #else
333         fatal("tun_phase_hook: TUN_FLAVOUR_LINUX unexpected");
334 #endif /* LINUX_TUN_SUPPORTED */
335     } else if (st->tun_flavour==TUN_FLAVOUR_STREAMS) {
336 #ifdef HAVE_TUN_STREAMS
337         int tun_fd, if_fd, ppa=-1, ip_fd;
338
339         if ((ip_fd=open(st->ip_path, O_RDWR)) < 0) {
340             fatal_perror("%s: can't open %s",st->nl.name,st->ip_path);
341         }
342         if ((tun_fd=open(st->device_path,O_RDWR)) < 0) {
343             fatal_perror("%s: can't open %s",st->nl.name,st->device_path);
344         }
345         if ((ppa=ioctl(tun_fd,TUNNEWPPA,ppa)) < 0) {
346             fatal_perror("%s: can't assign new interface");
347         }
348         if ((if_fd=open(st->device_path,O_RDWR)) < 0) {
349             fatal_perror("%s: can't open %s (2)",st->nl.name,st->device_path);
350         }
351         if (ioctl(if_fd,I_PUSH,"ip") < 0) {
352             fatal_perror("%s: can't push IP module",st->nl.name);
353         }
354         if (ioctl(if_fd,IF_UNITSEL,(char *)&ppa) < 0) {
355             fatal_perror("%s: can't set ppa %d",st->nl.name,ppa);
356         }
357         if (ioctl(ip_fd, I_LINK, if_fd) < 0) {
358             fatal_perror("%s: can't link TUN device to IP",st->nl.name);
359         }
360         st->interface_name=safe_malloc(10,"tun_apply");
361         sprintf(st->interface_name,"tun%d",ppa);
362         st->fd=tun_fd;
363         setcloexec(if_ifd);
364         setcloexec(ip_ifd);
365 #else
366         fatal("tun_phase_hook: TUN_FLAVOUR_STREAMS unexpected");
367 #endif /* HAVE_TUN_STREAMS */
368     } else {
369         fatal("tun_phase_hook: unknown flavour of TUN");
370     }
371     /* All the networks we'll be using have been registered. Invoke ifconfig
372        to set the TUN device's address, and route to add routes to all
373        our networks. */
374
375     setcloexec(st->fd);
376     setnonblock(st->fd);
377
378     hostaddr=ipaddr_to_string(st->nl.local_address);
379     secnetaddr=ipaddr_to_string(st->nl.secnet_address);
380     snprintf(mtu,sizeof(mtu),"%d",st->nl.mtu);
381     mtu[5]=0;
382
383     switch (st->ifconfig_type) {
384     case TUN_CONFIG_LINUX:
385         sys_cmd(st->ifconfig_path,"ifconfig",st->interface_name,
386                 hostaddr,"netmask","255.255.255.255","-broadcast",
387                 "-multicast",
388                 "pointopoint",secnetaddr,"mtu",mtu,"up",(char *)0);
389         break;
390     case TUN_CONFIG_BSD:
391         sys_cmd(st->ifconfig_path,"ifconfig",st->interface_name,
392                 hostaddr,"netmask","255.255.255.255",
393                 secnetaddr,"mtu",mtu,"up",(char *)0);
394         break;
395     case TUN_CONFIG_SOLARIS25:
396         sys_cmd(st->ifconfig_path,"ifconfig",st->interface_name,
397                 hostaddr,secnetaddr,"mtu",mtu,"up",(char *)0);
398         break;
399     case TUN_CONFIG_IOCTL:
400 #if HAVE_NET_IF_H && ! __APPLE__
401     {
402         int fd;
403         struct ifreq ifr;
404         struct sockaddr_in *sa;
405         fd=socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
406
407         /* Interface address */
408         strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ);
409         sa=(struct sockaddr_in *)&ifr.ifr_addr;
410         FILLZERO(*sa);
411         sa->sin_family=AF_INET;
412         sa->sin_addr.s_addr=htonl(st->nl.local_address);
413         if (ioctl(fd,SIOCSIFADDR, &ifr)!=0) {
414             fatal_perror("tun_apply: SIOCSIFADDR");
415         }
416 #ifdef SIOCSIFNETMASK
417         /* Netmask */
418         strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ);
419         sa=(struct sockaddr_in *)&ifr.ifr_netmask;
420         FILLZERO(*sa);
421         sa->sin_family=AF_INET;
422         sa->sin_addr.s_addr=htonl(0xffffffff);
423         if (ioctl(fd,SIOCSIFNETMASK, &ifr)!=0) {
424             fatal_perror("tun_apply: SIOCSIFNETMASK");
425         }
426 #endif
427         /* Destination address (point-to-point) */
428         strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ);
429         sa=(struct sockaddr_in *)&ifr.ifr_dstaddr;
430         FILLZERO(*sa);
431         sa->sin_family=AF_INET;
432         sa->sin_addr.s_addr=htonl(st->nl.secnet_address);
433         if (ioctl(fd,SIOCSIFDSTADDR, &ifr)!=0) {
434             fatal_perror("tun_apply: SIOCSIFDSTADDR");
435         }
436         /* MTU */
437         strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ);
438         ifr.ifr_mtu=st->nl.mtu;
439         if (ioctl(fd,SIOCSIFMTU, &ifr)!=0) {
440             fatal_perror("tun_apply: SIOCSIFMTU");
441         }
442         /* Flags */
443         strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ);
444         ifr.ifr_flags=IFF_UP|IFF_POINTOPOINT|IFF_RUNNING|IFF_NOARP;
445         if (ioctl(fd,SIOCSIFFLAGS, &ifr)!=0) {
446             fatal_perror("tun_apply: SIOCSIFFLAGS");
447         }
448
449         close(fd);
450     }
451 #else
452     fatal("tun_apply: ifconfig by ioctl() not supported");
453 #endif /* HAVE_NET_IF_H */
454     break;
455     default:
456         fatal("tun_apply: unsupported ifconfig method");
457         break;
458     }
459         
460     for (r=st->nl.clients; r; r=r->next) {
461         tun_set_route(st,r);
462     }
463
464     add_hook(PHASE_CHILDPERSIST,childpersist_closefd_hook,&st->fd);
465
466     /* Register for poll() */
467     register_for_poll(st, tun_beforepoll, tun_afterpoll, st->nl.name);
468 }
469
470 static list_t *tun_create(closure_t *self, struct cloc loc, dict_t *context,
471                           list_t *args,uint32_t default_flavour)
472 {
473     struct tun *st;
474     item_t *item;
475     dict_t *dict;
476     string_t flavour,type;
477
478     NEW(st);
479
480     /* First parameter must be a dict */
481     item=list_elem(args,0);
482     if (!item || item->type!=t_dict)
483         cfgfatal(loc,"tun","parameter must be a dictionary\n");
484     
485     dict=item->data.dict;
486
487     st->netlink_to_tunnel=
488         netlink_init(&st->nl,st,loc,dict,
489                      "netlink-tun",tun_set_route,tun_deliver_to_kernel);
490
491     flavour=dict_read_string(dict,"flavour",False,"tun-netlink",loc);
492     if (flavour)
493         st->tun_flavour=string_to_word(flavour,loc,flavours,"tun-flavour");
494     else
495         st->tun_flavour=default_flavour;
496
497     st->device_path=dict_read_string(dict,"device",False,"tun-netlink",loc);
498     st->ip_path=dict_read_string(dict,"ip-path",False,"tun-netlink",loc);
499     st->interface_name=dict_read_string(dict,"interface",False,
500                                         "tun-netlink",loc);
501     st->search_for_if=dict_read_bool(dict,"interface-search",False,
502                                      "tun-netlink",loc,st->device_path==NULL);
503
504     type=dict_read_string(dict,"ifconfig-type",False,"tun-netlink",loc);
505     if (type) st->ifconfig_type=string_to_word(type,loc,config_types,
506                                                "ifconfig-type");
507     else st->ifconfig_type=TUN_CONFIG_GUESS;
508     st->ifconfig_path=dict_read_string(dict,"ifconfig-path",False,
509                                        "tun-netlink",loc);
510
511     type=dict_read_string(dict,"route-type",False,"tun-netlink",loc);
512     if (type) st->route_type=string_to_word(type,loc,config_types,
513                                             "route-type");
514     else st->route_type=TUN_CONFIG_GUESS;
515     st->route_path=dict_read_string(dict,"route-path",False,"tun-netlink",loc);
516
517     st->buff=find_cl_if(dict,"buffer",CL_BUFFER,True,"tun-netlink",loc);
518
519     if (st->tun_flavour==TUN_FLAVOUR_GUESS) {
520         /* If we haven't been told what type of TUN we're using, take
521            a guess based on the system details. */
522         struct utsname u;
523         if (uname(&u)<0) {
524             fatal_perror("tun_create: uname");
525         }
526         if (strcmp(u.sysname,"Linux")==0) {
527             st->tun_flavour=TUN_FLAVOUR_LINUX;
528         } else if (strcmp(u.sysname,"SunOS")==0) {
529             st->tun_flavour=TUN_FLAVOUR_STREAMS;
530         } else if (strcmp(u.sysname,"FreeBSD")==0
531                    || strcmp(u.sysname,"Darwin")==0) {
532             st->tun_flavour=TUN_FLAVOUR_BSD;
533         }
534     }
535     if (st->tun_flavour==TUN_FLAVOUR_GUESS) {
536         cfgfatal(loc,"tun","cannot guess which type of TUN is in use; "
537                  "specify the flavour explicitly\n");
538     }
539
540     if (st->ifconfig_type==TUN_CONFIG_GUESS) {
541         switch (st->tun_flavour) {
542         case TUN_FLAVOUR_LINUX:
543             st->ifconfig_type=TUN_CONFIG_IOCTL;
544             break;
545         case TUN_FLAVOUR_BSD:
546 #if __linux__
547             /* XXX on Linux we still want TUN_CONFIG_IOCTL.  Perhaps we can
548                use this on BSD too. */
549             st->ifconfig_type=TUN_CONFIG_IOCTL;
550 #else     
551             st->ifconfig_type=TUN_CONFIG_BSD;
552 #endif
553             break;
554         case TUN_FLAVOUR_STREAMS:
555             st->ifconfig_type=TUN_CONFIG_BSD;
556             break;
557         }
558     }
559     if (st->route_type==TUN_CONFIG_GUESS)
560         st->route_type=st->ifconfig_type;
561
562     if (st->ifconfig_type==TUN_CONFIG_GUESS) {
563         cfgfatal(loc,"tun","cannot guess which ifconfig method to use\n");
564     }
565     if (st->route_type==TUN_CONFIG_GUESS) {
566         cfgfatal(loc,"tun","cannot guess which route method to use\n");
567     }
568
569     if (st->ifconfig_type==TUN_CONFIG_IOCTL && st->ifconfig_path) {
570         cfgfatal(loc,"tun","ifconfig-type \"ioctl\" is incompatible with "
571                  "ifconfig-path\n");
572     }
573     if (st->route_type==TUN_CONFIG_IOCTL && st->route_path) {
574         cfgfatal(loc,"tun","route-type \"ioctl\" is incompatible with "
575                  "route-path\n");
576     }
577
578     Message(M_DEBUG_CONFIG,"%s: tun flavour %s\n",st->nl.name,
579             tun_flavour_str(st->tun_flavour));
580     switch (st->tun_flavour) {
581     case TUN_FLAVOUR_BSD:
582         if (!st->device_path) st->device_path="/dev/tun";
583         break;
584     case TUN_FLAVOUR_LINUX:
585         if (!st->device_path) st->device_path="/dev/net/tun";
586         break;
587     case TUN_FLAVOUR_STREAMS:
588         if (!st->device_path) st->device_path="/dev/tun";
589         if (st->interface_name) cfgfatal(loc,"tun","interface name cannot "
590                                          "be specified with STREAMS TUN\n");
591         break;
592     }
593     
594     if (!st->ip_path) st->ip_path="/dev/ip";
595     if (!st->ifconfig_path) st->ifconfig_path="ifconfig";
596     if (!st->route_path) st->route_path="route";
597
598 #ifndef HAVE_TUN_STREAMS
599     if (st->tun_flavour==TUN_FLAVOUR_STREAMS) {
600         cfgfatal(loc,"tun","TUN flavour STREAMS unsupported in this build "
601                  "of secnet\n");
602     }
603 #endif
604 #ifndef LINUX_TUN_SUPPORTED
605     if (st->tun_flavour==TUN_FLAVOUR_LINUX) {
606         cfgfatal(loc,"tun","TUN flavour LINUX unsupported in this build "
607                  "of secnet\n");
608     }
609 #endif
610
611     /* Old TUN interface: the network interface name depends on which
612        /dev/tunX file we open. If 'interface-search' is set to true, treat
613        'device' as the prefix and try numbers from 0--255. If it's set
614        to false, treat 'device' as the whole name, and require than an
615        appropriate interface name be specified. */
616     if (st->tun_flavour==TUN_FLAVOUR_BSD) {
617         if (st->search_for_if && st->interface_name) {
618             cfgfatal(loc,"tun","you may not specify an interface name "
619                      "in interface-search mode\n");
620         }
621         if (!st->search_for_if && !st->interface_name) {
622             cfgfatal(loc,"tun","you must specify an interface name "
623                      "when you explicitly specify a TUN device file\n");
624         }
625     }
626
627     add_hook(PHASE_GETRESOURCES,tun_phase_hook,st);
628
629     return new_closure(&st->nl.cl);
630 }
631
632 static list_t *tun_apply(closure_t *self, struct cloc loc, dict_t *context,
633                          list_t *args)
634 {
635     return tun_create(self,loc,context,args,TUN_FLAVOUR_GUESS);
636 }
637
638 static list_t *tun_bsd_apply(closure_t *self, struct cloc loc, dict_t *context,
639                              list_t *args)
640 {
641     Message(M_WARNING,"(%s,%d): obsolete use of tun-old; replace with tun "
642             "and specify flavour \"bsd\".\n",loc.file,loc.line);
643     return tun_create(self,loc,context,args,TUN_FLAVOUR_BSD);
644 }
645
646 void tun_module(dict_t *dict)
647 {
648     add_closure(dict,"tun",tun_apply);
649     add_closure(dict,"tun-old",tun_bsd_apply);
650 }