Skip to content

Commit dc65fe8

Browse files
Florian Westphaldavem330
authored andcommitted
selftests: mptcp: add packet mark test case
Extend mptcp_connect tool with SO_MARK support (-M <value>) and add a test case that checks that the packet mark gets copied to all subflows. This is done by only allowing packets with either skb->mark 1 or 2 via iptables. DROP rule packet counter is checked; if its not zero, print an error message and fail the test case. Acked-by: Paolo Abeni <[email protected]> Signed-off-by: Florian Westphal <[email protected]> Signed-off-by: Mat Martineau <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent aa1fbd9 commit dc65fe8

File tree

3 files changed

+299
-2
lines changed

3 files changed

+299
-2
lines changed

tools/testing/selftests/net/mptcp/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ KSFT_KHDR_INSTALL := 1
66
CFLAGS = -Wall -Wl,--no-as-needed -O2 -g -I$(top_srcdir)/usr/include
77

88
TEST_PROGS := mptcp_connect.sh pm_netlink.sh mptcp_join.sh diag.sh \
9-
simult_flows.sh
9+
simult_flows.sh mptcp_sockopt.sh
1010

1111
TEST_GEN_FILES = mptcp_connect pm_nl_ctl
1212

tools/testing/selftests/net/mptcp/mptcp_connect.c

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ static bool cfg_join;
5757
static bool cfg_remove;
5858
static unsigned int cfg_do_w;
5959
static int cfg_wait;
60+
static uint32_t cfg_mark;
6061

6162
static void die_usage(void)
6263
{
@@ -69,6 +70,7 @@ static void die_usage(void)
6970
fprintf(stderr, "\t-p num -- use port num\n");
7071
fprintf(stderr, "\t-s [MPTCP|TCP] -- use mptcp(default) or tcp sockets\n");
7172
fprintf(stderr, "\t-m [poll|mmap|sendfile] -- use poll(default)/mmap+write/sendfile\n");
73+
fprintf(stderr, "\t-M mark -- set socket packet mark\n");
7274
fprintf(stderr, "\t-u -- check mptcp ulp\n");
7375
fprintf(stderr, "\t-w num -- wait num sec before closing the socket\n");
7476
exit(1);
@@ -140,6 +142,17 @@ static void set_sndbuf(int fd, unsigned int size)
140142
}
141143
}
142144

