• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/bin/bash
2#
3# Test connection tracking zone and NAT source port reallocation support.
4#
5
6source lib.sh
7
8# Don't increase too much, 2000 clients should work
9# just fine but script can then take several minutes with
10# KASAN/debug builds.
11maxclients=100
12
13have_socat=0
14ret=0
15
16[ "$KSFT_MACHINE_SLOW" = yes ] && maxclients=40
17# client1---.
18#            veth1-.
19#                  |
20#               NAT Gateway --veth0--> Server
21#                  | |
22#            veth2-' |
23# client2---'        |
24#  ....              |
25# clientX----vethX---'
26
27# All clients share identical IP address.
28# NAT Gateway uses policy routing and conntrack zones to isolate client
29# namespaces.  Each client connects to Server, each with colliding tuples:
30#   clientsaddr:10000 -> serveraddr:dport
31#   NAT Gateway is supposed to do port reallocation for each of the
32#   connections.
33
34v4gc1=$(sysctl -n net.ipv4.neigh.default.gc_thresh1 2>/dev/null)
35v4gc2=$(sysctl -n net.ipv4.neigh.default.gc_thresh2 2>/dev/null)
36v4gc3=$(sysctl -n net.ipv4.neigh.default.gc_thresh3 2>/dev/null)
37v6gc1=$(sysctl -n net.ipv6.neigh.default.gc_thresh1 2>/dev/null)
38v6gc2=$(sysctl -n net.ipv6.neigh.default.gc_thresh2 2>/dev/null)
39v6gc3=$(sysctl -n net.ipv6.neigh.default.gc_thresh3 2>/dev/null)
40
41cleanup()
42{
43	cleanup_all_ns
44
45	sysctl -q net.ipv4.neigh.default.gc_thresh1="$v4gc1" 2>/dev/null
46	sysctl -q net.ipv4.neigh.default.gc_thresh2="$v4gc2" 2>/dev/null
47	sysctl -q net.ipv4.neigh.default.gc_thresh3="$v4gc3" 2>/dev/null
48	sysctl -q net.ipv6.neigh.default.gc_thresh1="$v6gc1" 2>/dev/null
49	sysctl -q net.ipv6.neigh.default.gc_thresh2="$v6gc2" 2>/dev/null
50	sysctl -q net.ipv6.neigh.default.gc_thresh3="$v6gc3" 2>/dev/null
51}
52
53checktool "nft --version" echo "run test without nft tool"
54checktool "conntrack -V" "run test without conntrack tool"
55
56if socat -h >/dev/null 2>&1; then
57	have_socat=1
58fi
59
60setup_ns gw srv
61
62trap cleanup EXIT
63
64ip link add veth0 netns "$gw" type veth peer name eth0 netns "$srv"
65ip -net "$gw" link set veth0 up
66ip -net "$srv" link set eth0 up
67
68sysctl -q net.ipv6.neigh.default.gc_thresh1=512  2>/dev/null
69sysctl -q net.ipv6.neigh.default.gc_thresh2=1024 2>/dev/null
70sysctl -q net.ipv6.neigh.default.gc_thresh3=4096 2>/dev/null
71sysctl -q net.ipv4.neigh.default.gc_thresh1=512  2>/dev/null
72sysctl -q net.ipv4.neigh.default.gc_thresh2=1024 2>/dev/null
73sysctl -q net.ipv4.neigh.default.gc_thresh3=4096 2>/dev/null
74
75for i in $(seq 1 "$maxclients");do
76  setup_ns "cl$i"
77
78  cl=$(eval echo \$cl"$i")
79  if ! ip link add veth"$i" netns "$gw" type veth peer name eth0 netns "$cl" > /dev/null 2>&1;then
80    echo "SKIP: No virtual ethernet pair device support in kernel"
81    exit $ksft_skip
82  fi
83done
84
85for i in $(seq 1 "$maxclients");do
86  cl=$(eval echo \$cl"$i")
87  echo netns exec "$cl" ip link set eth0 up
88  echo netns exec "$cl" sysctl -q net.ipv4.tcp_syn_retries=2
89  echo netns exec "$gw" ip link set "veth$i" up
90  echo netns exec "$gw" sysctl -q net.ipv4.conf.veth"$i".arp_ignore=2
91  echo netns exec "$gw" sysctl -q net.ipv4.conf.veth"$i".rp_filter=0
92
93  # clients have same IP addresses.
94  echo netns exec "$cl" ip addr add 10.1.0.3/24 dev eth0
95  echo netns exec "$cl" ip addr add dead:1::3/64 dev eth0 nodad
96  echo netns exec "$cl" ip route add default via 10.1.0.2 dev eth0
97  echo netns exec "$cl" ip route add default via dead:1::2 dev eth0
98
99  # NB: same addresses on client-facing interfaces.
100  echo netns exec "$gw" ip addr add 10.1.0.2/24 dev "veth$i"
101  echo netns exec "$gw" ip addr add dead:1::2/64 dev "veth$i" nodad
102
103  # gw: policy routing
104  echo netns exec "$gw" ip route add 10.1.0.0/24 dev "veth$i" table $((1000+i))
105  echo netns exec "$gw" ip route add dead:1::0/64 dev "veth$i" table $((1000+i))
106  echo netns exec "$gw" ip route add 10.3.0.0/24 dev veth0 table $((1000+i))
107  echo netns exec "$gw" ip route add dead:3::0/64 dev veth0 table $((1000+i))
108  echo netns exec "$gw" ip rule add fwmark "$i" lookup $((1000+i))
109done | ip -batch /dev/stdin
110
111ip -net "$gw" addr add 10.3.0.1/24 dev veth0
112ip -net "$gw" addr add dead:3::1/64 dev veth0 nodad
113
114ip -net "$srv" addr add 10.3.0.99/24 dev eth0
115ip -net "$srv" addr add dead:3::99/64 dev eth0 nodad
116
117ip netns exec "$gw" nft -f /dev/stdin<<EOF
118table inet raw {
119	map iiftomark {
120		type ifname : mark
121	}
122
123	map iiftozone {
124		typeof iifname : ct zone
125	}
126
127	set inicmp {
128		flags dynamic
129		type ipv4_addr . ifname . ipv4_addr
130	}
131	set inflows {
132		flags dynamic
133		type ipv4_addr . inet_service . ifname . ipv4_addr . inet_service
134	}
135
136	set inflows6 {
137		flags dynamic
138		type ipv6_addr . inet_service . ifname . ipv6_addr . inet_service
139	}
140
141	chain prerouting {
142		type filter hook prerouting priority -64000; policy accept;
143		ct original zone set meta iifname map @iiftozone
144		meta mark set meta iifname map @iiftomark
145
146		tcp flags & (syn|ack) == ack add @inflows { ip saddr . tcp sport . meta iifname . ip daddr . tcp dport counter }
147		add @inflows6 { ip6 saddr . tcp sport . meta iifname . ip6 daddr . tcp dport counter }
148		ip protocol icmp add @inicmp { ip saddr . meta iifname . ip daddr counter }
149	}
150
151	chain nat_postrouting {
152		type nat hook postrouting priority 0; policy accept;
153                ct mark set meta mark meta oifname veth0 masquerade
154	}
155
156	chain mangle_prerouting {
157		type filter hook prerouting priority -100; policy accept;
158		ct direction reply meta mark set ct mark
159	}
160}
161EOF
162if [ "$?" -ne 0 ];then
163	echo "SKIP: Could not add nftables rules"
164	exit $ksft_skip
165fi
166
167( echo add element inet raw iiftomark \{
168	for i in $(seq 1 $((maxclients-1))); do
169		echo \"veth"$i"\" : "$i",
170	done
171	echo \"veth"$maxclients"\" : "$maxclients" \}
172	echo add element inet raw iiftozone \{
173	for i in $(seq 1 $((maxclients-1))); do
174		echo \"veth"$i"\" : "$i",
175	done
176	echo \"veth$maxclients\" : $maxclients \}
177) | ip netns exec "$gw" nft -f /dev/stdin
178
179ip netns exec "$gw" sysctl -q net.ipv4.conf.all.forwarding=1 > /dev/null
180ip netns exec "$gw" sysctl -q net.ipv6.conf.all.forwarding=1 > /dev/null
181ip netns exec "$gw" sysctl -q net.ipv4.conf.all.rp_filter=0 >/dev/null
182
183# useful for debugging: allows to use 'ping' from clients to gateway.
184ip netns exec "$gw" sysctl -q net.ipv4.fwmark_reflect=1 > /dev/null
185ip netns exec "$gw" sysctl -q net.ipv6.fwmark_reflect=1 > /dev/null
186
187for i in $(seq 1 "$maxclients"); do
188  cl=$(eval echo \$cl"$i")
189  ip netns exec "$cl" ping -i 0.5 -q -c 3 10.3.0.99 > /dev/null 2>&1 &
190done
191
192wait || ret=1
193
194[ "$ret" -ne 0 ] && "FAIL: Ping failure from $cl" 1>&2
195
196for i in $(seq 1 "$maxclients"); do
197   if ! ip netns exec "$gw" nft get element inet raw inicmp "{ 10.1.0.3 . \"veth$i\" . 10.3.0.99 }" | grep -q "{ 10.1.0.3 . \"veth$i\" . 10.3.0.99 counter packets 3 bytes 252 }"; then
198      ret=1
199      echo "FAIL: counter icmp mismatch for veth$i" 1>&2
200      ip netns exec "$gw" nft get element inet raw inicmp "{ 10.1.0.3 . \"veth$i\" . 10.3.0.99 }" 1>&2
201      break
202   fi
203done
204
205if ! ip netns exec "$gw" nft get element inet raw inicmp "{ 10.3.0.99 . \"veth0\" . 10.3.0.1 }" | grep -q "{ 10.3.0.99 . \"veth0\" . 10.3.0.1 counter packets $((3 * maxclients)) bytes $((252 * maxclients)) }"; then
206    ret=1
207    echo "FAIL: counter icmp mismatch for veth0: { 10.3.0.99 . \"veth0\" . 10.3.0.1 counter packets $((3 * maxclients)) bytes $((252 * maxclients)) }"
208    ip netns exec "$gw" nft get element inet raw inicmp "{ 10.3.99 . \"veth0\" . 10.3.0.1 }" 1>&2
209fi
210
211if [ $ret -eq 0 ]; then
212	echo "PASS: ping test from all $maxclients namespaces"
213fi
214
215if [ $have_socat -eq 0 ];then
216	echo "SKIP: socat not installed"
217	if [ $ret -ne 0 ];then
218	    exit $ret
219	fi
220	exit $ksft_skip
221fi
222
223listener_ready()
224{
225	ss -N "$1" -lnt -o "sport = :5201" | grep -q 5201
226}
227
228ip netns exec "$srv" socat -u TCP-LISTEN:5201,fork STDOUT > /dev/null 2>/dev/null &
229socatpid=$!
230
231busywait 1000 listener_ready "$srv"
232
233for i in $(seq 1 "$maxclients"); do
234  if [ $ret -ne 0 ]; then
235     break
236  fi
237  cl=$(eval echo \$cl"$i")
238  if ! ip netns exec "$cl" socat -4 -u STDIN TCP:10.3.0.99:5201,sourceport=10000 < /dev/null > /dev/null; then
239     echo "FAIL: Failure to connect for $cl" 1>&2
240     ip netns exec "$gw" conntrack -S 1>&2
241     ret=1
242  fi
243done
244if [ $ret -eq 0 ];then
245	echo "PASS: socat connections for all $maxclients net namespaces"
246fi
247
248kill $socatpid
249wait
250
251for i in $(seq 1 "$maxclients"); do
252   if ! ip netns exec "$gw" nft get element inet raw inflows "{ 10.1.0.3 . 10000 . \"veth$i\" . 10.3.0.99 . 5201 }" > /dev/null;then
253      ret=1
254      echo "FAIL: can't find expected tcp entry for veth$i" 1>&2
255      break
256   fi
257done
258if [ $ret -eq 0 ];then
259	echo "PASS: Found client connection for all $maxclients net namespaces"
260fi
261
262if ! ip netns exec "$gw" nft get element inet raw inflows "{ 10.3.0.99 . 5201 . \"veth0\" . 10.3.0.1 . 10000 }" > /dev/null;then
263    ret=1
264    echo "FAIL: cannot find return entry on veth0" 1>&2
265fi
266
267exit $ret
268