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