Skip to content

Commit e8ac615

Browse files
committed
Merge branch 'ipv6-fix-socket-connection-with-dscp-fib-rules'
Guillaume Nault says: ==================== ipv6: Fix socket connection with DSCP fib-rules. The "flowlabel" field of struct flowi6 is used to store both the actual flow label and the DS Field (or Traffic Class). However the .connect handlers of datagram and TCP sockets don't set the DS Field part when doing their route lookup. This breaks fib-rules that match on DSCP. ==================== Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
2 parents 6e16e67 + c21a20d commit e8ac615

File tree

4 files changed

+179
-3
lines changed

4 files changed

+179
-3
lines changed

net/ipv6/datagram.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ static void ip6_datagram_flow_key_init(struct flowi6 *fl6, struct sock *sk)
5151
fl6->flowi6_mark = sk->sk_mark;
5252
fl6->fl6_dport = inet->inet_dport;
5353
fl6->fl6_sport = inet->inet_sport;
54-
fl6->flowlabel = np->flow_label;
54+
fl6->flowlabel = ip6_make_flowinfo(np->tclass, np->flow_label);
5555
fl6->flowi6_uid = sk->sk_uid;
5656

5757
if (!oif)

net/ipv6/tcp_ipv6.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
272272
fl6.flowi6_proto = IPPROTO_TCP;
273273
fl6.daddr = sk->sk_v6_daddr;
274274
fl6.saddr = saddr ? *saddr : np->saddr;
275+
fl6.flowlabel = ip6_make_flowinfo(np->tclass, np->flow_label);
275276
fl6.flowi6_oif = sk->sk_bound_dev_if;
276277
fl6.flowi6_mark = sk->sk_mark;
277278
fl6.fl6_dport = usin->sin6_port;

tools/testing/selftests/net/fib_rule_tests.sh

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ ret=0
1010

1111
PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
1212
IP="ip -netns testns"
13+
IP_PEER="ip -netns peerns"
1314

1415
RTABLE=100
16+
RTABLE_PEER=101
1517
GW_IP4=192.51.100.2
1618
SRC_IP=192.51.100.3
1719
GW_IP6=2001:db8:1::2
@@ -20,7 +22,9 @@ SRC_IP6=2001:db8:1::3
2022
DEV_ADDR=192.51.100.1
2123
DEV_ADDR6=2001:db8:1::1
2224
DEV=dummy0
23-
TESTS="fib_rule6 fib_rule4"
25+
TESTS="fib_rule6 fib_rule4 fib_rule6_connect fib_rule4_connect"
26+
27+
SELFTEST_PATH=""
2428

2529
log_test()
2630
{
@@ -52,6 +56,31 @@ log_section()
5256
echo "######################################################################"
5357
}
5458

59+
check_nettest()
60+
{
61+
if which nettest > /dev/null 2>&1; then
62+
return 0
63+
fi
64+
65+
# Add the selftest directory to PATH if not already done
66+
if [ "${SELFTEST_PATH}" = "" ]; then
67+
SELFTEST_PATH="$(dirname $0)"
68+
PATH="${PATH}:${SELFTEST_PATH}"
69+
70+
# Now retry with the new path
71+
if which nettest > /dev/null 2>&1; then
72+
return 0
73+
fi
74+
75+
if [ "${ret}" -eq 0 ]; then
76+
ret="${ksft_skip}"
77+
fi
78+
echo "nettest not found (try 'make -C ${SELFTEST_PATH} nettest')"
79+
fi
80+
81+
return 1
82+
}
83+
5584
setup()
5685
{
5786
set -e
@@ -72,6 +101,39 @@ cleanup()
72101
ip netns del testns
73102
}
74103

104+
setup_peer()
105+
{
106+
set -e
107+
108+
ip netns add peerns
109+
$IP_PEER link set dev lo up
110+
111+
ip link add name veth0 netns testns type veth \
112+
peer name veth1 netns peerns
113+
$IP link set dev veth0 up
114+
$IP_PEER link set dev veth1 up
115+
116+
$IP address add 192.0.2.10 peer 192.0.2.11/32 dev veth0
117+
$IP_PEER address add 192.0.2.11 peer 192.0.2.10/32 dev veth1
118+
119+
$IP address add 2001:db8::10 peer 2001:db8::11/128 dev veth0 nodad
120+
$IP_PEER address add 2001:db8::11 peer 2001:db8::10/128 dev veth1 nodad
121+
122+
$IP_PEER address add 198.51.100.11/32 dev lo
123+
$IP route add table $RTABLE_PEER 198.51.100.11/32 via 192.0.2.11
124+
125+
$IP_PEER address add 2001:db8::1:11/128 dev lo
126+
$IP route add table $RTABLE_PEER 2001:db8::1:11/128 via 2001:db8::11
127+
128+
set +e
129+
}
130+
131+
cleanup_peer()
132+
{
133+
$IP link del dev veth0
134+
ip netns del peerns
135+
}
136+
75137
fib_check_iproute_support()
76138
{
77139
ip rule help 2>&1 | grep -q $1
@@ -190,6 +252,37 @@ fib_rule6_test()
190252
fi
191253
}
192254

