File Index
Files and directories
Sign in
FILE

nft_nat.sh

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/net/netfilter/nft_nat.sh BASH
   1#!/bin/bash
   2#
   3# This test is for basic NAT functionality: snat, dnat, redirect, masquerade.
   4#
   5
   6source lib.sh
   7
   8ret=0
   9test_inet_nat=true
  10
  11checktool "nft --version" "run test without nft tool"
  12checktool "socat -h" "run test without socat"
  13
  14cleanup()
  15{
  16	ip netns pids "$ns0" | xargs kill 2>/dev/null
  17	ip netns pids "$ns1" | xargs kill 2>/dev/null
  18	ip netns pids "$ns2" | xargs kill 2>/dev/null
  19
  20	rm -f "$INFILE" "$OUTFILE"
  21
  22	cleanup_all_ns
  23}
  24
  25trap cleanup EXIT
  26
  27INFILE=$(mktemp)
  28OUTFILE=$(mktemp)
  29
  30setup_ns ns0 ns1 ns2
  31
  32if ! ip link add veth0 netns "$ns0" type veth peer name eth0 netns "$ns1" > /dev/null 2>&1;then
  33    echo "SKIP: No virtual ethernet pair device support in kernel"
  34    exit $ksft_skip
  35fi
  36ip link add veth1 netns "$ns0" type veth peer name eth0 netns "$ns2"
  37
  38ip -net "$ns0" link set veth0 up
  39ip -net "$ns0" addr add 10.0.1.1/24 dev veth0
  40ip -net "$ns0" addr add dead:1::1/64 dev veth0 nodad
  41
  42ip -net "$ns0" link set veth1 up
  43ip -net "$ns0" addr add 10.0.2.1/24 dev veth1
  44ip -net "$ns0" addr add dead:2::1/64 dev veth1 nodad
  45
  46do_config()
  47{
  48	ns="$1"
  49	subnet="$2"
  50
  51	ip -net "$ns" link set eth0 up
  52	ip -net "$ns" addr add "10.0.$subnet.99/24" dev eth0
  53	ip -net "$ns" route add default via "10.0.$subnet.1"
  54	ip -net "$ns" addr add "dead:$subnet::99/64" dev eth0 nodad
  55	ip -net "$ns" route add default via "dead:$subnet::1"
  56}
  57
  58do_config "$ns1" 1
  59do_config "$ns2" 2
  60
  61bad_counter()
  62{
  63	local ns=$1
  64	local counter=$2
  65	local expect=$3
  66	local tag=$4
  67
  68	echo "ERROR: $counter counter in $ns has unexpected value (expected $expect) at $tag" 1>&2
  69	ip netns exec "$ns" nft list counter inet filter "$counter" 1>&2
  70}
  71
  72check_counters()
  73{
  74	ns=$1
  75	local lret=0
  76
  77	if ! ip netns exec "$ns" nft list counter inet filter ns0in | grep -q "packets 1 bytes 84";then
  78		bad_counter "$ns" ns0in "packets 1 bytes 84" "check_counters 1"
  79		lret=1
  80	fi
  81
  82	if ! ip netns exec "$ns" nft list counter inet filter ns0out | grep -q "packets 1 bytes 84";then
  83		bad_counter "$ns" ns0out "packets 1 bytes 84" "check_counters 2"
  84		lret=1
  85	fi
  86
  87	expect="packets 1 bytes 104"
  88	if ! ip netns exec "$ns" nft list counter inet filter ns0in6 | grep -q "$expect";then
  89		bad_counter "$ns" ns0in6 "$expect" "check_counters 3"
  90		lret=1
  91	fi
  92	if ! ip netns exec "$ns" nft list counter inet filter ns0out6 | grep -q "$expect";then
  93		bad_counter "$ns" ns0out6 "$expect" "check_counters 4"
  94		lret=1
  95	fi
  96
  97	return $lret
  98}
  99
 100check_ns0_counters()
 101{
 102	local ns=$1
 103	local lret=0
 104
 105	if ! ip netns exec "$ns0" nft list counter inet filter ns0in | grep -q "packets 0 bytes 0";then
 106		bad_counter "$ns0" ns0in "packets 0 bytes 0" "check_ns0_counters 1"
 107		lret=1
 108	fi
 109
 110	if ! ip netns exec "$ns0" nft list counter inet filter ns0in6 | grep -q "packets 0 bytes 0";then
 111		bad_counter "$ns0" ns0in6 "packets 0 bytes 0"
 112		lret=1
 113	fi
 114
 115	if ! ip netns exec "$ns0" nft list counter inet filter ns0out | grep -q "packets 0 bytes 0";then
 116		bad_counter "$ns0" ns0out "packets 0 bytes 0" "check_ns0_counters 2"
 117		lret=1
 118	fi
 119	if ! ip netns exec "$ns0" nft list counter inet filter ns0out6 | grep -q "packets 0 bytes 0";then
 120		bad_counter "$ns0" ns0out6 "packets 0 bytes 0" "check_ns0_counters3 "
 121		lret=1
 122	fi
 123
 124	for dir in "in" "out" ; do
 125		expect="packets 1 bytes 84"
 126		if ! ip netns exec "$ns0" nft list counter inet filter "${ns}${dir}" | grep -q "$expect";then
 127			bad_counter "$ns0" "$ns${dir}" "$expect" "check_ns0_counters 4"
 128			lret=1
 129		fi
 130
 131		expect="packets 1 bytes 104"
 132		if ! ip netns exec "$ns0" nft list counter inet filter "${ns}${dir}6" | grep -q "$expect";then
 133			bad_counter "$ns0" "$ns${dir}6" "$expect" "check_ns0_counters 5"
 134			lret=1
 135		fi
 136	done
 137
 138	return $lret
 139}
 140
 141reset_counters()
 142{
 143	for i in "$ns0" "$ns1" "$ns2" ;do
 144		ip netns exec "$i" nft reset counters inet > /dev/null
 145	done
 146}
 147
 148test_local_dnat6()
 149{
 150	local family=$1
 151	local lret=0
 152	local IPF=""
 153
 154	if [ "$family" = "inet" ];then
 155		IPF="ip6"
 156	fi
 157
 158ip netns exec "$ns0" nft -f /dev/stdin <<EOF
 159table $family nat {
 160	chain output {
 161		type nat hook output priority 0; policy accept;
 162		ip6 daddr dead:1::99 dnat $IPF to dead:2::99
 163	}
 164}
 165EOF
 166	if [ $? -ne 0 ]; then
 167		echo "SKIP: Could not add add $family dnat hook"
 168		return $ksft_skip
 169	fi
 170
 171	# ping netns1, expect rewrite to netns2
 172	if ! ip netns exec "$ns0" ping -q -c 1 dead:1::99 > /dev/null;then
 173		lret=1
 174		echo "ERROR: ping6 failed"
 175		return $lret
 176	fi
 177
 178	expect="packets 0 bytes 0"
 179	for dir in "in6" "out6" ; do
 180		if ! ip netns exec "$ns0" nft list counter inet filter "ns1${dir}" | grep -q "$expect";then
 181			bad_counter "$ns0" ns1$dir "$expect" "test_local_dnat6 1"
 182			lret=1
 183		fi
 184	done
 185
 186	expect="packets 1 bytes 104"
 187	for dir in "in6" "out6" ; do
 188		if ! ip netns exec "$ns0" nft list counter inet filter "ns2${dir}" | grep -q "$expect";then
 189			bad_counter "$ns0" ns2$dir "$expect" "test_local_dnat6 2"
 190			lret=1
 191		fi
 192	done
 193
 194	# expect 0 count in ns1
 195	expect="packets 0 bytes 0"
 196	for dir in "in6" "out6" ; do
 197		if ! ip netns exec "$ns1" nft list counter inet filter "ns0${dir}" | grep -q "$expect";then
 198			bad_counter "$ns1" ns0$dir "$expect" "test_local_dnat6 3"
 199			lret=1
 200		fi
 201	done
 202
 203	# expect 1 packet in ns2
 204	expect="packets 1 bytes 104"
 205	for dir in "in6" "out6" ; do
 206		if ! ip netns exec "$ns2" nft list counter inet filter "ns0${dir}" | grep -q "$expect";then
 207			bad_counter "$ns2" ns0$dir "$expect" "test_local_dnat6 4"
 208			lret=1
 209		fi
 210	done
 211
 212	test $lret -eq 0 && echo "PASS: ipv6 ping to $ns1 was $family NATted to $ns2"
 213	ip netns exec "$ns0" nft flush chain ip6 nat output
 214
 215	return $lret
 216}
 217
 218test_local_dnat()
 219{
 220	local family=$1
 221	local lret=0
 222	local IPF=""
 223
 224	if [ "$family" = "inet" ];then
 225		IPF="ip"
 226	fi
 227
 228ip netns exec "$ns0" nft -f /dev/stdin <<EOF 2>/dev/null
 229table $family nat {
 230	chain output {
 231		type nat hook output priority 0; policy accept;
 232		ip daddr 10.0.1.99 dnat $IPF to 10.0.2.99
 233	}
 234}
 235EOF
 236	if [ $? -ne 0 ]; then
 237		if [ "$family" = "inet" ];then
 238			echo "SKIP: inet nat tests"
 239			test_inet_nat=false
 240			return $ksft_skip
 241		fi
 242		echo "SKIP: Could not add add $family dnat hook"
 243		return $ksft_skip
 244	fi
 245
 246	# ping netns1, expect rewrite to netns2
 247	if ! ip netns exec "$ns0" ping -q -c 1 10.0.1.99 > /dev/null;then
 248		lret=1
 249		echo "ERROR: ping failed"
 250		return $lret
 251	fi
 252
 253	expect="packets 0 bytes 0"
 254	for dir in "in" "out" ; do
 255		if ! ip netns exec "$ns0" nft list counter inet filter "ns1${dir}" | grep -q "$expect";then
 256			bad_counter "$ns0" "ns1$dir" "$expect" "test_local_dnat 1"
 257			lret=1
 258		fi
 259	done
 260
 261	expect="packets 1 bytes 84"
 262	for dir in "in" "out" ; do
 263		if ! ip netns exec "$ns0" nft list counter inet filter "ns2${dir}" | grep -q "$expect";then
 264			bad_counter "$ns0" "ns2$dir" "$expect" "test_local_dnat 2"
 265			lret=1
 266		fi
 267	done
 268
 269	# expect 0 count in ns1
 270	expect="packets 0 bytes 0"
 271	for dir in "in" "out" ; do
 272		if ! ip netns exec "$ns1" nft list counter inet filter ns0${dir} | grep -q "$expect";then
 273			bad_counter "$ns1" "ns0$dir" "$expect" "test_local_dnat 3"
 274			lret=1
 275		fi
 276	done
 277
 278	# expect 1 packet in ns2
 279	expect="packets 1 bytes 84"
 280	for dir in "in" "out" ; do
 281		if ! ip netns exec "$ns2" nft list counter inet filter ns0${dir} | grep -q "$expect";then
 282			bad_counter "$ns2" "ns0$dir" "$expect" "test_local_dnat 4"
 283			lret=1
 284		fi
 285	done
 286
 287	test $lret -eq 0 && echo "PASS: ping to $ns1 was $family NATted to $ns2"
 288
 289	ip netns exec "$ns0" nft flush chain "$family" nat output
 290
 291	reset_counters
 292	if ! ip netns exec "$ns0" ping -q -c 1 10.0.1.99 > /dev/null;then
 293		lret=1
 294		echo "ERROR: ping failed"
 295		return $lret
 296	fi
 297
 298	expect="packets 1 bytes 84"
 299	for dir in "in" "out" ; do
 300		if ! ip netns exec "$ns0" nft list counter inet filter "ns1${dir}" | grep -q "$expect";then
 301			bad_counter "$ns1" ns1$dir "$expect" "test_local_dnat 5"
 302			lret=1
 303		fi
 304	done
 305	expect="packets 0 bytes 0"
 306	for dir in "in" "out" ; do
 307		if ! ip netns exec "$ns0" nft list counter inet filter "ns2${dir}" | grep -q "$expect";then
 308			bad_counter "$ns0" ns2$dir "$expect" "test_local_dnat 6"
 309			lret=1
 310		fi
 311	done
 312
 313	# expect 1 count in ns1
 314	expect="packets 1 bytes 84"
 315	for dir in "in" "out" ; do
 316		if ! ip netns exec "$ns1" nft list counter inet filter "ns0${dir}" | grep -q "$expect";then
 317			bad_counter "$ns0" ns0$dir "$expect" "test_local_dnat 7"
 318			lret=1
 319		fi
 320	done
 321
 322	# expect 0 packet in ns2
 323	expect="packets 0 bytes 0"
 324	for dir in "in" "out" ; do
 325		if ! ip netns exec "$ns2" nft list counter inet filter "ns0${dir}" | grep -q "$expect";then
 326			bad_counter "$ns2" ns0$dir "$expect" "test_local_dnat 8"
 327			lret=1
 328		fi
 329	done
 330
 331	test $lret -eq 0 && echo "PASS: ping to $ns1 OK after $family nat output chain flush"
 332
 333	return $lret
 334}
 335
 336listener_ready()
 337{
 338	local ns="$1"
 339	local port="$2"
 340	local proto="$3"
 341	ss -N "$ns" -ln "$proto" -o "sport = :$port" | grep -q "$port"
 342}
 343
 344test_local_dnat_portonly()
 345{
 346	local family=$1
 347	local daddr=$2
 348	local lret=0
 349
 350ip netns exec "$ns0" nft -f /dev/stdin <<EOF
 351table $family nat {
 352	chain output {
 353		type nat hook output priority 0; policy accept;
 354		meta l4proto tcp dnat to :2000
 355
 356	}
 357}
 358EOF
 359	if [ $? -ne 0 ]; then
 360		if [ "$family" = "inet" ];then
 361			echo "SKIP: inet port test"
 362			test_inet_nat=false
 363			return
 364		fi
 365		echo "SKIP: Could not add $family dnat hook"
 366		return
 367	fi
 368
 369	echo "SERVER-$family" | ip netns exec "$ns1" timeout 3 socat -u STDIN TCP-LISTEN:2000 &
 370
 371	busywait $BUSYWAIT_TIMEOUT listener_ready "$ns1" 2000 "-t"
 372
 373	result=$(ip netns exec "$ns0" timeout 1 socat -u TCP:"$daddr":2000 STDOUT)
 374
 375	if [ "$result" = "SERVER-inet" ];then
 376		echo "PASS: inet port rewrite without l3 address"
 377	else
 378		echo "ERROR: inet port rewrite without l3 address, got $result"
 379		ret=1
 380	fi
 381}
 382
 383test_masquerade6()
 384{
 385	local family=$1
 386	local natflags=$2
 387	local lret=0
 388
 389	ip netns exec "$ns0" sysctl net.ipv6.conf.all.forwarding=1 > /dev/null
 390
 391	if ! ip netns exec "$ns2" ping -q -c 1 dead:1::99 > /dev/null;then
 392		echo "ERROR: cannot ping $ns1 from $ns2 via ipv6"
 393		return 1
 394	fi
 395
 396	expect="packets 1 bytes 104"
 397	for dir in "in6" "out6" ; do
 398		if ! ip netns exec "$ns1" nft list counter inet filter "ns2${dir}" | grep -q "$expect";then
 399			bad_counter "$ns1" "ns2$dir" "$expect" "test_masquerade6 1"
 400			lret=1
 401		fi
 402
 403		if ! ip netns exec "$ns2" nft list counter inet filter "ns1${dir}" | grep -q "$expect";then
 404			bad_counter "$ns2" "ns1$dir" "$expect" "test_masquerade6 2"
 405			lret=1
 406		fi
 407	done
 408
 409	reset_counters
 410
 411# add masquerading rule
 412ip netns exec "$ns0" nft -f /dev/stdin <<EOF
 413table $family nat {
 414	chain postrouting {
 415		type nat hook postrouting priority 0; policy accept;
 416		meta oif veth0 masquerade $natflags
 417	}
 418}
 419EOF
 420	if [ $? -ne 0 ]; then
 421		echo "SKIP: Could not add add $family masquerade hook"
 422		return $ksft_skip
 423	fi
 424
 425	if ! ip netns exec "$ns2" ping -q -c 1 dead:1::99 > /dev/null;then
 426		echo "ERROR: cannot ping $ns1 from $ns2 with active $family masquerade $natflags"
 427		lret=1
 428	fi
 429
 430	# ns1 should have seen packets from ns0, due to masquerade
 431	expect="packets 1 bytes 104"
 432	for dir in "in6" "out6" ; do
 433		if ! ip netns exec "$ns1" nft list counter inet filter "ns0${dir}" | grep -q "$expect";then
 434			bad_counter "$ns1" ns0$dir "$expect" "test_masquerade6 3"
 435			lret=1
 436		fi
 437
 438		if ! ip netns exec "$ns2" nft list counter inet filter "ns1${dir}" | grep -q "$expect";then
 439			bad_counter "$ns2" ns1$dir "$expect" "test_masquerade6 4"
 440			lret=1
 441		fi
 442	done
 443
 444	# ns1 should not have seen packets from ns2, due to masquerade
 445	expect="packets 0 bytes 0"
 446	for dir in "in6" "out6" ; do
 447		if ! ip netns exec "$ns1" nft list counter inet filter "ns2${dir}" | grep -q "$expect";then
 448			bad_counter "$ns1" ns0$dir "$expect" "test_masquerade6 5"
 449			lret=1
 450		fi
 451
 452		if ! ip netns exec "$ns0" nft list counter inet filter "ns1${dir}" | grep -q "$expect";then
 453			bad_counter "$ns0" "ns1$dir" "$expect" "test_masquerade6 6"
 454			lret=1
 455		fi
 456	done
 457
 458	if ! ip netns exec "$ns2" ping -q -c 1 dead:1::99 > /dev/null;then
 459		echo "ERROR: cannot ping $ns1 from $ns2 with active ipv6 masquerade $natflags (attempt 2)"
 460		lret=1
 461	fi
 462
 463	if ! ip netns exec "$ns0" nft flush chain "$family" nat postrouting;then
 464		echo "ERROR: Could not flush $family nat postrouting" 1>&2
 465		lret=1
 466	fi
 467
 468	test $lret -eq 0 && echo "PASS: $family IPv6 masquerade $natflags for $ns2"
 469
 470	return $lret
 471}
 472
 473test_masquerade()
 474{
 475	local family=$1
 476	local natflags=$2
 477	local lret=0
 478
 479	ip netns exec "$ns0" sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
 480	ip netns exec "$ns0" sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
 481
 482	if ! ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null;then
 483		echo "ERROR: cannot ping $ns1 from $ns2 $natflags"
 484		lret=1
 485	fi
 486
 487	expect="packets 1 bytes 84"
 488	for dir in "in" "out" ; do
 489		if ! ip netns exec "$ns1" nft list counter inet filter "ns2${dir}" | grep -q "$expect";then
 490			bad_counter "$ns1" "ns2$dir" "$expect" "test_masquerade 1"
 491			lret=1
 492		fi
 493
 494		if ! ip netns exec "$ns2" nft list counter inet filter "ns1${dir}" | grep -q "$expect";then
 495			bad_counter "$ns2" "ns1$dir" "$expect" "test_masquerade 2"
 496			lret=1
 497		fi
 498	done
 499
 500	reset_counters
 501
 502# add masquerading rule
 503ip netns exec "$ns0" nft -f /dev/stdin <<EOF
 504table $family nat {
 505	chain postrouting {
 506		type nat hook postrouting priority 0; policy accept;
 507		meta oif veth0 masquerade $natflags
 508	}
 509}
 510EOF
 511	if [ $? -ne 0 ]; then
 512		echo "SKIP: Could not add add $family masquerade hook"
 513		return $ksft_skip
 514	fi
 515
 516	if ! ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null;then
 517		echo "ERROR: cannot ping $ns1 from $ns2 with active $family masquerade $natflags"
 518		lret=1
 519	fi
 520
 521	# ns1 should have seen packets from ns0, due to masquerade
 522	expect="packets 1 bytes 84"
 523	for dir in "in" "out" ; do
 524		if ! ip netns exec "$ns1" nft list counter inet filter "ns0${dir}" | grep -q "$expect";then
 525			bad_counter "$ns1" "ns0$dir" "$expect" "test_masquerade 3"
 526			lret=1
 527		fi
 528
 529		if ! ip netns exec "$ns2" nft list counter inet filter "ns1${dir}" | grep -q "$expect";then
 530			bad_counter "$ns2" "ns1$dir" "$expect" "test_masquerade 4"
 531			lret=1
 532		fi
 533	done
 534
 535	# ns1 should not have seen packets from ns2, due to masquerade
 536	expect="packets 0 bytes 0"
 537	for dir in "in" "out" ; do
 538		if ! ip netns exec "$ns1" nft list counter inet filter "ns2${dir}" | grep -q "$expect";then
 539			bad_counter "$ns1" "ns0$dir" "$expect" "test_masquerade 5"
 540			lret=1
 541		fi
 542
 543		if ! ip netns exec "$ns0" nft list counter inet filter "ns1${dir}" | grep -q "$expect";then
 544			bad_counter "$ns0" "ns1$dir" "$expect" "test_masquerade 6"
 545			lret=1
 546		fi
 547	done
 548
 549	if ! ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null;then
 550		echo "ERROR: cannot ping $ns1 from $ns2 with active ip masquerade $natflags (attempt 2)"
 551		lret=1
 552	fi
 553
 554	if ! ip netns exec "$ns0" nft flush chain "$family" nat postrouting; then
 555		echo "ERROR: Could not flush $family nat postrouting" 1>&2
 556		lret=1
 557	fi
 558
 559	test $lret -eq 0 && echo "PASS: $family IP masquerade $natflags for $ns2"
 560
 561	return $lret
 562}
 563
 564test_redirect6()
 565{
 566	local family=$1
 567	local lret=0
 568
 569	ip netns exec "$ns0" sysctl net.ipv6.conf.all.forwarding=1 > /dev/null
 570
 571	if ! ip netns exec "$ns2" ping -q -c 1 dead:1::99 > /dev/null;then
 572		echo "ERROR: cannot ping $ns1 from $ns2 via ipv6"
 573		lret=1
 574	fi
 575
 576	expect="packets 1 bytes 104"
 577	for dir in "in6" "out6" ; do
 578		if ! ip netns exec "$ns1" nft list counter inet filter "ns2${dir}" | grep -q "$expect";then
 579			bad_counter "$ns1" ns2$dir "$expect" "test_redirect6 1"
 580			lret=1
 581		fi
 582
 583		if ! ip netns exec "$ns2" nft list counter inet filter "ns1${dir}" | grep -q "$expect";then
 584			bad_counter "$ns2" ns1$dir "$expect" "test_redirect6 2"
 585			lret=1
 586		fi
 587	done
 588
 589	reset_counters
 590
 591# add redirect rule
 592ip netns exec "$ns0" nft -f /dev/stdin <<EOF
 593table $family nat {
 594	chain prerouting {
 595		type nat hook prerouting priority 0; policy accept;
 596		meta iif veth1 meta l4proto icmpv6 ip6 saddr dead:2::99 ip6 daddr dead:1::99 redirect
 597	}
 598}
 599EOF
 600	if [ $? -ne 0 ]; then
 601		echo "SKIP: Could not add add $family redirect hook"
 602		return $ksft_skip
 603	fi
 604
 605	if ! ip netns exec "$ns2" ping -q -c 1 dead:1::99 > /dev/null;then
 606		echo "ERROR: cannot ping $ns1 from $ns2 via ipv6 with active $family redirect"
 607		lret=1
 608	fi
 609
 610	# ns1 should have seen no packets from ns2, due to redirection
 611	expect="packets 0 bytes 0"
 612	for dir in "in6" "out6" ; do
 613		if ! ip netns exec "$ns1" nft list counter inet filter "ns2${dir}" | grep -q "$expect";then
 614			bad_counter "$ns1" ns0$dir "$expect" "test_redirect6 3"
 615			lret=1
 616		fi
 617	done
 618
 619	# ns0 should have seen packets from ns2, due to masquerade
 620	expect="packets 1 bytes 104"
 621	for dir in "in6" "out6" ; do
 622		if ! ip netns exec "$ns0" nft list counter inet filter "ns2${dir}" | grep -q "$expect";then
 623			bad_counter "$ns1" ns0$dir "$expect" "test_redirect6 4"
 624			lret=1
 625		fi
 626	done
 627
 628	if ! ip netns exec "$ns0" nft delete table "$family" nat;then
 629		echo "ERROR: Could not delete $family nat table" 1>&2
 630		lret=1
 631	fi
 632
 633	test $lret -eq 0 && echo "PASS: $family IPv6 redirection for $ns2"
 634
 635	return $lret
 636}
 637
 638test_redirect()
 639{
 640	local family=$1
 641	local lret=0
 642
 643	ip netns exec "$ns0" sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
 644	ip netns exec "$ns0" sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
 645
 646	if ! ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null;then
 647		echo "ERROR: cannot ping $ns1 from $ns2"
 648		lret=1
 649	fi
 650
 651	expect="packets 1 bytes 84"
 652	for dir in "in" "out" ; do
 653		if ! ip netns exec "$ns1" nft list counter inet filter "ns2${dir}" | grep -q "$expect";then
 654			bad_counter "$ns1" "$ns2$dir" "$expect" "test_redirect 1"
 655			lret=1
 656		fi
 657
 658		if ! ip netns exec "$ns2" nft list counter inet filter ns1${dir} | grep -q "$expect";then
 659			bad_counter "$ns2" ns1$dir "$expect" "test_redirect 2"
 660			lret=1
 661		fi
 662	done
 663
 664	reset_counters
 665
 666# add redirect rule
 667ip netns exec "$ns0" nft -f /dev/stdin <<EOF
 668table $family nat {
 669	chain prerouting {
 670		type nat hook prerouting priority 0; policy accept;
 671		meta iif veth1 ip protocol icmp ip saddr 10.0.2.99 ip daddr 10.0.1.99 redirect
 672	}
 673}
 674EOF
 675	if [ $? -ne 0 ]; then
 676		echo "SKIP: Could not add add $family redirect hook"
 677		return $ksft_skip
 678	fi
 679
 680	if ! ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null;then
 681		echo "ERROR: cannot ping $ns1 from $ns2 with active $family ip redirect"
 682		lret=1
 683	fi
 684
 685	# ns1 should have seen no packets from ns2, due to redirection
 686	expect="packets 0 bytes 0"
 687	for dir in "in" "out" ; do
 688
 689		if ! ip netns exec "$ns1" nft list counter inet filter "ns2${dir}" | grep -q "$expect";then
 690			bad_counter "$ns1" ns0$dir "$expect" "test_redirect 3"
 691			lret=1
 692		fi
 693	done
 694
 695	# ns0 should have seen packets from ns2, due to masquerade
 696	expect="packets 1 bytes 84"
 697	for dir in "in" "out" ; do
 698		if ! ip netns exec "$ns0" nft list counter inet filter "ns2${dir}" | grep -q "$expect";then
 699			bad_counter "$ns0" ns0$dir "$expect" "test_redirect 4"
 700			lret=1
 701		fi
 702	done
 703
 704	if ! ip netns exec "$ns0" nft delete table "$family" nat;then
 705		echo "ERROR: Could not delete $family nat table" 1>&2
 706		lret=1
 707	fi
 708
 709	test $lret -eq 0 && echo "PASS: $family IP redirection for $ns2"
 710
 711	return $lret
 712}
 713
 714# test port shadowing.
 715# create two listening services, one on router (ns0), one
 716# on client (ns2), which is masqueraded from ns1 point of view.
 717# ns2 sends udp packet coming from service port to ns1, on a highport.
 718# Later, if n1 uses same highport to connect to ns0:service, packet
 719# might be port-forwarded to ns2 instead.
 720
 721# second argument tells if we expect the 'fake-entry' to take effect
 722# (CLIENT) or not (ROUTER).
 723test_port_shadow()
 724{
 725	local test=$1
 726	local expect=$2
 727	local daddrc="10.0.1.99"
 728	local daddrs="10.0.1.1"
 729	local result=""
 730	local logmsg=""
 731
 732	# make shadow entry, from client (ns2), going to (ns1), port 41404, sport 1405.
 733	echo "fake-entry" | ip netns exec "$ns2" timeout 1 socat -u STDIN UDP:"$daddrc":41404,sourceport=1405
 734
 735	echo ROUTER | ip netns exec "$ns0" timeout 3 socat -T 3 -u STDIN UDP4-LISTEN:1405 2>/dev/null &
 736	local sc_r=$!
 737	echo CLIENT | ip netns exec "$ns2" timeout 3 socat -T 3 -u STDIN UDP4-LISTEN:1405,reuseport 2>/dev/null &
 738	local sc_c=$!
 739
 740	busywait $BUSYWAIT_TIMEOUT listener_ready "$ns0" 1405 "-u"
 741	busywait $BUSYWAIT_TIMEOUT listener_ready "$ns2" 1405 "-u"
 742
 743	# ns1 tries to connect to ns0:1405.  With default settings this should connect
 744	# to client, it matches the conntrack entry created above.
 745
 746	result=$(echo "data" | ip netns exec "$ns1" timeout 1 socat - UDP:"$daddrs":1405,sourceport=41404)
 747
 748	if [ "$result" = "$expect" ] ;then
 749		echo "PASS: portshadow test $test: got reply from ${expect}${logmsg}"
 750	else
 751		echo "ERROR: portshadow test $test: got reply from \"$result\", not $expect as intended"
 752		ret=1
 753	fi
 754
 755	kill $sc_r $sc_c 2>/dev/null
 756
 757	# flush udp entries for next test round, if any
 758	ip netns exec "$ns0" conntrack -F >/dev/null 2>&1
 759}
 760
 761# This prevents port shadow of router service via packet filter,
 762# packets claiming to originate from service port from internal
 763# network are dropped.
 764test_port_shadow_filter()
 765{
 766	local family=$1
 767
 768ip netns exec "$ns0" nft -f /dev/stdin <<EOF
 769table $family filter {
 770	chain forward {
 771		type filter hook forward priority 0; policy accept;
 772		meta iif veth1 udp sport 1405 drop
 773	}
 774}
 775EOF
 776	test_port_shadow "port-filter" "ROUTER"
 777
 778	ip netns exec "$ns0" nft delete table "$family" filter
 779}
 780
 781# This prevents port shadow of router service via notrack.
 782test_port_shadow_notrack()
 783{
 784	local family=$1
 785
 786ip netns exec "$ns0" nft -f /dev/stdin <<EOF
 787table $family raw {
 788	chain prerouting {
 789		type filter hook prerouting priority -300; policy accept;
 790		meta iif veth0 udp dport 1405 notrack
 791	}
 792	chain output {
 793		type filter hook output priority -300; policy accept;
 794		meta oif veth0 udp sport 1405 notrack
 795	}
 796}
 797EOF
 798	test_port_shadow "port-notrack" "ROUTER"
 799
 800	ip netns exec "$ns0" nft delete table "$family" raw
 801}
 802
 803# This prevents port shadow of router service via sport remap.
 804test_port_shadow_pat()
 805{
 806	local family=$1
 807
 808ip netns exec "$ns0" nft -f /dev/stdin <<EOF
 809table $family pat {
 810	chain postrouting {
 811		type nat hook postrouting priority -1; policy accept;
 812		meta iif veth1 udp sport <= 1405 masquerade to : 1406-65535 random
 813	}
 814}
 815EOF
 816	test_port_shadow "pat" "ROUTER"
 817
 818	ip netns exec "$ns0" nft delete table "$family" pat
 819}
 820
 821test_port_shadowing()
 822{
 823	local family="ip"
 824
 825	if ! conntrack -h >/dev/null 2>&1;then
 826		echo "SKIP: Could not run nat port shadowing test without conntrack tool"
 827		return
 828	fi
 829
 830	if ! socat -h > /dev/null 2>&1;then
 831		echo "SKIP: Could not run nat port shadowing test without socat tool"
 832		return
 833	fi
 834
 835	ip netns exec "$ns0" sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
 836	ip netns exec "$ns0" sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
 837
 838	ip netns exec "$ns0" nft -f /dev/stdin <<EOF
 839table $family nat {
 840	chain postrouting {
 841		type nat hook postrouting priority 0; policy accept;
 842		meta oif veth0 masquerade
 843	}
 844}
 845EOF
 846	if [ $? -ne 0 ]; then
 847		echo "SKIP: Could not add add $family masquerade hook"
 848		return $ksft_skip
 849	fi
 850
 851	# test default behaviour. Packet from ns1 to ns0 is redirected to ns2.
 852	test_port_shadow "default" "CLIENT"
 853
 854	# test packet filter based mitigation: prevent forwarding of
 855	# packets claiming to come from the service port.
 856	test_port_shadow_filter "$family"
 857
 858	# test conntrack based mitigation: connections going or coming
 859	# from router:service bypass connection tracking.
 860	test_port_shadow_notrack "$family"
 861
 862	# test nat based mitigation: forwarded packets coming from service port
 863	# are masqueraded with random highport.
 864	test_port_shadow_pat "$family"
 865
 866	ip netns exec "$ns0" nft delete table $family nat
 867}
 868
 869file_cmp()
 870{
 871	local infile="$1"
 872	local outfile="$2"
 873
 874	if ! cmp "$infile" "$outfile";then
 875		echo -n "Infile "
 876		ls -l "$infile"
 877		echo -n "Outfile "
 878		ls -l "$outfile"
 879		echo "ERROR: in and output file mismatch when checking $msg" 1>&1
 880		ret=1
 881		return 1
 882	fi
 883
 884	return 0
 885}
 886
 887test_stateless_nat_ip()
 888{
 889	local lret=0
 890
 891	ip netns exec "$ns0" sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
 892	ip netns exec "$ns0" sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
 893
 894	if ! ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null;then
 895		echo "ERROR: cannot ping $ns1 from $ns2 before loading stateless rules"
 896		return 1
 897	fi
 898
 899ip netns exec "$ns0" nft -f /dev/stdin <<EOF
 900table ip stateless {
 901	map xlate_in {
 902		typeof meta iifname . ip saddr . ip daddr : ip daddr
 903		elements = {
 904			"veth1" . 10.0.2.99 . 10.0.1.99 : 10.0.2.2,
 905		}
 906	}
 907	map xlate_out {
 908		typeof meta iifname . ip saddr . ip daddr : ip daddr
 909		elements = {
 910			"veth0" . 10.0.1.99 . 10.0.2.2 : 10.0.2.99
 911		}
 912	}
 913
 914	chain prerouting {
 915		type filter hook prerouting priority -400; policy accept;
 916		ip saddr set meta iifname . ip saddr . ip daddr map @xlate_in
 917		ip daddr set meta iifname . ip saddr . ip daddr map @xlate_out
 918	}
 919}
 920EOF
 921	if [ $? -ne 0 ]; then
 922		echo "SKIP: Could not add ip statless rules"
 923		return $ksft_skip
 924	fi
 925
 926	reset_counters
 927
 928	if ! ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null; then
 929		echo "ERROR: cannot ping $ns1 from $ns2 with stateless rules"
 930		lret=1
 931	fi
 932
 933	# ns1 should have seen packets from .2.2, due to stateless rewrite.
 934	expect="packets 1 bytes 84"
 935	if ! ip netns exec "$ns1" nft list counter inet filter ns0insl | grep -q "$expect";then
 936		bad_counter "$ns1" ns0insl "$expect" "test_stateless 1"
 937		lret=1
 938	fi
 939
 940	for dir in "in" "out" ; do
 941		if ! ip netns exec "$ns2" nft list counter inet filter ns1${dir} | grep -q "$expect";then
 942			bad_counter "$ns2" ns1$dir "$expect" "test_stateless 2"
 943			lret=1
 944		fi
 945	done
 946
 947	# ns1 should not have seen packets from ns2, due to masquerade
 948	expect="packets 0 bytes 0"
 949	for dir in "in" "out" ; do
 950		if ! ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect";then
 951			bad_counter "$ns1" ns0$dir "$expect" "test_stateless 3"
 952			lret=1
 953		fi
 954
 955		if ! ip netns exec "$ns0" nft list counter inet filter ns1${dir} | grep -q "$expect";then
 956			bad_counter "$ns0" ns1$dir "$expect" "test_stateless 4"
 957			lret=1
 958		fi
 959	done
 960
 961	reset_counters
 962
 963	if ! socat -h > /dev/null 2>&1;then
 964		echo "SKIP: Could not run stateless nat frag test without socat tool"
 965		if [ $lret -eq 0 ]; then
 966			return $ksft_skip
 967		fi
 968
 969		ip netns exec "$ns0" nft delete table ip stateless
 970		return $lret
 971	fi
 972
 973	dd if=/dev/urandom of="$INFILE" bs=4096 count=1 2>/dev/null
 974
 975	ip netns exec "$ns1" timeout 3 socat -u UDP4-RECV:4233 OPEN:"$OUTFILE" < /dev/null 2>/dev/null &
 976
 977	busywait $BUSYWAIT_TIMEOUT listener_ready "$ns1" 4233 "-u"
 978
 979	# re-do with large ping -> ip fragmentation
 980	if ! ip netns exec "$ns2" timeout 3 socat -u STDIN UDP4-SENDTO:"10.0.1.99:4233" < "$INFILE" > /dev/null;then
 981		echo "ERROR: failed to test udp $ns1 to $ns2 with stateless ip nat" 1>&2
 982		lret=1
 983	fi
 984
 985	wait
 986
 987	file_cmp "$INFILE" "$OUTFILE" "udp with stateless nat" || lret=1
 988
 989	:> "$OUTFILE"
 990
 991	# ns1 should have seen packets from 2.2, due to stateless rewrite.
 992	expect="packets 3 bytes 4164"
 993	if ! ip netns exec "$ns1" nft list counter inet filter ns0insl | grep -q "$expect";then
 994		bad_counter "$ns1" ns0insl "$expect" "test_stateless 5"
 995		lret=1
 996	fi
 997
 998	if ! ip netns exec "$ns0" nft delete table ip stateless; then
 999		echo "ERROR: Could not delete table ip stateless" 1>&2
1000		lret=1
1001	fi
1002
1003	test $lret -eq 0 && echo "PASS: IP statless for $ns2"
1004
1005	return $lret
1006}
1007
1008test_dnat_clash()
1009{
1010	local lret=0
1011
1012	if ! socat -h > /dev/null 2>&1;then
1013		echo "SKIP: Could not run dnat clash test without socat tool"
1014		[ $ret -eq 0 ] && ret=$ksft_skip
1015		return $ksft_skip
1016	fi
1017
1018ip netns exec "$ns0" nft -f /dev/stdin <<EOF
1019flush ruleset
1020table ip dnat-test {
1021 chain prerouting {
1022  type nat hook prerouting priority dstnat; policy accept;
1023  ip daddr 10.0.2.1 udp dport 1234 counter dnat to 10.0.1.1:1234
1024 }
1025}
1026EOF
1027	if [ $? -ne 0 ]; then
1028		echo "SKIP: Could not add dnat rules"
1029		[ $ret -eq 0 ] && ret=$ksft_skip
1030		return $ksft_skip
1031	fi
1032
1033	local udpdaddr="10.0.2.1"
1034	for i in 1 2;do
1035		echo "PING $udpdaddr" > "$INFILE"
1036		echo "PONG 10.0.1.1 step $i" | ip netns exec "$ns0" timeout 3 socat STDIO UDP4-LISTEN:1234,bind=10.0.1.1 > "$OUTFILE" 2>/dev/null &
1037		local lpid=$!
1038
1039		busywait $BUSYWAIT_TIMEOUT listener_ready "$ns0" 1234 "-u"
1040
1041		result=$(ip netns exec "$ns1" timeout 3 socat STDIO UDP4-SENDTO:"$udpdaddr:1234,sourceport=4321" < "$INFILE")
1042		udpdaddr="10.0.1.1"
1043
1044		if [ "$result" != "PONG 10.0.1.1 step $i" ] ; then
1045			echo "ERROR: failed to test udp $ns1 to $ns2 with dnat rule step $i, result: \"$result\"" 1>&2
1046			lret=1
1047			ret=1
1048		fi
1049
1050		wait
1051
1052		file_cmp "$INFILE" "$OUTFILE" "udp dnat step $i" || lret=1
1053
1054		:> "$OUTFILE"
1055	done
1056
1057	test $lret -eq 0 && echo "PASS: IP dnat clash $ns1:$ns2"
1058
1059	ip netns exec "$ns0" nft flush ruleset
1060
1061	return $lret
1062}
1063
1064# ip netns exec "$ns0" ping -c 1 -q 10.0.$i.99
1065for i in "$ns0" "$ns1" "$ns2" ;do
1066ip netns exec "$i" nft -f /dev/stdin <<EOF
1067table inet filter {
1068	counter ns0in {}
1069	counter ns1in {}
1070	counter ns2in {}
1071
1072	counter ns0out {}
1073	counter ns1out {}
1074	counter ns2out {}
1075
1076	counter ns0in6 {}
1077	counter ns1in6 {}
1078	counter ns2in6 {}
1079
1080	counter ns0out6 {}
1081	counter ns1out6 {}
1082	counter ns2out6 {}
1083
1084	map nsincounter {
1085		type ipv4_addr : counter
1086		elements = { 10.0.1.1 : "ns0in",
1087			     10.0.2.1 : "ns0in",
1088			     10.0.1.99 : "ns1in",
1089			     10.0.2.99 : "ns2in" }
1090	}
1091
1092	map nsincounter6 {
1093		type ipv6_addr : counter
1094		elements = { dead:1::1 : "ns0in6",
1095			     dead:2::1 : "ns0in6",
1096			     dead:1::99 : "ns1in6",
1097			     dead:2::99 : "ns2in6" }
1098	}
1099
1100	map nsoutcounter {
1101		type ipv4_addr : counter
1102		elements = { 10.0.1.1 : "ns0out",
1103			     10.0.2.1 : "ns0out",
1104			     10.0.1.99: "ns1out",
1105			     10.0.2.99: "ns2out" }
1106	}
1107
1108	map nsoutcounter6 {
1109		type ipv6_addr : counter
1110		elements = { dead:1::1 : "ns0out6",
1111			     dead:2::1 : "ns0out6",
1112			     dead:1::99 : "ns1out6",
1113			     dead:2::99 : "ns2out6" }
1114	}
1115
1116	chain input {
1117		type filter hook input priority 0; policy accept;
1118		counter name ip saddr map @nsincounter
1119		icmpv6 type { "echo-request", "echo-reply" } counter name ip6 saddr map @nsincounter6
1120	}
1121	chain output {
1122		type filter hook output priority 0; policy accept;
1123		counter name ip daddr map @nsoutcounter
1124		icmpv6 type { "echo-request", "echo-reply" } counter name ip6 daddr map @nsoutcounter6
1125	}
1126}
1127EOF
1128done
1129
1130# special case for stateless nat check, counter needs to
1131# be done before (input) ip defragmentation
1132ip netns exec "$ns1" nft -f /dev/stdin <<EOF
1133table inet filter {
1134	counter ns0insl {}
1135
1136	chain pre {
1137		type filter hook prerouting priority -400; policy accept;
1138		ip saddr 10.0.2.2 counter name "ns0insl"
1139	}
1140}
1141EOF
1142
1143ping_basic()
1144{
1145	i="$1"
1146	if ! ip netns exec "$ns0" ping -c 1 -q 10.0."$i".99 > /dev/null;then
1147		echo "ERROR: Could not reach other namespace(s)" 1>&2
1148		ret=1
1149	fi
1150
1151	if ! ip netns exec "$ns0" ping -c 1 -q dead:"$i"::99 > /dev/null;then
1152		echo "ERROR: Could not reach other namespace(s) via ipv6" 1>&2
1153		ret=1
1154	fi
1155}
1156
1157test_basic_conn()
1158{
1159	local nsexec
1160	name="$1"
1161
1162	nsexec=$(eval echo \$"$1")
1163
1164	ping_basic 1
1165	ping_basic 2
1166
1167	if ! check_counters "$nsexec";then
1168		return 1
1169	fi
1170
1171	if ! check_ns0_counters "$name";then
1172		return 1
1173	fi
1174
1175	reset_counters
1176	return 0
1177}
1178
1179if ! test_basic_conn "ns1" ; then
1180	echo "ERROR: basic test for ns1 failed" 1>&2
1181	exit 1
1182fi
1183if ! test_basic_conn "ns2"; then
1184	echo "ERROR: basic test for ns1 failed" 1>&2
1185fi
1186
1187if [ $ret -eq 0 ];then
1188	echo "PASS: netns routing/connectivity: $ns0 can reach $ns1 and $ns2"
1189fi
1190
1191reset_counters
1192test_local_dnat ip
1193test_local_dnat6 ip6
1194
1195reset_counters
1196test_local_dnat_portonly inet 10.0.1.99
1197
1198reset_counters
1199$test_inet_nat && test_local_dnat inet
1200$test_inet_nat && test_local_dnat6 inet
1201
1202for flags in "" "fully-random"; do
1203reset_counters
1204test_masquerade ip $flags
1205test_masquerade6 ip6 $flags
1206reset_counters
1207$test_inet_nat && test_masquerade inet $flags
1208$test_inet_nat && test_masquerade6 inet $flags
1209done
1210
1211reset_counters
1212test_redirect ip
1213test_redirect6 ip6
1214reset_counters
1215$test_inet_nat && test_redirect inet
1216$test_inet_nat && test_redirect6 inet
1217
1218test_port_shadowing
1219test_stateless_nat_ip
1220test_dnat_clash
1221
1222if [ $ret -ne 0 ];then
1223	echo -n "FAIL: "
1224	nft --version
1225fi
1226
1227exit $ret
~/ Read-only file preview.