2
* OpenConnect (SSL + DTLS) VPN client
4
* Copyright © 2008 Intel Corporation.
6
* Author: David Woodhouse <dwmw2@infradead.org>
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.
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.
17
* You should have received a copy of the GNU Lesser General Public
18
* License along with this library; if not, write to:
20
* Free Software Foundation, Inc.
21
* 51 Franklin Street, Fifth Floor,
22
* Boston, MA 02110-1301 USA
26
#include <sys/socket.h>
27
#include <sys/ioctl.h>
29
#include <linux/if_tun.h>
31
#include <sys/types.h>
35
#include <netinet/in.h>
37
#include <arpa/inet.h>
40
#include "openconnect.h"
42
static int local_config_tun(struct openconnect_info *vpninfo, int mtu_only)
47
net_fd = socket(PF_INET, SOCK_DGRAM, 0);
52
memset(&ifr, 0, sizeof(ifr));
53
strncpy(ifr.ifr_name, vpninfo->ifname, sizeof(ifr.ifr_name) - 1);
56
struct sockaddr_in *addr = (struct sockaddr_in *) &ifr.ifr_addr;
58
if (ioctl(net_fd, SIOCGIFFLAGS, &ifr) < 0)
59
perror("SIOCGIFFLAGS");
61
ifr.ifr_flags |= IFF_UP | IFF_POINTOPOINT;
62
if (ioctl(net_fd, SIOCSIFFLAGS, &ifr) < 0)
63
perror("SIOCSIFFLAGS");
65
addr->sin_family = AF_INET;
66
addr->sin_addr.s_addr = inet_addr(vpninfo->vpn_addr);
67
if (ioctl(net_fd, SIOCSIFADDR, &ifr) < 0)
68
perror("SIOCSIFADDR");
71
ifr.ifr_mtu = vpninfo->mtu;
72
if (ioctl(net_fd, SIOCSIFMTU, &ifr) < 0)
80
static int setenv_int(const char *opt, int value)
83
sprintf(buf, "%d", value);
84
return setenv(opt, buf, 1);
87
static int process_split_include(struct openconnect_info *vpninfo,
88
char *route, int *nr_incs)
95
slash = strchr(route, '/');
98
vpninfo->progress(vpninfo, PRG_ERR,
99
"Discard bad split include: \"%s\"\n",
105
if (!inet_aton(route, &addr)) {
111
snprintf(envname, 79, "CISCO_SPLIT_INC_%d_ADDR", *nr_incs);
112
setenv(envname, route, 1);
114
/* Put it back how we found it */
117
if (!inet_aton(slash+1, &addr))
120
snprintf(envname, 79, "CISCO_SPLIT_INC_%d_MASK", *nr_incs);
121
setenv(envname, slash+1, 1);
123
for (masklen = 0; masklen < 32; masklen++) {
124
if (ntohl(addr.s_addr) >= (0xffffffff << masklen))
127
masklen = 32 - masklen;
129
snprintf(envname, 79, "CISCO_SPLIT_INC_%d_MASKLEN", *nr_incs);
130
setenv_int(envname, masklen);
136
static int appendenv(const char *opt, const char *new)
139
char *old = getenv(opt);
143
snprintf(buf, 1023, "%s %s", old, new);
145
snprintf(buf, 1023, "%s", new);
147
return setenv(opt, buf, 1);
150
static void set_script_env(struct openconnect_info *vpninfo)
152
struct sockaddr_in *sin = (void *)vpninfo->peer_addr;
154
setenv("VPNGATEWAY", inet_ntoa(sin->sin_addr), 1);
155
setenv("TUNDEV", vpninfo->ifname, 1);
156
setenv("reason", "connect", 1);
157
unsetenv("CISCO_BANNER");
158
unsetenv("CISCO_SPLIT_INC");
160
setenv_int("INTERNAL_IP4_MTU", vpninfo->mtu);
162
setenv("INTERNAL_IP4_ADDRESS", vpninfo->vpn_addr, 1);
163
setenv("INTERNAL_IP4_NETMASK", vpninfo->vpn_netmask, 1);
165
if (vpninfo->vpn_dns[0])
166
setenv("INTERNAL_IP4_DNS", vpninfo->vpn_dns[0], 1);
168
unsetenv("INTERNAL_IP4_DNS");
169
if (vpninfo->vpn_dns[1])
170
appendenv("INTERNAL_IP4_DNS", vpninfo->vpn_dns[1]);
171
if (vpninfo->vpn_dns[2])
172
appendenv("INTERNAL_IP4_DNS", vpninfo->vpn_dns[2]);
174
if (vpninfo->vpn_nbns[0])
175
setenv("INTERNAL_IP4_NBNS", vpninfo->vpn_nbns[0], 1);
177
unsetenv("INTERNAL_IP4_NBNS");
178
if (vpninfo->vpn_nbns[1])
179
appendenv("INTERNAL_IP4_NBNS", vpninfo->vpn_nbns[1]);
180
if (vpninfo->vpn_nbns[2])
181
appendenv("INTERNAL_IP4_NBNS", vpninfo->vpn_nbns[2]);
183
if (vpninfo->vpn_domain)
184
setenv("CISCO_DEF_DOMAIN", vpninfo->vpn_domain, 1);
185
else unsetenv ("CISCO_DEF_DOMAIN");
187
if (vpninfo->split_includes) {
188
struct split_include *this = vpninfo->split_includes;
189
int nr_split_includes = 0;
192
process_split_include(vpninfo, this->route,
196
setenv_int("CISCO_SPLIT_INC", nr_split_includes);
202
static int script_config_tun(struct openconnect_info *vpninfo)
204
if (vpninfo->peer_addr->sa_family != AF_INET) {
205
vpninfo->progress(vpninfo, PRG_ERR, "Script cannot handle anything but Legacy IP\n");
209
set_script_env(vpninfo);
211
system(vpninfo->vpnc_script);
216
/* Set up a tuntap device. */
217
int setup_tun(struct openconnect_info *vpninfo)
222
if (vpninfo->script_tun) {
226
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, fds)) {
227
perror("socketpair");
237
setenv_int("VPNFD", fds[1]);
238
execl("/bin/sh", "/bin/sh", "-c", vpninfo->vpnc_script, NULL);
243
vpninfo->script_tun = child;
244
vpninfo->ifname = "(script)";
246
#ifdef IFF_TUN /* Linux */
247
tun_fd = open("/dev/net/tun", O_RDWR);
249
vpninfo->progress(vpninfo, PRG_ERR,
250
"Failed to open tun device: %s\n",
254
memset(&ifr, 0, sizeof(ifr));
255
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
257
strncpy(ifr.ifr_name, vpninfo->ifname,
258
sizeof(ifr.ifr_name) - 1);
259
if (ioctl(tun_fd, TUNSETIFF, (void *) &ifr) < 0) {
260
vpninfo->progress(vpninfo, PRG_ERR,
261
"TUNSETIFF failed: %s\n",
265
if (!vpninfo->ifname)
266
vpninfo->ifname = strdup(ifr.ifr_name);
268
#else /* BSD et al have /dev/tun$x devices */
269
static char tun_name[80];
271
for (i=0; i < 255; i++) {
272
sprintf(tun_name, "/dev/tun%d", i);
273
tun_fd = open(tun_name, O_RDWR);
281
vpninfo->ifname = tun_name + 5;
283
if (vpninfo->vpnc_script) {
284
script_config_tun(vpninfo);
285
/* We have to set the MTU for ourselves, because the script doesn't */
286
local_config_tun(vpninfo, 1);
288
local_config_tun(vpninfo, 0);
291
fcntl(tun_fd, F_SETFD, FD_CLOEXEC);
293
vpninfo->tun_fd = tun_fd;
295
if (vpninfo->select_nfds <= tun_fd)
296
vpninfo->select_nfds = tun_fd + 1;
298
FD_SET(tun_fd, &vpninfo->select_rfds);
300
fcntl(vpninfo->tun_fd, F_SETFL, fcntl(vpninfo->tun_fd, F_GETFL) | O_NONBLOCK);
305
int tun_mainloop(struct openconnect_info *vpninfo, int *timeout)
311
if (FD_ISSET(vpninfo->tun_fd, &vpninfo->select_rfds)) {
312
while ((len = read(vpninfo->tun_fd, buf, sizeof(buf))) > 0) {
313
if (queue_new_packet(&vpninfo->outgoing_queue, AF_INET, buf, len))
317
vpninfo->outgoing_qlen++;
318
if (vpninfo->outgoing_qlen == vpninfo->max_qlen) {
319
FD_CLR(vpninfo->tun_fd, &vpninfo->select_rfds);
323
} else if (vpninfo->outgoing_qlen < vpninfo->max_qlen) {
324
FD_SET(vpninfo->tun_fd, &vpninfo->select_rfds);
327
/* The kernel returns -ENOMEM when the queue is full, so theoretically
328
we could handle that and retry... but it doesn't let us poll() for
329
the no-longer-full situation, so let's not bother. */
330
while (vpninfo->incoming_queue) {
331
struct pkt *this = vpninfo->incoming_queue;
332
vpninfo->incoming_queue = this->next;
333
if (write(vpninfo->tun_fd, this->data, this->len) < 0 &&
335
vpninfo->quit_reason = "Client connection terminated";
339
/* Work is not done if we just got rid of packets off the queue */