255+
# Verify that the IPV6_TCLASS option of UDPv6 and TCPv6 sockets is properly
256+
# taken into account when connecting the socket and when sending packets.
257+
fib_rule6_connect_test()
258+
{
259+
local dsfield
260+
261+
if ! check_nettest; then
262+
echo "SKIP: Could not run test without nettest tool"
263+
return
264+
fi
265+
266+
setup_peer
267+
$IP -6 rule add dsfield 0x04 table $RTABLE_PEER
268+
269+
# Combine the base DS Field value (0x04) with all possible ECN values
270+
# (Not-ECT: 0, ECT(1): 1, ECT(0): 2, CE: 3).
271+
# The ECN bits shouldn't influence the result of the test.
272+
for dsfield in 0x04 0x05 0x06 0x07; do
273+
nettest -q -6 -B -t 5 -N testns -O peerns -U -D \
274+
-Q "${dsfield}" -l 2001:db8::1:11 -r 2001:db8::1:11
275+
log_test $? 0 "rule6 dsfield udp connect (dsfield ${dsfield})"
276+
277+
nettest -q -6 -B -t 5 -N testns -O peerns -Q "${dsfield}" \
278+
-l 2001:db8::1:11 -r 2001:db8::1:11
279+
log_test $? 0 "rule6 dsfield tcp connect (dsfield ${dsfield})"
280+
done
281+
282+
$IP -6 rule del dsfield 0x04 table $RTABLE_PEER
283+
cleanup_peer
284+
}
285+
193286
fib_rule4_del()
194287
{
195288
$IP rule del $1
@@ -296,6 +389,37 @@ fib_rule4_test()
296389
fi
297390
}
298391

392+
# Verify that the IP_TOS option of UDPv4 and TCPv4 sockets is properly taken
393+
# into account when connecting the socket and when sending packets.
394+
fib_rule4_connect_test()
395+
{
396+
local dsfield
397+
398+
if ! check_nettest; then
399+
echo "SKIP: Could not run test without nettest tool"
400+
return
401+
fi
402+
403+
setup_peer
404+
$IP -4 rule add dsfield 0x04 table $RTABLE_PEER
405+
406+
# Combine the base DS Field value (0x04) with all possible ECN values
407+
# (Not-ECT: 0, ECT(1): 1, ECT(0): 2, CE: 3).
408+
# The ECN bits shouldn't influence the result of the test.
409+
for dsfield in 0x04 0x05 0x06 0x07; do
410+
nettest -q -B -t 5 -N testns -O peerns -D -U -Q "${dsfield}" \
411+
-l 198.51.100.11 -r 198.51.100.11
412+
log_test $? 0 "rule4 dsfield udp connect (dsfield ${dsfield})"
413+
414+
nettest -q -B -t 5 -N testns -O peerns -Q "${dsfield}" \
415+
-l 198.51.100.11 -r 198.51.100.11
416+
log_test $? 0 "rule4 dsfield tcp connect (dsfield ${dsfield})"
417+
done
418+
419+
$IP -4 rule del dsfield 0x04 table $RTABLE_PEER
420+
cleanup_peer
421+
}
422+
299423
run_fibrule_tests()
300424
{
301425
log_section "IPv4 fib rule"
@@ -345,6 +469,8 @@ do
345469
case $t in
346470
fib_rule6_test|fib_rule6) fib_rule6_test;;
347471
fib_rule4_test|fib_rule4) fib_rule4_test;;
472+
fib_rule6_connect_test|fib_rule6_connect) fib_rule6_connect_test;;
473+
fib_rule4_connect_test|fib_rule4_connect) fib_rule4_connect_test;;
348474

349475
help) echo "Test names: $TESTS"; exit 0;;
350476

tools/testing/selftests/net/nettest.c

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ struct sock_args {
8787
int use_setsockopt;
8888
int use_freebind;
8989
int use_cmsg;
90+
uint8_t dsfield;
9091
const char *dev;
9192
const char *server_dev;
9293
int ifindex;
@@ -580,6 +581,36 @@ static int set_reuseaddr(int sd)
580581
return rc;
581582
}
582583

