1 /* source: xio-tun.c */
2 /* Copyright Gerhard Rieger and contributors (see file CHANGES) */
3 /* Published under the GNU General Public License V.2, see file COPYING */
4 
5 /* this file contains the source for opening addresses of tun/tap type */
6 
7 #include "xiosysincludes.h"
8 #if WITH_TUN
9 #include "xioopen.h"
10 
11 #include "xio-named.h"
12 #include "xio-socket.h"
13 #include "xio-ip.h"
14 
15 #include "xio-tun.h"
16 
17 
18 static int xioopen_tun(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, unsigned groups, int dummy1, int dummy2, int dummy3);
19 
20 /****** TUN addresses ******/
21 const struct optdesc opt_tun_device    = { "tun-device",     NULL,      OPT_TUN_DEVICE,      GROUP_TUN,       PH_OPEN, TYPE_FILENAME, OFUNC_SPEC };
22 const struct optdesc opt_tun_name      = { "tun-name",       NULL,      OPT_TUN_NAME,        GROUP_INTERFACE, PH_FD,   TYPE_STRING,   OFUNC_SPEC };
23 const struct optdesc opt_tun_type      = { "tun-type",       NULL,      OPT_TUN_TYPE,        GROUP_INTERFACE, PH_FD,   TYPE_STRING,   OFUNC_SPEC };
24 const struct optdesc opt_iff_no_pi     = { "iff-no-pi",       "no-pi",       OPT_IFF_NO_PI,         GROUP_TUN,       PH_FD,   TYPE_BOOL,   OFUNC_SPEC };
25 /*0 const struct optdesc opt_interface_addr    = { "interface-addr",    "address", OPT_INTERFACE_ADDR,    GROUP_INTERFACE, PH_FD, TYPE_STRING,   OFUNC_SPEC };*/
26 /*0 const struct optdesc opt_interface_netmask = { "interface-netmask", "netmask", OPT_INTERFACE_NETMASK, GROUP_INTERFACE, PH_FD, TYPE_STRING,   OFUNC_SPEC };*/
27 const struct optdesc opt_iff_up          = { "iff-up",          "up",          OPT_IFF_UP,          GROUP_INTERFACE, PH_FD,   TYPE_BOOL,     OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.tun.iff_opts), XIO_SIZEOF(para.tun.iff_opts), IFF_UP };
28 const struct optdesc opt_iff_broadcast   = { "iff-broadcast",   NULL,          OPT_IFF_BROADCAST,   GROUP_INTERFACE, PH_FD,   TYPE_BOOL,     OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.tun.iff_opts), XIO_SIZEOF(para.tun.iff_opts), IFF_BROADCAST };
29 const struct optdesc opt_iff_debug       = { "iff-debug"    ,   NULL,          OPT_IFF_DEBUG,       GROUP_INTERFACE, PH_FD,   TYPE_BOOL,     OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.tun.iff_opts), XIO_SIZEOF(para.tun.iff_opts), IFF_DEBUG };
30 const struct optdesc opt_iff_loopback    = { "iff-loopback" ,   "loopback",    OPT_IFF_LOOPBACK,    GROUP_INTERFACE, PH_FD,   TYPE_BOOL,     OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.tun.iff_opts), XIO_SIZEOF(para.tun.iff_opts), IFF_LOOPBACK };
31 const struct optdesc opt_iff_pointopoint = { "iff-pointopoint", "pointopoint",OPT_IFF_POINTOPOINT, GROUP_INTERFACE, PH_FD,   TYPE_BOOL,     OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.tun.iff_opts), XIO_SIZEOF(para.tun.iff_opts), IFF_POINTOPOINT };
32 const struct optdesc opt_iff_notrailers  = { "iff-notrailers",  "notrailers",  OPT_IFF_NOTRAILERS,  GROUP_INTERFACE, PH_FD,   TYPE_BOOL,     OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.tun.iff_opts), XIO_SIZEOF(para.tun.iff_opts), IFF_NOTRAILERS };
33 const struct optdesc opt_iff_running     = { "iff-running",     "running",     OPT_IFF_RUNNING,     GROUP_INTERFACE, PH_FD,   TYPE_BOOL,     OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.tun.iff_opts), XIO_SIZEOF(para.tun.iff_opts), IFF_RUNNING };
34 const struct optdesc opt_iff_noarp       = { "iff-noarp",       "noarp",       OPT_IFF_NOARP,       GROUP_INTERFACE, PH_FD,   TYPE_BOOL,     OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.tun.iff_opts), XIO_SIZEOF(para.tun.iff_opts), IFF_NOARP };
35 const struct optdesc opt_iff_promisc     = { "iff-promisc",     "promisc",     OPT_IFF_PROMISC,     GROUP_INTERFACE, PH_FD,   TYPE_BOOL,     OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.tun.iff_opts), XIO_SIZEOF(para.tun.iff_opts), IFF_PROMISC };
36 const struct optdesc opt_iff_allmulti    = { "iff-allmulti",    "allmulti",    OPT_IFF_ALLMULTI,    GROUP_INTERFACE, PH_FD,   TYPE_BOOL,     OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.tun.iff_opts), XIO_SIZEOF(para.tun.iff_opts), IFF_ALLMULTI };
37 const struct optdesc opt_iff_master      = { "iff-master",      "master",      OPT_IFF_MASTER,      GROUP_INTERFACE, PH_FD,   TYPE_BOOL,     OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.tun.iff_opts), XIO_SIZEOF(para.tun.iff_opts), IFF_MASTER };
38 const struct optdesc opt_iff_slave       = { "iff-slave",       "slave",       OPT_IFF_SLAVE,       GROUP_INTERFACE, PH_FD,   TYPE_BOOL,     OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.tun.iff_opts), XIO_SIZEOF(para.tun.iff_opts), IFF_SLAVE };
39 const struct optdesc opt_iff_multicast   = { "iff-multicast",   NULL,          OPT_IFF_MULTICAST,   GROUP_INTERFACE, PH_FD,   TYPE_BOOL,     OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.tun.iff_opts), XIO_SIZEOF(para.tun.iff_opts), IFF_MULTICAST };
40 const struct optdesc opt_iff_portsel     = { "iff-portsel",     "portsel",     OPT_IFF_PORTSEL,     GROUP_INTERFACE, PH_FD,   TYPE_BOOL,     OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.tun.iff_opts), XIO_SIZEOF(para.tun.iff_opts), IFF_PORTSEL };
41 const struct optdesc opt_iff_automedia   = { "iff-automedia",   "automedia",   OPT_IFF_AUTOMEDIA,   GROUP_INTERFACE, PH_FD,   TYPE_BOOL,     OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.tun.iff_opts), XIO_SIZEOF(para.tun.iff_opts), IFF_AUTOMEDIA };
42 /*const struct optdesc opt_iff_dynamic   = { "iff-dynamic",     "dynamic",     OPT_IFF_DYNAMIC,     GROUP_INTERFACE, PH_FD,   TYPE_BOOL,     OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.tun.iff_opts), XIO_SIZEOF(short), IFF_DYNAMIC };*/
43 #if LATER
44 const struct optdesc opt_route           = { "route",           NULL,          OPT_ROUTE,           GROUP_INTERFACE, PH_INIT, TYPE_STRING,   OFUNC_SPEC };
45 #endif
46 
47 const struct addrdesc xioaddr_tun    = { "tun",    3, xioopen_tun, GROUP_FD|GROUP_CHR|GROUP_NAMED|GROUP_OPEN|GROUP_TUN, 0, 0, 0 HELP("[:<ip-addr>/<bits>]") };
48 /* "if-name"=tun3
49 // "route"=address/netmask
50 // "ip6-route"=address/netmask
51 // "iff-broadcast"
52 // "iff-debug"
53 // "iff-promisc"
54 // see .../linux/if.h
55 */
56 
57 
58 #if LATER
59 /* sub options for route option */
60 #define IFOPT_ROUTE 1
61 static const struct optdesc opt_route_tos = { "route", NULL, IFOPT_ROUTE, };
62 static const struct optname xio_route_options[] = {
63    {"tos", &xio_route_tos }
64 } ;
65 #endif
66 
xioopen_tun(int argc,const char * argv[],struct opt * opts,int xioflags,xiofile_t * xfd,unsigned groups,int dummy1,int dummy2,int dummy3)67 static int xioopen_tun(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, unsigned groups, int dummy1, int dummy2, int dummy3) {
68    char *tundevice = NULL;
69    char *tunname = NULL, *tuntype = NULL;
70    int pf = /*! PF_UNSPEC*/ PF_INET;
71    struct xiorange network;
72    bool no_pi = false;
73    const char *namedargv[] = { "tun", NULL, NULL };
74    int rw = (xioflags & XIO_ACCMODE);
75    bool exists;
76    struct ifreq ifr;
77    int sockfd;
78    char *ifaddr;
79    int result;
80 
81    if (argc > 2 || argc < 0) {
82       Error2("%s: wrong number of parameters (%d instead of 0 or 1)",
83 	     argv[0], argc-1);
84    }
85 
86    if (retropt_string(opts, OPT_TUN_DEVICE, &tundevice) != 0) {
87       tundevice = strdup("/dev/net/tun");
88    }
89 
90    /*! socket option here? */
91    retropt_socket_pf(opts, &pf);
92 
93    namedargv[1] = tundevice;
94    /* open the tun cloning device */
95    if ((result = _xioopen_named_early(2, namedargv, xfd, groups, &exists, opts)) < 0) {
96       return result;
97    }
98 
99    /*========================= the tunnel interface =========================*/
100    Notice("creating tunnel network interface");
101    if ((result = _xioopen_open(tundevice, rw, opts)) < 0)
102       return result;
103    xfd->stream.fd = result;
104 
105    /* prepare configuration of the new network interface */
106    memset(&ifr, 0,sizeof(ifr));
107 
108    if (retropt_string(opts, OPT_TUN_NAME, &tunname) == 0) {
109       strncpy(ifr.ifr_name, tunname, IFNAMSIZ);	/* ok */
110       free(tunname);
111    } else {
112       ifr.ifr_name[0] = '\0';
113    }
114 
115    ifr.ifr_flags = IFF_TUN;
116    if (retropt_string(opts, OPT_TUN_TYPE, &tuntype) == 0) {
117       if (!strcmp(tuntype, "tap")) {
118 	 ifr.ifr_flags = IFF_TAP;
119       } else if (strcmp(tuntype, "tun")) {
120 	 Error1("unknown tun-type \"%s\"", tuntype);
121       }
122    }
123 
124    if (retropt_bool(opts, OPT_IFF_NO_PI, &no_pi) == 0) {
125       if (no_pi) {
126 	 ifr.ifr_flags |= IFF_NO_PI;
127 #if 0 /* not neccessary for now */
128       } else {
129 	 ifr.ifr_flags &= ~IFF_NO_PI;
130 #endif
131       }
132    }
133 
134    if (Ioctl(xfd->stream.fd, TUNSETIFF, &ifr) < 0) {
135       Error3("ioctl(%d, TUNSETIFF, {\"%s\"}: %s",
136 	     xfd->stream.fd, ifr.ifr_name, strerror(errno));
137       Close(xfd->stream.fd);
138    }
139 
140    /*===================== setting interface properties =====================*/
141 
142    /* we seem to need a socket for manipulating the interface */
143    if ((sockfd = Socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
144       Error1("socket(PF_INET, SOCK_DGRAM, 0): %s", strerror(errno));
145       sockfd = xfd->stream.fd;	/* desparate fallback attempt */
146    }
147 
148    /*--------------------- setting interface address and netmask ------------*/
149    if (argc == 2) {
150        if ((ifaddr = strdup(argv[1])) == NULL) {
151           Error1("strdup(\"%s\"): out of memory", argv[1]);
152           return STAT_RETRYLATER;
153        }
154        if ((result = xioparsenetwork(ifaddr, pf, &network)) != STAT_OK) {
155           /*! recover */
156           return result;
157        }
158        socket_init(pf, (union sockaddr_union *)&ifr.ifr_addr);
159        ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr =
160           network.netaddr.ip4.sin_addr;
161        if (Ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) {
162           Error4("ioctl(%d, SIOCSIFADDR, {\"%s\", \"%s\"}: %s",
163              sockfd, ifr.ifr_name, ifaddr, strerror(errno));
164        }
165        ((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr =
166           network.netmask.ip4.sin_addr;
167        if (Ioctl(sockfd, SIOCSIFNETMASK, &ifr) < 0) {
168           Error4("ioctl(%d, SIOCSIFNETMASK, {\"0x%08u\", \"%s\"}, %s",
169              sockfd, ((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr,
170              ifaddr, strerror(errno));
171        }
172        free(ifaddr);
173    }
174    /*--------------------- setting interface flags --------------------------*/
175    applyopts_single(&xfd->stream, opts, PH_FD);
176 
177    if (Ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
178       Error3("ioctl(%d, SIOCGIFFLAGS, {\"%s\"}: %s",
179 	     sockfd, ifr.ifr_name, strerror(errno));
180    }
181    Debug2("\"%s\": system set flags: 0x%hx", ifr.ifr_name, ifr.ifr_flags);
182    ifr.ifr_flags |= xfd->stream.para.tun.iff_opts[0];
183    ifr.ifr_flags &= ~xfd->stream.para.tun.iff_opts[1];
184    Debug2("\"%s\": xio merged flags: 0x%hx", ifr.ifr_name, ifr.ifr_flags);
185    if (Ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
186       Error4("ioctl(%d, SIOCSIFFLAGS, {\"%s\", %hd}: %s",
187 	     sockfd, ifr.ifr_name, ifr.ifr_flags, strerror(errno));
188    }
189    ifr.ifr_flags = 0;
190    if (Ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
191       Error3("ioctl(%d, SIOCGIFFLAGS, {\"%s\"}: %s",
192 	     sockfd, ifr.ifr_name, strerror(errno));
193    }
194    Debug2("\"%s\": resulting flags: 0x%hx", ifr.ifr_name, ifr.ifr_flags);
195 
196 
197 #if LATER
198    applyopts_named(tundevice, opts, PH_FD);
199 #endif
200    applyopts(xfd->stream.fd, opts, PH_FD);
201    applyopts_cloexec(xfd->stream.fd, opts);
202 
203    applyopts_fchown(xfd->stream.fd, opts);
204 
205    if ((result = _xio_openlate(&xfd->stream, opts)) < 0)
206       return result;
207 
208    return 0;
209 }
210 
211 #endif /* WITH_TUN */
212