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