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