Merge remote-tracking branch 'mdw/mdw/powm-sec'
[secnet] / tun.c
CommitLineData
c215a4bc
IJ
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 d 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
9d3a4132
SE
20#include "secnet.h"
21#include "util.h"
22#include "netlink.h"
23#include <stdio.h>
24#include <string.h>
090dbeef 25#include <errno.h>
9d3a4132
SE
26#include <unistd.h>
27#include <fcntl.h>
28#include <sys/ioctl.h>
ff05a229
SE
29#include <sys/utsname.h>
30#include <sys/socket.h>
9d3a4132 31
ff05a229
SE
32#ifdef HAVE_NET_IF_H
33#include <net/if.h>
94ca562b 34#ifdef HAVE_LINUX_IF_TUN_H
9d3a4132 35#include <linux/if_tun.h>
ff05a229 36#define LINUX_TUN_SUPPORTED
9d3a4132 37#endif
ff05a229
SE
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) && \
45defined(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
60static 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};
9d3a4132 69
ff05a229
SE
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
76static 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};
9d3a4132
SE
85
86/* Connection to the kernel through the universal TUN/TAP driver */
87
88struct tun {
89 struct netlink nl;
90 int fd;
fe5e9cc4
SE
91 cstring_t device_path;
92 cstring_t ip_path;
9d3a4132 93 string_t interface_name;
fe5e9cc4 94 cstring_t ifconfig_path;
ff05a229 95 uint32_t ifconfig_type;
fe5e9cc4 96 cstring_t route_path;
ff05a229
SE
97 uint32_t route_type;
98 uint32_t tun_flavour;
99 bool_t search_for_if; /* Applies to tun-BSD only */
9d3a4132
SE
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
fe5e9cc4 105static cstring_t tun_flavour_str(uint32_t flavour)
ff05a229
SE
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
9d3a4132 116static int tun_beforepoll(void *sst, struct pollfd *fds, int *nfds_io,
90a39563 117 int *timeout_io)
9d3a4132
SE
118{
119 struct tun *st=sst;
ee697dd9 120 BEFOREPOLL_WANT_FDS(1);
9d3a4132 121 fds[0].fd=st->fd;
fe5e9cc4 122 fds[0].events=POLLIN;
9d3a4132
SE
123 return 0;
124}
125
90a39563 126static void tun_afterpoll(void *sst, struct pollfd *fds, int nfds)
9d3a4132
SE
127{
128 struct tun *st=sst;
129 int l;
130
7138d0c5 131 if (nfds==0) return;
9d3a4132
SE
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");
3abd18e8 137 buffer_init(st->buff,calculate_max_start_pad());
92795040 138 l=read(st->fd, st->buff->start, buf_remaining_space(st->buff));
9d3a4132 139 if (l<0) {
ba703386 140 if (errno==EINTR || iswouldblock(errno)) return;
9d3a4132
SE
141 fatal_perror("tun_afterpoll: read()");
142 }
143 if (l==0) {
4f5e39ec 144 fatal("tun_afterpoll: read()=0; device gone away?");
9d3a4132
SE
145 }
146 if (l>0) {
147 st->buff->size=l;
469fd1d9 148 st->netlink_to_tunnel(&st->nl,st->buff);
9d3a4132
SE
149 BUF_ASSERT_FREE(st->buff);
150 }
151 }
152}
153
469fd1d9 154static void tun_deliver_to_kernel(void *sst, struct buffer_if *buf)
9d3a4132
SE
155{
156 struct tun *st=sst;
090dbeef 157 ssize_t rc;
9d3a4132
SE
158
159 BUF_ASSERT_USED(buf);
090dbeef
RK
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 }
9d3a4132
SE
178 BUF_FREE(buf);
179}
180
d3fe100d 181static bool_t tun_set_route(void *sst, struct netlink_client *routes)
9d3a4132
SE
182{
183 struct tun *st=sst;
184 string_t network, mask, secnetaddr;
d3fe100d 185 struct subnet_list *nets;
1caa23ff 186 int32_t i;
ff05a229 187 int fd=-1;
efacf9e0 188 bool_t up;
ff05a229 189
efacf9e0
ST
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;
ff05a229
SE
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",
efacf9e0
ST
215 st->nl.name,up?"adding":"deleting",network,
216 nets->list[i].len,up?"to":"from");
ff05a229
SE
217 switch (st->route_type) {
218 case TUN_CONFIG_LINUX:
efacf9e0 219 sys_cmd(st->route_path,"route",up?"add":"del",
ff05a229
SE
220 "-net",network,"netmask",mask,
221 "gw",secnetaddr,(char *)0);
222 break;
223 case TUN_CONFIG_BSD:
efacf9e0 224 sys_cmd(st->route_path,"route",up?"add":"del",
ff05a229
SE
225 "-net",network,secnetaddr,mask,(char *)0);
226 break;
227 case TUN_CONFIG_SOLARIS25:
efacf9e0 228 sys_cmd(st->route_path,"route",up?"add":"del",
ff05a229
SE
229 network,secnetaddr,(char *)0);
230 break;
231 case TUN_CONFIG_IOCTL:
232 {
ea7ec970
SE
233 /* darwin rtentry has a different format, use /sbin/route instead */
234#if HAVE_NET_ROUTE_H && ! __APPLE__
ff05a229
SE
235 struct rtentry rt;
236 struct sockaddr_in *sa;
237 int action;
238
076bb54e 239 FILLZERO(rt);
ff05a229
SE
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;
efacf9e0 250 action=up?SIOCADDRT:SIOCDELRT;
ff05a229
SE
251 if (ioctl(fd,action,&rt)<0) {
252 fatal_perror("tun_set_route: ioctl()");
253 }
254#else
4f5e39ec 255 fatal("tun_set_route: ioctl method not supported");
ff05a229 256#endif
d3fe100d 257 }
ff05a229
SE
258 break;
259 default:
4f5e39ec 260 fatal("tun_set_route: unsupported route command type");
ff05a229
SE
261 break;
262 }
ff05a229 263 }
56fd04e4 264 if (fd >= 0) {
ff05a229 265 close(fd);
9d3a4132 266 }
efacf9e0 267 routes->kup=up;
ff05a229 268 return True;
9d3a4132
SE
269}
270
271static void tun_phase_hook(void *sst, uint32_t newphase)
272{
273 struct tun *st=sst;
274 string_t hostaddr,secnetaddr;
7c006408 275 char mtu[6];
d3fe100d 276 struct netlink_client *r;
9d3a4132 277
ff05a229 278 if (st->tun_flavour==TUN_FLAVOUR_BSD) {
9d3a4132
SE
279 if (st->search_for_if) {
280 string_t dname;
281 int i;
282
9d3a4132
SE
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) {
4f5e39ec 297 fatal("%s: unable to open any TUN device (%s...)",
9d3a4132
SE
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 }
ff05a229
SE
307 } else if (st->tun_flavour==TUN_FLAVOUR_LINUX) {
308#ifdef LINUX_TUN_SUPPORTED
9d3a4132
SE
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 }
076bb54e 318 FILLZERO(ifr);
9d3a4132
SE
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);
9d3a4132
SE
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
4f5e39ec 333 fatal("tun_phase_hook: TUN_FLAVOUR_LINUX unexpected");
ff05a229
SE
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;
4fb0f88d
IJ
363 setcloexec(if_ifd);
364 setcloexec(ip_ifd);
ff05a229 365#else
4f5e39ec 366 fatal("tun_phase_hook: TUN_FLAVOUR_STREAMS unexpected");
ff05a229
SE
367#endif /* HAVE_TUN_STREAMS */
368 } else {
4f5e39ec 369 fatal("tun_phase_hook: unknown flavour of TUN");
9d3a4132
SE
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
4fb0f88d 375 setcloexec(st->fd);
ba703386 376 setnonblock(st->fd);
4fb0f88d 377
091433c6 378 hostaddr=ipaddr_to_string(st->nl.local_address);
9d3a4132 379 secnetaddr=ipaddr_to_string(st->nl.secnet_address);
c1d2109a 380 snprintf(mtu,sizeof(mtu),"%d",st->nl.mtu);
9d3a4132
SE
381 mtu[5]=0;
382
fe5e9cc4 383 switch (st->ifconfig_type) {
ff05a229
SE
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:
ea7ec970 400#if HAVE_NET_IF_H && ! __APPLE__
ff05a229
SE
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;
076bb54e 410 FILLZERO(*sa);
ff05a229 411 sa->sin_family=AF_INET;
091433c6 412 sa->sin_addr.s_addr=htonl(st->nl.local_address);
ff05a229
SE
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;
076bb54e 420 FILLZERO(*sa);
ff05a229
SE
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;
076bb54e 430 FILLZERO(*sa);
ff05a229
SE
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 }
9d3a4132 448
ff05a229
SE
449 close(fd);
450 }
451#else
4f5e39ec 452 fatal("tun_apply: ifconfig by ioctl() not supported");
ff05a229
SE
453#endif /* HAVE_NET_IF_H */
454 break;
455 default:
4f5e39ec 456 fatal("tun_apply: unsupported ifconfig method");
ff05a229
SE
457 break;
458 }
459
d3fe100d
SE
460 for (r=st->nl.clients; r; r=r->next) {
461 tun_set_route(st,r);
9d3a4132
SE
462 }
463
32654a31
IJ
464 add_hook(PHASE_CHILDPERSIST,childpersist_closefd_hook,&st->fd);
465
9d3a4132 466 /* Register for poll() */
32fc582f 467 register_for_poll(st, tun_beforepoll, tun_afterpoll, st->nl.name);
9d3a4132
SE
468}
469
ff05a229
SE
470static list_t *tun_create(closure_t *self, struct cloc loc, dict_t *context,
471 list_t *args,uint32_t default_flavour)
9d3a4132
SE
472{
473 struct tun *st;
474 item_t *item;
475 dict_t *dict;
ff05a229 476 string_t flavour,type;
9d3a4132 477
b7886fd4 478 NEW(st);
9d3a4132
SE
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
ff05a229
SE
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
9d3a4132 497 st->device_path=dict_read_string(dict,"device",False,"tun-netlink",loc);
ff05a229 498 st->ip_path=dict_read_string(dict,"ip-path",False,"tun-netlink",loc);
9d3a4132
SE
499 st->interface_name=dict_read_string(dict,"interface",False,
500 "tun-netlink",loc);
ff05a229
SE
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);
9d3a4132 516
9d3a4132
SE
517 st->buff=find_cl_if(dict,"buffer",CL_BUFFER,True,"tun-netlink",loc);
518
ff05a229
SE
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) {
621c2861 527 st->tun_flavour=TUN_FLAVOUR_LINUX;
ff05a229
SE
528 } else if (strcmp(u.sysname,"SunOS")==0) {
529 st->tun_flavour=TUN_FLAVOUR_STREAMS;
ea7ec970
SE
530 } else if (strcmp(u.sysname,"FreeBSD")==0
531 || strcmp(u.sysname,"Darwin")==0) {
ff05a229
SE
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 }
9d3a4132 539
ff05a229
SE
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:
ea7ec970 546#if __linux__
4f5e39ec
SE
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;
ea7ec970
SE
550#else
551 st->ifconfig_type=TUN_CONFIG_BSD;
552#endif
ff05a229
SE
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;
9d3a4132 561
ff05a229
SE
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 }
9d3a4132 568
ff05a229
SE
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 }
9d3a4132 577
ff05a229
SE
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 }
9d3a4132 593
ff05a229 594 if (!st->ip_path) st->ip_path="/dev/ip";
9d3a4132
SE
595 if (!st->ifconfig_path) st->ifconfig_path="ifconfig";
596 if (!st->route_path) st->route_path="route";
ff05a229
SE
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
9d3a4132
SE
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. */
ff05a229
SE
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 }
9d3a4132
SE
625 }
626
9d3a4132
SE
627 add_hook(PHASE_GETRESOURCES,tun_phase_hook,st);
628
629 return new_closure(&st->nl.cl);
630}
631
ff05a229
SE
632static 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
638static 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
9d3a4132
SE
646void tun_module(dict_t *dict)
647{
9d3a4132 648 add_closure(dict,"tun",tun_apply);
ff05a229 649 add_closure(dict,"tun-old",tun_bsd_apply);
9d3a4132 650}