Mailing List Archive

Patch for utun support for xnu/darwin (OS X)
Hi,

I just added utun support to vpnc for use with OS X so that tuntaposx kext drivers are not required. This has become an issue with 10.10 “Yosemite” because Yosemite does not allow loading unsigned kexts. This patch allows running vpnc on OS X without tuntaposx.

The xnu utun is implemented as a socket instead of a character device but otherwise works similarly.

Repository is on Gihub: https://github.com/breiter/vpnc

Best.

//brian reiter

diff --git a/sysdep.c b/sysdep.c
index d8f181d..5933c91 100644
--- a/sysdep.c
+++ b/sysdep.c
@@ -61,7 +61,14 @@
#elif defined(__linux__)
#include <linux/if_tun.h>
#elif defined(__APPLE__)
-/* no header for tun */
+/* xnu 1456.1.26 (OS X 10.6)+ supports native user tunnel (utun) sockets */
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <sys/kern_control.h>
+#include <sys/uio.h>
+#include <sys/sys_domain.h>
+#include <net/if_utun.h>
+#include <netinet/ip.h>
#elif defined(__CYGWIN__)
#include "tap-win32.h"
#else
@@ -430,6 +437,101 @@ int tun_open (char *dev, enum if_mode_enum mode)

return fd;
}
+#elif defined(__APPLE__)
+/* xnu 1456.1.26 (OS X 10.6)+ implements native user tunnels. untun devices are sockets
+ * rather than character devices. They behave like FreeBSD or OpenBSD tunnels and do not
+ * require the tuntaposx driver kext to be loaded to work.
+ */
+
+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)
+ {
+ return -2;
+ }
+
+ if (ioctl(fd, CTLIOCGINFO, &ctlInfo) == -1)
+ {
+ close (fd);
+ 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)
+ {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+int open_darwin_utun (char *dev)
+{
+ struct ctl_info ctlInfo;
+ int fd;
+ char utunname[20];
+ int utunnum =-1;
+ socklen_t utunname_len = sizeof(utunname);
+
+ if (strlcpy(ctlInfo.ctl_name, UTUN_CONTROL_NAME, sizeof(ctlInfo.ctl_name)) >=
+ sizeof(ctlInfo.ctl_name))
+ {
+ printf("Opening utun: UTUN_CONTROL_NAME too long\n");
+ return -1;
+ }
+
+ /* 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);
+ }
+
+ if(fd < 0) return fd; //error
+
+ /* Retrieve the assigned interface name. */
+ if (getsockopt (fd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, utunname, &utunname_len)) {
+ printf("Error retrieving utun interface name\n");
+ return -1;
+ }
+
+ printf("Opened utun device %s\n", utunname);
+ strcpy(dev, utunname); //return device name
+ return fd;
+}
+
+int tun_open(char *dev, enum if_mode_enum mode)
+{
+ return open_darwin_utun(dev);
+}
+
#elif defined(IFF_TUN)
int tun_open(char *dev, enum if_mode_enum mode)
{
@@ -587,6 +689,49 @@ int tun_write(int fd, unsigned char *buf, int len)

return -1;
}
+#elif defined(__APPLE__)
+
+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;
+}
+
+//read from utun
+int tun_read(int fd, uint8_t *buf, int len) {
+ u_int32_t type;
+ struct iovec iv[2];
+
+ iv[0].iov_base = (char *)&type;
+ iv[0].iov_len = sizeof (type);
+ iv[1].iov_base = buf;
+ iv[1].iov_len = len;
+
+ return header_modify_read_write_return(readv(fd, iv, 2));
+}
+//write to utun
+int tun_write(int fd, uint8_t *buf, int len)
+{
+ u_int32_t type;
+ struct iovec iv[2];
+ struct ip *iph;
+
+ iph = (struct ip *) buf;
+
+ //not obvious how to support IPV6 hereclear
+
+ type = htonl (AF_INET);
+
+ iv[0].iov_base = (char *)&type;
+ iv[0].iov_len = sizeof (type);
+ iv[1].iov_base = buf;
+ iv[1].iov_len = len;
+
+ return header_modify_read_write_return(writev (fd, iv, 2));
+}
+
#elif defined(NEW_TUN)
#define MAX_MRU 2048
struct tun_data {
Re: Patch for utun support for xnu/darwin (OS X) [ In reply to ]
On Wed, 2014-10-29 at 12:58 +0200, Brian Reiter wrote:
>
> + //not obvious how to support IPV6 hereclear
> +
> + type = htonl (AF_INET);

By checking iph->ip_v?

Your patch looks like it would disable the tuntaposx support completely.
Is that intended? You say utun is working from OS X 10.6 onwards... are
there no caveats to that, and do we no longer care about older versions?

--
dwmw2
Re: Patch for utun support for xnu/darwin (OS X) [ In reply to ]
On Oct 29, 2014, at 2:06 PM, David Woodhouse <dwmw2@infradead.org> wrote:

On Wed, 2014-10-29 at 12:58 +0200, Brian Reiter wrote:

+ //not obvious how to support IPV6 hereclear
+
+ type = htonl (AF_INET);

By checking iph->ip_v?



Duh!

if(iph->ip_v == 6)
type = htonl(AF_INET6);
else
type = htonl(AF_INET);


Your patch looks like it would disable the tuntaposx support completely.
Is that intended? You say utun is working from OS X 10.6 onwards... are
there no caveats to that, and do we no longer care about older versions?

Yes. It completely replaces the tun/tap character device that requires tun.kext and tap.kext. My rationale is that it is always better to use the native support in the kernel than load 3rd party code. Also, in 10.9 Mavericks, unsigned kexts are deprecated and cause a warning to be logged and in 10.10 Yosemite, they are simply not loaded.

The support for utun came in the GM version of Snow Leopard 10.6.

http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/net/if_utun.c

The current implementation 10.10 Yosemite is different and comes from NetBSD:

http://www.opensource.apple.com/source/xnu/xnu-123.5/bsd/net/if_tun.c

However, I think the feature is the same.
Re: Patch for utun support for xnu/darwin (OS X) [ In reply to ]
On Wed, 2014-10-29 at 15:50 +0200, Brian Reiter wrote:
> if(iph->ip_v == 6)
> type = htonl(AF_INET6);
> else
> type = htonl(AF_INET);

That's better, although of course it's academic in vpnc which has yet to
join us in the 21st century and support IPv6.

I've implemented something based on your patch in OpenConnect:
http://git.infradead.org/users/dwmw2/openconnect.git/commitdiff/898246b2

I've tested on OS X 10.6.8 and it seems to work fine for both IPv6 and
Legacy IP.

Note that I don't iterate through unit numbers 0-255 to try to find a
free one; just setting sc.sc_unit=0 seems to do what we need without the
need to loop.

> Yes. It completely replaces the tun/tap character device that requires
> tun.kext and tap.kext. My rationale is that it is always better to use
> the native support in the kernel than load 3rd party code. Also, in
> 10.9 Mavericks, unsigned kexts are deprecated and cause a warning to
> be logged and in 10.10 Yosemite, they are simply not loaded.

Makes sense. I've taken a different approach; I need to preserve the BSD
code path *anyway* for non-OSX systems so it doesn't hurt to keep it on
OSX too. So the utun code is disabled at compile time if <net/if_utun.h>
can't be found, and at runtime if there are errors or if the user
explicitly requests a tun%d device, we still fall back to tuntaposx.

--
dwmw2
Re: Patch for utun support for xnu/darwin (OS X) [ In reply to ]
> On Oct 29, 2014, at 6:27 PM, David Woodhouse <dwmw2@infradead.org> wrote:
>
> Note that I don't iterate through unit numbers 0-255 to try to find a
> free one; just setting sc.sc_unit=0 seems to do what we need without the
> need to loop.

Hi David,

The loop is there because other utun devices can exist. On my machine there utun0-3 happen to be consumed by other VPN tunnels. OpenVPN allocates utun interfaces as does OS X built-in VPN client service and Back to My Mac.

Best.
//brian
_______________________________________________
vpnc-devel mailing list
vpnc-devel@unix-ag.uni-kl.de
https://lists.unix-ag.uni-kl.de/mailman/listinfo/vpnc-devel
http://www.unix-ag.uni-kl.de/~massar/vpnc/
Re: Patch for utun support for xnu/darwin (OS X) [ In reply to ]
On Wed, 2014-10-29 at 18:33 +0200, Brian Reiter wrote:
>
>
> The loop is there because other utun devices can exist. On my machine
> there utun0-3 happen to be consumed by other VPN tunnels. OpenVPN
> allocates utun interfaces as does OS X built-in VPN client service and
> Back to My Mac.

Right. Your loop tries with sc.sc_unit=1 to open 'utun0', with
sc.sc_unit=2 to open 'utun1', and eventually succeeds with sc.sc_unit=5
to open 'utun4'.

My version sets sc.sc_unit=0 and the kernel just gives me back the first
unused device without me having to keep trying.

--
dwmw2
Re: Patch for utun support for xnu/darwin (OS X) [ In reply to ]
On Oct 29, 2014, at 7:43 PM, David Woodhouse <dwmw2@infradead.org> wrote:

On Wed, 2014-10-29 at 18:33 +0200, Brian Reiter wrote:


The loop is there because other utun devices can exist. On my machine
there utun0-3 happen to be consumed by other VPN tunnels. OpenVPN
allocates utun interfaces as does OS X built-in VPN client service and
Back to My Mac.

Right. Your loop tries with sc.sc_unit=1 to open 'utun0', with
sc.sc_unit=2 to open 'utun1', and eventually succeeds with sc.sc_unit=5
to open 'utun4'.

My version sets sc.sc_unit=0 and the kernel just gives me back the first
unused device without me having to keep trying.

Hi David,

Thanks. You are correct. Setting sc.sc_unit=0 works for me also to get the next available utunX.

https://github.com/breiter/vpnc/commit/4e00e6cc93780e6c3ae2469a053471438705ece2

//brian

PS

As an American expat living abroad, I found these bullets on your website quite amusing:

2. I do not want to move to the United States
4. I do not want to move to the United States
6. I do not want to move to the United States
8. I do not want to move to the United States
Re: Patch for utun support for xnu/darwin (OS X) [ In reply to ]
> On Oct 29, 2014, at 7:43 PM, David Woodhouse <dwmw2@infradead.org> wrote:
>
> On Wed, 2014-10-29 at 18:33 +0200, Brian Reiter wrote:
>>
>>
>> The loop is there because other utun devices can exist. On my machine
>> there utun0-3 happen to be consumed by other VPN tunnels. OpenVPN
>> allocates utun interfaces as does OS X built-in VPN client service and
>> Back to My Mac.
>
> Right. Your loop tries with sc.sc_unit=1 to open 'utun0', with
> sc.sc_unit=2 to open 'utun1', and eventually succeeds with sc.sc_unit=5
> to open 'utun4'.
>
> My version sets sc.sc_unit=0 and the kernel just gives me back the first
> unused device without me having to keep trying.

Hi David,

I discovered a problem with using sc.sc_unit = 0 to allocate the next available utun, it’s unreliable.

Sometimes it re-allocates utun0. Sometimes it allocates a utun that causes a kernel panic as soon as it is accessed.

It seems to be fine until after resuming from sleep (with a Thunderbolt-connected gigabit ethernet). I went back to using the loop and I am not longer reproducing the issue.

https://github.com/breiter/vpnc/commit/20d794085e92f9aa87cd921bb925a944a97557b2

Cheers.
//brian
_______________________________________________
vpnc-devel mailing list
vpnc-devel@unix-ag.uni-kl.de
https://lists.unix-ag.uni-kl.de/mailman/listinfo/vpnc-devel
http://www.unix-ag.uni-kl.de/~massar/vpnc/
Re: Patch for utun support for xnu/darwin (OS X) [ In reply to ]
On Sat, 2014-11-01 at 12:55 +0200, Brian Reiter wrote:
>
> Sometimes it re-allocates utun0. Sometimes it allocates a utun that
> causes a kernel panic as soon as it is accessed.

Thanks for the warning. I've fixed it thus:
http://git.infradead.org/users/dwmw2/openconnect.git/commitdiff/5b9c521a92

Has the problem been reported? Maybe it would be nice if in the future
we could go back to using sc_unit=0 if we're running on a new enough
kernel...

--
dwmw2
Re: Patch for utun support for xnu/darwin (OS X) [ In reply to ]
On Nov 4, 2014, at 2:50 AM, David Woodhouse <dwmw2@infradead.org> wrote:

Has the problem been reported?

I just submitted this issue as radar 18878714 to Apple.