diff --git a/gtests/net/packetdrill/config.c b/gtests/net/packetdrill/config.c
index e79061c40be49f10ca9228845a027540f77b21dc..676b2d189ae524edaeafb813644a164c9164aae1 100644
--- a/gtests/net/packetdrill/config.c
+++ b/gtests/net/packetdrill/config.c
@@ -61,6 +61,9 @@ enum option_codes {
 	OPT_VERBOSE = 'v',	/* our only single-letter option */
 	OPT_DEBUG,
 	OPT_UDP_ENCAPS,
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+	OPT_TUN_DEV,
+#endif
 };
 
 /* Specification of command line options for getopt_long(). */
@@ -91,6 +94,9 @@ struct option options[] = {
 	{ "verbose",		.has_arg = false, NULL, OPT_VERBOSE },
 	{ "debug",		.has_arg = false, NULL, OPT_DEBUG },
 	{ "udp_encapsulation",	.has_arg = true,  NULL, OPT_UDP_ENCAPS },
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+	{ "tun_dev",		.has_arg = true,  NULL, OPT_TUN_DEV },
+#endif
 	{ NULL },
 };
 
@@ -123,6 +129,9 @@ void show_usage(void)
 		"\t[--verbose|-v]\n"
 		"\t[--debug] * requires compilation with DEBUG *\n"
 		"\t[--udp_encapsulation=[sctp,tcp]]\n"
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+		"\t[--tun_dev=<tun_dev_name>]\n"
+#endif
 		"\tscript_path ...\n");
 }
 
