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