145+
static void set_mark(int fd, uint32_t mark)
146+
{
147+
int err;
148+
149+
err = setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(mark));
150+
if (err) {
151+
perror("set SO_MARK");
152+
exit(1);
153+
}
154+
}
155+
143156
static int sock_listen_mptcp(const char * const listenaddr,
144157
const char * const port)
145158
{
@@ -248,6 +261,9 @@ static int sock_connect_mptcp(const char * const remoteaddr,
248261
continue;
249262
}
250263

264+
if (cfg_mark)
265+
set_mark(sock, cfg_mark);
266+
251267
if (connect(sock, a->ai_addr, a->ai_addrlen) == 0)
252268
break; /* success */
253269

@@ -830,7 +846,7 @@ static void parse_opts(int argc, char **argv)
830846
{
831847
int c;
832848

833-
while ((c = getopt(argc, argv, "6jr:lp:s:hut:m:S:R:w:")) != -1) {
849+
while ((c = getopt(argc, argv, "6jr:lp:s:hut:m:S:R:w:M:")) != -1) {
834850
switch (c) {
835851
case 'j':
836852
cfg_join = true;
@@ -880,6 +896,9 @@ static void parse_opts(int argc, char **argv)
880896
case 'w':
881897
cfg_wait = atoi(optarg)*1000000;
882898
break;
899+
case 'M':
900+
cfg_mark = strtol(optarg, NULL, 0);
901+
break;
883902
}
884903
}
885904

@@ -911,6 +930,8 @@ int main(int argc, char *argv[])
911930
set_rcvbuf(fd, cfg_rcvbuf);
912931
if (cfg_sndbuf)
913932
set_sndbuf(fd, cfg_sndbuf);
933+
if (cfg_mark)
934+
set_mark(fd, cfg_mark);
914935

915936
return main_loop_s(fd);
916937
}
Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
#!/bin/bash
2+
# SPDX-License-Identifier: GPL-2.0
3+
4+
ret=0
5+
sin=""
6+
sout=""
7+
cin=""
8+
cout=""
9+
ksft_skip=4
10+
timeout_poll=30
11+
timeout_test=$((timeout_poll * 2 + 1))
12+
mptcp_connect=""
13+
do_all_tests=1
14+
15+
add_mark_rules()
16+
{
17+
local ns=$1
18+
local m=$2
19+
20+
for t in iptables ip6tables; do
21+
# just to debug: check we have multiple subflows connection requests
22+
ip netns exec $ns $t -A OUTPUT -p tcp --syn -m mark --mark $m -j ACCEPT
23+
24+
# RST packets might be handled by a internal dummy socket
25+
ip netns exec $ns $t -A OUTPUT -p tcp --tcp-flags RST RST -m mark --mark 0 -j ACCEPT
26+
27+
ip netns exec $ns $t -A OUTPUT -p tcp -m mark --mark $m -j ACCEPT
28+
ip netns exec $ns $t -A OUTPUT -p tcp -m mark --mark 0 -j DROP
29+
done
30+
}
31+
32+
init()
33+
{
34+
rndh=$(printf %x $sec)-$(mktemp -u XXXXXX)
35+
36+
ns1="ns1-$rndh"
37+
ns2="ns2-$rndh"
38+
39+
for netns in "$ns1" "$ns2";do
40+
ip netns add $netns || exit $ksft_skip
41+
ip -net $netns link set lo up
42+
ip netns exec $netns sysctl -q net.mptcp.enabled=1
43+
ip netns exec $netns sysctl -q net.ipv4.conf.all.rp_filter=0
44+
ip netns exec $netns sysctl -q net.ipv4.conf.default.rp_filter=0
45+
done
46+
47+
for i in `seq 1 4`; do
48+
ip link add ns1eth$i netns "$ns1" type veth peer name ns2eth$i netns "$ns2"
49+
ip -net "$ns1" addr add 10.0.$i.1/24 dev ns1eth$i
50+
ip -net "$ns1" addr add dead:beef:$i::1/64 dev ns1eth$i nodad
51+
ip -net "$ns1" link set ns1eth$i up
52+
53+
ip -net "$ns2" addr add 10.0.$i.2/24 dev ns2eth$i
54+
ip -net "$ns2" addr add dead:beef:$i::2/64 dev ns2eth$i nodad
55+
ip -net "$ns2" link set ns2eth$i up
56+
57+
# let $ns2 reach any $ns1 address from any interface
58+
ip -net "$ns2" route add default via 10.0.$i.1 dev ns2eth$i metric 10$i
59+
60+
ip netns exec $ns1 ./pm_nl_ctl add 10.0.$i.1 flags signal
61+
ip netns exec $ns1 ./pm_nl_ctl add dead:beef:$i::1 flags signal
62+
63+
ip netns exec $ns2 ./pm_nl_ctl add 10.0.$i.2 flags signal
64+
ip netns exec $ns2 ./pm_nl_ctl add dead:beef:$i::2 flags signal
65+
done
66+
67+
ip netns exec $ns1 ./pm_nl_ctl limits 8 8
68+
ip netns exec $ns2 ./pm_nl_ctl limits 8 8
69+
70+
add_mark_rules $ns1 1
71+
add_mark_rules $ns2 2
72+
}
73+
74+
cleanup()
75+
{
76+
for netns in "$ns1" "$ns2"; do
77+
ip netns del $netns
78+
done
79+
rm -f "$cin" "$cout"
80+
rm -f "$sin" "$sout"
81+
}
82+
83+
ip -Version > /dev/null 2>&1
84+
if [ $? -ne 0 ];then
85+
echo "SKIP: Could not run test without ip tool"
86+
exit $ksft_skip
87+
fi
88+
89+
iptables -V > /dev/null 2>&1
90+
if [ $? -ne 0 ];then
91+
echo "SKIP: Could not run all tests without iptables tool"
92+
exit $ksft_skip
93+
fi
94+
95+
ip6tables -V > /dev/null 2>&1
96+
if [ $? -ne 0 ];then
97+
echo "SKIP: Could not run all tests without ip6tables tool"
98+
exit $ksft_skip
99+
fi
100+
101+
check_mark()
102+
{
103+
local ns=$1
104+
local af=$2
105+
106+
tables=iptables
107+
108+
if [ $af -eq 6 ];then
109+
tables=ip6tables
110+
fi
111+
112+
counters=$(ip netns exec $ns $tables -v -L OUTPUT | grep DROP)
113+
values=${counters%DROP*}
114+
115+
for v in $values; do
116+
if [ $v -ne 0 ]; then
117+
echo "FAIL: got $tables $values in ns $ns , not 0 - not all expected packets marked" 1>&2
118+
return 1
119+
fi
120+
done
121+
122+
return 0
123+
}
124+
125+
print_file_err()
126+
{
127+
ls -l "$1" 1>&2
128+
echo "Trailing bytes are: "
129+
tail -c 27 "$1"
130+
}
131+
132+
check_transfer()
133+
{
134+
in=$1
135+
out=$2
136+
what=$3
137+
138+
cmp "$in" "$out" > /dev/null 2>&1
139+
if [ $? -ne 0 ] ;then
140+
echo "[ FAIL ] $what does not match (in, out):"
141+
print_file_err "$in"
142+
print_file_err "$out"
143+
ret=1
144+
145+
return 1
146+
fi
147+
148+
return 0
149+
}
150+
151+
# $1: IP address
152+
is_v6()
153+
{
154+
[ -z "${1##*:*}" ]
155+
}
156+
157+
do_transfer()
158+
{
159+
listener_ns="$1"
160+
connector_ns="$2"
161+
cl_proto="$3"
162+
srv_proto="$4"
163+
connect_addr="$5"
164+
165+
port=12001
166+
167+
:> "$cout"
168+
:> "$sout"
169+
170+
mptcp_connect="./mptcp_connect -r 20"
171+
172+
local local_addr
173+
if is_v6 "${connect_addr}"; then
174+
local_addr="::"
175+
else
176+
local_addr="0.0.0.0"
177+
fi
178+
179+
timeout ${timeout_test} \
180+
ip netns exec ${listener_ns} \
181+
$mptcp_connect -t ${timeout_poll} -l -M 1 -p $port -s ${srv_proto} \
182+
${local_addr} < "$sin" > "$sout" &
183+
spid=$!
184+
185+
sleep 1
186+
187+
timeout ${timeout_test} \
188+
ip netns exec ${connector_ns} \
189+
$mptcp_connect -t ${timeout_poll} -M 2 -p $port -s ${cl_proto} \
190+
$connect_addr < "$cin" > "$cout" &
191+
192+
cpid=$!
193+
194+
wait $cpid
195+
retc=$?
196+
wait $spid
197+
rets=$?
198+
199+
if [ ${rets} -ne 0 ] || [ ${retc} -ne 0 ]; then
200+
echo " client exit code $retc, server $rets" 1>&2
201+
echo -e "\nnetns ${listener_ns} socket stat for ${port}:" 1>&2
202+
ip netns exec ${listener_ns} ss -Menita 1>&2 -o "sport = :$port"
203+
204+
echo -e "\nnetns ${connector_ns} socket stat for ${port}:" 1>&2
205+
ip netns exec ${connector_ns} ss -Menita 1>&2 -o "dport = :$port"
206+
207+
ret=1
208+
return 1
209+
fi
210+
211+
if [ $local_addr = "::" ];then
212+
check_mark $listener_ns 6
213+
check_mark $connector_ns 6
214+
else
215+
check_mark $listener_ns 4
216+
check_mark $connector_ns 4
217+
fi
218+
219+
check_transfer $cin $sout "file received by server"
220+
221+
rets=$?
222+
223+
if [ $retc -eq 0 ] && [ $rets -eq 0 ];then
224+
return 0
225+
fi
226+
227+
return 1
228+
}
229+
230+
make_file()
231+
{
232+
name=$1
233+
who=$2
234+
size=$3
235+
236+
dd if=/dev/urandom of="$name" bs=1024 count=$size 2> /dev/null
237+
echo -e "\nMPTCP_TEST_FILE_END_MARKER" >> "$name"
238+
239+
echo "Created $name (size $size KB) containing data sent by $who"
240+
}
241+
242+
run_tests()
243+
{
244+
listener_ns="$1"
245+
connector_ns="$2"
246+
connect_addr="$3"
247+
lret=0
248+
249+
do_transfer ${listener_ns} ${connector_ns} MPTCP MPTCP ${connect_addr}
250+
251+
lret=$?
252+
253+
if [ $lret -ne 0 ]; then
254+
ret=$lret
255+
return
256+
fi
257+
}
258+
259+
sin=$(mktemp)
260+
sout=$(mktemp)
261+
cin=$(mktemp)
262+
cout=$(mktemp)
263+
init
264+
make_file "$cin" "client" 1
265+
make_file "$sin" "server" 1
266+
trap cleanup EXIT
267+
268+
run_tests $ns1 $ns2 10.0.1.1
269+
run_tests $ns1 $ns2 dead:beef:1::1
270+
271+
272+
if [ $ret -eq 0 ];then
273+
echo "PASS: all packets had packet mark set"
274+
fi
275+
276+
exit $ret

0 commit comments

Comments
 (0)