From 5b078e866d9182860b6492132ef566c5dd4da0b4 Mon Sep 17 00:00:00 2001
From: Michael Tuexen <tuexen@fh-muenster.de>
Date: Sat, 21 Apr 2018 21:34:48 +0200
Subject: [PATCH] On MacOS use utun devices.

This requires the tunneling devices to be used in P2P mode. So
pass the gateway address around.
This is not working:
* Can't configure IPv6 addresses on utun devices.
* Can't use libpcap on utun devices
* Can't use libpcap as currently being used (taking the direction
  into account.
---
 gtests/net/packetdrill/net_utils.c          | 72 +++++++++++--------
 gtests/net/packetdrill/net_utils.h          | 14 ++--
 gtests/net/packetdrill/netdev.c             | 78 ++++++++++++++++++---
 gtests/net/packetdrill/wire_client_netdev.c | 10 +--
 gtests/net/packetdrill/wire_server_netdev.c |  3 +-
 5 files changed, 121 insertions(+), 56 deletions(-)

diff --git a/gtests/net/packetdrill/net_utils.c b/gtests/net/packetdrill/net_utils.c
index 1b59d645..06a14e43 100644
--- a/gtests/net/packetdrill/net_utils.c
+++ b/gtests/net/packetdrill/net_utils.c
@@ -43,13 +43,16 @@ static void verbose_system(const char *command)
 
 /* Configure a local IPv4 address and netmask for the device */
 static void net_add_ipv4_address(const char *dev_name,
-				 const struct ip_address *ip,
-				 int prefix_len)
+				 const struct ip_address *local_ip,
+				 int prefix_len,
+				 const struct ip_address *gateway_ip)
 {
 	char *command = NULL;
-	char ip_string[ADDR_STR_LEN];
+	char local_ip_string[ADDR_STR_LEN];
+	char gateway_ip_string[ADDR_STR_LEN];
 
-	ip_to_string(ip, ip_string);
+	ip_to_string(local_ip, local_ip_string);
+	ip_to_string(gateway_ip, gateway_ip_string);
 
 #ifdef linux
 	asprintf(&command, "ip addr add %s/%d dev %s > /dev/null 2>&1",
@@ -57,33 +60,41 @@ static void net_add_ipv4_address(const char *dev_name,
 #endif
 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
 	asprintf(&command, "/sbin/ifconfig %s %s/%d alias",
-		 dev_name, ip_string, prefix_len);
+		 dev_name, local_ip_string, prefix_len);
 #endif /* defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) */
-
+#if defined(__APPLE__)
+	asprintf(&command, "/sbin/ifconfig %s %s/%d %s alias",
+		 dev_name, local_ip_string, prefix_len, gateway_ip_string);
+#endif /* defined(__APPLE__) */
 	verbose_system(command);
 	free(command);
 }
 
 /* Configure a local IPv6 address and prefix length for the device */
 static void net_add_ipv6_address(const char *dev_name,
-				 const struct ip_address *ip,
-				 int prefix_len)
+				 const struct ip_address *local_ip,
+				 int prefix_len,
+				 const struct ip_address *gateway_ip)
 {
 	char *command = NULL;
-	char ip_string[ADDR_STR_LEN];
+	char local_ip_string[ADDR_STR_LEN];
+	char gateway_ip_string[ADDR_STR_LEN];
 
-	ip_to_string(ip, ip_string);
+	ip_to_string(local_ip, local_ip_string);
+	ip_to_string(gateway_ip, gateway_ip_string);
 
 #ifdef linux
-
 	asprintf(&command, "ip addr add %s/%d dev %s > /dev/null 2>&1",
 		 ip_string, prefix_len, dev_name);
 #endif
 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
-
 	asprintf(&command, "/sbin/ifconfig %s inet6 %s/%d",
-		 dev_name, ip_string, prefix_len);
+		 dev_name, local_ip_string, prefix_len);
 #endif /* defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) */
+#if defined(__APPLE__)
+	asprintf(&command, "/sbin/ifconfig %s inet6 %s/%d %s",
+		 dev_name, local_ip_string, prefix_len, gateway_ip_string);
+#endif /* defined(__APPLE__) */
 
 	verbose_system(command);
 	free(command);
@@ -97,21 +108,22 @@ static void net_add_ipv6_address(const char *dev_name,
 	if (!strstr(dev_name, "tun"))
 		sleep(2);
 #endif
-#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
 	sleep(3);
 #endif
 }
 
