1#!/bin/bash
2#
3# check that ICMP df-needed/pkttoobig icmp are set are set as related
4# state
5#
6# Setup is:
7#
8# nsclient1 -> nsrouter1 -> nsrouter2 -> nsclient2
9# MTU 1500, except for nsrouter2 <-> nsclient2 link (1280).
10# ping nsclient2 from nsclient1, checking that conntrack did set RELATED
11# 'fragmentation needed' icmp packet.
12#
13# In addition, nsrouter1 will perform IP masquerading, i.e. also
14# check the icmp errors are propagated to the correct host as per
15# nat of "established" icmp-echo "connection".
16
17# Kselftest framework requirement - SKIP code is 4.
18ksft_skip=4
19ret=0
20
21nft --version > /dev/null 2>&1
22if [ $? -ne 0 ];then
23	echo "SKIP: Could not run test without nft tool"
24	exit $ksft_skip
25fi
26
27ip -Version > /dev/null 2>&1
28if [ $? -ne 0 ];then
29	echo "SKIP: Could not run test without ip tool"
30	exit $ksft_skip
31fi
32
33cleanup() {
34	for i in 1 2;do ip netns del nsclient$i;done
35	for i in 1 2;do ip netns del nsrouter$i;done
36}
37
38trap cleanup EXIT
39
40ipv4() {
41    echo -n 192.168.$1.2
42}
43
44ipv6 () {
45    echo -n dead:$1::2
46}
47
48check_counter()
49{
50	ns=$1
51	name=$2
52	expect=$3
53	local lret=0
54
55	cnt=$(ip netns exec $ns nft list counter inet filter "$name" | grep -q "$expect")
56	if [ $? -ne 0 ]; then
57		echo "ERROR: counter $name in $ns has unexpected value (expected $expect)" 1>&2
58		ip netns exec $ns nft list counter inet filter "$name" 1>&2
59		lret=1
60	fi
61
62	return $lret
63}
64
65check_unknown()
66{
67	expect="packets 0 bytes 0"
68	for n in nsclient1 nsclient2 nsrouter1 nsrouter2; do
69		check_counter $n "unknown" "$expect"
70		if [ $? -ne 0 ] ;then
71			return 1
72		fi
73	done
74
75	return 0
76}
77
78for n in nsclient1 nsclient2 nsrouter1 nsrouter2; do
79  ip netns add $n
80  ip -net $n link set lo up
81done
82
83DEV=veth0
84ip link add $DEV netns nsclient1 type veth peer name eth1 netns nsrouter1
85DEV=veth0
86ip link add $DEV netns nsclient2 type veth peer name eth1 netns nsrouter2
87
88DEV=veth0
89ip link add $DEV netns nsrouter1 type veth peer name eth2 netns nsrouter2
90
91DEV=veth0
92for i in 1 2; do
93    ip -net nsclient$i link set $DEV up
94    ip -net nsclient$i addr add $(ipv4 $i)/24 dev $DEV
95    ip -net nsclient$i addr add $(ipv6 $i)/64 dev $DEV
96done
97
98ip -net nsrouter1 link set eth1 up
99ip -net nsrouter1 link set veth0 up
100
101ip -net nsrouter2 link set eth1 up
102ip -net nsrouter2 link set eth2 up
103
104ip -net nsclient1 route add default via 192.168.1.1
105ip -net nsclient1 -6 route add default via dead:1::1
106
107ip -net nsclient2 route add default via 192.168.2.1
108ip -net nsclient2 route add default via dead:2::1
109
110i=3
111ip -net nsrouter1 addr add 192.168.1.1/24 dev eth1
112ip -net nsrouter1 addr add 192.168.3.1/24 dev veth0
113ip -net nsrouter1 addr add dead:1::1/64 dev eth1
114ip -net nsrouter1 addr add dead:3::1/64 dev veth0
115ip -net nsrouter1 route add default via 192.168.3.10
116ip -net nsrouter1 -6 route add default via dead:3::10
117
118ip -net nsrouter2 addr add 192.168.2.1/24 dev eth1
119ip -net nsrouter2 addr add 192.168.3.10/24 dev eth2
120ip -net nsrouter2 addr add dead:2::1/64 dev eth1
121ip -net nsrouter2 addr add dead:3::10/64 dev eth2
122ip -net nsrouter2 route add default via 192.168.3.1
123ip -net nsrouter2 route add default via dead:3::1
124
125sleep 2
126for i in 4 6; do
127	ip netns exec nsrouter1 sysctl -q net.ipv$i.conf.all.forwarding=1
128	ip netns exec nsrouter2 sysctl -q net.ipv$i.conf.all.forwarding=1
129done
130
131for netns in nsrouter1 nsrouter2; do
132ip netns exec $netns nft -f - <<EOF
133table inet filter {
134	counter unknown { }
135	counter related { }
136	chain forward {
137		type filter hook forward priority 0; policy accept;
138		meta l4proto icmpv6 icmpv6 type "packet-too-big" ct state "related" counter name "related" accept
139		meta l4proto icmp icmp type "destination-unreachable" ct state "related" counter name "related" accept
140		meta l4proto { icmp, icmpv6 } ct state new,established accept
141		counter name "unknown" drop
142	}
143}
144EOF
145done
146
147ip netns exec nsclient1 nft -f - <<EOF
148table inet filter {
149	counter unknown { }
150	counter related { }
151	counter redir4 { }
152	counter redir6 { }
153	chain input {
154		type filter hook input priority 0; policy accept;
155
156		icmp type "redirect" ct state "related" counter name "redir4" accept
157		icmpv6 type "nd-redirect" ct state "related" counter name "redir6" accept
158
159		meta l4proto { icmp, icmpv6 } ct state established,untracked accept
160		meta l4proto { icmp, icmpv6 } ct state "related" counter name "related" accept
161
162		counter name "unknown" drop
163	}
164}
165EOF
166
167ip netns exec nsclient2 nft -f - <<EOF
168table inet filter {
169	counter unknown { }
170	counter new { }
171	counter established { }
172
173	chain input {
174		type filter hook input priority 0; policy accept;
175		meta l4proto { icmp, icmpv6 } ct state established,untracked accept
176
177		meta l4proto { icmp, icmpv6 } ct state "new" counter name "new" accept
178		meta l4proto { icmp, icmpv6 } ct state "established" counter name "established" accept
179		counter name "unknown" drop
180	}
181	chain output {
182		type filter hook output priority 0; policy accept;
183		meta l4proto { icmp, icmpv6 } ct state established,untracked accept
184
185		meta l4proto { icmp, icmpv6 } ct state "new" counter name "new"
186		meta l4proto { icmp, icmpv6 } ct state "established" counter name "established"
187		counter name "unknown" drop
188	}
189}
190EOF
191
192
193# make sure NAT core rewrites adress of icmp error if nat is used according to
194# conntrack nat information (icmp error will be directed at nsrouter1 address,
195# but it needs to be routed to nsclient1 address).
196ip netns exec nsrouter1 nft -f - <<EOF
197table ip nat {
198	chain postrouting {
199		type nat hook postrouting priority 0; policy accept;
200		ip protocol icmp oifname "veth0" counter masquerade
201	}
202}
203table ip6 nat {
204	chain postrouting {
205		type nat hook postrouting priority 0; policy accept;
206		ip6 nexthdr icmpv6 oifname "veth0" counter masquerade
207	}
208}
209EOF
210
211ip netns exec nsrouter2 ip link set eth1  mtu 1280
212ip netns exec nsclient2 ip link set veth0 mtu 1280
213sleep 1
214
215ip netns exec nsclient1 ping -c 1 -s 1000 -q -M do 192.168.2.2 >/dev/null
216if [ $? -ne 0 ]; then
217	echo "ERROR: netns ip routing/connectivity broken" 1>&2
218	cleanup
219	exit 1
220fi
221ip netns exec nsclient1 ping6 -q -c 1 -s 1000 dead:2::2 >/dev/null
222if [ $? -ne 0 ]; then
223	echo "ERROR: netns ipv6 routing/connectivity broken" 1>&2
224	cleanup
225	exit 1
226fi
227
228check_unknown
229if [ $? -ne 0 ]; then
230	ret=1
231fi
232
233expect="packets 0 bytes 0"
234for netns in nsrouter1 nsrouter2 nsclient1;do
235	check_counter "$netns" "related" "$expect"
236	if [ $? -ne 0 ]; then
237		ret=1
238	fi
239done
240
241expect="packets 2 bytes 2076"
242check_counter nsclient2 "new" "$expect"
243if [ $? -ne 0 ]; then
244	ret=1
245fi
246
247ip netns exec nsclient1 ping -q -c 1 -s 1300 -M do 192.168.2.2 > /dev/null
248if [ $? -eq 0 ]; then
249	echo "ERROR: ping should have failed with PMTU too big error" 1>&2
250	ret=1
251fi
252
253# nsrouter2 should have generated the icmp error, so
254# related counter should be 0 (its in forward).
255expect="packets 0 bytes 0"
256check_counter "nsrouter2" "related" "$expect"
257if [ $? -ne 0 ]; then
258	ret=1
259fi
260
261# but nsrouter1 should have seen it, same for nsclient1.
262expect="packets 1 bytes 576"
263for netns in nsrouter1 nsclient1;do
264	check_counter "$netns" "related" "$expect"
265	if [ $? -ne 0 ]; then
266		ret=1
267	fi
268done
269
270ip netns exec nsclient1 ping6 -c 1 -s 1300 dead:2::2 > /dev/null
271if [ $? -eq 0 ]; then
272	echo "ERROR: ping6 should have failed with PMTU too big error" 1>&2
273	ret=1
274fi
275
276expect="packets 2 bytes 1856"
277for netns in nsrouter1 nsclient1;do
278	check_counter "$netns" "related" "$expect"
279	if [ $? -ne 0 ]; then
280		ret=1
281	fi
282done
283
284if [ $ret -eq 0 ];then
285	echo "PASS: icmp mtu error had RELATED state"
286else
287	echo "ERROR: icmp error RELATED state test has failed"
288fi
289
290# add 'bad' route,  expect icmp REDIRECT to be generated
291ip netns exec nsclient1 ip route add 192.168.1.42 via 192.168.1.1
292ip netns exec nsclient1 ip route add dead:1::42 via dead:1::1
293
294ip netns exec "nsclient1" ping -q -c 2 192.168.1.42 > /dev/null
295
296expect="packets 1 bytes 112"
297check_counter nsclient1 "redir4" "$expect"
298if [ $? -ne 0 ];then
299	ret=1
300fi
301
302ip netns exec "nsclient1" ping -c 1 dead:1::42 > /dev/null
303expect="packets 1 bytes 192"
304check_counter nsclient1 "redir6" "$expect"
305if [ $? -ne 0 ];then
306	ret=1
307fi
308
309if [ $ret -eq 0 ];then
310	echo "PASS: icmp redirects had RELATED state"
311else
312	echo "ERROR: icmp redirect RELATED state test has failed"
313fi
314
315exit $ret
316