X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=secnet.git;a=blobdiff_plain;f=tun.c;h=dcfd62392d825994665a787a88f3afe588230d3c;hp=01c0cb8960f942d505ccf8d3bc3d1fd077d2717d;hb=2694a33edb14ea5abf5637e4a88a5aeff2e77453;hpb=b2a56f7c93d221607864761c590952b9a614dc9f diff --git a/tun.c b/tun.c index 01c0cb8..dcfd623 100644 --- a/tun.c +++ b/tun.c @@ -3,120 +3,265 @@ #include "netlink.h" #include #include +#include #include #include #include +#include +#include +#ifdef HAVE_NET_IF_H +#include #ifdef HAVE_LINUX_IF_H -#include #include +#define LINUX_TUN_SUPPORTED #endif +#endif + +#ifdef HAVE_NET_ROUTE_H +#include +#endif + +#if defined(HAVE_STROPTS_H) && defined(HAVE_SYS_SOCKIO_H) && \ +defined(HAVE_NET_IF_TUN_H) +#define HAVE_TUN_STREAMS +#endif + +#ifdef HAVE_TUN_STREAMS +#include +#include +#include +#endif + +#define TUN_FLAVOUR_GUESS 0 +#define TUN_FLAVOUR_BSD 1 +#define TUN_FLAVOUR_LINUX 2 +#define TUN_FLAVOUR_STREAMS 3 + +static struct flagstr flavours[]={ + {"guess", TUN_FLAVOUR_GUESS}, + {"bsd", TUN_FLAVOUR_BSD}, + {"BSD", TUN_FLAVOUR_BSD}, + {"linux", TUN_FLAVOUR_LINUX}, + {"streams", TUN_FLAVOUR_STREAMS}, + {"STREAMS", TUN_FLAVOUR_STREAMS}, + {NULL, 0} +}; -/* XXX where do we find if_tun on other platforms? */ +#define TUN_CONFIG_GUESS 0 +#define TUN_CONFIG_IOCTL 1 +#define TUN_CONFIG_BSD 2 +#define TUN_CONFIG_LINUX 3 +#define TUN_CONFIG_SOLARIS25 4 + +static struct flagstr config_types[]={ + {"guess", TUN_CONFIG_GUESS}, + {"ioctl", TUN_CONFIG_IOCTL}, + {"bsd", TUN_CONFIG_BSD}, + {"BSD", TUN_CONFIG_BSD}, + {"linux", TUN_CONFIG_LINUX}, + {"solaris-2.5", TUN_CONFIG_SOLARIS25}, + {NULL, 0} +}; /* Connection to the kernel through the universal TUN/TAP driver */ struct tun { struct netlink nl; int fd; - string_t device_path; + cstring_t device_path; + cstring_t ip_path; string_t interface_name; - string_t ifconfig_path; - string_t route_path; - bool_t tun_old; - bool_t search_for_if; /* Applies to tun-old only */ + cstring_t ifconfig_path; + uint32_t ifconfig_type; + cstring_t route_path; + uint32_t route_type; + uint32_t tun_flavour; + bool_t search_for_if; /* Applies to tun-BSD only */ struct buffer_if *buff; /* We receive packets into here and send them to the netlink code. */ netlink_deliver_fn *netlink_to_tunnel; - uint32_t local_address; /* host interface address */ }; +static cstring_t tun_flavour_str(uint32_t flavour) +{ + switch (flavour) { + case TUN_FLAVOUR_GUESS: return "guess"; + case TUN_FLAVOUR_BSD: return "BSD"; + case TUN_FLAVOUR_LINUX: return "linux"; + case TUN_FLAVOUR_STREAMS: return "STREAMS"; + default: return "unknown"; + } +} + static int tun_beforepoll(void *sst, struct pollfd *fds, int *nfds_io, - int *timeout_io, const struct timeval *tv_now, - uint64_t *now) + int *timeout_io) { struct tun *st=sst; *nfds_io=1; fds[0].fd=st->fd; - fds[0].events=POLLIN|POLLERR|POLLHUP; + fds[0].events=POLLIN; return 0; } -static void tun_afterpoll(void *sst, struct pollfd *fds, int nfds, - const struct timeval *tv_now, uint64_t *now) +static void tun_afterpoll(void *sst, struct pollfd *fds, int nfds) { struct tun *st=sst; int l; + if (nfds==0) return; if (fds[0].revents&POLLERR) { printf("tun_afterpoll: hup!\n"); } if (fds[0].revents&POLLIN) { BUF_ALLOC(st->buff,"tun_afterpoll"); - buffer_init(st->buff,st->nl.max_start_pad); - l=read(st->fd,st->buff->start,st->buff->len-st->nl.max_start_pad); + buffer_init(st->buff,calculate_max_start_pad()); + l=read(st->fd,st->buff->start,st->buff->len-calculate_max_start_pad()); if (l<0) { fatal_perror("tun_afterpoll: read()"); } if (l==0) { - fatal("tun_afterpoll: read()=0; device gone away?\n"); + fatal("tun_afterpoll: read()=0; device gone away?"); } if (l>0) { st->buff->size=l; - st->netlink_to_tunnel(&st->nl,NULL,st->buff); + st->netlink_to_tunnel(&st->nl,st->buff); BUF_ASSERT_FREE(st->buff); } } } -static void tun_deliver_to_kernel(void *sst, void *cid, - struct buffer_if *buf) +static void tun_deliver_to_kernel(void *sst, struct buffer_if *buf) { struct tun *st=sst; + ssize_t rc; BUF_ASSERT_USED(buf); - - /* No error checking, because we'd just throw the packet away anyway */ - write(st->fd,buf->start,buf->size); + + /* Log errors, so we can tell what's going on, but only once a + minute, so we don't flood the logs. Short writes count as + errors. */ + rc = write(st->fd,buf->start,buf->size); + if(rc != buf->size) { + static struct timeval last_report; + if(tv_now_global.tv_sec >= last_report.tv_sec + 60) { + if(rc < 0) + Message(M_WARNING, + "failed to deliver packet to tun device: %s\n", + strerror(errno)); + else + Message(M_WARNING, + "truncated packet delivered to tun device\n"); + last_report = tv_now_global; + } + } BUF_FREE(buf); } -static bool_t tun_set_route(void *sst, struct netlink_route *route) +static bool_t tun_set_route(void *sst, struct netlink_client *routes) { struct tun *st=sst; string_t network, mask, secnetaddr; - - if (route->up != route->kup) { - network=ipaddr_to_string(route->net.prefix); - mask=ipaddr_to_string(route->net.mask); - secnetaddr=ipaddr_to_string(st->nl.secnet_address); + struct subnet_list *nets; + int32_t i; + int fd=-1; + bool_t up; + + if (routes->options & OPT_SOFTROUTE) + up = routes->up; + else + up = routes->link_quality > LINK_QUALITY_UNUSED; + + if (up == routes->kup) return False; + if (st->route_type==TUN_CONFIG_IOCTL) { + if (st->tun_flavour==TUN_FLAVOUR_STREAMS) { + fd=open(st->ip_path,O_RDWR); + if (fd<0) { + fatal_perror("tun_set_route: can't open %s",st->ip_path); + } + } else { + fd=socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + if (fd<0) { + fatal_perror("tun_set_route: socket()"); + } + } + } + nets=routes->subnets; + secnetaddr=ipaddr_to_string(st->nl.secnet_address); + for (i=0; ientries; i++) { + network=ipaddr_to_string(nets->list[i].prefix); + mask=ipaddr_to_string(nets->list[i].mask); Message(M_INFO,"%s: %s route %s/%d %s kernel routing table\n", - st->nl.name,route->up?"adding":"deleting",network, - route->net.len,route->up?"to":"from"); - sys_cmd(st->route_path,"route",route->up?"add":"del","-net",network, - "netmask",mask,"gw",secnetaddr,(char *)0); - free(network); free(mask); free(secnetaddr); - route->kup=route->up; - return True; + st->nl.name,up?"adding":"deleting",network, + nets->list[i].len,up?"to":"from"); + switch (st->route_type) { + case TUN_CONFIG_LINUX: + sys_cmd(st->route_path,"route",up?"add":"del", + "-net",network,"netmask",mask, + "gw",secnetaddr,(char *)0); + break; + case TUN_CONFIG_BSD: + sys_cmd(st->route_path,"route",up?"add":"del", + "-net",network,secnetaddr,mask,(char *)0); + break; + case TUN_CONFIG_SOLARIS25: + sys_cmd(st->route_path,"route",up?"add":"del", + network,secnetaddr,(char *)0); + break; + case TUN_CONFIG_IOCTL: + { + /* darwin rtentry has a different format, use /sbin/route instead */ +#if HAVE_NET_ROUTE_H && ! __APPLE__ + struct rtentry rt; + struct sockaddr_in *sa; + int action; + + FILLZERO(rt); + sa=(struct sockaddr_in *)&rt.rt_dst; + sa->sin_family=AF_INET; + sa->sin_addr.s_addr=htonl(nets->list[i].prefix); + sa=(struct sockaddr_in *)&rt.rt_genmask; + sa->sin_family=AF_INET; + sa->sin_addr.s_addr=htonl(nets->list[i].mask); + sa=(struct sockaddr_in *)&rt.rt_gateway; + sa->sin_family=AF_INET; + sa->sin_addr.s_addr=htonl(st->nl.secnet_address); + rt.rt_flags=RTF_UP|RTF_GATEWAY; + action=up?SIOCADDRT:SIOCDELRT; + if (ioctl(fd,action,&rt)<0) { + fatal_perror("tun_set_route: ioctl()"); + } +#else + fatal("tun_set_route: ioctl method not supported"); +#endif + } + break; + default: + fatal("tun_set_route: unsupported route command type"); + break; + } + free(network); free(mask); } - return False; + free(secnetaddr); + if (st->route_type==TUN_CONFIG_IOCTL) { + close(fd); + } + routes->kup=up; + return True; } static void tun_phase_hook(void *sst, uint32_t newphase) { struct tun *st=sst; string_t hostaddr,secnetaddr; - uint8_t mtu[6]; - string_t network,mask; - struct netlink_route *r; - int i; + char mtu[6]; + struct netlink_client *r; - if (st->tun_old) { + if (st->tun_flavour==TUN_FLAVOUR_BSD) { if (st->search_for_if) { string_t dname; int i; - /* ASSERT st->interface_name */ dname=safe_malloc(strlen(st->device_path)+4,"tun_old_apply"); st->interface_name=safe_malloc(8,"tun_phase_hook"); @@ -131,7 +276,7 @@ static void tun_phase_hook(void *sst, uint32_t newphase) } } if (st->fd==-1) { - fatal("%s: unable to open any TUN device (%s...)\n", + fatal("%s: unable to open any TUN device (%s...)", st->nl.name,st->device_path); } } else { @@ -141,8 +286,8 @@ static void tun_phase_hook(void *sst, uint32_t newphase) st->nl.name,st->device_path); } } - } else { -#ifdef HAVE_LINUX_IF_H + } else if (st->tun_flavour==TUN_FLAVOUR_LINUX) { +#ifdef LINUX_TUN_SUPPORTED struct ifreq ifr; /* New TUN interface: open the device, then do ioctl TUNSETIFF @@ -152,12 +297,11 @@ static void tun_phase_hook(void *sst, uint32_t newphase) fatal_perror("%s: can't open device file %s",st->nl.name, st->device_path); } - memset(&ifr,0,sizeof(ifr)); + FILLZERO(ifr); ifr.ifr_flags = IFF_TUN | IFF_NO_PI; /* Just send/receive IP packets, no extra headers */ if (st->interface_name) strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ); - Message(M_DEBUG,"%s: about to ioctl(TUNSETIFF)...\n",st->nl.name); if (ioctl(st->fd,TUNSETIFF,&ifr)<0) { fatal_perror("%s: ioctl(TUNSETIFF)",st->nl.name); } @@ -168,44 +312,143 @@ static void tun_phase_hook(void *sst, uint32_t newphase) st->interface_name); } #else - fatal("netlink.c:tun_phase_hook:!tun_old unexpected\n"); -#endif /* HAVE_LINUX_IF_H */ + fatal("tun_phase_hook: TUN_FLAVOUR_LINUX unexpected"); +#endif /* LINUX_TUN_SUPPORTED */ + } else if (st->tun_flavour==TUN_FLAVOUR_STREAMS) { +#ifdef HAVE_TUN_STREAMS + int tun_fd, if_fd, ppa=-1, ip_fd; + + if ((ip_fd=open(st->ip_path, O_RDWR)) < 0) { + fatal_perror("%s: can't open %s",st->nl.name,st->ip_path); + } + if ((tun_fd=open(st->device_path,O_RDWR)) < 0) { + fatal_perror("%s: can't open %s",st->nl.name,st->device_path); + } + if ((ppa=ioctl(tun_fd,TUNNEWPPA,ppa)) < 0) { + fatal_perror("%s: can't assign new interface"); + } + if ((if_fd=open(st->device_path,O_RDWR)) < 0) { + fatal_perror("%s: can't open %s (2)",st->nl.name,st->device_path); + } + if (ioctl(if_fd,I_PUSH,"ip") < 0) { + fatal_perror("%s: can't push IP module",st->nl.name); + } + if (ioctl(if_fd,IF_UNITSEL,(char *)&ppa) < 0) { + fatal_perror("%s: can't set ppa %d",st->nl.name,ppa); + } + if (ioctl(ip_fd, I_LINK, if_fd) < 0) { + fatal_perror("%s: can't link TUN device to IP",st->nl.name); + } + st->interface_name=safe_malloc(10,"tun_apply"); + sprintf(st->interface_name,"tun%d",ppa); + st->fd=tun_fd; +#else + fatal("tun_phase_hook: TUN_FLAVOUR_STREAMS unexpected"); +#endif /* HAVE_TUN_STREAMS */ + } else { + fatal("tun_phase_hook: unknown flavour of TUN"); } /* All the networks we'll be using have been registered. Invoke ifconfig to set the TUN device's address, and route to add routes to all our networks. */ - hostaddr=ipaddr_to_string(st->local_address); + hostaddr=ipaddr_to_string(st->nl.local_address); secnetaddr=ipaddr_to_string(st->nl.secnet_address); - snprintf(mtu,6,"%d",st->nl.mtu); + snprintf(mtu,sizeof(mtu),"%d",st->nl.mtu); mtu[5]=0; - sys_cmd(st->ifconfig_path,"ifconfig",st->interface_name, - hostaddr,"netmask","255.255.255.255","-broadcast", - "pointopoint",secnetaddr,"mtu",mtu,"up",(char *)0); - - r=st->nl.routes; - for (i=0; inl.n_routes; i++) { - if (r[i].up && !r[i].kup) { - network=ipaddr_to_string(r[i].net.prefix); - mask=ipaddr_to_string(r[i].net.mask); - sys_cmd(st->route_path,"route","add","-net",network, - "netmask",mask,"gw",secnetaddr,(char *)0); - r[i].kup=True; + switch (st->ifconfig_type) { + case TUN_CONFIG_LINUX: + sys_cmd(st->ifconfig_path,"ifconfig",st->interface_name, + hostaddr,"netmask","255.255.255.255","-broadcast", + "-multicast", + "pointopoint",secnetaddr,"mtu",mtu,"up",(char *)0); + break; + case TUN_CONFIG_BSD: + sys_cmd(st->ifconfig_path,"ifconfig",st->interface_name, + hostaddr,"netmask","255.255.255.255", + secnetaddr,"mtu",mtu,"up",(char *)0); + break; + case TUN_CONFIG_SOLARIS25: + sys_cmd(st->ifconfig_path,"ifconfig",st->interface_name, + hostaddr,secnetaddr,"mtu",mtu,"up",(char *)0); + break; + case TUN_CONFIG_IOCTL: +#if HAVE_NET_IF_H && ! __APPLE__ + { + int fd; + struct ifreq ifr; + struct sockaddr_in *sa; + fd=socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + + /* Interface address */ + strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ); + sa=(struct sockaddr_in *)&ifr.ifr_addr; + FILLZERO(*sa); + sa->sin_family=AF_INET; + sa->sin_addr.s_addr=htonl(st->nl.local_address); + if (ioctl(fd,SIOCSIFADDR, &ifr)!=0) { + fatal_perror("tun_apply: SIOCSIFADDR"); + } +#ifdef SIOCSIFNETMASK + /* Netmask */ + strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ); + sa=(struct sockaddr_in *)&ifr.ifr_netmask; + FILLZERO(*sa); + sa->sin_family=AF_INET; + sa->sin_addr.s_addr=htonl(0xffffffff); + if (ioctl(fd,SIOCSIFNETMASK, &ifr)!=0) { + fatal_perror("tun_apply: SIOCSIFNETMASK"); + } +#endif + /* Destination address (point-to-point) */ + strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ); + sa=(struct sockaddr_in *)&ifr.ifr_dstaddr; + FILLZERO(*sa); + sa->sin_family=AF_INET; + sa->sin_addr.s_addr=htonl(st->nl.secnet_address); + if (ioctl(fd,SIOCSIFDSTADDR, &ifr)!=0) { + fatal_perror("tun_apply: SIOCSIFDSTADDR"); + } + /* MTU */ + strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ); + ifr.ifr_mtu=st->nl.mtu; + if (ioctl(fd,SIOCSIFMTU, &ifr)!=0) { + fatal_perror("tun_apply: SIOCSIFMTU"); + } + /* Flags */ + strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ); + ifr.ifr_flags=IFF_UP|IFF_POINTOPOINT|IFF_RUNNING|IFF_NOARP; + if (ioctl(fd,SIOCSIFFLAGS, &ifr)!=0) { + fatal_perror("tun_apply: SIOCSIFFLAGS"); } + + close(fd); + } +#else + fatal("tun_apply: ifconfig by ioctl() not supported"); +#endif /* HAVE_NET_IF_H */ + break; + default: + fatal("tun_apply: unsupported ifconfig method"); + break; + } + + for (r=st->nl.clients; r; r=r->next) { + tun_set_route(st,r); } /* Register for poll() */ register_for_poll(st, tun_beforepoll, tun_afterpoll, 1, st->nl.name); } -#ifdef HAVE_LINUX_IF_H -static list_t *tun_apply(closure_t *self, struct cloc loc, dict_t *context, - list_t *args) +static list_t *tun_create(closure_t *self, struct cloc loc, dict_t *context, + list_t *args,uint32_t default_flavour) { struct tun *st; item_t *item; dict_t *dict; + string_t flavour,type; st=safe_malloc(sizeof(*st),"tun_apply"); @@ -220,93 +463,163 @@ static list_t *tun_apply(closure_t *self, struct cloc loc, dict_t *context, netlink_init(&st->nl,st,loc,dict, "netlink-tun",tun_set_route,tun_deliver_to_kernel); - st->tun_old=False; + flavour=dict_read_string(dict,"flavour",False,"tun-netlink",loc); + if (flavour) + st->tun_flavour=string_to_word(flavour,loc,flavours,"tun-flavour"); + else + st->tun_flavour=default_flavour; + st->device_path=dict_read_string(dict,"device",False,"tun-netlink",loc); + st->ip_path=dict_read_string(dict,"ip-path",False,"tun-netlink",loc); st->interface_name=dict_read_string(dict,"interface",False, "tun-netlink",loc); - st->ifconfig_path=dict_read_string(dict,"ifconfig-path", - False,"tun-netlink",loc); - st->route_path=dict_read_string(dict,"route-path", - False,"tun-netlink",loc); - - if (!st->device_path) st->device_path="/dev/net/tun"; - if (!st->ifconfig_path) st->ifconfig_path="ifconfig"; - if (!st->route_path) st->route_path="route"; - st->buff=find_cl_if(dict,"buffer",CL_BUFFER,True,"tun-netlink",loc); - st->local_address=string_to_ipaddr( - dict_find_item(dict,"local-address", True, "netlink", loc),"netlink"); - - add_hook(PHASE_GETRESOURCES,tun_phase_hook,st); + st->search_for_if=dict_read_bool(dict,"interface-search",False, + "tun-netlink",loc,st->device_path==NULL); - return new_closure(&st->nl.cl); -} -#endif /* HAVE_LINUX_IF_H */ + type=dict_read_string(dict,"ifconfig-type",False,"tun-netlink",loc); + if (type) st->ifconfig_type=string_to_word(type,loc,config_types, + "ifconfig-type"); + else st->ifconfig_type=TUN_CONFIG_GUESS; + st->ifconfig_path=dict_read_string(dict,"ifconfig-path",False, + "tun-netlink",loc); -static list_t *tun_old_apply(closure_t *self, struct cloc loc, dict_t *context, - list_t *args) -{ - struct tun *st; - item_t *item; - dict_t *dict; + type=dict_read_string(dict,"route-type",False,"tun-netlink",loc); + if (type) st->route_type=string_to_word(type,loc,config_types, + "route-type"); + else st->route_type=TUN_CONFIG_GUESS; + st->route_path=dict_read_string(dict,"route-path",False,"tun-netlink",loc); - st=safe_malloc(sizeof(*st),"tun_old_apply"); + st->buff=find_cl_if(dict,"buffer",CL_BUFFER,True,"tun-netlink",loc); - Message(M_WARNING,"the tun-old code has never been tested. Please report " - "success or failure to steve@greenend.org.uk\n"); + if (st->tun_flavour==TUN_FLAVOUR_GUESS) { + /* If we haven't been told what type of TUN we're using, take + a guess based on the system details. */ + struct utsname u; + if (uname(&u)<0) { + fatal_perror("tun_create: uname"); + } + if (strcmp(u.sysname,"Linux")==0) { + st->tun_flavour=TUN_FLAVOUR_LINUX; + } else if (strcmp(u.sysname,"SunOS")==0) { + st->tun_flavour=TUN_FLAVOUR_STREAMS; + } else if (strcmp(u.sysname,"FreeBSD")==0 + || strcmp(u.sysname,"Darwin")==0) { + st->tun_flavour=TUN_FLAVOUR_BSD; + } + } + if (st->tun_flavour==TUN_FLAVOUR_GUESS) { + cfgfatal(loc,"tun","cannot guess which type of TUN is in use; " + "specify the flavour explicitly\n"); + } - /* First parameter must be a dict */ - item=list_elem(args,0); - if (!item || item->type!=t_dict) - cfgfatal(loc,"tun","parameter must be a dictionary\n"); - - dict=item->data.dict; + if (st->ifconfig_type==TUN_CONFIG_GUESS) { + switch (st->tun_flavour) { + case TUN_FLAVOUR_LINUX: + st->ifconfig_type=TUN_CONFIG_IOCTL; + break; + case TUN_FLAVOUR_BSD: +#if __linux__ + /* XXX on Linux we still want TUN_CONFIG_IOCTL. Perhaps we can + use this on BSD too. */ + st->ifconfig_type=TUN_CONFIG_IOCTL; +#else + st->ifconfig_type=TUN_CONFIG_BSD; +#endif + break; + case TUN_FLAVOUR_STREAMS: + st->ifconfig_type=TUN_CONFIG_BSD; + break; + } + } + if (st->route_type==TUN_CONFIG_GUESS) + st->route_type=st->ifconfig_type; - st->netlink_to_tunnel= - netlink_init(&st->nl,st,loc,dict, - "netlink-tun",NULL,tun_deliver_to_kernel); + if (st->ifconfig_type==TUN_CONFIG_GUESS) { + cfgfatal(loc,"tun","cannot guess which ifconfig method to use\n"); + } + if (st->route_type==TUN_CONFIG_GUESS) { + cfgfatal(loc,"tun","cannot guess which route method to use\n"); + } - st->tun_old=True; - st->device_path=dict_read_string(dict,"device",False,"tun-netlink",loc); - st->interface_name=dict_read_string(dict,"interface",False, - "tun-netlink",loc); - st->search_for_if=dict_read_bool(dict,"interface-search",False, - "tun-netlink",loc,st->device_path==NULL); - st->ifconfig_path=dict_read_string(dict,"ifconfig-path",False, - "tun-netlink",loc); - st->route_path=dict_read_string(dict,"route-path",False,"tun-netlink",loc); + if (st->ifconfig_type==TUN_CONFIG_IOCTL && st->ifconfig_path) { + cfgfatal(loc,"tun","ifconfig-type \"ioctl\" is incompatible with " + "ifconfig-path\n"); + } + if (st->route_type==TUN_CONFIG_IOCTL && st->route_path) { + cfgfatal(loc,"tun","route-type \"ioctl\" is incompatible with " + "route-path\n"); + } - if (!st->device_path) st->device_path="/dev/tun"; + Message(M_DEBUG_CONFIG,"%s: tun flavour %s\n",st->nl.name, + tun_flavour_str(st->tun_flavour)); + switch (st->tun_flavour) { + case TUN_FLAVOUR_BSD: + if (!st->device_path) st->device_path="/dev/tun"; + break; + case TUN_FLAVOUR_LINUX: + if (!st->device_path) st->device_path="/dev/net/tun"; + break; + case TUN_FLAVOUR_STREAMS: + if (!st->device_path) st->device_path="/dev/tun"; + if (st->interface_name) cfgfatal(loc,"tun","interface name cannot " + "be specified with STREAMS TUN\n"); + break; + } + + if (!st->ip_path) st->ip_path="/dev/ip"; if (!st->ifconfig_path) st->ifconfig_path="ifconfig"; if (!st->route_path) st->route_path="route"; - st->buff=find_cl_if(dict,"buffer",CL_BUFFER,True,"tun-netlink",loc); - st->local_address=string_to_ipaddr( - dict_find_item(dict,"local-address", True, "netlink", loc),"netlink"); + +#ifndef HAVE_TUN_STREAMS + if (st->tun_flavour==TUN_FLAVOUR_STREAMS) { + cfgfatal(loc,"tun","TUN flavour STREAMS unsupported in this build " + "of secnet\n"); + } +#endif +#ifndef LINUX_TUN_SUPPORTED + if (st->tun_flavour==TUN_FLAVOUR_LINUX) { + cfgfatal(loc,"tun","TUN flavour LINUX unsupported in this build " + "of secnet\n"); + } +#endif /* Old TUN interface: the network interface name depends on which /dev/tunX file we open. If 'interface-search' is set to true, treat 'device' as the prefix and try numbers from 0--255. If it's set to false, treat 'device' as the whole name, and require than an appropriate interface name be specified. */ - if (st->search_for_if && st->interface_name) { - cfgfatal(loc,"tun-old","you may not specify an interface name " - "in interface-search mode\n"); - } - if (!st->search_for_if && !st->interface_name) { - cfgfatal(loc,"tun-old","you must specify an interface name " - "when you explicitly specify a TUN device file\n"); + if (st->tun_flavour==TUN_FLAVOUR_BSD) { + if (st->search_for_if && st->interface_name) { + cfgfatal(loc,"tun","you may not specify an interface name " + "in interface-search mode\n"); + } + if (!st->search_for_if && !st->interface_name) { + cfgfatal(loc,"tun","you must specify an interface name " + "when you explicitly specify a TUN device file\n"); + } } - add_hook(PHASE_GETRESOURCES,tun_phase_hook,st); return new_closure(&st->nl.cl); } -init_module tun_module; +static list_t *tun_apply(closure_t *self, struct cloc loc, dict_t *context, + list_t *args) +{ + return tun_create(self,loc,context,args,TUN_FLAVOUR_GUESS); +} + +static list_t *tun_bsd_apply(closure_t *self, struct cloc loc, dict_t *context, + list_t *args) +{ + Message(M_WARNING,"(%s,%d): obsolete use of tun-old; replace with tun " + "and specify flavour \"bsd\".\n",loc.file,loc.line); + return tun_create(self,loc,context,args,TUN_FLAVOUR_BSD); +} + void tun_module(dict_t *dict) { -#ifdef HAVE_LINUX_IF_H add_closure(dict,"tun",tun_apply); -#endif - add_closure(dict,"tun-old",tun_old_apply); + add_closure(dict,"tun-old",tun_bsd_apply); }