1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3#
4# Test bridge netfilter + conntrack, a combination that doesn't really work,
5# with multicast/broadcast packets racing for hash table insertion.
6
7#           eth0    br0     eth0
8# setup is: ns1 <->,ns0 <-> ns3
9#           ns2 <-'    `'-> ns4
10
11# Kselftest framework requirement - SKIP code is 4.
12ksft_skip=4
13ret=0
14
15sfx=$(mktemp -u "XXXXXXXX")
16ns0="ns0-$sfx"
17ns1="ns1-$sfx"
18ns2="ns2-$sfx"
19ns3="ns3-$sfx"
20ns4="ns4-$sfx"
21
22ebtables -V > /dev/null 2>&1
23if [ $? -ne 0 ];then
24	echo "SKIP: Could not run test without ebtables"
25	exit $ksft_skip
26fi
27
28ip -Version > /dev/null 2>&1
29if [ $? -ne 0 ];then
30	echo "SKIP: Could not run test without ip tool"
31	exit $ksft_skip
32fi
33
34for i in $(seq 0 4); do
35  eval ip netns add \$ns$i
36done
37
38cleanup() {
39  for i in $(seq 0 4); do eval ip netns del \$ns$i;done
40}
41
42trap cleanup EXIT
43
44do_ping()
45{
46	fromns="$1"
47	dstip="$2"
48
49	ip netns exec $fromns ping -c 1 -q $dstip > /dev/null
50	if [ $? -ne 0 ]; then
51		echo "ERROR: ping from $fromns to $dstip"
52		ip netns exec ${ns0} nft list ruleset
53		ret=1
54	fi
55}
56
57bcast_ping()
58{
59	fromns="$1"
60	dstip="$2"
61
62	for i in $(seq 1 1000); do
63		ip netns exec $fromns ping -q -f -b -c 1 -q $dstip > /dev/null 2>&1
64		if [ $? -ne 0 ]; then
65			echo "ERROR: ping -b from $fromns to $dstip"
66			ip netns exec ${ns0} nft list ruleset
67			fi
68	done
69}
70
71ip link add veth1 netns ${ns0} type veth peer name eth0 netns ${ns1}
72if [ $? -ne 0 ]; then
73	echo "SKIP: Can't create veth device"
74	exit $ksft_skip
75fi
76
77ip link add veth2 netns ${ns0} type veth peer name eth0 netns $ns2
78ip link add veth3 netns ${ns0} type veth peer name eth0 netns $ns3
79ip link add veth4 netns ${ns0} type veth peer name eth0 netns $ns4
80
81ip -net ${ns0} link set lo up
82
83for i in $(seq 1 4); do
84  ip -net ${ns0} link set veth$i up
85done
86
87ip -net ${ns0} link add br0 type bridge stp_state 0 forward_delay 0 nf_call_iptables 1 nf_call_ip6tables 1 nf_call_arptables 1
88if [ $? -ne 0 ]; then
89	echo "SKIP: Can't create bridge br0"
90	exit $ksft_skip
91fi
92
93# make veth0,1,2 part of bridge.
94for i in $(seq 1 3); do
95  ip -net ${ns0} link set veth$i master br0
96done
97
98# add a macvlan on top of the bridge.
99MACVLAN_ADDR=ba:f3:13:37:42:23
100ip -net ${ns0} link add link br0 name macvlan0 type macvlan mode private
101ip -net ${ns0} link set macvlan0 address ${MACVLAN_ADDR}
102ip -net ${ns0} link set macvlan0 up
103ip -net ${ns0} addr add 10.23.0.1/24 dev macvlan0
104
105# add a macvlan on top of veth4.
106MACVLAN_ADDR=ba:f3:13:37:42:24
107ip -net ${ns0} link add link veth4 name macvlan4 type macvlan mode vepa
108ip -net ${ns0} link set macvlan4 address ${MACVLAN_ADDR}
109ip -net ${ns0} link set macvlan4 up
110
111# make the macvlan part of the bridge.
112# veth4 is not a bridge port, only the macvlan on top of it.
113ip -net ${ns0} link set macvlan4 master br0
114
115ip -net ${ns0} link set br0 up
116ip -net ${ns0} addr add 10.0.0.1/24 dev br0
117ip netns exec ${ns0} sysctl -q net.bridge.bridge-nf-call-iptables=1
118ret=$?
119if [ $ret -ne 0 ] ; then
120	echo "SKIP: bridge netfilter not available"
121	ret=$ksft_skip
122fi
123
124# for testing, so namespaces will reply to ping -b probes.
125ip netns exec ${ns0} sysctl -q net.ipv4.icmp_echo_ignore_broadcasts=0
126
127# enable conntrack in ns0 and drop broadcast packets in forward to
128# avoid them from getting confirmed in the postrouting hook before
129# the cloned skb is passed up the stack.
130ip netns exec ${ns0} nft -f - <<EOF
131table ip filter {
132	chain input {
133		type filter hook input priority 1; policy accept
134		iifname br0 counter
135		ct state new accept
136	}
137}
138
139table bridge filter {
140	chain forward {
141		type filter hook forward priority 0; policy accept
142		meta pkttype broadcast ip protocol icmp counter drop
143	}
144}
145EOF
146
147# place 1, 2 & 3 in same subnet, connected via ns0:br0.
148# ns4 is placed in same subnet as well, but its not
149# part of the bridge: the corresponding veth4 is not
150# part of the bridge, only its macvlan interface.
151for i in $(seq 1 4); do
152  eval ip -net \$ns$i link set lo up
153  eval ip -net \$ns$i link set eth0 up
154done
155for i in $(seq 1 2); do
156  eval ip -net \$ns$i addr add 10.0.0.1$i/24 dev eth0
157done
158
159ip -net ${ns3} addr add 10.23.0.13/24 dev eth0
160ip -net ${ns4} addr add 10.23.0.14/24 dev eth0
161
162# test basic connectivity
163do_ping ${ns1} 10.0.0.12
164do_ping ${ns3} 10.23.0.1
165do_ping ${ns4} 10.23.0.1
166
167if [ $ret -eq 0 ];then
168	echo "PASS: netns connectivity: ns1 can reach ns2, ns3 and ns4 can reach ns0"
169fi
170
171bcast_ping ${ns1} 10.0.0.255
172
173# This should deliver broadcast to macvlan0, which is on top of ns0:br0.
174bcast_ping ${ns3} 10.23.0.255
175
176# same, this time via veth4:macvlan4.
177bcast_ping ${ns4} 10.23.0.255
178
179read t < /proc/sys/kernel/tainted
180
181if [ $t -eq 0 ];then
182	echo PASS: kernel not tainted
183else
184	echo ERROR: kernel is tainted
185	ret=1
186fi
187
188exit $ret
189