[PATCH] tests: make IPv6 subtests optional on IPv4-only systems

Jan Onderka <[email protected]>
Newsgroups org.kernel.vger.selinux
Message-ID <[email protected]>
Skip or adapt IPv6-dependent subtests when the running system does not
have usable IPv6 on loopback.

Problem
-------
Several subtests assume IPv6 is available: they bind or connect to ::1,
load ip6tables/nftables IPv6 rules, or create AF_INET6/SCTP IPv6 sockets.
On IPv4-only systems (for example RHIVOS kernels built without usable
IPv6, or hosts with net.ipv6.conf.*.disable_ipv6=1), these subtests
hang or fail in ways that block the entire make test run.

Change
------
- Add tests/has_ipv6 helper that checks /proc/net/if_inet6 and the
  net.ipv6.conf.all/lo.disable_ipv6 sysctls.
- Wrap IPv6-only cases in inet_socket, extended_socket_class, and sctp
  Perl tests behind $test_ipv6 / ipv6_enabled() checks and reduce test
  counts accordingly.
- Make inet_socket/server.c fall back to AF_INET when IPv6 is absent.
- Split IPv6 firewall/load script rules into nftables-ipv6.load and only
  invoke ip6tables/nftables IPv6 setup from load scripts when has_ipv6
  succeeds.
- Add ipv6_enabled() to sctp_common for C helpers and SCTP bindx paths.

Justification
-------------
IPv6 is optional in many deployment targets. The testsuite should detect
runtime IPv6 usability and skip only the affected subtests instead of
assuming ::1 and ip6tables always work. This mirrors how other optional
features are handled in the tree and keeps the fix self-contained in
selinux-testsuite.

Testing
-------
Verified on RHIVOS (no /proc/net/if_inet6):

  - inet_socket/tcp and inet_socket/udp complete successfully
  - extended_socket_class IPv6 cases are skipped
  - make test progresses past former IPv6 hang points

Verified on a host with IPv6 enabled:

  - IPv6 subtests still run and pass

Signed-off-by: Jan Onderka <[email protected]>
---
 tests/extended_socket_class/test      | 17 ++++++++++
 tests/has_ipv6                        | 19 +++++++++++
 tests/inet_socket/ipsec-load          |  4 +++
 tests/inet_socket/iptables-flush      |  9 +++--
 tests/inet_socket/iptables-load       |  5 +++
 tests/inet_socket/nftables-flush      |  1 -
 tests/inet_socket/nftables-ipv6-flush |  1 +
 tests/inet_socket/nftables-ipv6.load  | 35 +++++++++++++++++++
 tests/inet_socket/nftables-load       | 35 ++-----------------
 tests/inet_socket/server.c            | 19 ++++++++++-
 tests/inet_socket/test                | 49 +++++++++++++++++++++------
 tests/sctp/fb-deny-label-flush        |  5 +++
 tests/sctp/fb-deny-label-load         |  7 ++++
 tests/sctp/fb-label-flush             |  5 +++
 tests/sctp/fb-label-load              |  7 ++++
 tests/sctp/iptables-flush             |  5 +++
 tests/sctp/iptables-load              |  9 +++++
 tests/sctp/nftables-flush             |  1 -
 tests/sctp/nftables-ipv6-flush        |  1 +
 tests/sctp/nftables-ipv6.load         | 33 ++++++++++++++++++
 tests/sctp/nftables-load              | 33 ++----------------
 tests/sctp/sctp_bindx.c               | 46 +++++++++++++++++--------
 tests/sctp/sctp_common.c              | 15 ++++++++
 tests/sctp/sctp_common.h              |  2 ++
 tests/sctp/sctp_peeloff_server.c      |  2 +-
 tests/sctp/sctp_server.c              |  2 +-
 tests/sctp/test                       | 42 +++++++++++++++++++++++
 27 files changed, 312 insertions(+), 97 deletions(-)
 create mode 100755 tests/has_ipv6
 create mode 100644 tests/inet_socket/nftables-ipv6-flush
 create mode 100644 tests/inet_socket/nftables-ipv6.load
 create mode 100644 tests/sctp/nftables-ipv6-flush
 create mode 100644 tests/sctp/nftables-ipv6.load

diff --git a/tests/extended_socket_class/test b/tests/extended_socket_class/test
index 1e6299f..9a6ea16 100755
--- a/tests/extended_socket_class/test
+++ b/tests/extended_socket_class/test
@@ -26,6 +26,17 @@ BEGIN {
         $test_smc = 1;
     }
 
+    # Determine if IPv6 is enabled on loopback.
+    my $testdir = $0;
+    $testdir =~ s|(.*)/[^/]*|$1|;
+    $test_ipv6 = system("$testdir/../has_ipv6") == 0 ? 1 : 0;
+    if ( !$test_ipv6 ) {
+        $test_count -= 2;
+        if ($test_sctp) {
+            $test_count -= 4;
+        }
+    }
+
     plan tests => $test_count;
 }
 
@@ -46,6 +57,8 @@ $result = system(
 );
 ok($result);
 
