diff mbox series

[RFC] selinux-testsuite: Add TUN/TAP driver tests

Message ID 20191127094702.3418-1-richard_c_haines@btinternet.com (mailing list archive)
State Superseded
Headers show
Series [RFC] selinux-testsuite: Add TUN/TAP driver tests | expand

Commit Message

Richard Haines Nov. 27, 2019, 9:47 a.m. UTC
Test TUN/TAP tun_socket permissions.

I've made this an RFC patch as TAP supports adding BPF prog file
descriptors, therefore I've added a simple test that just checks that it
works. However, there does not seem to be a requirement to test any
additional SELinux functionality that the basic BPF tests do not
already cover. Any views !!!

Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
---
 defconfig                   |   4 ++
 policy/Makefile             |   6 +-
 policy/test_tap_bpf.te      |  30 +++++++++
 policy/test_tun_tap.te      | 117 ++++++++++++++++++++++++++++++++++
 tests/Makefile              |   4 ++
 tests/bpf/Makefile          |   2 +-
 tests/bpf/test              |  19 +++++-
 tests/tun_tap/.gitignore    |   3 +
 tests/tun_tap/Makefile      |  22 +++++++
 tests/tun_tap/tap_bpf.c     |  89 ++++++++++++++++++++++++++
 tests/tun_tap/test          |  88 +++++++++++++++++++++++++
 tests/tun_tap/tun_common.c  | 100 +++++++++++++++++++++++++++++
 tests/tun_tap/tun_common.h  |  22 +++++++
 tests/tun_tap/tun_relabel.c | 124 ++++++++++++++++++++++++++++++++++++
 tests/tun_tap/tun_tap.c     | 123 +++++++++++++++++++++++++++++++++++
 15 files changed, 749 insertions(+), 4 deletions(-)
 create mode 100644 policy/test_tap_bpf.te
 create mode 100644 policy/test_tun_tap.te
 create mode 100644 tests/tun_tap/.gitignore
 create mode 100644 tests/tun_tap/Makefile
 create mode 100644 tests/tun_tap/tap_bpf.c
 create mode 100755 tests/tun_tap/test
 create mode 100644 tests/tun_tap/tun_common.c
 create mode 100644 tests/tun_tap/tun_common.h
 create mode 100644 tests/tun_tap/tun_relabel.c
 create mode 100644 tests/tun_tap/tun_tap.c

Comments

Stephen Smalley Nov. 27, 2019, 2:59 p.m. UTC | #1
On 11/27/19 4:47 AM, Richard Haines wrote:
> Test TUN/TAP tun_socket permissions.
> 
> I've made this an RFC patch as TAP supports adding BPF prog file
> descriptors, therefore I've added a simple test that just checks that it
> works. However, there does not seem to be a requirement to test any
> additional SELinux functionality that the basic BPF tests do not
> already cover. Any views !!!

That seems reasonable to me.  Generally our focus is on ensuring test 
coverage of the LSM/SELinux hooks and SELinux kernel APIs, so anything 
that is already covered by an existing test doesn't need to be repeated. 
  In the case of tun/tap, likely only the tun_socket checks themselves 
are unique to these tests.

I would prioritize on 1) ensuring that we have test coverage of new 
security hooks, new functionality within existing hooks, or new SELinux 
kernel APIs as they get added to the kernel, e.g. the perf permission 
checks (github issue #71), the socketpair peer labeling support (#63), 
etc, and then 2) gradually expanding our test coverage of existing 
security hooks and kernel APIs to ensure that future kernels do not 
regress, e.g. it would have been nice to have had explicit tests for 
context mounts as per issue #20 to ensure that we didn't regress during 
the vfs new mount API overhaul (we happen to exercise context mounts as 
part of overlay testing and as part of binderfs testing but not as a 
separate test, not in a comprehensive way, and not for anything other 
than overlay mounts), and likewise for many of the tests identified as 
open github issues.

I haven't looked too closely at this yet, but I did have one minor 
comment on your test policy below.

