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