+if ($test_ipv6) {
+
 # Verify that test_icmp_socket_t can create an ICMPv6 socket.
 $result = system(
 "runcon -t test_icmp_socket_t -- $basedir/sockcreate inet6 dgram icmpv6 2>&1"
@@ -57,6 +70,7 @@ $result = system(
 "runcon -t test_no_icmp_socket_t -- $basedir/sockcreate inet6 dgram icmpv6 2>&1"
 );
 ok($result);
+}
 
 # Restore to the kernel defaults - no one allowed to create ICMP sockets.
 system("echo 1 0 > /proc/sys/net/ipv4/ping_group_range");
@@ -87,6 +101,8 @@ if ($test_sctp) {
     );
     ok($result);
 
+    if ($test_ipv6) {
+
     # Verify that test_sctp_socket_t can create an IPv6 stream SCTP socket.
     $result = system(
 "runcon -t test_sctp_socket_t -- $basedir/sockcreate inet6 stream sctp 2>&1"
@@ -110,6 +126,7 @@ if ($test_sctp) {
 "runcon -t test_no_sctp_socket_t -- $basedir/sockcreate inet6 seqpacket sctp 2>&1"
     );
     ok($result);
+    }
 }
 
 if ($test_bluetooth) {
diff --git a/tests/has_ipv6 b/tests/has_ipv6
new file mode 100755
index 0000000..21f12e7
--- /dev/null
+++ b/tests/has_ipv6
@@ -0,0 +1,19 @@
+#!/bin/sh
+#
+# Return 0 if IPv6 is enabled and usable on loopback, 1 otherwise.
+#
+# IPv6 may be absent (ipv6.disable=1 on the kernel command line), disabled
+# globally (net.ipv6.conf.all.disable_ipv6=1), or disabled on loopback
+# (net.ipv6.conf.lo.disable_ipv6=1).  Following the LTP tst_net.sh approach,
+# check /proc/net/if_inet6 and the disable_ipv6 sysctls rather than module
+# parameters that may be blocked by SELinux policy.
+
+[ -f /proc/net/if_inet6 ] || exit 1
+
+disabled=$(sysctl -n net.ipv6.conf.all.disable_ipv6 2>/dev/null) || exit 1
+[ "$disabled" = 1 ] && exit 1
+
+disabled=$(sysctl -n net.ipv6.conf.lo.disable_ipv6 2>/dev/null) || exit 1
+[ "$disabled" = 1 ] && exit 1
+
+exit 0
diff --git a/tests/inet_socket/ipsec-load b/tests/inet_socket/ipsec-load
index 21e2dfe..e286de3 100644
--- a/tests/inet_socket/ipsec-load
+++ b/tests/inet_socket/ipsec-load
@@ -10,8 +10,12 @@ ip xfrm state add src 127.0.0.1 dst 127.0.0.1 proto ah spi 0x250 ctx $badclientc
 ip xfrm policy add src 127.0.0.1 dst 127.0.0.1 proto tcp dir out ctx "system_u:object_r:test_spd_t:s0" tmpl proto ah mode transport level required
 ip xfrm policy add src 127.0.0.1 dst 127.0.0.1 proto udp dir out ctx "system_u:object_r:test_spd_t:s0" tmpl proto ah mode transport level required
 
+SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
+HAS_IPV6="$SCRIPT_DIR/../has_ipv6"
+if "$HAS_IPV6"; then
 # IPv6 loopback
 ip xfrm state add src ::1 dst ::1 proto ah spi 0x200 ctx $goodclientcon auth sha1 0123456789012345
 ip xfrm state add src ::1 dst ::1 proto ah spi 0x250 ctx $badclientcon auth sha1 0123456789012345
 ip xfrm policy add src ::1 dst ::1 proto tcp dir out ctx "system_u:object_r:test_spd_t:s0" tmpl proto ah mode transport level required
 ip xfrm policy add src ::1 dst ::1 proto udp dir out ctx "system_u:object_r:test_spd_t:s0" tmpl proto ah mode transport level required
+fi
diff --git a/tests/inet_socket/iptables-flush b/tests/inet_socket/iptables-flush
index c168d89..198bb85 100644
--- a/tests/inet_socket/iptables-flush
+++ b/tests/inet_socket/iptables-flush
@@ -1,6 +1,11 @@
 #!/bin/sh
+SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
+HAS_IPV6="$SCRIPT_DIR/../has_ipv6"
+
 # Flush the security table.
 iptables -t security -F
-iptables -t security -X NEWCONN
+iptables -t security -X NEWCONN 2>/dev/null
+if "$HAS_IPV6"; then
 ip6tables -t security -F
-ip6tables -t security -X NEWCONN
+ip6tables -t security -X NEWCONN 2>/dev/null
+fi
diff --git a/tests/inet_socket/iptables-load b/tests/inet_socket/iptables-load
index 5be94f4..d097ed7 100644
--- a/tests/inet_socket/iptables-load
+++ b/tests/inet_socket/iptables-load
@@ -8,6 +8,9 @@
 # - Specified the interface since the tests are only performed over loopback.
 # - Set the port number and context to the values used by the test script and policy.
 
+SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
+HAS_IPV6="$SCRIPT_DIR/../has_ipv6"
+
 # Flush the security table.
 iptables -t security -F
 
@@ -28,6 +31,7 @@ iptables -t security -A OUTPUT -m state --state ESTABLISHED,RELATED -j CONNSECMA
 iptables -t security -A INPUT -i lo -p udp --dport 65535 -j SECMARK --selctx system_u:object_r:test_server_packet_t:s0
 iptables -t security -A OUTPUT -o lo -p udp --sport 65535 -j SECMARK --selctx system_u:object_r:test_server_packet_t:s0
 
+if "$HAS_IPV6"; then
 ##### IPv6 entries
 ip6tables -t security -F
 
@@ -47,3 +51,4 @@ ip6tables -t security -A OUTPUT -m state --state ESTABLISHED,RELATED -j CONNSECM
 # Label UDP packets similarly.
 ip6tables -t security -A INPUT -i lo -p udp --dport 65535 -j SECMARK --selctx system_u:object_r:test_server_packet_t:s0
 ip6tables -t security -A OUTPUT -o lo -p udp --sport 65535 -j SECMARK --selctx system_u:object_r:test_server_packet_t:s0
+fi
diff --git a/tests/inet_socket/nftables-flush b/tests/inet_socket/nftables-flush
index 7d62b8d..d0fee0c 100644
--- a/tests/inet_socket/nftables-flush
+++ b/tests/inet_socket/nftables-flush
@@ -1,2 +1 @@
 delete table ip security
-delete table ip6 security
diff --git a/tests/inet_socket/nftables-ipv6-flush b/tests/inet_socket/nftables-ipv6-flush
new file mode 100644
index 0000000..0c9b69a
--- /dev/null
+++ b/tests/inet_socket/nftables-ipv6-flush
@@ -0,0 +1 @@
+delete table ip6 security
diff --git a/tests/inet_socket/nftables-ipv6.load b/tests/inet_socket/nftables-ipv6.load
new file mode 100644
index 0000000..6116343
--- /dev/null
+++ b/tests/inet_socket/nftables-ipv6.load
@@ -0,0 +1,35 @@
+# IPv6 secmark rules for inet_socket tests (loaded only when IPv6 is enabled).
+
+add table ip6 security
+
+table ip6 security {
+
+	secmark inet_server {
+		"system_u:object_r:test_server_packet_t:s0"
+	}
+
+	map secmapping_in_out {
+		type inet_service : secmark
+		elements = { 65535 : "inet_server" }
+	}
+
+	chain input {
+		type filter hook input priority 0;
+
+		ct state new meta secmark set tcp dport map @secmapping_in_out
+		ct state new meta secmark set udp dport map @secmapping_in_out
+		ct state new ct secmark set meta secmark
+
+		ct state established,related meta secmark set ct secmark
+	}
+
+	chain output {
+		type filter hook output priority 0;
+
+		ct state new meta secmark set tcp dport map @secmapping_in_out
+		ct state established meta secmark set udp dport map @secmapping_in_out
+		ct state new ct secmark set meta secmark
+
+		ct state established,related meta secmark set ct secmark
+	}
+}
diff --git a/tests/inet_socket/nftables-load b/tests/inet_socket/nftables-load
index 11ec382..14d6efe 100644
--- a/tests/inet_socket/nftables-load
+++ b/tests/inet_socket/nftables-load
@@ -1,7 +1,8 @@
 # Based on NFT project example. Requires kernel >= 4.20 and nft >= 0.9.3
+#
+# IPv6 rules are in nftables-ipv6.load and loaded only when IPv6 is enabled.
 
 add table ip security
-add table ip6 security
 
 table ip security {
 
@@ -40,35 +41,3 @@ table ip security {
 		ct state established,related meta secmark set ct secmark
 	}
 }
-
-table ip6 security {
-
-	secmark inet_server {
-		"system_u:object_r:test_server_packet_t:s0"
-	}
-
-	map secmapping_in_out {
-		type inet_service : secmark
-		elements = { 65535 : "inet_server" }
-	}
-
-	chain input {
-		type filter hook input priority 0;
-
-		ct state new meta secmark set tcp dport map @secmapping_in_out
-		ct state new meta secmark set udp dport map @secmapping_in_out
-		ct state new ct secmark set meta secmark
-
-		ct state established,related meta secmark set ct secmark
-	}
-
-	chain output {
-		type filter hook output priority 0;
-
-		ct state new meta secmark set tcp dport map @secmapping_in_out
-		ct state established meta secmark set udp dport map @secmapping_in_out
-		ct state new ct secmark set meta secmark
-
-		ct state established,related meta secmark set ct secmark
-	}
-}
diff --git a/tests/inet_socket/server.c b/tests/inet_socket/server.c
index 63b6849..c4e4432 100644
--- a/tests/inet_socket/server.c
+++ b/tests/inet_socket/server.c
@@ -10,6 +10,8 @@
 #include <stdio.h>
 #include <stdbool.h>
 
+#include <unistd.h>
+
 #ifndef SO_PEERSEC
 #define SO_PEERSEC 31
 #endif
@@ -36,6 +38,21 @@ void usage(char *progname)
 	exit(1);
 }
 
+static bool ipv6_enabled(void)
+{
+	int fd;
+
+	if (access("/proc/net/if_inet6", F_OK) != 0)
+		return false;
+
+	fd = socket(AF_INET6, SOCK_DGRAM, 0);
+	if (fd < 0)
+		return false;
+
+	close(fd);
+	return true;
+}
+
 int main(int argc, char **argv)
 {
 	int sock, result, opt, sockprotocol, on = 1;
@@ -64,7 +81,7 @@ int main(int argc, char **argv)
 
 	memset(&hints, 0, sizeof(struct addrinfo));
 	hints.ai_flags = AI_PASSIVE;
-	hints.ai_family = AF_INET6;
+	hints.ai_family = ipv6_enabled() ? AF_INET6 : AF_INET;
 
 	if (!strcmp(argv[optind], "tcp")) {
 		hints.ai_socktype = SOCK_STREAM;
diff --git a/tests/inet_socket/test b/tests/inet_socket/test
index 4cc3852..01acf8a 100755
--- a/tests/inet_socket/test
+++ b/tests/inet_socket/test
@@ -63,6 +63,25 @@ BEGIN {
         $test_nft = 1;
     }
 
+    # Determine if IPv6 is enabled on loopback.
+    $test_ipv6 = system("$basedir/../has_ipv6") == 0 ? 1 : 0;
+
+    if ( !$test_ipv6 ) {
+        if ($test_ipsec) {
+            $test_count -= 2;
+        }
+        if ($test_calipso) {
+            $test_count -= $is_stream ? 3 : 2;
+            $test_calipso = 0;
+        }
+        if ($test_iptables) {
+            $test_count -= 2;
+        }
+        if ($test_nft) {
+            $test_count -= 2;
+        }
+    }
+
     plan tests => $test_count;
 }
 
@@ -329,7 +348,7 @@ if ($test_ipsec) {
 "runcon -t test_inet_bad_client_t -- $basedir/client $proto 127.0.0.1 65535 2>&1";
     ok( $result >> 8 eq $fail_value2 );
 
-    if ($is_stream) {
+    if ($is_stream && $test_ipv6) {
 
         # Verify that authorized client can communicate with the server.
         $result =
@@ -338,15 +357,18 @@ if ($test_ipsec) {
         ok( $result eq 0 );
     }
 
-    # Verify that unauthorized client cannot communicate with the server.
-    $result = system
+    if ($test_ipv6) {
+
+        # Verify that unauthorized client cannot communicate with the server.
+        $result = system
 "runcon -t test_inet_bad_client_t -- $basedir/client $proto ::1 65535 2>&1";
-    ok( $result >> 8 eq $fail_value2 );
+        ok( $result >> 8 eq $fail_value2 );
+    }
 
     # Kill the server.
     server_end($pid);
 
-    if ( not $is_stream ) {
+    if ( not $is_stream && $test_ipv6 ) {
 
    # Start the server for IPSEC test using IPv6 but do not request peer context.
         $pid = server_start( "-t test_inet_server_t", "-n $proto 65535" );
@@ -382,15 +404,18 @@ sub test_tables {
 "runcon -t test_inet_bad_client_t -- $basedir/client -e nopeer $proto 127.0.0.1 65535 2>&1";
     ok( $result >> 8 eq $fail_value2 );
 
-    # Verify that authorized client can communicate with the server.
-    $result = system
+    if ($test_ipv6) {
+
+        # Verify that authorized client can communicate with the server.
+        $result = system
 "runcon -t test_inet_client_t -- $basedir/client -e nopeer $proto ::1 65535";
-    ok( $result eq 0 );
+        ok( $result eq 0 );
 
-    # Verify that unauthorized client cannot communicate with the server.
-    $result = system
+        # Verify that unauthorized client cannot communicate with the server.
+        $result = system
 "runcon -t test_inet_bad_client_t -- $basedir/client -e nopeer $proto ::1 65535 2>&1";
-    ok( $result >> 8 eq $fail_value2 );
+        ok( $result >> 8 eq $fail_value2 );
+    }
 
     # Kill the server.
     server_end($pid);
@@ -406,7 +431,9 @@ if ($test_iptables) {
 if ($test_nft) {
     print "Testing nftables (IPv4/IPv6).\n";
     system "nft -f $basedir/nftables-load";
+    system "nft -f $basedir/nftables-ipv6.load" if $test_ipv6;
     test_tables();
+    system "nft -f $basedir/nftables-ipv6-flush" if $test_ipv6;
     system "nft -f $basedir/nftables-flush";
 }
 
diff --git a/tests/sctp/fb-deny-label-flush b/tests/sctp/fb-deny-label-flush
index 059e0b7..41e76e6 100644
--- a/tests/sctp/fb-deny-label-flush
+++ b/tests/sctp/fb-deny-label-flush
@@ -1,6 +1,11 @@
 #!/bin/sh
 
+SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
+HAS_IPV6="$SCRIPT_DIR/../has_ipv6"
+
 netlabelctl map del default
 netlabelctl map add default protocol:unlbl
 netlabelctl unlbl del interface:lo address:127.0.0.0/8 label:system_u:object_r:netlabel_sctp_peer_t:s0
+if "$HAS_IPV6"; then
 netlabelctl unlbl del interface:lo address:::1/128 label:system_u:object_r:deny_assoc_sctp_peer_t:s0
+fi
diff --git a/tests/sctp/fb-deny-label-load b/tests/sctp/fb-deny-label-load
index 7c0bd87..647399b 100644
--- a/tests/sctp/fb-deny-label-load
+++ b/tests/sctp/fb-deny-label-load
@@ -1,7 +1,14 @@
 #!/bin/sh
 
+SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
+HAS_IPV6="$SCRIPT_DIR/../has_ipv6"
+
 netlabelctl map del default
 netlabelctl map add default address:0.0.0.0/0 protocol:unlbl
+if "$HAS_IPV6"; then
 netlabelctl map add default address:::/0 protocol:unlbl
+fi
 netlabelctl unlbl add interface:lo address:127.0.0.0/8 label:system_u:object_r:netlabel_sctp_peer_t:s0
+if "$HAS_IPV6"; then
 netlabelctl unlbl add interface:lo address:::1/128 label:system_u:object_r:deny_assoc_sctp_peer_t:s0
+fi
diff --git a/tests/sctp/fb-label-flush b/tests/sctp/fb-label-flush
index 13573a8..c1ceeb5 100644
--- a/tests/sctp/fb-label-flush
+++ b/tests/sctp/fb-label-flush
@@ -1,6 +1,11 @@
 #!/bin/sh
 
+SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
+HAS_IPV6="$SCRIPT_DIR/../has_ipv6"
+
 netlabelctl map del default
 netlabelctl map add default protocol:unlbl
 netlabelctl unlbl del interface:lo address:127.0.0.0/8 label:system_u:object_r:netlabel_sctp_peer_t:s0
+if "$HAS_IPV6"; then
 netlabelctl unlbl del interface:lo address:::1/128 label:system_u:object_r:netlabel_sctp_peer_t:s0
+fi
diff --git a/tests/sctp/fb-label-load b/tests/sctp/fb-label-load
index a501515..065ec34 100644
--- a/tests/sctp/fb-label-load
+++ b/tests/sctp/fb-label-load
@@ -1,8 +1,15 @@
 #!/bin/sh
 
+SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
+HAS_IPV6="$SCRIPT_DIR/../has_ipv6"
+
 netlabelctl map del default
 netlabelctl map add default address:0.0.0.0/0 protocol:unlbl
+if "$HAS_IPV6"; then
 netlabelctl map add default address:::/0 protocol:unlbl
+fi
 netlabelctl unlbl add interface:lo address:127.0.0.0/8 label:system_u:object_r:netlabel_sctp_peer_t:s0
+if "$HAS_IPV6"; then
 netlabelctl unlbl add interface:lo address:::1/128 label:system_u:object_r:netlabel_sctp_peer_t:s0
+fi
 #netlabelctl -p unlbl list
diff --git a/tests/sctp/iptables-flush b/tests/sctp/iptables-flush
index e74271a..112ae79 100644
--- a/tests/sctp/iptables-flush
+++ b/tests/sctp/iptables-flush
@@ -1,4 +1,9 @@
 #!/bin/sh
 # Flush the security table after IPv4 and IPv6 tests.
+SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
+HAS_IPV6="$SCRIPT_DIR/../has_ipv6"
+
 iptables -t security -F
+if "$HAS_IPV6"; then
 ip6tables -t security -F
+fi
diff --git a/tests/sctp/iptables-load b/tests/sctp/iptables-load
index 9dac576..1a91e19 100644
--- a/tests/sctp/iptables-load
+++ b/tests/sctp/iptables-load
@@ -1,9 +1,14 @@
 #!/bin/sh
 ############################ SECMARK IPTABLE ENTRIES ########################
 #
+SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
+HAS_IPV6="$SCRIPT_DIR/../has_ipv6"
+
 # Flush the security table first:
 iptables -t security -F
+if "$HAS_IPV6"; then
 ip6tables -t security -F
+fi
 
 #-------------- INPUT IP Stream --------------------#
 # These rules will replace the above context if sctp ports 1024:1035 are found in the packets:
@@ -11,9 +16,11 @@ iptables -t security -A INPUT -i lo -p sctp -m multiport --port 1024:1035 -j SEC
 
 iptables -t security -A INPUT -m state --state ESTABLISHED,RELATED -j CONNSECMARK --save
 
+if "$HAS_IPV6"; then
 ip6tables -t security -A INPUT -i lo -p sctp -m multiport --port 1024:1035 -j SECMARK --selctx system_u:object_r:test_sctp_server_packet_t:s0
 
 ip6tables -t security -A INPUT -m state --state ESTABLISHED,RELATED -j CONNSECMARK --save
+fi
 
 #-------------- OUTPUT IP Stream --------------------#
 # These rules will replace the above context if sctp ports 1024:1035 are found in the packets:
@@ -21,7 +28,9 @@ iptables -t security -A OUTPUT -o lo -p sctp -m multiport --port 1024:1035 -j SE
 
 iptables -t security -A OUTPUT -m state --state ESTABLISHED,RELATED -j CONNSECMARK --save
 
+if "$HAS_IPV6"; then
 ip6tables -t security -A OUTPUT -o lo -p sctp -m multiport --port 1024:1035 -j SECMARK --selctx system_u:object_r:test_sctp_server_packet_t:s0
 
 ip6tables -t security -A OUTPUT -m state --state ESTABLISHED,RELATED -j CONNSECMARK --save
+fi
 
diff --git a/tests/sctp/nftables-flush b/tests/sctp/nftables-flush
index 7d62b8d..d0fee0c 100644
--- a/tests/sctp/nftables-flush
+++ b/tests/sctp/nftables-flush
@@ -1,2 +1 @@
 delete table ip security
-delete table ip6 security
diff --git a/tests/sctp/nftables-ipv6-flush b/tests/sctp/nftables-ipv6-flush
new file mode 100644
index 0000000..0c9b69a
--- /dev/null
+++ b/tests/sctp/nftables-ipv6-flush
@@ -0,0 +1 @@
+delete table ip6 security
diff --git a/tests/sctp/nftables-ipv6.load b/tests/sctp/nftables-ipv6.load
new file mode 100644
index 0000000..1268140
--- /dev/null
+++ b/tests/sctp/nftables-ipv6.load
@@ -0,0 +1,33 @@
+# IPv6 secmark rules for SCTP tests (loaded only when IPv6 is enabled).
+
+add table ip6 security
+
+table ip6 security {
+
+	secmark sctp_server {
+		"system_u:object_r:test_sctp_server_packet_t:s0"
+	}
+
+	map secmapping_in_out {
+		type inet_service : secmark
+		elements = { 1035 : "sctp_server" }
+	}
+
+	chain input {
+		type filter hook input priority 0;
+
+		ct state new meta secmark set sctp dport map @secmapping_in_out
+		ct state new ct secmark set meta secmark
+
+		ct state established,related meta secmark set ct secmark
+	}
+
+	chain output {
+		type filter hook output priority 0;
+
+		ct state new meta secmark set sctp dport map @secmapping_in_out
+		ct state new ct secmark set meta secmark
+
+		ct state established,related meta secmark set ct secmark
+	}
+}
diff --git a/tests/sctp/nftables-load b/tests/sctp/nftables-load
index 2cac3bb..56803b6 100644
--- a/tests/sctp/nftables-load
+++ b/tests/sctp/nftables-load
@@ -1,7 +1,8 @@
 # Based on NFT project example. Requires kernel >= 4.20 and nft >= 0.9.3
+#
+# IPv6 rules are in nftables-ipv6.load and loaded only when IPv6 is enabled.
 
 add table ip security
-add table ip6 security
 
 table ip security {
 
@@ -36,33 +37,3 @@ table ip security {
 		ct state established,related meta secmark set ct secmark
 	}
 }
-
-table ip6 security {
-
-	secmark sctp_server {
-		"system_u:object_r:test_sctp_server_packet_t:s0"
-	}
-
-	map secmapping_in_out {
-		type inet_service : secmark
-		elements = { 1035 : "sctp_server" }
-	}
-
-	chain input {
-		type filter hook input priority 0;
-
-		ct state new meta secmark set sctp dport map @secmapping_in_out
-		ct state new ct secmark set meta secmark
-
-		ct state established,related meta secmark set ct secmark
-	}
-
-	chain output {
-		type filter hook output priority 0;
-
-		ct state new meta secmark set sctp dport map @secmapping_in_out
-		ct state new ct secmark set meta secmark
-
-		ct state established,related meta secmark set ct secmark
-	}
-}
diff --git a/tests/sctp/sctp_bindx.c b/tests/sctp/sctp_bindx.c
index 74bf985..2240404 100644
--- a/tests/sctp/sctp_bindx.c
+++ b/tests/sctp/sctp_bindx.c
@@ -1,4 +1,5 @@
 #include "sctp_common.h"
+#include <errno.h>
 
 static void usage(char *progname)
 {
@@ -22,6 +23,7 @@ int main(int argc, char **argv)
 	unsigned short port;
 	bool rem = false;
 	bool verbose = false;
+	bool ipv6_bound = false;
 	char *context;
 
 	while ((opt = getopt(argc, argv, "rv")) != -1) {
@@ -58,7 +60,7 @@ int main(int argc, char **argv)
 		free(context);
 	}
 
-	sock = socket(PF_INET6, type, IPPROTO_SCTP);
+	sock = socket(ipv6_enabled() ? PF_INET6 : PF_INET, type, IPPROTO_SCTP);
 	if (sock < 0) {
 		perror("socket");
 		exit(1);
@@ -88,27 +90,41 @@ int main(int argc, char **argv)
 	ipv6.sin6_port = htons(port);
 	ipv6.sin6_addr = in6addr_loopback;
 
+	if (ipv6_enabled()) {
 	result = sctp_bindx(sock, (struct sockaddr *)&ipv6, 1,
 			    SCTP_BINDX_ADD_ADDR);
 	if (result < 0) {
-		perror("sctp_bindx ADD - ipv6");
-		close(sock);
-		exit(3);
+		if (verbose)
+			printf("sctp_bindx ADD - ipv6 not available\n");
+	} else {
+		ipv6_bound = true;
+		if (verbose)
+			printf("sctp_bindx ADD - ipv6\n");
+	}
 	}
-
-	if (verbose)
-		printf("sctp_bindx ADD - ipv6\n");
 
 	if (rem) {
-		result = sctp_bindx(sock, (struct sockaddr *)&ipv6, 1,
-				    SCTP_BINDX_REM_ADDR);
-		if (result < 0) {
-			perror("sctp_bindx - REM");
-			close(sock);
-			exit(4);
+		if (ipv6_bound) {
+			result = sctp_bindx(sock, (struct sockaddr *)&ipv6, 1,
+					    SCTP_BINDX_REM_ADDR);
+			if (result < 0) {
+				perror("sctp_bindx - REM");
+				close(sock);
+				exit(4);
+			}
+			if (verbose)
+				printf("sctp_bindx REM - ipv6\n");
+		} else {
+			result = sctp_bindx(sock, (struct sockaddr *)&ipv4, 1,
+					    SCTP_BINDX_REM_ADDR);
+			if (result < 0) {
+				perror("sctp_bindx - REM");
+				close(sock);
+				exit(4);
+			}
+			if (verbose)
+				printf("sctp_bindx REM - ipv4\n");
 		}
-		if (verbose)
-			printf("sctp_bindx REM - ipv6\n");
 	}
 
 	close(sock);
diff --git a/tests/sctp/sctp_common.c b/tests/sctp/sctp_common.c
index d10225c..3adce02 100644
--- a/tests/sctp/sctp_common.c
+++ b/tests/sctp/sctp_common.c
@@ -344,3 +344,18 @@ int handle_event(void *buf, char *cmp_addr, sctp_assoc_t *assoc_id,
 
 	return EVENT_OK;
 }
+
+bool ipv6_enabled(void)
+{
+	int fd;
+
+	if (access("/proc/net/if_inet6", F_OK) != 0)
+		return false;
+
+	fd = socket(AF_INET6, SOCK_DGRAM, 0);
+	if (fd < 0)
+		return false;
+
+	close(fd);
+	return true;
+}
diff --git a/tests/sctp/sctp_common.h b/tests/sctp/sctp_common.h
index 3a2545a..9f21d3e 100644
--- a/tests/sctp/sctp_common.h
+++ b/tests/sctp/sctp_common.h
@@ -36,3 +36,5 @@ void print_ip_option(int fd, bool ipv4, char *text);
 int set_subscr_events(int fd, int data_io, int assoc, int addr, int shutd);
 int handle_event(void *buf, char *cmp_addr, sctp_assoc_t *assoc_id,
 		 bool verbose, char *text);
+
+bool ipv6_enabled(void);
diff --git a/tests/sctp/sctp_peeloff_server.c b/tests/sctp/sctp_peeloff_server.c
index 3e48df7..e7b33c5 100644
--- a/tests/sctp/sctp_peeloff_server.c
+++ b/tests/sctp/sctp_peeloff_server.c
@@ -59,7 +59,7 @@ int main(int argc, char **argv)
 	if (ipv4)
 		hints.ai_family = AF_INET;
 	else
-		hints.ai_family = AF_INET6;
+		hints.ai_family = ipv6_enabled() ? AF_INET6 : AF_INET;
 
 	/* sctp_peeloff(3) must be from 1 to Many style socket */
 	hints.ai_socktype = SOCK_SEQPACKET;
diff --git a/tests/sctp/sctp_server.c b/tests/sctp/sctp_server.c
index a7832eb..6edadcf 100644
--- a/tests/sctp/sctp_server.c
+++ b/tests/sctp/sctp_server.c
@@ -74,7 +74,7 @@ int main(int argc, char **argv)
 	if (ipv4)
 		hints.ai_family = AF_INET;
 	else
-		hints.ai_family = AF_INET6;
+		hints.ai_family = ipv6_enabled() ? AF_INET6 : AF_INET;
 
 	if (!strcmp(argv[optind], "stream"))
 		hints.ai_socktype = SOCK_STREAM;
diff --git a/tests/sctp/test b/tests/sctp/test
index 5626ab8..1e547a3 100755
--- a/tests/sctp/test
+++ b/tests/sctp/test
@@ -104,6 +104,27 @@ BEGIN {
             $test_count += 8;
             $test_nft = 1;
         }
+
+        # Determine if IPv6 is enabled on loopback.
+        $test_ipv6 = system("$basedir/../has_ipv6") == 0 ? 1 : 0;
+
+        if ( !$test_ipv6 ) {
+            $test_count -= 12;
+
+            if ($test_calipso) {
+                $test_count -= 13;
+                if ($test_clpeeloff) {
+                    $test_count -= 6;
+                }
+                $test_calipso = 0;
+            }
+            if ($test_iptables) {
+                $test_count -= 4;
+            }
+            if ($test_nft) {
+                $test_count -= 4;
+            }
+        }
     }
 
     plan tests => $test_count;
@@ -164,6 +185,8 @@ $result = system
 "runcon -t test_sctp_client_t $basedir/sctp_client $v -n -e nopeer seq 127.0.0.1 1035";
 ok( $result eq 0 );
 
+if ($test_ipv6) {
+
 # Verify that authorized client can communicate with the server SEQ->STREAM.
 $result = system
   "runcon -t test_sctp_client_t $basedir/sctp_client $v -e nopeer seq ::1 1035";
@@ -174,6 +197,7 @@ ok( $result eq 0 );
 $result = system
 "runcon -t test_sctp_client_t -- $basedir/sctp_client $v -e nopeer stream ::1 1023 2>&1";
 ok( $result >> 8 eq 8 );
+}
 
 # Kill the stream server.
 server_end($pid);
@@ -194,6 +218,7 @@ $result = system
 "runcon -t test_sctp_connectx_t $basedir/sctp_connectx $v stream 127.0.0.1 1035";
 ok( $result eq 0 );
 
+if ($test_ipv6) {
 $result =
   system
   "runcon -t test_sctp_connectx_t $basedir/sctp_connectx $v seq ::1 1035";
@@ -203,6 +228,7 @@ $result =
   system
 "runcon -t test_sctp_deny_connectx_t $basedir/sctp_connectx $v seq ::1 1035 2>&1";
 ok( $result >> 8 eq 7 );
+}
 
 #
 ########################### SCTP_SENDMSG_CONNECT #############################
@@ -215,10 +241,12 @@ $result =
 "runcon -t test_sctp_connectx_t $basedir/sctp_connectx $v -n seq 127.0.0.1 1035";
 ok( $result eq 0 );
 
+if ($test_ipv6) {
 $result =
   system
 "runcon -t test_sctp_deny_connectx_t $basedir/sctp_connectx $v -n seq ::1 1035 2>&1";
 ok( $result >> 8 eq 8 );
+}
 
 #
 ################################ BINDX #######################################
@@ -351,6 +379,8 @@ server_end($pid);
 # Start seq server.
 $pid = server_start( "-t test_sctp_server_t", "sctp_server", "$v seq 1035" );
 
+if ($test_ipv6) {
+
 # Verify that authorized client can communicate with the server SEQ->SEQ.
 $result = system
 "runcon -t test_sctp_client_t $basedir/sctp_client $v -e system_u:object_r:netlabel_sctp_peer_t:s0 seq ::1 1035";
@@ -375,6 +405,7 @@ ok( $result >> 8 eq 6 );
 $result = system
 "runcon -t test_sctp_deny_peer_client_t -- $basedir/sctp_client $v -n -e system_u:object_r:netlabel_sctp_peer_t:s0 seq ::1 1035 2>&1";
 ok( $result >> 8 eq 13 );
+}
 
 # Kill the seq server.
 server_end($pid);
@@ -396,10 +427,13 @@ $result = system
 "runcon -t test_sctp_client_t $basedir/sctp_client $v -e system_u:object_r:netlabel_sctp_peer_t:s0 stream 127.0.0.1 1035";
 ok( $result eq 0 );
 
+if ($test_ipv6) {
+
 # Verify that the server is denied this association as the client will timeout on connect.
 $result = system
 "runcon -t test_sctp_client_t -- $basedir/sctp_client $v -e system_u:object_r:deny_assoc_sctp_peer_t:s0 stream ::1 1035 2>&1";
 ok( $result >> 8 eq 6 );
+}
 
 # Kill the seq server.
 server_end($pid);
@@ -1141,6 +1175,8 @@ sub test_tables {
 "runcon -t test_sctp_deny_peer_client_t -- $basedir/sctp_client $v -e nopeer stream 127.0.0.1 1035 2>&1";
     ok( $result >> 8 eq 6 );
 
+    if ($test_ipv6) {
+
  # Verify that authorized client can communicate with the server STREAM->STREAM.
     $result = system
 "runcon -t test_sctp_client_t $basedir/sctp_client $v -e nopeer stream ::1 1035";
@@ -1150,6 +1186,7 @@ sub test_tables {
     $result = system
 "runcon -t test_sctp_deny_peer_client_t -- $basedir/sctp_client $v -e nopeer stream ::1 1035 2>&1";
     ok( $result >> 8 eq 6 );
+    }
 
     # Kill the stream server.
     server_end($pid);
@@ -1168,6 +1205,8 @@ sub test_tables {
 "runcon -t test_sctp_deny_peer_client_t -- $basedir/sctp_client $v -e nopeer seq 127.0.0.1 1035 2>&1";
     ok( $result >> 8 eq 6 );
 
+    if ($test_ipv6) {
+
     # Verify that authorized client can communicate with the server SEQ->SEQ.
     $result = system
 "runcon -t test_sctp_client_t $basedir/sctp_client $v -e nopeer seq ::1 1035";
@@ -1177,6 +1216,7 @@ sub test_tables {
     $result = system
 "runcon -t test_sctp_deny_peer_client_t -- $basedir/sctp_client $v -e nopeer seq ::1 1035 2>&1";
     ok( $result >> 8 eq 6 );
+    }
 
     # Kill the seq server.
     server_end($pid);
@@ -1192,7 +1232,9 @@ if ($test_iptables) {
 if ($test_nft) {
     print "# Testing nftables (IPv4/IPv6).\n";
     system "nft -f $basedir/nftables-load";
+    system "nft -f $basedir/nftables-ipv6.load" if $test_ipv6;
     test_tables();
+    system "nft -f $basedir/nftables-ipv6-flush" if $test_ipv6;
     system "nft -f $basedir/nftables-flush";
 }
 
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.