-void net_add_dev_address(const char *dev_name,
-			 const struct ip_address *ip,
-			 int prefix_len)
+static void net_add_dev_address(const char *dev_name,
+				const struct ip_address *local_ip,
+				int prefix_len,
+				const struct ip_address *gateway_ip)
 {
-	switch (ip->address_family) {
+	switch (local_ip->address_family) {
 	case AF_INET:
-		net_add_ipv4_address(dev_name, ip, prefix_len);
+		net_add_ipv4_address(dev_name, local_ip, prefix_len, gateway_ip);
 		break;
 	case AF_INET6:
-		net_add_ipv6_address(dev_name, ip, prefix_len);
+		net_add_ipv6_address(dev_name, local_ip, prefix_len, gateway_ip);
 		break;
 	default:
 		assert(!"bad family");
@@ -127,18 +139,15 @@ void net_del_dev_address(const char *dev_name,
 	char ip_string[ADDR_STR_LEN];
 
 	ip_to_string(ip, ip_string);
-
-#ifdef linux
+#if defined(linux)
 	asprintf(&command, "ip addr del %s/%d dev %s > /dev/null 2>&1",
 		 ip_string, prefix_len, dev_name);
-#endif
-#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+#else
 	asprintf(&command, "/sbin/ifconfig %s %s %s/%d -alias",
 		 dev_name,
 		 ip->address_family ==  AF_INET6 ? "inet6" : "",
 		 ip_string, prefix_len);
-#endif /* defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) */
-
+#endif /* defined(linux) */
 	verbose_system(command);
 	free(command);
 }
@@ -152,12 +161,13 @@ void net_del_dev_address(const char *dev_name,
  * and add it on the newly-requested device.
  */
 void net_setup_dev_address(const char *dev_name,
-			   const struct ip_address *ip,
-			   int prefix_len)
+			   const struct ip_address *local_ip,
+			   int prefix_len,
+			   const struct ip_address *gateway_ip)
 {
 	char cur_dev_name[IFNAMSIZ];
 
-	bool found = get_ip_device(ip, cur_dev_name);
+	bool found = get_ip_device(local_ip, cur_dev_name);
 
 	DEBUGP("net_setup_dev_address: found: %d\n", found);
 
@@ -167,6 +177,6 @@ void net_setup_dev_address(const char *dev_name,
 	}
 
 	if (found)
-		net_del_dev_address(cur_dev_name, ip, prefix_len);
-	net_add_dev_address(dev_name, ip, prefix_len);
+		net_del_dev_address(cur_dev_name, local_ip, prefix_len);
+	net_add_dev_address(dev_name, local_ip, prefix_len, gateway_ip);
 }
diff --git a/gtests/net/packetdrill/net_utils.h b/gtests/net/packetdrill/net_utils.h
index bdc10096..5bb00588 100644
--- a/gtests/net/packetdrill/net_utils.h
+++ b/gtests/net/packetdrill/net_utils.h
@@ -30,13 +30,6 @@
 
 #include "ip_address.h"
 
-/* Add the given IP address, with the given subnet/prefix length,
- * to the given device.
- */
-extern void net_add_dev_address(const char *dev_name,
-				const struct ip_address *ip,
-				int prefix_len);
-
 /* Delete the given IP address, with the given subnet/prefix length,
  * from the given device.
  */
@@ -48,9 +41,12 @@ extern void net_del_dev_address(const char *dev_name,
  * is already on the given device. If so, return without doing
  * anything.  If not, delete it from any device it's currently on, and
  * add it to the given network device.
+ * On some platforms P2P devices are used, so also provide the gateway
+ * address.
  */
 extern void net_setup_dev_address(const char *dev_name,
-				  const struct ip_address *ip,
-				  int prefix_len);
+				  const struct ip_address *local_ip,
+				  int prefix_len,
+				  const struct ip_address *gateway_ip);
 
 #endif /* __NET_UTILS_H__ */
diff --git a/gtests/net/packetdrill/netdev.c b/gtests/net/packetdrill/netdev.c
index 6dc334d8..d8bcd366 100644
--- a/gtests/net/packetdrill/netdev.c
+++ b/gtests/net/packetdrill/netdev.c
@@ -44,7 +44,14 @@
 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
 #include <net/if_tun.h>
 #endif /* defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) */
-
+#if defined(__APPLE__)
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_utun.h>
+#include <sys/kern_control.h>
+#include <sys/kern_event.h>
+#endif
 #include "ip.h"
 #include "ipv6.h"
 #include "logging.h"
@@ -119,6 +126,52 @@ static void check_remote_address(struct config *config,
 }
 
 /* Create a tun device for the lifetime of this test. */
+#if defined(__APPLE__)
+static void create_device(struct config *config, struct local_netdev *netdev)
+{
+	struct sockaddr_ctl addr;
+	struct ctl_info info;
+	char name[IFNAMSIZ];
+	char *command;
+	socklen_t len;
+	int tun_fd;
+
+	tun_fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
+	if (tun_fd < 0) {
+		die_perror("open utun device");
+	}
+	memset(&info, 0, sizeof(struct ctl_info));
+	strncpy(info.ctl_name, UTUN_CONTROL_NAME, MAX_KCTL_NAME);
+	if (ioctl(tun_fd, CTLIOCGINFO, &info) < 0) {
+		die_perror("open utun device");
+	}
+	addr.sc_len = sizeof(struct sockaddr_ctl);
+	addr.sc_family = AF_SYSTEM;
+	addr.ss_sysaddr = AF_SYS_CONTROL;
+	addr.sc_id = info.ctl_id;
+	addr.sc_unit = 0;
+	if (connect(tun_fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_ctl)) < 0) {
+		die_perror("open utun device");
+	}
+	netdev->tun_fd = tun_fd;
+	len = IFNAMSIZ;
+	if (getsockopt(tun_fd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, name, &len) < 0) {
+		die_perror("open utun device");
+	}
+	netdev->name = strdup(name);
+	DEBUGP("utun name: '%s'\n", netdev->name);
+	netdev->index = if_nametoindex(netdev->name);
+	if (netdev->index == 0)
+		die_perror("if_nametoindex");
+	DEBUGP("utun index: '%d'\n", netdev->index);
+	if (config->mtu != TUN_DRIVER_DEFAULT_MTU) {
+		asprintf(&command, "ifconfig %s mtu %d", netdev->name, config->mtu);
+		if (system(command) < 0)
+			die("Error executing %s\n", command);
+		free(command);
+	}
+}
+#else
 static void create_device(struct config *config, struct local_netdev *netdev)
 {
 	/* Open the tun device, which "clones" it for our purposes. */
@@ -252,6 +305,7 @@ static void create_device(struct config *config, struct local_netdev *netdev)
 	if (netdev->ipv4_control_fd < 0)
 		die_perror("opening AF_INET, SOCK_DGRAM, IPPROTO_IP socket");
 }
+#endif
 
 /* Set the offload flags to be like a typical ethernet device */
 static void set_device_offload_flags(struct local_netdev *netdev)
@@ -267,6 +321,7 @@ static void set_device_offload_flags(struct local_netdev *netdev)
 #endif
 }
 
+#if !defined(__APPLE__)
 /* Bring up the device */
 static void bring_up_device(struct local_netdev *netdev)
 {
@@ -284,13 +339,14 @@ static void bring_up_device(struct local_netdev *netdev)
 	if (ioctl(netdev->ipv4_control_fd, SIOCSIFFLAGS, &ifr) < 0)
 		die_perror("SIOCSIFFLAGS");
 }
+#endif
 
 /* Route traffic destined for our remote IP through this device */
 static void route_traffic_to_device(struct config *config,
 				    struct local_netdev *netdev)
 {
 	char *route_command = NULL;
-#ifdef linux
+#if defined(linux)
 	asprintf(&route_command,
 		 "ip route del %s > /dev/null 2>&1 ; "
 		 "ip route add %s dev %s via %s > /dev/null 2>&1",
@@ -298,8 +354,7 @@ static void route_traffic_to_device(struct config *config,
 		 config->live_remote_prefix_string,
 		 netdev->name,
 		 config->live_gateway_ip_string);
-#endif
-#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+#else
 	if (config->wire_protocol == AF_INET) {
 		asprintf(&route_command,
 			 "route delete %s > /dev/null 2>&1 ; "
@@ -317,7 +372,7 @@ static void route_traffic_to_device(struct config *config,
 	} else {
 		assert(!"bad wire protocol");
 	}
-#endif /* defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) */
+#endif /* defined(linux) */
 	int result = system(route_command);
 	if ((result == -1) || (WEXITSTATUS(result) != 0)) {
 		die("error executing route command '%s'\n",
@@ -337,11 +392,14 @@ struct netdev *local_netdev_new(struct config *config)
 	check_remote_address(config, netdev);
 	create_device(config, netdev);
 	set_device_offload_flags(netdev);
+#if !defined(__APPLE__)
 	bring_up_device(netdev);
+#endif
 
 	net_setup_dev_address(netdev->name,
 			      &config->live_local_ip,
-			      config->live_prefix_len);
+			      config->live_prefix_len,
+			      &config->live_gateway_ip);
 
 	route_traffic_to_device(config, netdev);
 	netdev->psock = packet_socket_new(netdev->name);
@@ -379,7 +437,7 @@ static void local_netdev_free(struct netdev *a_netdev)
 	free(netdev);
 }
 
-#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
 /* According to `man 4 tun` on OpenBSD: "Each packet read or written
  * is prefixed with a tunnel header consisting of a 4-byte network
  * byte order integer containing the address family in the case of
@@ -400,7 +458,7 @@ static void bsd_tun_write(struct local_netdev *netdev,
 	if (writev(netdev->tun_fd, vector, ARRAY_SIZE(vector)) < 0)
 		die_perror("BSD tun write()");
 }
-#endif /* defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) */
+#endif /* defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) */
 
 #ifdef linux
 static void linux_tun_write(struct local_netdev *netdev,
@@ -425,9 +483,9 @@ static int local_netdev_send(struct netdev *a_netdev,
 
 	DEBUGP("local_netdev_send\n");
 
-#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
 	bsd_tun_write(netdev, packet);
-#endif /* defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) */
+#endif /* defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) */
 
 #ifdef linux
 	linux_tun_write(netdev, packet);
diff --git a/gtests/net/packetdrill/wire_client_netdev.c b/gtests/net/packetdrill/wire_client_netdev.c
index 049f9821..f7f47b9b 100644
--- a/gtests/net/packetdrill/wire_client_netdev.c
+++ b/gtests/net/packetdrill/wire_client_netdev.c
@@ -65,7 +65,7 @@ static void route_traffic_to_wire_server(struct config *config,
 					 struct wire_client_netdev *netdev)
 {
 	char *route_command = NULL;
-#ifdef linux
+#if defined(linux)
 	asprintf(&route_command,
 		 "ip %s route del %s > /dev/null 2>&1 ; "
 		 "ip %s route add %s dev %s via %s > /dev/null 2>&1",
@@ -75,8 +75,7 @@ static void route_traffic_to_wire_server(struct config *config,
 		 config->live_remote_prefix_string,
 		 netdev->name,
 		 config->live_gateway_ip_string);
-#endif
-#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+#else
 	if (config->wire_protocol == AF_INET) {
 		asprintf(&route_command,
 			 "route delete %s > /dev/null 2>&1 ; "
@@ -94,7 +93,7 @@ static void route_traffic_to_wire_server(struct config *config,
 	} else {
 		assert(!"bad wire protocol");
 	}
-#endif /* defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) */
+#endif /* defined(linux) */
 
 	/* We intentionally ignore failures and output to stderr,
 	 * since they can happen if there is no previously existing
@@ -121,7 +120,8 @@ struct netdev *wire_client_netdev_new(struct config *config)
 	/* Add the client live local IP to our NIC, so we can send/receive */
 	net_setup_dev_address(netdev->name,
 			      &config->live_local_ip,
-			      config->live_prefix_len);
+			      config->live_prefix_len,
+			      &config->live_gateway_ip);
 
 	route_traffic_to_wire_server(config, netdev);
 
diff --git a/gtests/net/packetdrill/wire_server_netdev.c b/gtests/net/packetdrill/wire_server_netdev.c
index 02905614..bf17f204 100644
--- a/gtests/net/packetdrill/wire_server_netdev.c
+++ b/gtests/net/packetdrill/wire_server_netdev.c
@@ -126,7 +126,8 @@ struct netdev *wire_server_netdev_new(
 	 */
 	net_setup_dev_address(netdev->name,
 			      &config->live_gateway_ip,
-			      config->live_prefix_len);
+			      config->live_prefix_len,
+			      &config->live_gateway_ip);
 
 	netdev->psock = packet_socket_new(netdev->name);
 
-- 
GitLab