@@ -485,6 +494,11 @@ static void process_option(int opt, char *optarg, struct config *config,
 		else
 			die("%s: bad --udp_encapsulation: %s\n", where, optarg);
 		break;
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+	case OPT_TUN_DEV:
+		config->tun_device = strdup(optarg);
+		break;
+#endif
 	default:
 		show_usage();
 		exit(EXIT_FAILURE);
diff --git a/gtests/net/packetdrill/config.h b/gtests/net/packetdrill/config.h
index d789826123469ef781d328898de7c2dcafbdbfc6..796887dcc49141b6f776edbfa9e79b5bd5c33b67 100644
--- a/gtests/net/packetdrill/config.h
+++ b/gtests/net/packetdrill/config.h
@@ -106,6 +106,11 @@ struct config {
 	struct ip_address wire_server_ip;  /* IP of on-the-wire server */
 	char *wire_server_ip_string;	   /* malloc-ed server IP string */
 	u16 wire_server_port;		   /* the port the server listens on */
+
+	/* For local testing using a tun interface. */
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+	char *tun_device;
+#endif
 };
 
 /* Top-level info about the invocation of a test script */
diff --git a/gtests/net/packetdrill/netdev.c b/gtests/net/packetdrill/netdev.c
index a8cb5175a554e0d0e2858b43d9e3c4744a28cd66..83f81964c5e884e20c6dfbfb7d7c3a90c5153ba4 100644
--- a/gtests/net/packetdrill/netdev.c
+++ b/gtests/net/packetdrill/netdev.c
@@ -88,9 +88,12 @@ static void cleanup_old_device(struct config *config,
 	int result;
 #endif
 
+	if (config->tun_device == NULL) {
+		return;
+	}
 	asprintf(&cleanup_command,
 		 "/sbin/ifconfig %s down delete > /dev/null 2>&1",
-		 TUN_DEV);
+		 config->tun_device);
 	DEBUGP("running: '%s'\n", cleanup_command);
 #ifdef DEBUG
 	result = system(cleanup_command);
@@ -119,16 +122,31 @@ static void create_device(struct config *config, struct local_netdev *netdev)
 {
 	/* Open the tun device, which "clones" it for our purposes. */
 	int tun_fd;
+	char *tun_path;
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+	struct stat buf;
+#endif
 
-	tun_fd = open(TUN_PATH, O_RDWR);
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+	if (config->tun_device != NULL) {
+		asprintf(&tun_path, "%s/%s", TUN_DIR, config->tun_device);
+	} else {
+		asprintf(&tun_path, "%s/%s", TUN_DIR, "tun");
+	}
+#endif
+#if defined(linux)
+	asprintf(&tun_path, "%s/%s", TUN_DIR, "tun");
+#endif
+	tun_fd = open(tun_path, O_RDWR);
 #if defined(__FreeBSD__)
 	if ((tun_fd < 0) && (errno == ENOENT)) {
 		if (system("kldload -q if_tun") < 0) {
 			die_perror("kldload -q if_tun");
 		}
-		tun_fd = open(TUN_PATH, O_RDWR);
+		tun_fd = open(tun_path, O_RDWR);
 	}
 #endif
+	free(tun_path);
 	if (tun_fd < 0) {
 		die_perror("open tun device");
 	}
@@ -153,8 +171,10 @@ static void create_device(struct config *config, struct local_netdev *netdev)
 	const int mode = IFF_BROADCAST | IFF_MULTICAST;
 	if (ioctl(netdev->tun_fd, TUNSIFMODE, &mode, sizeof(mode)) < 0)
 		die_perror("TUNSIFMODE");
-
-	netdev->name = strdup(TUN_DEV);
+	if (fstat(netdev->tun_fd, &buf) < 0) {
+		die_perror("fstat tun device");
+	}
+	netdev->name = strdup(devname(buf.st_rdev, S_IFCHR));
 #endif /* defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) */
 
 #if defined(__FreeBSD__) ||  defined(__NetBSD__)
@@ -328,8 +348,20 @@ static void local_netdev_free(struct netdev *a_netdev)
 
 	if (netdev->psock)
 		packet_socket_free(netdev->psock);
-	if (netdev->tun_fd >= 0)
+	if (netdev->tun_fd >= 0) {
 		close(netdev->tun_fd);
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+		if (netdev->name != NULL) {
+			char *cleanup_command = NULL;
+
+			asprintf(&cleanup_command,
+			         "/sbin/ifconfig %s destroy > /dev/null 2>&1",
+			         netdev->name);
+			system(cleanup_command);
+			free(cleanup_command);
+		}
+#endif
+	}
 	if (netdev->ipv4_control_fd >= 0)
 		close(netdev->ipv4_control_fd);
 	if (netdev->ipv6_control_fd >= 0)
diff --git a/gtests/net/packetdrill/platforms.h b/gtests/net/packetdrill/platforms.h
index 5ad852661837e27c31902b706f076ab96df56488..cf6fc9fb7680ac18e79413feabb5f9bb9af9d090 100644
--- a/gtests/net/packetdrill/platforms.h
+++ b/gtests/net/packetdrill/platforms.h
@@ -38,7 +38,7 @@
 #include <netinet/sctp.h>
 #define HAVE_OPEN_MEMSTREAM     1
 #define HAVE_FMEMOPEN           1
-#define TUN_PATH                "/dev/net/tun"
+#define TUN_DIR                 "/dev/net"
 #define HAVE_TCP_INFO           1
 
 #endif  /* linux */
@@ -54,8 +54,7 @@
 #include <netinet/udplite.h>
 #endif
 #define USE_LIBPCAP             1
-#define TUN_PATH                "/dev/tun0"
-#define TUN_DEV                 "tun0"
+#define TUN_DIR                 "/dev"
 #define HAVE_TCP_INFO           1
 #if (__FreeBSD_version < 1000000 && __FreeBSD_version > 902000) || __FreeBSD_version > 1000028
 #define HAVE_FMEMOPEN           1
@@ -63,7 +62,7 @@
 #include "fmemopen.h"
 #endif
 #if (__FreeBSD_version > 902000)
-#define HAVE_OPEN_MEMSTREAM	1
+#define HAVE_OPEN_MEMSTREAM     1
 #else
 #include "open_memstream.h"
 #endif
@@ -75,8 +74,7 @@
 #if defined(__OpenBSD__)
 
 #define USE_LIBPCAP             1
-#define TUN_PATH                "/dev/tun0"
-#define TUN_DEV                 "tun0"
+#define TUN_DIR                 "/dev"
 
 #define HAVE_TCP_INFO           0
 
@@ -92,8 +90,7 @@
 #if defined(__NetBSD__)
 
 #define USE_LIBPCAP             1
-#define TUN_PATH                "/dev/tun0"
-#define TUN_DEV                 "tun0"
+#define TUN_DIR                 "/dev"
 
 #define HAVE_TCP_INFO           0
 
diff --git a/gtests/net/packetdrill/run.c b/gtests/net/packetdrill/run.c
index 54f4ebbdd09d0c67ad960c46b17b7afb5d0ddf42..f5170a9725cbe7f5c81db5bd73af6731add98d90 100644
--- a/gtests/net/packetdrill/run.c
+++ b/gtests/net/packetdrill/run.c
@@ -513,9 +513,12 @@ static s64 schedule_start_time_usecs(void)
 }
 
 void signal_handler(int signal_number) {
-	if (state != NULL)
+	if (state != NULL) {
 		close_all_sockets(state);
-	
+		if (state->netdev != NULL) {
+			netdev_free(state->netdev);
+		}
+	}
 	die("Handled signal %d\n", signal_number);
 }
 
@@ -524,10 +527,16 @@ void run_script(struct config *config, struct script *script)
 	char *error = NULL;
 	struct netdev *netdev = NULL;
 	struct event *event = NULL;
-	
+
 	if (signal(SIGINT, signal_handler) == SIG_ERR) {
 		die("could not set up signal handler for SIGINT!");
 	}
+	if (signal(SIGTERM, signal_handler) == SIG_ERR) {
+		die("could not set up signal handler for SIGTERM!");
+	}
+	if (signal(SIGHUP, signal_handler) == SIG_ERR) {
+		die("could not set up signal handler for SIGHUP!");
+	}
 
 	DEBUGP("run_script: running script\n");