584+
static int set_dsfield(int sd, int version, int dsfield)
585+
{
586+
if (!dsfield)
587+
return 0;
588+
589+
switch (version) {
590+
case AF_INET:
591+
if (setsockopt(sd, SOL_IP, IP_TOS, &dsfield,
592+
sizeof(dsfield)) < 0) {
593+
log_err_errno("setsockopt(IP_TOS)");
594+
return -1;
595+
}
596+
break;
597+
598+
case AF_INET6:
599+
if (setsockopt(sd, SOL_IPV6, IPV6_TCLASS, &dsfield,
600+
sizeof(dsfield)) < 0) {
601+
log_err_errno("setsockopt(IPV6_TCLASS)");
602+
return -1;
603+
}
604+
break;
605+
606+
default:
607+
log_error("Invalid address family\n");
608+
return -1;
609+
}
610+
611+
return 0;
612+
}
613+
583614
static int str_to_uint(const char *str, int min, int max, unsigned int *value)
584615
{
585616
int number;
@@ -1317,6 +1348,9 @@ static int msock_init(struct sock_args *args, int server)
13171348
(char *)&one, sizeof(one)) < 0)
13181349
log_err_errno("Setting SO_BROADCAST error");
13191350

1351+
if (set_dsfield(sd, AF_INET, args->dsfield) != 0)
1352+
goto out_err;
1353+
13201354
if (args->dev && bind_to_device(sd, args->dev) != 0)
13211355
goto out_err;
13221356
else if (args->use_setsockopt &&
@@ -1445,6 +1479,9 @@ static int lsock_init(struct sock_args *args)
14451479
if (set_reuseport(sd) != 0)
14461480
goto err;
14471481

1482+
if (set_dsfield(sd, args->version, args->dsfield) != 0)
1483+
goto err;
1484+
14481485
if (args->dev && bind_to_device(sd, args->dev) != 0)
14491486
goto err;
14501487
else if (args->use_setsockopt &&
@@ -1658,6 +1695,9 @@ static int connectsock(void *addr, socklen_t alen, struct sock_args *args)
16581695
if (set_reuseport(sd) != 0)
16591696
goto err;
16601697

1698+
if (set_dsfield(sd, args->version, args->dsfield) != 0)
1699+
goto err;
1700+
16611701
if (args->dev && bind_to_device(sd, args->dev) != 0)
16621702
goto err;
16631703
else if (args->use_setsockopt &&
@@ -1862,7 +1902,7 @@ static int ipc_parent(int cpid, int fd, struct sock_args *args)
18621902
return client_status;
18631903
}
18641904

1865-
#define GETOPT_STR "sr:l:c:p:t:g:P:DRn:M:X:m:d:I:BN:O:SUCi6xL:0:1:2:3:Fbqf"
1905+
#define GETOPT_STR "sr:l:c:Q:p:t:g:P:DRn:M:X:m:d:I:BN:O:SUCi6xL:0:1:2:3:Fbqf"
18661906
#define OPT_FORCE_BIND_KEY_IFINDEX 1001
18671907
#define OPT_NO_BIND_KEY_IFINDEX 1002
18681908

@@ -1893,6 +1933,8 @@ static void print_usage(char *prog)
18931933
" -D|R datagram (D) / raw (R) socket (default stream)\n"
18941934
" -l addr local address to bind to in server mode\n"
18951935
" -c addr local address to bind to in client mode\n"
1936+
" -Q dsfield DS Field value of the socket (the IP_TOS or\n"
1937+
" IPV6_TCLASS socket option)\n"
18961938
" -x configure XFRM policy on socket\n"
18971939
"\n"
18981940
" -d dev bind socket to given device name\n"
@@ -1971,6 +2013,13 @@ int main(int argc, char *argv[])
19712013
args.has_local_ip = 1;
19722014
args.client_local_addr_str = optarg;
19732015
break;
2016+
case 'Q':
2017+
if (str_to_uint(optarg, 0, 255, &tmp) != 0) {
2018+
fprintf(stderr, "Invalid DS Field\n");
2019+
return 1;
2020+
}
2021+
args.dsfield = tmp;
2022+
break;
19742023
case 'p':
19752024
if (str_to_uint(optarg, 1, 65535, &tmp) != 0) {
19762025
fprintf(stderr, "Invalid port\n");

0 commit comments

Comments
 (0)