summaryrefslogtreecommitdiff
path: root/src/openvpn/tun.c
diff options
context:
space:
mode:
authorAlberto Gonzalez Iniesta <agi@inittab.org>2014-04-16 17:32:08 +0200
committerAlberto Gonzalez Iniesta <agi@inittab.org>2014-04-16 17:32:08 +0200
commite5caec6bb07020a3e552fc78f679e0517c4569cf (patch)
tree7e6c612db8bf52fdde057f35b2b4a5559e86e61b /src/openvpn/tun.c
parent72eb879255b983286b83678fd62228187fbedbb2 (diff)
parent0af7f64094c65cba7ee45bd2679e6826bcf598cb (diff)
Merge tag 'upstream/2.3.3'
Upstream version 2.3.3
Diffstat (limited to 'src/openvpn/tun.c')
-rw-r--r--src/openvpn/tun.c361
1 files changed, 285 insertions, 76 deletions
diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
index a361233..976cc52 100644
--- a/src/openvpn/tun.c
+++ b/src/openvpn/tun.c
@@ -73,6 +73,12 @@ static void solaris_error_close (struct tuntap *tt, const struct env_set *es, co
#include <stropts.h>
#endif
+#if defined(TARGET_DARWIN) && HAVE_NET_IF_UTUN_H
+#include <sys/kern_control.h>
+#include <net/if_utun.h>
+#include <sys/sys_domain.h>
+#endif
+
static void clear_tuntap (struct tuntap *tuntap);
bool
@@ -867,7 +873,7 @@ do_ifconfig (struct tuntap *tt,
if (!tun && tt->topology == TOP_SUBNET)
{
/* Add a network route for the local tun interface */
- struct route r;
+ struct route_ipv4 r;
CLEAR (r);
r.flags = RT_DEFINED | RT_METRIC_DEFINED;
r.network = tt->local & tt->remote_netmask;
@@ -1064,7 +1070,7 @@ do_ifconfig (struct tuntap *tt,
/* Add a network route for the local tun interface */
if (!tun && tt->topology == TOP_SUBNET)
{
- struct route r;
+ struct route_ipv4 r;
CLEAR (r);
r.flags = RT_DEFINED;
r.network = tt->local & tt->remote_netmask;
@@ -1130,7 +1136,7 @@ do_ifconfig (struct tuntap *tt,
/* Add a network route for the local tun interface */
if (!tun && tt->topology == TOP_SUBNET)
{
- struct route r;
+ struct route_ipv4 r;
CLEAR (r);
r.flags = RT_DEFINED;
r.network = tt->local & tt->remote_netmask;
@@ -1248,6 +1254,87 @@ open_null (struct tuntap *tt)
tt->actual_name = string_alloc ("null", NULL);
}
+
+#if defined (TARGET_OPENBSD) || (defined(TARGET_DARWIN) && HAVE_NET_IF_UTUN_H)
+
+/*
+ * OpenBSD and Mac OS X when using utun
+ * have a slightly incompatible TUN device from
+ * the rest of the world, in that it prepends a
+ * uint32 to the beginning of the IP header
+ * to designate the protocol (why not just
+ * look at the version field in the IP header to
+ * determine v4 or v6?).
+ *
+ * We strip off this field on reads and
+ * put it back on writes.
+ *
+ * I have not tested TAP devices on OpenBSD,
+ * but I have conditionalized the special
+ * TUN handling code described above to
+ * go away for TAP devices.
+ */
+
+#include <netinet/ip.h>
+#include <sys/uio.h>
+
+static inline int
+header_modify_read_write_return (int len)
+{
+ if (len > 0)
+ return len > sizeof (u_int32_t) ? len - sizeof (u_int32_t) : 0;
+ else
+ return len;
+}
+
+int
+write_tun_header (struct tuntap* tt, uint8_t *buf, int len)
+{
+ if (tt->type == DEV_TYPE_TUN)
+ {
+ u_int32_t type;
+ struct iovec iv[2];
+ struct ip *iph;
+
+ iph = (struct ip *) buf;
+
+ if (tt->ipv6 && iph->ip_v == 6)
+ type = htonl (AF_INET6);
+ else
+ type = htonl (AF_INET);
+
+ iv[0].iov_base = &type;
+ iv[0].iov_len = sizeof (type);
+ iv[1].iov_base = buf;
+ iv[1].iov_len = len;
+
+ return header_modify_read_write_return (writev (tt->fd, iv, 2));
+ }
+ else
+ return write (tt->fd, buf, len);
+}
+
+int
+read_tun_header (struct tuntap* tt, uint8_t *buf, int len)
+{
+ if (tt->type == DEV_TYPE_TUN)
+ {
+ u_int32_t type;
+ struct iovec iv[2];
+
+ iv[0].iov_base = &type;
+ iv[0].iov_len = sizeof (type);
+ iv[1].iov_base = buf;
+ iv[1].iov_len = len;
+
+ return header_modify_read_write_return (readv (tt->fd, iv, 2));
+ }
+ else
+ return read (tt->fd, buf, len);
+}
+#endif
+
+
#ifndef WIN32
static void
open_tun_generic (const char *dev, const char *dev_type, const char *dev_node,
@@ -1972,23 +2059,6 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len)
#elif defined(TARGET_OPENBSD)
-/*
- * OpenBSD has a slightly incompatible TUN device from
- * the rest of the world, in that it prepends a
- * uint32 to the beginning of the IP header
- * to designate the protocol (why not just
- * look at the version field in the IP header to
- * determine v4 or v6?).
- *
- * We strip off this field on reads and
- * put it back on writes.
- *
- * I have not tested TAP devices on OpenBSD,
- * but I have conditionalized the special
- * TUN handling code described above to
- * go away for TAP devices.
- */
-
void
open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
{
@@ -2055,59 +2125,16 @@ close_tun (struct tuntap* tt)
}
}
-static inline int
-openbsd_modify_read_write_return (int len)
-{
- if (len > 0)
- return len > sizeof (u_int32_t) ? len - sizeof (u_int32_t) : 0;
- else
- return len;
-}
-
int
-write_tun (struct tuntap* tt, uint8_t *buf, int len)
+write_tun(struct tuntap *tt, uint8_t *buf, int len)
{
- if (tt->type == DEV_TYPE_TUN)
- {
- u_int32_t type;
- struct iovec iv[2];
- struct ip *iph;
-
- iph = (struct ip *) buf;
-
- if (tt->ipv6 && iph->ip_v == 6)
- type = htonl (AF_INET6);
- else
- type = htonl (AF_INET);
-
- iv[0].iov_base = &type;
- iv[0].iov_len = sizeof (type);
- iv[1].iov_base = buf;
- iv[1].iov_len = len;
-
- return openbsd_modify_read_write_return (writev (tt->fd, iv, 2));
- }
- else
- return write (tt->fd, buf, len);
+ return write_tun_header (tt, buf, len);
}
int
-read_tun (struct tuntap* tt, uint8_t *buf, int len)
+read_tun (struct tuntap *tt, uint8_t *buf, int len)
{
- if (tt->type == DEV_TYPE_TUN)
- {
- u_int32_t type;
- struct iovec iv[2];
-
- iv[0].iov_base = &type;
- iv[0].iov_len = sizeof (type);
- iv[1].iov_base = buf;
- iv[1].iov_len = len;
-
- return openbsd_modify_read_write_return (readv (tt->fd, iv, 2));
- }
- else
- return read (tt->fd, buf, len);
+ return read_tun_header (tt, buf, len);
}
#elif defined(TARGET_NETBSD)
@@ -2467,10 +2494,177 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len)
* pointing to lo0. Need to unconfigure... (observed on 10.5)
*/
+/*
+ * utun is the native Darwin tun driver present since at least 10.7
+ * Thanks goes to Jonathan Levin for providing an example how to utun
+ * (http://newosxbook.com/src.jl?tree=listings&file=17-15-utun.c)
+ */
+
+#ifdef HAVE_NET_IF_UTUN_H
+
+/* Helper functions that tries to open utun device
+ return -2 on early initialization failures (utun not supported
+ at all (old OS X) and -1 on initlization failure of utun
+ device (utun works but utunX is already used */
+static
+int utun_open_helper (struct ctl_info ctlInfo, int utunnum)
+{
+ struct sockaddr_ctl sc;
+ int fd;
+
+ fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
+
+ if (fd < 0)
+ {
+ msg (M_INFO, "Opening utun (%s): %s", "socket(SYSPROTO_CONTROL)",
+ strerror (errno));
+ return -2;
+ }
+
+ if (ioctl(fd, CTLIOCGINFO, &ctlInfo) == -1)
+ {
+ close (fd);
+ msg (M_INFO, "Opening utun (%s): %s", "ioctl(CTLIOCGINFO)",
+ strerror (errno));
+ return -2;
+ }
+
+
+ sc.sc_id = ctlInfo.ctl_id;
+ sc.sc_len = sizeof(sc);
+ sc.sc_family = AF_SYSTEM;
+ sc.ss_sysaddr = AF_SYS_CONTROL;
+
+ sc.sc_unit = utunnum+1;
+
+
+ /* If the connect is successful, a utun%d device will be created, where "%d"
+ * is (sc.sc_unit - 1) */
+
+ if (connect (fd, (struct sockaddr *)&sc, sizeof(sc)) < 0)
+ {
+ msg (M_INFO, "Opening utun (%s): %s", "connect(AF_SYS_CONTROL)",
+ strerror (errno));
+ close(fd);
+ return -1;
+ }
+
+ set_nonblock (fd);
+ set_cloexec (fd); /* don't pass fd to scripts */
+
+ return fd;
+}
+
+void
+open_darwin_utun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+{
+ struct ctl_info ctlInfo;
+ int fd;
+ char utunname[20];
+ int utunnum =-1;
+ socklen_t utunname_len = sizeof(utunname);
+
+ /* dev_node is simply utun, do the normal dynamic utun
+ * otherwise try to parse the utun number */
+ if (dev_node && !strcmp ("utun", dev_node)==0)
+ {
+ if (!sscanf (dev_node, "utun%d", &utunnum)==1)
+ msg (M_FATAL, "Cannot parse 'dev-node %s' please use 'dev-node utunX'"
+ "to use a utun device number X", dev_node);
+ }
+
+
+
+ CLEAR (ctlInfo);
+ if (strlcpy(ctlInfo.ctl_name, UTUN_CONTROL_NAME, sizeof(ctlInfo.ctl_name)) >=
+ sizeof(ctlInfo.ctl_name))
+ {
+ msg (M_ERR, "Opening utun: UTUN_CONTROL_NAME too long");
+ }
+
+ /* try to open first available utun device if no specific utun is requested */
+ if (utunnum == -1)
+ {
+ for (utunnum=0; utunnum<255; utunnum++)
+ {
+ fd = utun_open_helper (ctlInfo, utunnum);
+ /* Break if the fd is valid,
+ * or if early initalization failed (-2) */
+ if (fd !=-1)
+ break;
+ }
+ }
+ else
+ {
+ fd = utun_open_helper (ctlInfo, utunnum);
+ }
+
+ /* opening an utun device failed */
+ tt->fd = fd;
+
+ if (fd < 0)
+ return;
+
+ /* Retrieve the assigned interface name. */
+ if (getsockopt (fd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, utunname, &utunname_len))
+ msg (M_ERR | M_ERRNO, "Error retrieving utun interface name");
+
+ tt->actual_name = string_alloc (utunname, NULL);
+
+ msg (M_INFO, "Opened utun device %s", utunname);
+ tt->is_utun = true;
+}
+
+#endif
+
void
open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
{
- open_tun_generic (dev, dev_type, dev_node, true, true, tt);
+#ifdef HAVE_NET_IF_UTUN_H
+ /* If dev_node does not start start with utun assume regular tun/tap */
+ if ((!dev_node && tt->type==DEV_TYPE_TUN) ||
+ (dev_node && !strncmp (dev_node, "utun", 4)))
+ {
+
+ /* Check if user has specific dev_type tap and forced utun with
+ dev-node utun */
+ if (tt->type!=DEV_TYPE_TUN)
+ msg (M_FATAL, "Cannot use utun devices with --dev-type %s",
+ dev_type_string (dev, dev_type));
+
+ /* Try utun first and fall back to normal tun if utun fails
+ and dev_node is not specified */
+ open_darwin_utun(dev, dev_type, dev_node, tt);
+
+ if (!tt->is_utun)
+ {
+ if (!dev_node)
+ {
+ /* No explicit utun and utun failed, try the generic way) */
+ msg (M_INFO, "Failed to open utun device. Falling back to /dev/tun device");
+ open_tun_generic (dev, dev_type, NULL, true, true, tt);
+ }
+ else
+ {
+ /* Specific utun device or generic utun request with no tun
+ fall back failed, consider this a fatal failure */
+ msg (M_FATAL, "Cannot open utun device");
+ }
+ }
+ }
+ else
+#endif
+ {
+
+ /* Use plain dev-node tun to select /dev/tun style
+ * Unset dev_node variable prior to passing to open_tun_generic to
+ * let open_tun_generic pick the first available tun device */
+
+ if (dev_node && strcmp (dev_node, "tun")==0)
+ dev_node=NULL;
+
+ open_tun_generic (dev, dev_type, dev_node, true, true, tt);
+ }
}
void
@@ -2503,13 +2697,23 @@ close_tun (struct tuntap* tt)
int
write_tun (struct tuntap* tt, uint8_t *buf, int len)
{
- return write (tt->fd, buf, len);
+#ifdef HAVE_NET_IF_UTUN_H
+ if (tt->is_utun)
+ return write_tun_header (tt, buf, len);
+ else
+#endif
+ return write (tt->fd, buf, len);
}
int
read_tun (struct tuntap* tt, uint8_t *buf, int len)
{
- return read (tt->fd, buf, len);
+#ifdef HAVE_NET_IF_UTUN_H
+ if (tt->is_utun)
+ return read_tun_header (tt, buf, len);
+ else
+#endif
+ return read (tt->fd, buf, len);
}
#elif defined(WIN32)
@@ -2854,9 +3058,9 @@ get_panel_reg (struct gc_arena *gc)
char enum_name[256];
char connection_string[256];
HKEY connection_key;
- char name_data[256];
+ WCHAR name_data[256];
DWORD name_type;
- const char name_string[] = "Name";
+ const WCHAR name_string[] = L"Name";
len = sizeof (enum_name);
status = RegEnumKeyEx(
@@ -2890,12 +3094,12 @@ get_panel_reg (struct gc_arena *gc)
else
{
len = sizeof (name_data);
- status = RegQueryValueEx(
+ status = RegQueryValueExW(
connection_key,
name_string,
NULL,
&name_type,
- name_data,
+ (LPBYTE) name_data,
&len);
if (status != ERROR_SUCCESS || name_type != REG_SZ)
@@ -2903,10 +3107,15 @@ get_panel_reg (struct gc_arena *gc)
NETWORK_CONNECTIONS_KEY, connection_string, name_string);
else
{
+ int n;
+ LPSTR name;
struct panel_reg *reg;
ALLOC_OBJ_CLEAR_GC (reg, struct panel_reg, gc);
- reg->name = string_alloc (name_data, gc);
+ n = WideCharToMultiByte (CP_UTF8, 0, name_data, -1, NULL, 0, NULL, NULL);
+ name = gc_malloc (n, false, gc);
+ WideCharToMultiByte (CP_UTF8, 0, name_data, -1, name, n, NULL, NULL);
+ reg->name = name;
reg->guid = string_alloc (enum_name, gc);
/* link into return list */
@@ -3116,7 +3325,7 @@ static void
at_least_one_tap_win (const struct tap_reg *tap_reg)
{
if (!tap_reg)
- msg (M_FATAL, "There are no TAP-Windows adapters on this system. You should be able to create a TAP-Windows adapter by going to Start -> All Programs -> " PACKAGE_NAME " -> Add a new TAP-Windows virtual ethernet adapter.");
+ msg (M_FATAL, "There are no TAP-Windows adapters on this system. You should be able to create a TAP-Windows adapter by going to Start -> All Programs -> TAP-Windows -> Utilities -> Add a new TAP-Windows virtual ethernet adapter.");
}
/*