From fa37039c3bbadf902cef87ae92c9c624f0991942 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20S=C3=BC=C3=9F?= <dominik.suess@outlook.at>
Date: Tue, 13 Feb 2018 16:43:07 +0100
Subject: [PATCH] Reimplemented bind_rtmgrp in pure Go
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Getting rid of the Cgo dependency for listing on netlink.

Ported original patch from "syscall" to "golang.org/x/sys/unix".

Signed-off-by: Dominik Süß <dominik.suess@outlook.at>
Co-Authored-By: Mathias Hall-Andersen <mathias@hall-andersen.dk>
---
 tun_linux.go | 70 ++++++++++++++++++++++------------------------------
 1 file changed, 30 insertions(+), 40 deletions(-)

diff --git a/tun_linux.go b/tun_linux.go
index daa2462..b35e08d 100644
--- a/tun_linux.go
+++ b/tun_linux.go
@@ -16,38 +16,9 @@ import (
 	"unsafe"
 )
 
-// #include <string.h>
-// #include <unistd.h>
-// #include <net/if.h>
-// #include <netinet/in.h>
-// #include <linux/netlink.h>
-// #include <linux/rtnetlink.h>
-//
-// /* Creates a netlink socket
-//  * listening to the RTMGRP_LINK multicast group
-//  */
-//
-// int bind_rtmgrp() {
-//   int nl_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
-//   if (nl_sock < 0)
-//     return -1;
-//
-//	 struct sockaddr_nl addr;
-//   memset ((void *) &addr, 0, sizeof (addr));
-//   addr.nl_family = AF_NETLINK;
-//   addr.nl_pid = getpid ();
-//   addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;
-//
-//   if (bind(nl_sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
-//     return -1;
-//
-//   return nl_sock;
-// }
-import "C"
-
 const (
-	CloneDevicePath = "/dev/net/tun"
-	IFReqSize       = unix.IFNAMSIZ + 64
+	cloneDevicePath = "/dev/net/tun"
+	ifReqSize       = unix.IFNAMSIZ + 64
 )
 
 type NativeTun struct {
@@ -58,6 +29,26 @@ type NativeTun struct {
 	events chan TUNEvent // device related events
 }
 
+func toRTMGRP(sc uint) uint {
+	return 1 << (sc - 1)
+}
+
+func (tun *NativeTun) bindRTMGRP() (int, error) {
+	groups := toRTMGRP(unix.RTNLGRP_LINK)
+	groups |= toRTMGRP(unix.RTNLGRP_IPV4_IFADDR)
+	groups |= toRTMGRP(unix.RTNLGRP_IPV6_IFADDR)
+	sock, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW, unix.NETLINK_ROUTE)
+	if err != nil {
+		return 0, err
+	}
+	saddr := &unix.SockaddrNetlink{
+		Family: unix.AF_NETLINK,
+		Pid:    uint32(os.Getpid()),
+		Groups: uint32(groups),
+	}
+	return sock, unix.Bind(sock, saddr)
+}
+
 func (tun *NativeTun) File() *os.File {
 	return tun.fd
 }
@@ -81,9 +72,8 @@ func (tun *NativeTun) RoutineHackListener() {
 }
 
 func (tun *NativeTun) RoutineNetlinkListener() {
-
-	sock := int(C.bind_rtmgrp())
-	if sock < 0 {
+	sock, err := tun.bindRTMGRP()
+	if err != nil {
 		tun.errors <- errors.New("Failed to create netlink event listener")
 		return
 	}
@@ -159,7 +149,7 @@ func getIFIndex(name string) (int32, error) {
 
 	defer unix.Close(fd)
 
-	var ifr [IFReqSize]byte
+	var ifr [ifReqSize]byte
 	copy(ifr[:], name)
 	_, _, errno := unix.Syscall(
 		unix.SYS_IOCTL,
@@ -194,7 +184,7 @@ func (tun *NativeTun) setMTU(n int) error {
 
 	// do ioctl call
 
-	var ifr [IFReqSize]byte
+	var ifr [ifReqSize]byte
 	copy(ifr[:], tun.name)
 	binary.LittleEndian.PutUint32(ifr[16:20], uint32(n))
 	_, _, errno := unix.Syscall(
@@ -229,7 +219,7 @@ func (tun *NativeTun) MTU() (int, error) {
 
 	// do ioctl call
 
-	var ifr [IFReqSize]byte
+	var ifr [ifReqSize]byte
 	copy(ifr[:], tun.name)
 	_, _, errno := unix.Syscall(
 		unix.SYS_IOCTL,
@@ -324,15 +314,15 @@ func CreateTUN(name string) (TUNDevice, error) {
 
 	// open clone device
 
-	fd, err := os.OpenFile(CloneDevicePath, os.O_RDWR, 0)
+	fd, err := os.OpenFile(cloneDevicePath, os.O_RDWR, 0)
 	if err != nil {
 		return nil, err
 	}
 
 	// create new device
 
-	var ifr [IFReqSize]byte
-	var flags uint16 = unix.IFF_TUN // | unix.IFF_NO_PI
+	var ifr [ifReqSize]byte
+	var flags uint16 = unix.IFF_TUN // | unix.IFF_NO_PI (disabled for TUN status hack)
 	nameBytes := []byte(name)
 	if len(nameBytes) >= unix.IFNAMSIZ {
 		return nil, errors.New("Interface name too long")