Add IPv6 support for FreeBSD
[platform/upstream/openconnect.git] / tun.c
1 /*
2  * OpenConnect (SSL + DTLS) VPN client
3  *
4  * Copyright © 2008 Intel Corporation.
5  *
6  * Author: David Woodhouse <dwmw2@infradead.org>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * version 2.1, as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to:
19  *
20  *   Free Software Foundation, Inc.
21  *   51 Franklin Street, Fifth Floor,
22  *   Boston, MA 02110-1301 USA
23  */
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/socket.h>
28 #include <sys/ioctl.h>
29 #include <string.h>
30 #include <signal.h>
31 #ifdef __linux__
32 #include <linux/if_tun.h>
33 #elif defined(__sun__)
34 #include <net/if_tun.h>
35 #include <stropts.h>
36 #include <sys/sockio.h>
37 #elif defined(__FreeBSD__)
38 #include <net/if_tun.h>
39 #endif
40 #include <fcntl.h>
41 #include <unistd.h>
42 #include <netinet/in_systm.h>
43 #include <netinet/in.h>
44 #include <netinet/ip.h>
45 #include <net/if.h>
46 #include <arpa/inet.h>
47 #include <errno.h>
48
49 #include "openconnect.h"
50
51 #ifdef __OpenBSD__
52 #define TUN_HAS_AF_PREFIX 1
53 #elif defined(TUNSIFHEAD)
54 #define TUN_HAS_AF_PREFIX 1
55 #endif
56
57 #ifdef __sun__
58 static int local_config_tun(struct openconnect_info *vpninfo, int mtu_only)
59 {
60         if (!mtu_only)
61                 vpninfo->progress(vpninfo, PRG_ERR,
62                                   "No vpnc-script configured. Need Solaris IP-setting code\n");
63         return 0;
64 }
65 #else
66 static int local_config_tun(struct openconnect_info *vpninfo, int mtu_only)
67 {
68         struct ifreq ifr;
69         int net_fd;
70
71         net_fd = socket(PF_INET, SOCK_DGRAM, 0);
72         if (net_fd < 0) {
73                 perror("open net");
74                 return -EINVAL;
75         }
76         memset(&ifr, 0, sizeof(ifr));
77         strncpy(ifr.ifr_name, vpninfo->ifname, sizeof(ifr.ifr_name) - 1);
78
79         if (!mtu_only) {
80                 struct sockaddr_in addr;
81
82                 if (ioctl(net_fd, SIOCGIFFLAGS, &ifr) < 0)
83                         perror("SIOCGIFFLAGS");
84
85                 ifr.ifr_flags |= IFF_UP | IFF_POINTOPOINT;
86                 if (ioctl(net_fd, SIOCSIFFLAGS, &ifr) < 0)
87                         perror("SIOCSIFFLAGS");
88
89                 addr.sin_family = AF_INET;
90                 addr.sin_addr.s_addr = inet_addr(vpninfo->vpn_addr);
91                 memcpy(&ifr.ifr_addr, &addr, sizeof(addr));
92                 if (ioctl(net_fd, SIOCSIFADDR, &ifr) < 0)
93                         perror("SIOCSIFADDR");
94         }
95
96         ifr.ifr_mtu = vpninfo->mtu;
97         if (ioctl(net_fd, SIOCSIFMTU, &ifr) < 0)
98                 perror("SIOCSIFMTU");
99
100         close(net_fd);
101
102         return 0;
103 }
104 #endif
105
106 static int setenv_int(const char *opt, int value)
107 {
108         char buf[16];
109         sprintf(buf, "%d", value);
110         return setenv(opt, buf, 1);
111 }
112
113 static int netmasklen(struct in_addr addr)
114 {
115         int masklen;
116
117         for (masklen = 0; masklen < 32; masklen++) {
118                 if (ntohl(addr.s_addr) >= (0xffffffff << masklen))
119                         break;
120         }
121         return 32 - masklen;
122 }
123
124 static int process_split_xxclude(struct openconnect_info *vpninfo,
125                                  char *in_ex, char *route, int *v4_incs,
126                                  int *v6_incs)
127 {
128         struct in_addr addr;
129         char envname[80];
130         char *slash;
131
132         slash = strchr(route, '/');
133         if (!slash) {
134         badinc:
135                 vpninfo->progress(vpninfo, PRG_ERR,
136                                   "Discard bad split %sclude: \"%s\"\n",
137                                   in_ex, route);
138                 return -EINVAL;
139         }
140
141         *slash = 0;
142
143         if (strchr(route, ':')) {
144                 snprintf(envname, 79, "CISCO_IPV6_SPLIT_%sC_%d_ADDR", in_ex,
145                          *v6_incs);
146                 setenv(envname, route, 1);
147
148                 snprintf(envname, 79, "CISCO_IPV6_SPLIT_%sC_%d_MASKLEN", in_ex,
149                          *v6_incs);
150                 setenv(envname, slash+1, 1);
151
152                 (*v6_incs)++;
153                 return 0;
154         }
155                 
156         if (!inet_aton(route, &addr)) {
157                 *slash = '/';
158                 goto badinc;
159         }
160
161         envname[79] = 0;
162         snprintf(envname, 79, "CISCO_SPLIT_%sC_%d_ADDR", in_ex, *v4_incs);
163         setenv(envname, route, 1);
164
165         /* Put it back how we found it */
166         *slash = '/';
167
168         if (!inet_aton(slash+1, &addr))
169                 goto badinc;
170
171         snprintf(envname, 79, "CISCO_SPLIT_%sC_%d_MASK", in_ex, *v4_incs);
172         setenv(envname, slash+1, 1);
173
174         snprintf(envname, 79, "CISCO_SPLIT_%sC_%d_MASKLEN", in_ex, *v4_incs);
175         setenv_int(envname, netmasklen(addr));
176
177         (*v4_incs)++;
178         return 0;
179 }
180
181 static int appendenv(const char *opt, const char *new)
182 {
183         char buf[1024];
184         char *old = getenv(opt);
185
186         buf[1023] = 0;
187         if (old)
188                 snprintf(buf, 1023, "%s %s", old, new);
189         else
190                 snprintf(buf, 1023, "%s", new);
191
192         return setenv(opt, buf, 1);
193 }
194
195 static void setenv_cstp_opts(struct openconnect_info *vpninfo)
196 {
197         char *env_buf;
198         int buflen = 0;
199         int bufofs = 0;
200         struct vpn_option *opt;
201
202         for (opt = vpninfo->cstp_options; opt; opt = opt->next)
203                 buflen += 2 + strlen(opt->option) + strlen(opt->value);
204
205         env_buf = malloc(buflen + 1);
206         if (!env_buf)
207                 return;
208
209         env_buf[buflen] = 0;
210
211         for (opt = vpninfo->cstp_options; opt; opt = opt->next)
212                 bufofs += snprintf(env_buf + bufofs, buflen - bufofs,
213                                    "%s=%s\n", opt->option, opt->value);
214
215         setenv("CISCO_CSTP_OPTIONS", env_buf, 1);
216         free(env_buf);
217 }
218
219 static void set_script_env(struct openconnect_info *vpninfo)
220 {
221         struct sockaddr_in *sin = (void *)vpninfo->peer_addr;
222
223         setenv("VPNGATEWAY", inet_ntoa(sin->sin_addr), 1);
224         setenv("TUNDEV", vpninfo->ifname, 1);
225         setenv("reason", "connect", 1);
226         unsetenv("CISCO_BANNER");
227         unsetenv("CISCO_SPLIT_INC");
228         unsetenv("CISCO_SPLIT_EXC");
229
230         setenv_int("INTERNAL_IP4_MTU", vpninfo->mtu);
231
232         if (vpninfo->vpn_addr) {
233                 setenv("INTERNAL_IP4_ADDRESS", vpninfo->vpn_addr, 1);
234                 if (vpninfo->vpn_netmask) {
235                         struct in_addr addr;
236                         struct in_addr mask;
237
238                         if (inet_aton(vpninfo->vpn_addr, &addr) &&
239                             inet_aton(vpninfo->vpn_netmask, &mask)) {
240                                 char *netaddr;
241
242                                 addr.s_addr &= mask.s_addr;
243                                 netaddr = inet_ntoa(addr);
244
245                                 setenv("INTERNAL_IP4_NETADDR", netaddr, 1);
246                                 setenv("INTERNAL_IP4_NETMASK", vpninfo->vpn_netmask, 1);
247                                 setenv_int("INTERNAL_IP4_NETMASKLEN", netmasklen(mask));
248                         }
249                 }
250         }
251         if (vpninfo->vpn_addr6) {
252                 setenv("INTERNAL_IP6_ADDRESS", vpninfo->vpn_addr6, 1);
253                 setenv("INTERNAL_IP6_NETMASK", vpninfo->vpn_netmask6, 1);
254         }
255
256         if (vpninfo->vpn_dns[0])
257                 setenv("INTERNAL_IP4_DNS", vpninfo->vpn_dns[0], 1);
258         else
259                 unsetenv("INTERNAL_IP4_DNS");
260         if (vpninfo->vpn_dns[1])
261                 appendenv("INTERNAL_IP4_DNS", vpninfo->vpn_dns[1]);
262         if (vpninfo->vpn_dns[2])
263                 appendenv("INTERNAL_IP4_DNS", vpninfo->vpn_dns[2]);
264
265         if (vpninfo->vpn_nbns[0])
266                 setenv("INTERNAL_IP4_NBNS", vpninfo->vpn_nbns[0], 1);
267         else
268                 unsetenv("INTERNAL_IP4_NBNS");
269         if (vpninfo->vpn_nbns[1])
270                 appendenv("INTERNAL_IP4_NBNS", vpninfo->vpn_nbns[1]);
271         if (vpninfo->vpn_nbns[2])
272                 appendenv("INTERNAL_IP4_NBNS", vpninfo->vpn_nbns[2]);
273
274         if (vpninfo->vpn_domain)
275                 setenv("CISCO_DEF_DOMAIN", vpninfo->vpn_domain, 1);
276         else unsetenv ("CISCO_DEF_DOMAIN");
277
278         if (vpninfo->vpn_proxy_pac)
279                 setenv("CISCO_PROXY_PAC", vpninfo->vpn_proxy_pac, 1);
280
281         if (vpninfo->split_includes) {
282                 struct split_include *this = vpninfo->split_includes;
283                 int nr_split_includes = 0;
284                 int nr_v6_split_includes = 0;
285
286                 while (this) {
287                         process_split_xxclude(vpninfo, "IN", this->route,
288                                               &nr_split_includes,
289                                               &nr_v6_split_includes);
290                         this = this->next;
291                 }
292                 if (nr_split_includes)
293                         setenv_int("CISCO_SPLIT_INC", nr_split_includes);
294                 if (nr_v6_split_includes)
295                         setenv_int("CISCO_IPV6_SPLIT_INC", nr_v6_split_includes);
296         }
297         if (vpninfo->split_excludes) {
298                 struct split_include *this = vpninfo->split_excludes;
299                 int nr_split_excludes = 0;
300                 int nr_v6_split_excludes = 0;
301
302                 while (this) {
303                         process_split_xxclude(vpninfo, "EX", this->route,
304                                               &nr_split_excludes,
305                                               &nr_v6_split_excludes);
306                         this = this->next;
307                 }
308                 if (nr_split_excludes)
309                         setenv_int("CISCO_SPLIT_EXC", nr_split_excludes);
310                 if (nr_v6_split_excludes)
311                         setenv_int("CISCO_IPV6_SPLIT_EXC", nr_v6_split_excludes);
312         }
313         setenv_cstp_opts(vpninfo);
314 }
315
316 static int script_config_tun(struct openconnect_info *vpninfo)
317 {
318         if (vpninfo->peer_addr->sa_family != AF_INET || !vpninfo->vpn_addr) {
319                 vpninfo->progress(vpninfo, PRG_ERR,
320                                   "Script can only handle Legacy IP\n");
321                 return -EINVAL;
322         }
323
324         set_script_env(vpninfo);
325
326         system(vpninfo->vpnc_script);
327         return 0;
328 }
329
330
331 /* Set up a tuntap device. */
332 int setup_tun(struct openconnect_info *vpninfo)
333 {
334         int tun_fd;
335
336         if (vpninfo->script_tun) {
337                 pid_t child;
338                 int fds[2];
339
340                 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, fds)) {
341                         perror("socketpair");
342                         exit(1);
343                 }
344                 tun_fd = fds[0];
345                 child = fork();
346                 if (child < 0) {
347                         perror("fork");
348                         exit(1);
349                 } else if (!child) {
350                         close(tun_fd);
351                         setenv_int("VPNFD", fds[1]);
352                         execl("/bin/sh", "/bin/sh", "-c", vpninfo->vpnc_script, NULL);
353                         perror("execl");
354                         exit(1);
355                 }
356                 close(fds[1]);
357                 vpninfo->script_tun = child;
358                 vpninfo->ifname = "(script)";
359         } else {
360 #ifdef IFF_TUN /* Linux */
361                 struct ifreq ifr;
362
363                 tun_fd = open("/dev/net/tun", O_RDWR);
364                 if (tun_fd < 0) {
365                         vpninfo->progress(vpninfo, PRG_ERR,
366                                           "Failed to open tun device: %s\n",
367                                           strerror(errno));
368                         exit(1);
369                 }
370                 memset(&ifr, 0, sizeof(ifr));
371                 ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
372                 if (vpninfo->ifname)
373                         strncpy(ifr.ifr_name, vpninfo->ifname,
374                                 sizeof(ifr.ifr_name) - 1);
375                 if (ioctl(tun_fd, TUNSETIFF, (void *) &ifr) < 0) {
376                         vpninfo->progress(vpninfo, PRG_ERR,
377                                           "TUNSETIFF failed: %s\n",
378                                           strerror(errno));
379                         exit(1);
380                 }
381                 if (!vpninfo->ifname)
382                         vpninfo->ifname = strdup(ifr.ifr_name);
383 #elif defined (__sun__)
384                 static char tun_name[80];
385                 int tun2_fd, ip_fd = open("/dev/ip", O_RDWR);
386                 int unit_nr, mux_id;
387                 struct ifreq ifr;
388
389                 if (ip_fd < 0) {
390                         perror("open /dev/ip");
391                         return -EIO;
392                 }
393
394                 tun_fd = open("/dev/tun", O_RDWR);
395                 if (tun_fd < 0) {
396                         perror("open /dev/tun");
397                         close(ip_fd);
398                         return -EIO;
399                 }
400
401                 unit_nr = ioctl(tun_fd, TUNNEWPPA, -1);
402                 if (unit_nr < 0) {
403                         perror("Failed to create new tun");
404                         close(tun_fd);
405                         close(ip_fd);
406                         return -EIO;
407                 }
408                 
409                 tun2_fd = open("/dev/tun", O_RDWR);
410                 if (tun2_fd < 0) {
411                         perror("open /dev/tun again");
412                         close(tun_fd);
413                         close(ip_fd);
414                         return -EIO;
415                 }
416                 if (ioctl(tun2_fd, I_PUSH, "ip") < 0) {
417                         perror("Can't push IP");
418                         close(tun2_fd);
419                         close(tun_fd);
420                         close(ip_fd);
421                         return -EIO;
422                 }
423                 if (ioctl(tun2_fd, IF_UNITSEL, &unit_nr) < 0) {
424                         perror("Can't select unit");
425                         close(tun2_fd);
426                         close(tun_fd);
427                         close(ip_fd);
428                         return -EIO;
429                 }
430                 mux_id = ioctl(ip_fd, I_PLINK, tun2_fd);
431                 if (mux_id < 0) {
432                         perror("Can't link tun to IP");
433                         close(tun2_fd);
434                         close(tun_fd);
435                         close(ip_fd);
436                         return -EIO;
437                 }
438                 close(tun2_fd);
439
440                 sprintf(tun_name, "tun%d", unit_nr);
441                 vpninfo->ifname = tun_name;
442
443                 memset(&ifr, 0, sizeof(ifr));
444                 strcpy(ifr.ifr_name, tun_name);
445                 ifr.ifr_ip_muxid = mux_id;
446
447                 if (ioctl(ip_fd, SIOCSIFMUXID, &ifr) < 0) {
448                         perror("Set mux id");
449                         close(tun_fd);
450                         ioctl(ip_fd, I_PUNLINK, mux_id);
451                         close(ip_fd);
452                         return -EIO;
453                 }
454                 /* Solaris tunctl needs this in order to tear it down */
455                 vpninfo->progress(vpninfo, PRG_DEBUG, "mux id is %d\n", mux_id);
456                 vpninfo->tun_muxid = mux_id;
457                 vpninfo->ip_fd = ip_fd;
458
459 #else /* BSD et al have /dev/tun$x devices */
460                 static char tun_name[80];
461                 int i;
462                 for (i = 0; i < 255; i++) {
463                         sprintf(tun_name, "/dev/tun%d", i);
464                         tun_fd = open(tun_name, O_RDWR);
465                         if (tun_fd >= 0)
466                                 break;
467                 }
468                 if (tun_fd < 0) {
469                         perror("open tun");
470                         exit(1);
471                 }
472                 vpninfo->ifname = tun_name + 5;
473 #ifdef TUNSIFHEAD
474                 i = 1;
475                 if (ioctl(tun_fd, TUNSIFHEAD, &i) < 0) {
476                         perror("TUNSIFHEAD");
477                         exit(1);
478                 }
479 #endif
480 #endif
481                 if (vpninfo->vpnc_script) {
482                         script_config_tun(vpninfo);
483                         /* We have to set the MTU for ourselves, because the script doesn't */
484                         local_config_tun(vpninfo, 1);
485                 } else
486                         local_config_tun(vpninfo, 0);
487         }
488
489         fcntl(tun_fd, F_SETFD, FD_CLOEXEC);
490
491         vpninfo->tun_fd = tun_fd;
492
493         if (vpninfo->select_nfds <= tun_fd)
494                 vpninfo->select_nfds = tun_fd + 1;
495
496         FD_SET(tun_fd, &vpninfo->select_rfds);
497
498         fcntl(vpninfo->tun_fd, F_SETFL, fcntl(vpninfo->tun_fd, F_GETFL) | O_NONBLOCK);
499
500         return 0;
501 }
502
503 int tun_mainloop(struct openconnect_info *vpninfo, int *timeout)
504 {
505         unsigned char buf[2000];
506         int len;
507         int work_done = 0;
508
509         if (FD_ISSET(vpninfo->tun_fd, &vpninfo->select_rfds)) {
510                 while ((len = read(vpninfo->tun_fd, buf, sizeof(buf))) > 0) {
511                         unsigned char *pkt = buf;
512 #ifdef TUN_HAS_AF_PREFIX
513                         pkt += 4;
514                         len -= 4;
515 #endif
516                         if (queue_new_packet(&vpninfo->outgoing_queue, pkt,
517                                              len))
518                                 break;
519
520                         work_done = 1;
521                         vpninfo->outgoing_qlen++;
522                         if (vpninfo->outgoing_qlen == vpninfo->max_qlen) {
523                                 FD_CLR(vpninfo->tun_fd, &vpninfo->select_rfds);
524                                 break;
525                         }
526                 }
527         } else if (vpninfo->outgoing_qlen < vpninfo->max_qlen) {
528                 FD_SET(vpninfo->tun_fd, &vpninfo->select_rfds);
529         }
530
531         /* The kernel returns -ENOMEM when the queue is full, so theoretically
532            we could handle that and retry... but it doesn't let us poll() for
533            the no-longer-full situation, so let's not bother. */
534         while (vpninfo->incoming_queue) {
535                 struct pkt *this = vpninfo->incoming_queue;
536                 unsigned char *data = this->data;
537                 int len = this->len;
538
539 #ifdef TUN_HAS_AF_PREFIX
540                 struct ip *iph = (void *)data;
541                 int type;
542
543                 if (iph->ip_v == 6)
544                         type = AF_INET6;
545                 else if (iph->ip_v == 4)
546                         type = AF_INET;
547                 else {
548                         static int complained = 0;
549                         if (!complained) {
550                                 complained = 1;
551                                 vpninfo->progress(vpninfo, PRG_ERR,
552                                                   "Unknown packet (len %d) received: %02x %02x %02x %02x...\n",
553                                                   len, data[0], data[1], data[2], data[3]);
554                         }
555                         free(this);
556                         continue;
557                 }
558                 data -= 4;
559                 len += 4;
560                 *(int *)data = htonl(type);
561 #endif
562                 vpninfo->incoming_queue = this->next;
563
564                 if (write(vpninfo->tun_fd, data, len) < 0 &&
565                     errno == ENOTCONN) {
566                         vpninfo->quit_reason = "Client connection terminated";
567                         return 1;
568                 }
569                 free(this);
570         }
571         /* Work is not done if we just got rid of packets off the queue */
572         return work_done;
573 }
574
575 void shutdown_tun(struct openconnect_info *vpninfo)
576 {       
577         if (vpninfo->script_tun) {
578                 kill(vpninfo->script_tun, SIGHUP);
579         } else {
580                 if (vpninfo->vpnc_script) {
581                         setenv("TUNDEV", vpninfo->ifname, 1);
582                         setenv("reason", "disconnect", 1);
583                         system(vpninfo->vpnc_script);
584                 }
585 #ifdef __sun__
586                 if (ioctl(vpninfo->ip_fd, I_PUNLINK, vpninfo->tun_muxid) < 0)
587                         perror("ioctl(I_PUNLINK)");
588
589                 close(vpninfo->ip_fd);
590                 vpninfo->ip_fd = -1;
591 #endif
592         }
593
594         close(vpninfo->tun_fd);
595         vpninfo->tun_fd = -1;
596 }