<snip>
> diff --git a/policy/test_tap_bpf.te b/policy/test_tap_bpf.te
> new file mode 100644
> index 0000000..f921a5a
> --- /dev/null
> +++ b/policy/test_tap_bpf.te
> @@ -0,0 +1,30 @@
> +#
> +########### Test TAP/BPF - 'tun_socket' #################
> +#
> +attribute tapbpfdomain;
> +
> +# For CONFIG_TUN=m
> +kernel_request_load_module(tapbpfdomain)
> +
> +gen_require(`
> +	type tun_tap_device_t;
> +')

Any time you find yourself needing a require, look to see if there is a 
refpolicy interface you could use instead that would provide that 
require and the necessary rules.  In this case, I think you could have 
used corenet_rw_tun_tap_dev().
Richard Haines Nov. 28, 2019, 12:53 p.m. UTC | #2
On Wed, 2019-11-27 at 09:59 -0500, Stephen Smalley wrote:
> On 11/27/19 4:47 AM, Richard Haines wrote:
> > Test TUN/TAP tun_socket permissions.
> > 
> > I've made this an RFC patch as TAP supports adding BPF prog file
> > descriptors, therefore I've added a simple test that just checks
> > that it
> > works. However, there does not seem to be a requirement to test any
> > additional SELinux functionality that the basic BPF tests do not
> > already cover. Any views !!!
> 
> That seems reasonable to me.  Generally our focus is on ensuring
> test 
> coverage of the LSM/SELinux hooks and SELinux kernel APIs, so
> anything 
> that is already covered by an existing test doesn't need to be
> repeated. 
>   In the case of tun/tap, likely only the tun_socket checks
> themselves 
> are unique to these tests.

I'll remove the BPF check and send an update.

> 
> I would prioritize on 1) ensuring that we have test coverage of new 
> security hooks, new functionality within existing hooks, or new
> SELinux 
> kernel APIs as they get added to the kernel, e.g. the perf
> permission 
> checks (github issue #71), the socketpair peer labeling support
> (#63), 
> etc, and then 2) gradually expanding our test coverage of existing 
> security hooks and kernel APIs to ensure that future kernels do not 
> regress, e.g. it would have been nice to have had explicit tests for 
> context mounts as per issue #20 to ensure that we didn't regress
> during 
> the vfs new mount API overhaul (we happen to exercise context mounts
> as 
> part of overlay testing and as part of binderfs testing but not as a 
> separate test, not in a comprehensive way, and not for anything
> other 
> than overlay mounts), and likewise for many of the tests identified
> as 
> open github issues.

I'll have a go at the perf checks next as I guess it will make it to
5.5 unlike the keys-acl (that I did tests for). I also have the simple
'watch_key' test that I'll send once in 5.5 (if it makes it).

> 
> I haven't looked too closely at this yet, but I did have one minor 
> comment on your test policy below.

I'll change this (I try to avoid interfaces in tests as you need to
lookup what they really contain and it may be more than you want, plus
just plain allow rules are much clearer (but that's just me))
> 
> <snip>
> > diff --git a/policy/test_tap_bpf.te b/policy/test_tap_bpf.te
> > new file mode 100644
> > index 0000000..f921a5a
> > --- /dev/null
> > +++ b/policy/test_tap_bpf.te
> > @@ -0,0 +1,30 @@
> > +#
> > +########### Test TAP/BPF - 'tun_socket' #################
> > +#
> > +attribute tapbpfdomain;
> > +
> > +# For CONFIG_TUN=m
> > +kernel_request_load_module(tapbpfdomain)
> > +
> > +gen_require(`
> > +	type tun_tap_device_t;
> > +')
> 
> Any time you find yourself needing a require, look to see if there is
> a 
> refpolicy interface you could use instead that would provide that 
> require and the necessary rules.  In this case, I think you could
> have 
> used corenet_rw_tun_tap_dev().
Stephen Smalley Dec. 2, 2019, 2:20 p.m. UTC | #3
On 11/28/19 7:53 AM, Richard Haines wrote:
> I'll change this (I try to avoid interfaces in tests as you need to
> lookup what they really contain and it may be more than you want, plus
> just plain allow rules are much clearer (but that's just me))

I'd agree if the interface is a poor match / overly broad but I think in 
this case it works well.  I am sympathetic to the view that refpolicy 
interfaces are harder to use than raw policy rules (hence the almost 
complete lack of interfaces aside from common macros in the Android 
policy), but the one advantage they should carry is greater portability 
across refpolicy versions since they should automatically pick up 
changes in the base policy (e.g. if a type is renamed or split or 
coalesced, if a new permission besides the one being tested is 
introduced and needed for the abstract operation, etc).  And if/when we 
transition refpolicy to a truly higher level policy language on top of 
CIL, then I would anticipate them providing greater benefits as first 
class abstractions rather than mere m4 macros. But I guess only time 
will tell...
diff mbox series

Patch

diff --git a/defconfig b/defconfig
index 0574f1d..abf8df4 100644
--- a/defconfig
+++ b/defconfig
@@ -78,3 +78,7 @@  CONFIG_KEY_DH_OPERATIONS=y
 # Test key management socket.
 # This is not required for SELinux operation itself.
 CONFIG_NET_KEY=m
+
+# Test TUN/TAP driver support.
+# This is not required for SELinux operation itself.
+CONFIG_TUN=m
diff --git a/policy/Makefile b/policy/Makefile
index 57cc172..ea50ef2 100644
--- a/policy/Makefile
+++ b/policy/Makefile
@@ -82,7 +82,7 @@  TARGETS += test_sctp.te
 endif
 
 ifeq ($(shell grep -q bpf $(POLDEV)/include/support/all_perms.spt && echo true),true)
-TARGETS += test_bpf.te test_fdreceive_bpf.te test_binder_bpf.te
+TARGETS += test_bpf.te test_fdreceive_bpf.te test_binder_bpf.te test_tap_bpf.te
 endif
 
 ifeq ($(shell grep -q all_key_perms $(POLDEV)/include/support/all_perms.spt && echo true),true)
@@ -97,6 +97,10 @@  ifeq ($(shell grep -q key_socket $(POLDEV)/include/support/all_perms.spt && echo
 TARGETS += test_key_socket.te
 endif
 
+ifeq ($(shell grep -q tun_socket $(POLDEV)/include/support/all_perms.spt && echo true),true)
+TARGETS += test_tun_tap.te
+endif
+
 ifeq (x$(DISTRO),$(filter x$(DISTRO),xRHEL4 xRHEL5 xRHEL6))
 TARGETS:=$(filter-out test_overlayfs.te test_mqueue.te test_ibpkey.te, $(TARGETS))
 endif
diff --git a/policy/test_tap_bpf.te b/policy/test_tap_bpf.te
new file mode 100644
index 0000000..f921a5a
--- /dev/null
+++ b/policy/test_tap_bpf.te
@@ -0,0 +1,30 @@ 
+#
+########### Test TAP/BPF - 'tun_socket' #################
+#
+attribute tapbpfdomain;
+
+# For CONFIG_TUN=m
+kernel_request_load_module(tapbpfdomain)
+
+gen_require(`
+	type tun_tap_device_t;
+')
+
+type test_tap_bpf_t;
+domain_type(test_tap_bpf_t)
+unconfined_runs_test(test_tap_bpf_t)
+typeattribute test_tap_bpf_t testdomain;
+typeattribute test_tap_bpf_t tapbpfdomain;
+
+# tun_socket rules:
+allow test_tap_bpf_t self:capability { net_admin sys_resource };
+allow test_tap_bpf_t tun_tap_device_t:chr_file { open read write ioctl };
+allow test_tap_bpf_t self:tun_socket { create };
+allow test_tap_bpf_t self:process { setrlimit };
+allow test_tap_bpf_t self:bpf { prog_load prog_run };
+
+#
+########### Allow these domains to be entered from sysadm domain ############
+#
+miscfiles_domain_entry_test_files(tapbpfdomain)
+userdom_sysadm_entry_spec_domtrans_to(tapbpfdomain)
diff --git a/policy/test_tun_tap.te b/policy/test_tun_tap.te
new file mode 100644
index 0000000..a898372
--- /dev/null
+++ b/policy/test_tun_tap.te
@@ -0,0 +1,117 @@ 
+#
+########### Test TUN/TAP device driver support - 'tun_socket' ##############
+#
+attribute tuntapdomain;
+
+# For CONFIG_TUN=m
+kernel_request_load_module(tuntapdomain)
+
+gen_require(`
+	type tun_tap_device_t;
+')
+
+type test_tun_tap_t;
+domain_type(test_tun_tap_t)
+unconfined_runs_test(test_tun_tap_t)
+typeattribute test_tun_tap_t testdomain;
+typeattribute test_tun_tap_t tuntapdomain;
+
+# tun_socket rules:
+allow test_tun_tap_t self:capability { net_admin };
+allow test_tun_tap_t tun_tap_device_t:chr_file { open read write ioctl };
+allow test_tun_tap_t self:tun_socket { create attach_queue };
+
+################## Deny capability { net_admin } ##########################
+#
+# Note that when capability { net_admin } is removed for the test
+# there will not be an audit message in the log as the Fedora policy
+# is built with 'hide_broken_symptoms' that adds the following:
+#   dontaudit test_tun_tap_no_net_admin_t self:capability { net_admin sys_module };
+#
+type test_tun_tap_no_net_admin_t;
+domain_type(test_tun_tap_no_net_admin_t)
+unconfined_runs_test(test_tun_tap_no_net_admin_t)
+typeattribute test_tun_tap_no_net_admin_t testdomain;
+typeattribute test_tun_tap_no_net_admin_t tuntapdomain;
+
+neverallow test_tun_tap_t self:capability { net_admin };
+allow test_tun_tap_no_net_admin_t tun_tap_device_t:chr_file { open read write ioctl };
+allow test_tun_tap_no_net_admin_t self:tun_socket { create write read setopt };
+
+####################### Deny tun_socket { create } ##########################
+type test_tun_tap_no_create_t;
+domain_type(test_tun_tap_no_create_t)
+unconfined_runs_test(test_tun_tap_no_create_t)
+typeattribute test_tun_tap_no_create_t testdomain;
+typeattribute test_tun_tap_no_create_t tuntapdomain;
+
+allow test_tun_tap_no_create_t self:capability { net_admin };
+allow test_tun_tap_no_create_t tun_tap_device_t:chr_file { open read write ioctl };
+neverallow test_tun_tap_no_create_t self:tun_socket { create };
+
+################## Deny tun_socket { attach_queue } ########################
+type test_tun_tap_no_queue_t;
+domain_type(test_tun_tap_no_queue_t)
+unconfined_runs_test(test_tun_tap_no_queue_t)
+typeattribute test_tun_tap_no_queue_t testdomain;
+typeattribute test_tun_tap_no_queue_t tuntapdomain;
+
+allow test_tun_tap_no_queue_t self:capability { net_admin };
+allow test_tun_tap_no_queue_t tun_tap_device_t:chr_file { open read write ioctl };
+allow test_tun_tap_no_queue_t self:tun_socket { create };
+neverallow test_tun_tap_no_queue_t self:tun_socket { attach_queue };
+
+#
+############ Test relabelto/relabelfrom via new context #####################
+type test_newcon_tun_tap_t;
+domain_type(test_newcon_tun_tap_t)
+unconfined_runs_test(test_newcon_tun_tap_t)
+typeattribute test_newcon_tun_tap_t testdomain;
+typeattribute test_newcon_tun_tap_t keydomain;
+
+allow test_tun_tap_t test_newcon_tun_tap_t:process { dyntransition };
+allow test_newcon_tun_tap_t tun_tap_device_t:chr_file { open read write ioctl };
+allow test_newcon_tun_tap_t test_tun_tap_t:tun_socket { relabelfrom };
+allow test_newcon_tun_tap_t self:tun_socket { relabelto };
+
+# For error handling when switching back to original context:
+allow test_newcon_tun_tap_t test_tun_tap_t:fd use;
+allow test_newcon_tun_tap_t test_tun_tap_t:process dyntransition;
+
+############ Deny relabelto via new context #####################
+type test_newcon_no_to_tun_tap_t;
+domain_type(test_newcon_no_to_tun_tap_t)
+unconfined_runs_test(test_newcon_no_to_tun_tap_t)
+typeattribute test_newcon_no_to_tun_tap_t testdomain;
+typeattribute test_newcon_no_to_tun_tap_t keydomain;
+
+allow test_tun_tap_t test_newcon_no_to_tun_tap_t:process { dyntransition };
+allow test_tun_tap_t test_newcon_no_to_tun_tap_t:fd { use };
+allow test_newcon_no_to_tun_tap_t tun_tap_device_t:chr_file { open read write ioctl };
+allow test_newcon_no_to_tun_tap_t test_tun_tap_t:tun_socket { relabelfrom };
+neverallow test_newcon_no_to_tun_tap_t self:tun_socket { relabelto };
+
+# For switch back on error:
+allow test_newcon_no_to_tun_tap_t test_tun_tap_t:process { dyntransition };
+
+############ Deny relabelfrom via new context #####################
+type test_newcon_no_from_tun_tap_t;
+domain_type(test_newcon_no_from_tun_tap_t)
+unconfined_runs_test(test_newcon_no_from_tun_tap_t)
+typeattribute test_newcon_no_from_tun_tap_t testdomain;
+typeattribute test_newcon_no_from_tun_tap_t keydomain;
+
+allow test_tun_tap_t test_newcon_no_from_tun_tap_t:process { dyntransition };
+allow test_newcon_no_from_tun_tap_t tun_tap_device_t:chr_file { open read write ioctl };
+neverallow test_newcon_no_from_tun_tap_t test_tun_tap_t:tun_socket { relabelfrom };
+allow test_newcon_no_from_tun_tap_t self:tun_socket { relabelto };
+
+# For switch back on error:
+allow test_tun_tap_t test_newcon_no_from_tun_tap_t:fd { use };
+allow test_newcon_no_from_tun_tap_t test_tun_tap_t:process { dyntransition };
+
+#
+########### Allow these domains to be entered from sysadm domain ############
+#
+miscfiles_domain_entry_test_files(tuntapdomain)
+userdom_sysadm_entry_spec_domtrans_to(tuntapdomain)
diff --git a/tests/Makefile b/tests/Makefile
index cca6648..a97fd76 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -56,6 +56,10 @@  ifeq ($(shell grep -q key_socket $(POLDEV)/include/support/all_perms.spt && test
 SUBDIRS += key_socket
 endif
 
+ifeq ($(shell grep -q tun_socket $(POLDEV)/include/support/all_perms.spt && echo true),true)
+SUBDIRS += tun_tap
+endif
+
 ifeq ($(shell grep "^SELINUX_INFINIBAND_ENDPORT_TEST=" infiniband_endport/ibendport_test.conf | cut -d'=' -f 2),1)
 SUBDIRS += infiniband_endport
 endif
diff --git a/tests/bpf/Makefile b/tests/bpf/Makefile
index 6fb230d..3f55ab4 100644
--- a/tests/bpf/Makefile
+++ b/tests/bpf/Makefile
@@ -5,7 +5,7 @@  LDLIBS += -lselinux -lbpf
 # export so that BPF_ENABLED entries get built correctly on local build
 export CFLAGS += -DHAVE_BPF
 
-BPF_ENABLED = ../fdreceive ../binder
+BPF_ENABLED = ../fdreceive ../binder ../tun_tap
 
 all: $(TARGETS)
 	@set -e; for i in $(BPF_ENABLED); do $(MAKE) -C $$i all ; done
diff --git a/tests/bpf/test b/tests/bpf/test
index 6ab7686..135fce7 100755
--- a/tests/bpf/test
+++ b/tests/bpf/test
@@ -4,8 +4,9 @@  use Test::More;
 BEGIN {
     $basedir = $0;
     $basedir =~ s|(.*)/[^/]*|$1|;
-    $fdr_basedir    = "$basedir/../fdreceive";
-    $binder_basedir = "$basedir/../binder";
+    $fdr_basedir     = "$basedir/../fdreceive";
+    $binder_basedir  = "$basedir/../binder";
+    $tun_tap_basedir = "$basedir/../tun_tap";
 
     $test_bpf_count       = 7;
     $test_fdreceive_count = 4;
@@ -49,6 +50,14 @@  BEGIN {
         print "Error checking Binder driver\n";
     }
 
+    # Test if tun_tap Driver is supported
+    $test_tun_tap = 0;
+
+    if ( -e "$tun_tap_basedir/tap_bpf" ) {
+        $test_tun_tap = 1;
+        $test_count += 1;
+    }
+
     plan tests => $test_count;
 }
 
@@ -205,4 +214,10 @@  if ($test_binder) {
     system("/bin/sh $binder_basedir/cleanup_binder.sh $v 2>/dev/null");
 }
 
+if ($test_tun_tap) {
+    ##### Test tun_socket #############
+    $result = system "runcon -t test_tap_bpf_t $tun_tap_basedir/tap_bpf $v";
+    ok( $result eq 0 );
+}
+
 exit;
diff --git a/tests/tun_tap/.gitignore b/tests/tun_tap/.gitignore
new file mode 100644
index 0000000..68a6214
--- /dev/null
+++ b/tests/tun_tap/.gitignore
@@ -0,0 +1,3 @@ 
+tun_tap
+tun_relabel
+tap_bpf
diff --git a/tests/tun_tap/Makefile b/tests/tun_tap/Makefile
new file mode 100644
index 0000000..e11a20e
--- /dev/null
+++ b/tests/tun_tap/Makefile
@@ -0,0 +1,22 @@ 
+TARGETS = tun_tap tun_relabel
+DEPS = tun_common.c tun_common.h
+LDLIBS += -lselinux
+
+# export so that BPF command gets built on local build
+export CFLAGS += -DHAVE_BPF
+
+# Support for TUNSETFILTEREBPF & TUNSETSTEERINGEBPF ioctl's added in 4.16
+ifneq ($(shell ../kvercmp $$(uname -r) 4.16),-1)
+	ifneq (,$(findstring -DHAVE_BPF,$(CFLAGS)))
+		TARGETS += tap_bpf
+		DEPS += ../bpf/bpf_common.c ../bpf/bpf_common.h
+		LDLIBS += -lbpf
+	endif
+endif
+
+all: $(TARGETS)
+
+clean:
+	rm -f $(TARGETS)
+
+$(TARGETS): $(DEPS)
diff --git a/tests/tun_tap/tap_bpf.c b/tests/tun_tap/tap_bpf.c
new file mode 100644
index 0000000..8d225ea
--- /dev/null
+++ b/tests/tun_tap/tap_bpf.c
@@ -0,0 +1,89 @@ 
+#include "tun_common.h"
+#include "../bpf/bpf_common.h"
+
+static void print_usage(char *progname)
+{
+	fprintf(stderr,
+		"usage:  %s [-v]\n"
+		"Where:\n\t"
+		"-v  Print information.\n", progname);
+	exit(-1);
+}
+
+int main(int argc, char *argv[])
+{
+	int opt, result, fd, bpf_fd;
+	char *context;
+	bool verbose = false;
+	struct ifreq ifr;
+
+	while ((opt = getopt(argc, argv, "v")) != -1) {
+		switch (opt) {
+		case 'v':
+			verbose = true;
+			break;
+		default:
+			print_usage(argv[0]);
+		}
+	}
+
+	if (verbose) {
+		result = getcon(&context);
+		if (result < 0) {
+			fprintf(stderr, "Failed to obtain process context\n");
+			exit(-1);
+		}
+
+		printf("Process context:\n\t%s\n", context);
+		free(context);
+	}
+
+	/* Start TUN/TAP */
+	result = open_dev(&fd, "TAP", verbose);
+	if (result != 0)
+		exit(result);
+
+	/* Let TUNSETIFF allocate the ifr_name */
+	memset(&ifr, 0, sizeof(struct ifreq));
+	ifr.ifr_flags = IFF_TAP | IFF_MULTI_QUEUE;
+	result = setiff(fd, &ifr, verbose);
+	if (result != 0) {
+		close(fd);
+		exit(result);
+	}
+
+	bpf_setrlimit();
+
+	bpf_fd = create_bpf_prog();
+	if (bpf_fd < 0) {
+		fprintf(stderr, "Failed to obtain BPF prog fd: %s\n",
+			strerror(errno));
+		exit(-1);
+	}
+	if (verbose)
+		printf("Obtained BPF prog fd\n");
+
+	result = ioctl(fd, TUNSETFILTEREBPF, &bpf_fd);
+	if (result < 0) {
+		fprintf(stderr, "Failed ioctl(TUNSETFILTEREBPF) Add BPF prog fd: %s\n",
+			strerror(errno));
+		goto err;
+	}
+	if (verbose)
+		printf("ioctl(TUNSETFILTEREBPF) Added BPF prog fd to: %s\n",
+		       ifr.ifr_name);
+
+	result = ioctl(fd, TUNSETSTEERINGEBPF, &bpf_fd);
+	if (result < 0) {
+		fprintf(stderr, "Failed ioctl(TUNSETSTEERINGEBPF) Add BPF prog fd: %s\n",
+			strerror(errno));
+		goto err;
+	}
+	if (verbose)
+		printf("ioctl(TUNSETSTEERINGEBPF) Added BPF prog fd to: %s\n",
+		       ifr.ifr_name);
+err:
+	close(fd);
+	close(bpf_fd);
+	return result;
+}
diff --git a/tests/tun_tap/test b/tests/tun_tap/test
new file mode 100755
index 0000000..3daf2eb
--- /dev/null
+++ b/tests/tun_tap/test
@@ -0,0 +1,88 @@ 
+#!/usr/bin/perl
+use Test::More;
+
+BEGIN {
+    $basedir = $0;
+    $basedir =~ s|(.*)/[^/]*|$1|;
+
+    # allow info to be shown during tests
+    $v = $ARGV[0];
+    if ($v) {
+        if ( $v ne "-v" ) {
+            plan skip_all => "Invalid option (use -v)";
+        }
+    }
+    else {
+        $v = " ";
+    }
+
+    plan tests => 14;
+}
+
+############ Test tun_socket TUN #############
+print "Test TUN device driver support - 'tun_socket'\n";
+$result = system "runcon -t test_tun_tap_t $basedir/tun_tap $v -s";
+ok( $result eq 0 );
+
+# Deny capability { net_admin } - EPERM
+$result =
+  system "runcon -t test_tun_tap_no_net_admin_t $basedir/tun_tap $v 2>&1";
+ok( $result >> 8 eq 1 );
+
+# Deny tun_socket { create } - EACCES
+$result = system "runcon -t test_tun_tap_no_create_t $basedir/tun_tap $v 2>&1";
+ok( $result >> 8 eq 13 );
+
+# Deny tun_socket { attach_queue } - EACCES
+$result = system "runcon -t test_tun_tap_no_queue_t $basedir/tun_tap $v 2>&1";
+ok( $result >> 8 eq 13 );
+
+$result = system
+  "runcon -t test_tun_tap_t $basedir/tun_relabel $v test_newcon_tun_tap_t";
+ok( $result eq 0 );
+
+# Deny tun_socket { relabelto } - EACCES
+$result = system
+"runcon -t test_tun_tap_t $basedir/tun_relabel $v test_newcon_no_to_tun_tap_t 2>&1";
+ok( $result >> 8 eq 13 );
+
+# Deny tun_socket { relabelfrom } - EACCES
+$result = system
+"runcon -t test_tun_tap_t $basedir/tun_relabel $v test_newcon_no_from_tun_tap_t 2>&1";
+ok( $result >> 8 eq 13 );
+
+############ Test tun_socket TAP #############
+print "Test TAP device driver support - 'tun_socket'\n";
+$result = system "runcon -t test_tun_tap_t $basedir/tun_tap -p $v";
+ok( $result eq 0 );
+
+# Deny capability { net_admin } - EPERM
+$result =
+  system "runcon -t test_tun_tap_no_net_admin_t $basedir/tun_tap -p $v 2>&1";
+ok( $result >> 8 eq 1 );
+
+# Deny tun_socket { create } - EACCES
+$result =
+  system "runcon -t test_tun_tap_no_create_t $basedir/tun_tap -p $v 2>&1";
+ok( $result >> 8 eq 13 );
+
+# Deny tun_socket { attach_queue } - EACCES
+$result =
+  system "runcon -t test_tun_tap_no_queue_t $basedir/tun_tap -p $v 2>&1";
+ok( $result >> 8 eq 13 );
+
+$result = system
+  "runcon -t test_tun_tap_t $basedir/tun_relabel -p $v test_newcon_tun_tap_t";
+ok( $result eq 0 );
+
+# Deny tun_socket { relabelto } - EACCES
+$result = system
+"runcon -t test_tun_tap_t $basedir/tun_relabel -p $v test_newcon_no_to_tun_tap_t 2>&1";
+ok( $result >> 8 eq 13 );
+
+# Deny tun_socket { relabelfrom } - EACCES
+$result = system
+"runcon -t test_tun_tap_t $basedir/tun_relabel -p $v test_newcon_no_from_tun_tap_t 2>&1";
+ok( $result >> 8 eq 13 );
+
+exit;
diff --git a/tests/tun_tap/tun_common.c b/tests/tun_tap/tun_common.c
new file mode 100644
index 0000000..5a4a5ee
--- /dev/null
+++ b/tests/tun_tap/tun_common.c
@@ -0,0 +1,100 @@ 
+#include "tun_common.h"
+
+int open_dev(int *fd, char *test_str, bool verbose)
+{
+	char *tun_dev = "/dev/net/tun";
+
+	*fd = open(tun_dev, O_RDWR);
+	if (fd < 0) {
+		fprintf(stderr, "Failed to open device: %s\n",
+			strerror(errno));
+		return errno;
+	}
+	if (verbose)
+		printf("Opened: %s and testing %s service.\n",
+		       tun_dev, test_str);
+
+	return 0;
+}
+
+int setiff(int fd, struct ifreq *ifr, bool verbose)
+{
+	int result;
+
+	result = ioctl(fd, TUNSETIFF, ifr);
+	if (result < 0) {
+		fprintf(stderr, "Failed ioctl(TUNSETIFF): %s\n",
+			strerror(errno));
+		return errno;
+	}
+	if (verbose)
+		printf("ioctl(TUNSETIFF) name: %s\n", ifr->ifr_name);
+
+	return 0;
+}
+
+int persist(int fd, int op, char *name, bool verbose)
+{
+	int result;
+
+	result = ioctl(fd, TUNSETPERSIST, op);
+	if (result < 0) {
+		fprintf(stderr, "Failed ioctl(TUNSETPERSIST %s): %s\n",
+			op ? "Set" : "Unset", strerror(errno));
+		return errno;
+	}
+	if (verbose)
+		printf("%s ioctl(TUNSETPERSIST) name: %s\n",
+		       op ? "Set" : "Unset", name);
+
+	return 0;
+}
+
+int tunsetqueue(int fd, int op, char *name, bool verbose)
+{
+	int result;
+	struct ifreq ifr;
+
+	memset(&ifr, 0, sizeof(ifr));
+	ifr.ifr_flags = (op ? IFF_ATTACH_QUEUE : IFF_DETACH_QUEUE);
+	result = ioctl(fd, TUNSETQUEUE, &ifr);
+	if (result < 0) {
+		fprintf(stderr, "Failed ioctl(TUNSETQUEUE %s): %s\n",
+			op ? "IFF_ATTACH_QUEUE" : "IFF_DETACH_QUEUE",
+			strerror(errno));
+		return errno;
+	}
+	if (verbose)
+		printf("ioctl(TUNSETQUEUE) %s name: %s\n",
+		       op ? "IFF_ATTACH_QUEUE" : "IFF_DETACH_QUEUE", name);
+
+	return 0;
+}
+
+int switch_context(char *newcon, bool verbose)
+{
+	int result;
+
+	result = setcon(newcon);
+	if (result < 0) {
+		fprintf(stderr, "setcon() failed to set new process context:\n\t%s\n",
+			newcon);
+		return -1;
+	}
+
+	if (verbose)
+		printf("New process context:\n\t%s\n", newcon);
+
+	free(newcon);
+
+	return 0;
+}
+
+void del_tuntap_name(int fd, char *context, char *name, bool verbose)
+{
+	if (verbose)
+		printf("Switching back to orig context to remove persistent name: %s\n",
+		       name);
+	switch_context(context, verbose);
+	persist(fd, 0, name, verbose);
+}
diff --git a/tests/tun_tap/tun_common.h b/tests/tun_tap/tun_common.h
new file mode 100644
index 0000000..0259563
--- /dev/null
+++ b/tests/tun_tap/tun_common.h
@@ -0,0 +1,22 @@ 
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <fcntl.h>
+#include <net/if.h>
+#include <linux/if_tun.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <selinux/selinux.h>
+#include <selinux/context.h>
+
+extern int open_dev(int *fd, char *test_str, bool verbose);
+extern int setiff(int fd, struct ifreq *ifr, bool verbose);
+/* Persistent state 'op': 0 = unset, 1 = set */
+extern int persist(int fd, int op, char *name, bool verbose);
+/* Queue state 'op': 0 = IFF_DETACH_QUEUE, 1 = IFF_ATTACH_QUEUE */
+extern int tunsetqueue(int fd, int op, char *name, bool verbose);
+extern int switch_context(char *newcon, bool verbose);
+extern void del_tuntap_name(int fd, char *context, char *name, bool verbose);
diff --git a/tests/tun_tap/tun_relabel.c b/tests/tun_tap/tun_relabel.c
new file mode 100644
index 0000000..7aeabd1
--- /dev/null
+++ b/tests/tun_tap/tun_relabel.c
@@ -0,0 +1,124 @@ 
+#include "tun_common.h"
+
+static void print_usage(char *progname)
+{
+	fprintf(stderr,
+		"usage:  %s [-v] newcon\n"
+		"Where:\n\t"
+		"-p      Test TAP driver, default is TUN driver.\n\t"
+		"-v      Print information.\n\t"
+		"newcon  New process context.\n", progname);
+	exit(-1);
+}
+
+int main(int argc, char *argv[])
+{
+	char *origcon, *newcon, *test_str;
+	char alloc_name[IFNAMSIZ];
+	int opt, result, test, fd1, fd2;
+	bool verbose = false;
+	context_t con_t;
+	struct ifreq ifr;
+
+	test = IFF_TUN;
+	test_str = "TUN";
+
+	while ((opt = getopt(argc, argv, "pv")) != -1) {
+		switch (opt) {
+		case 'p':
+			test = IFF_TAP;
+			test_str = "TAP";
+			break;
+		case 'v':
+			verbose = true;
+			break;
+		default:
+			print_usage(argv[0]);
+		}
+	}
+
+	if (optind >= argc)
+		print_usage(argv[0]);
+
+	/*
+	 * Need to keep a copy of this to switch back when testing deny
+	 * relabelto/from in newcon. This will allow the removal of the
+	 * allocated TUN/TAP name.
+	 */
+	result = getcon(&origcon);
+	if (result < 0) {
+		fprintf(stderr, "Failed to obtain process context\n");
+		exit(-1);
+	}
+	if (verbose)
+		printf("Current process context:\n\t%s\n", origcon);
+
+	/* Build new context for transition */
+	con_t = context_new(origcon);
+	if (!con_t) {
+		fprintf(stderr, "Unable to create context structure\n");
+		exit(-1);
+	}
+
+	if (context_type_set(con_t, argv[optind])) {
+		fprintf(stderr, "Unable to set new type\n");
+		exit(-1);
+	}
+
+	newcon = context_str(con_t);
+	if (!newcon) {
+		fprintf(stderr, "Unable to obtain new context string\n");
+		exit(-1);
+	}
+
+	/* Start TUN/TAP */
+	result = open_dev(&fd1, test_str, verbose);
+	if (result != 0)
+		exit(result);
+
+	/* Let TUNSETIFF allocate the ifr_name for later use */
+	memset(&ifr, 0, sizeof(struct ifreq));
+	ifr.ifr_flags = test | IFF_MULTI_QUEUE;
+	result = setiff(fd1, &ifr, verbose);
+	if (result != 0)
+		goto err1;
+
+	strcpy(alloc_name, ifr.ifr_name);
+
+	/* Make this name persist */
+	result = persist(fd1, 1, alloc_name, verbose);
+	if (result != 0)
+		goto err1;
+
+	/* Switch to new context for relabel tests */
+	result = switch_context(newcon, verbose);
+	if (result < 0) { /* If fail remove the persistent one */
+		del_tuntap_name(fd1, origcon, alloc_name, verbose);
+		goto err1;
+	}
+
+	/* Start TUN/TAP in new context */
+	result = open_dev(&fd2, test_str, verbose);
+	if (result != 0) {
+		del_tuntap_name(fd1, origcon, alloc_name, verbose);
+		goto err1;
+	}
+
+	memset(&ifr, 0, sizeof(struct ifreq));
+	ifr.ifr_flags = test | IFF_MULTI_QUEUE;
+	/* Use the TUN/TAP name allocated initially */
+	strcpy(ifr.ifr_name, alloc_name);
+	result = setiff(fd2, &ifr, verbose);
+	if (result != 0) {
+		del_tuntap_name(fd1, origcon, alloc_name, verbose);
+		goto now_ok;
+	}
+
+	persist(fd2, 0, alloc_name, verbose);
+now_ok:
+	close(fd2);
+err1:
+	close(fd1);
+
+	return result;
+}
diff --git a/tests/tun_tap/tun_tap.c b/tests/tun_tap/tun_tap.c
new file mode 100644
index 0000000..a3db6c9
--- /dev/null
+++ b/tests/tun_tap/tun_tap.c
@@ -0,0 +1,123 @@ 
+#include "tun_common.h"
+
+static void print_usage(char *progname)
+{
+	fprintf(stderr,
+		"usage:  %s [-p] [-s ] [-v]\n"
+		"Where:\n\t"
+		"-p  Test TAP driver, default is TUN driver.\n\t"
+		"-s  If -v, then show TUN/TAP Features.\n\t"
+		"-v  Print information.\n", progname);
+	exit(-1);
+}
+
+int main(int argc, char *argv[])
+{
+	char *context, *test_str;
+	int opt, result, fd, bit, count, test;
+	unsigned int features, f_switch;
+	bool verbose = false, show = false;
+	struct ifreq ifr;
+
+	test = IFF_TUN;
+	test_str = "TUN";
+
+	while ((opt = getopt(argc, argv, "psv")) != -1) {
+		switch (opt) {
+		case 'p':
+			test = IFF_TAP;
+			test_str = "TAP";
+			break;
+		case 's':
+			show = true;
+			break;
+		case 'v':
+			verbose = true;
+			break;
+		default:
+			print_usage(argv[0]);
+		}
+	}
+
+	if (verbose) {
+		result = getcon(&context);
+		if (result < 0) {
+			fprintf(stderr, "Failed to obtain process context\n");
+			exit(-1);
+		}
+
+		printf("Process context:\n\t%s\n", context);
+		free(context);
+	}
+
+	/* Start TUN/TAP */
+	result = open_dev(&fd, test_str, verbose);
+	if (result != 0)
+		exit(result);
+
+	if (verbose && show) {
+		result = ioctl(fd, TUNGETFEATURES, &features);
+		if (result < 0) {
+			fprintf(stderr, "Failed ioctl(TUNGETFEATURES): %s\n",
+				strerror(errno));
+			exit(-1);
+		}
+
+		bit = 1;
+		count = 0xffff;
+		printf("TUN/TAP supported features:\n");
+		while (count) {
+			f_switch = bit & features;
+			switch (f_switch) {
+			case IFF_TUN:
+				printf("\tIFF_TUN\n");
+				break;
+			case IFF_TAP:
+				printf("\tIFF_TAP\n");
+				break;
+			case IFF_NAPI:
+				printf("\tIFF_NAPI\n");
+				break;
+			case IFF_NAPI_FRAGS:
+				printf("\tIFF_NAPI_FRAGS\n");
+				break;
+			case IFF_NO_PI:
+				printf("\tIFF_NO_PI\n");
+				break;
+			case IFF_ONE_QUEUE:
+				printf("\tIFF_ONE_QUEUE\n");
+				break;
+			case IFF_VNET_HDR:
+				printf("\tIFF_VNET_HDR\n");
+				break;
+			/* Added in kernel 3.8 */
+			case IFF_MULTI_QUEUE:
+				printf("\tIFF_MULTI_QUEUE\n");
+				break;
+			}
+			bit <<= 1;
+			count >>= 1;
+		}
+	}
+
+	/* Let TUNSETIFF allocate the ifr_name */
+	memset(&ifr, 0, sizeof(struct ifreq));
+	ifr.ifr_flags = test | IFF_MULTI_QUEUE;
+	result = setiff(fd, &ifr, verbose);
+	if (result != 0)
+		goto err;
+
+	/*
+	 * Test 'tun_socket { attach_queue }' permission by removing
+	 * the queue. Adding it back then generates the call to:
+	 *     selinux_tun_dev_attach_queue().
+	 */
+	result = tunsetqueue(fd, 0, ifr.ifr_name, verbose);
+	if (result != 0)
+		goto err;
+
+	result = tunsetqueue(fd, 1, ifr.ifr_name, verbose);
+err:
+	close(fd);
+	return result;
+}