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