diff options
Diffstat (limited to 'tools/testing')
| -rw-r--r-- | tools/testing/selftests/bpf/prog_tests/test_xsk.c | 50 | ||||
| -rw-r--r-- | tools/testing/selftests/net/af_unix/.gitignore | 1 | ||||
| -rw-r--r-- | tools/testing/selftests/net/af_unix/Makefile | 1 | ||||
| -rw-r--r-- | tools/testing/selftests/net/af_unix/unix_listen.c | 187 | ||||
| -rwxr-xr-x | tools/testing/selftests/net/netfilter/nft_flowtable.sh | 14 |
5 files changed, 230 insertions, 23 deletions
diff --git a/tools/testing/selftests/bpf/prog_tests/test_xsk.c b/tools/testing/selftests/bpf/prog_tests/test_xsk.c index 6eb9096d084c..38ce6060b8fa 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_xsk.c +++ b/tools/testing/selftests/bpf/prog_tests/test_xsk.c @@ -427,14 +427,14 @@ static u32 pkt_nb_frags(u32 frame_size, struct pkt_stream *pkt_stream, struct pk } /* Search for the end of the packet in verbatim mode */ - if (!pkt_continues(pkt->options) || !pkt->valid) + if (!pkt_continues(pkt->options)) return nb_frags; next_frag = pkt_stream->current_pkt_nb; pkt++; while (next_frag++ < pkt_stream->nb_pkts) { nb_frags++; - if (!pkt_continues(pkt->options) || !pkt->valid) + if (!pkt_continues(pkt->options)) break; pkt++; } @@ -665,11 +665,11 @@ static struct pkt_stream *__pkt_stream_generate_custom(struct ifobject *ifobj, s if (!frame->valid || !pkt_continues(frame->options)) payload++; } else { - if (frame->valid) + if (frame->valid) { len += frame->len; - if (frame->valid && pkt_continues(frame->options)) - continue; - + if (pkt_continues(frame->options)) + continue; + } pkt->pkt_nb = pkt_nb; pkt->len = len; pkt->valid = frame->valid; @@ -1250,10 +1250,9 @@ static int __send_pkts(struct ifobject *ifobject, struct xsk_socket_info *xsk, } } - if (pkt && pkt->valid) { + if (pkt && pkt->valid) valid_pkts++; - valid_frags += nb_frags; - } + valid_frags += nb_frags; } pthread_mutex_lock(&pacing_mutex); @@ -2099,13 +2098,16 @@ int testapp_invalid_desc_mb(struct test_spec *test) {0, 0, 0, false, 0}, /* Invalid address in the second frame */ {0, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XDP_PKT_CONTD}, - {umem_sz, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XDP_PKT_CONTD}, + {umem_sz * 2, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XDP_PKT_CONTD}, + {0, MIN_PKT_SIZE, 0, false, 0}, /* Invalid len in the middle */ {0, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XDP_PKT_CONTD}, {0, XSK_UMEM__INVALID_FRAME_SIZE, 0, false, XDP_PKT_CONTD}, + {0, MIN_PKT_SIZE, 0, false, 0}, /* Invalid options in the middle */ {0, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XDP_PKT_CONTD}, {0, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XSK_DESC__INVALID_OPTION}, + {0, MIN_PKT_SIZE, 0, false, 0}, /* Transmit 2 frags, receive 3 */ {0, XSK_UMEM__MAX_FRAME_SIZE, 0, true, XDP_PKT_CONTD}, {0, XSK_UMEM__MAX_FRAME_SIZE, 0, true, 0}, @@ -2117,8 +2119,8 @@ int testapp_invalid_desc_mb(struct test_spec *test) if (umem->unaligned_mode) { /* Crossing a chunk boundary allowed */ - pkts[12].valid = true; - pkts[13].valid = true; + pkts[15].valid = true; + pkts[16].valid = true; } test->mtu = MAX_ETH_JUMBO_SIZE; @@ -2270,7 +2272,7 @@ int testapp_too_many_frags(struct test_spec *test) max_frags += 1; } - pkts = calloc(2 * max_frags + 2, sizeof(struct pkt)); + pkts = calloc(2 * max_frags + 3, sizeof(struct pkt)); if (!pkts) return TEST_FAILURE; @@ -2288,24 +2290,30 @@ int testapp_too_many_frags(struct test_spec *test) } pkts[max_frags].options = 0; - /* An invalid packet with the max amount of frags but signals packet - * continues on the last frag - */ - for (i = max_frags + 1; i < 2 * max_frags + 1; i++) { + /* An invalid packet with the max + 1 amount of frags */ + for (i = max_frags + 1; i < 2 * max_frags + 2; i++) { pkts[i].len = MIN_PKT_SIZE; pkts[i].options = XDP_PKT_CONTD; - pkts[i].valid = false; + pkts[i].valid = true; } + pkts[2 * max_frags + 1].options = 0; /* Valid packet for synch */ - pkts[2 * max_frags + 1].len = MIN_PKT_SIZE; - pkts[2 * max_frags + 1].valid = true; + pkts[2 * max_frags + 2].len = MIN_PKT_SIZE; + pkts[2 * max_frags + 2].valid = true; - if (pkt_stream_generate_custom(test, pkts, 2 * max_frags + 2)) { + if (pkt_stream_generate_custom(test, pkts, 2 * max_frags + 3)) { free(pkts); return TEST_FAILURE; } + /* The generated Tx stream must keep the too-big packet valid so that + * __send_pkts() accounts its descriptors in outstanding_tx. The Rx + * stream, however, must not expect this packet on the wire. + */ + test->ifobj_rx->xsk->pkt_stream->pkts[2].valid = false; + test->ifobj_rx->xsk->pkt_stream->nb_valid_entries--; + ret = testapp_validate_traffic(test); free(pkts); return ret; diff --git a/tools/testing/selftests/net/af_unix/.gitignore b/tools/testing/selftests/net/af_unix/.gitignore index 240b26740c9e..973176644103 100644 --- a/tools/testing/selftests/net/af_unix/.gitignore +++ b/tools/testing/selftests/net/af_unix/.gitignore @@ -6,3 +6,4 @@ scm_rights so_peek_off unix_connect unix_connreset +unix_listen diff --git a/tools/testing/selftests/net/af_unix/Makefile b/tools/testing/selftests/net/af_unix/Makefile index 4c0375e28bbe..57d159803a3a 100644 --- a/tools/testing/selftests/net/af_unix/Makefile +++ b/tools/testing/selftests/net/af_unix/Makefile @@ -14,6 +14,7 @@ TEST_GEN_PROGS := \ so_peek_off \ unix_connect \ unix_connreset \ + unix_listen \ # end of TEST_GEN_PROGS include ../../lib.mk diff --git a/tools/testing/selftests/net/af_unix/unix_listen.c b/tools/testing/selftests/net/af_unix/unix_listen.c new file mode 100644 index 000000000000..416fa3e5bfe9 --- /dev/null +++ b/tools/testing/selftests/net/af_unix/unix_listen.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Tests for the state checks in AF_UNIX listen(). + * + * The central case is a regression test: listen() on a bound socket that + * is already connected (i.e. not in TCP_CLOSE or TCP_LISTEN state) must + * fail with EINVAL. A prior change accidentally let it return success + * without doing anything, because a helper called in between reset the + * error code to 0. The neighbouring checks (unbound, already listening) + * are tested too so they cannot silently regress the same way. + * + * Every case runs for both listenable socket types (SOCK_STREAM and + * SOCK_SEQPACKET) and both pathname and abstract addresses. + */ +#define _GNU_SOURCE + +#include <errno.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <sys/socket.h> +#include <sys/un.h> + +#include "kselftest_harness.h" + +#define SK_NAME "unix_listen_sk" +#define SRV_NAME "unix_listen_srv" + +FIXTURE(unix_listen) +{ + int sk; /* socket under test */ + int server; /* a listening peer, when a test needs one */ + struct sockaddr_un addr, srv_addr; + socklen_t addrlen, srv_addrlen; +}; + +FIXTURE_VARIANT(unix_listen) +{ + int type; + int abstract; +}; + +FIXTURE_VARIANT_ADD(unix_listen, stream_pathname) +{ + .type = SOCK_STREAM, + .abstract = 0, +}; + +FIXTURE_VARIANT_ADD(unix_listen, stream_abstract) +{ + .type = SOCK_STREAM, + .abstract = 1, +}; + +FIXTURE_VARIANT_ADD(unix_listen, seqpacket_pathname) +{ + .type = SOCK_SEQPACKET, + .abstract = 0, +}; + +FIXTURE_VARIANT_ADD(unix_listen, seqpacket_abstract) +{ + .type = SOCK_SEQPACKET, + .abstract = 1, +}; + +/* Fill @addr with a pathname or abstract address named @name. */ +static socklen_t unix_set_addr(struct sockaddr_un *addr, const char *name, + int abstract) +{ + size_t len = strlen(name); + + memset(addr, 0, sizeof(*addr)); + addr->sun_family = AF_UNIX; + /* An abstract address leads with a NUL and has no filesystem entry. */ + memcpy(addr->sun_path + (abstract ? 1 : 0), name, len); + + return offsetof(struct sockaddr_un, sun_path) + len + 1; +} + +FIXTURE_SETUP(unix_listen) +{ + self->sk = -1; + self->server = -1; + self->addrlen = unix_set_addr(&self->addr, SK_NAME, variant->abstract); + self->srv_addrlen = unix_set_addr(&self->srv_addr, SRV_NAME, + variant->abstract); +} + +FIXTURE_TEARDOWN(unix_listen) +{ + if (self->sk >= 0) + close(self->sk); + if (self->server >= 0) + close(self->server); + + /* Pathname sockets leave a filesystem entry behind; abstract ones do not. */ + if (!variant->abstract) { + remove(SK_NAME); + remove(SRV_NAME); + } +} + +/* A bound socket in TCP_CLOSE is the normal, allowed case. */ +TEST_F(unix_listen, bound_is_ok) +{ + int err; + + self->sk = socket(AF_UNIX, variant->type, 0); + ASSERT_LE(0, self->sk); + + err = bind(self->sk, (struct sockaddr *)&self->addr, self->addrlen); + ASSERT_EQ(0, err); + + err = listen(self->sk, 8); + EXPECT_EQ(0, err); +} + +/* Listening again on an already-listening socket (TCP_LISTEN) is allowed. */ +TEST_F(unix_listen, relisten_is_ok) +{ + int err; + + self->sk = socket(AF_UNIX, variant->type, 0); + ASSERT_LE(0, self->sk); + + err = bind(self->sk, (struct sockaddr *)&self->addr, self->addrlen); + ASSERT_EQ(0, err); + + err = listen(self->sk, 8); + ASSERT_EQ(0, err); + + err = listen(self->sk, 16); + EXPECT_EQ(0, err); +} + +/* listen() on an unbound socket fails: there is nothing to listen on. */ +TEST_F(unix_listen, unbound_is_einval) +{ + int err; + + self->sk = socket(AF_UNIX, variant->type, 0); + ASSERT_LE(0, self->sk); + + err = listen(self->sk, 8); + EXPECT_EQ(-1, err); + EXPECT_EQ(EINVAL, errno); +} + +/* + * The regression: a bound socket that has already been connected is not in + * TCP_CLOSE or TCP_LISTEN, so listen() must reject it with EINVAL rather + * than quietly succeeding. + */ +TEST_F(unix_listen, connected_is_einval) +{ + int err; + + self->server = socket(AF_UNIX, variant->type, 0); + ASSERT_LE(0, self->server); + + err = bind(self->server, (struct sockaddr *)&self->srv_addr, + self->srv_addrlen); + ASSERT_EQ(0, err); + + err = listen(self->server, 8); + ASSERT_EQ(0, err); + + self->sk = socket(AF_UNIX, variant->type, 0); + ASSERT_LE(0, self->sk); + + /* Bind first so the unbound check does not mask the state check. */ + err = bind(self->sk, (struct sockaddr *)&self->addr, self->addrlen); + ASSERT_EQ(0, err); + + err = connect(self->sk, (struct sockaddr *)&self->srv_addr, + self->srv_addrlen); + ASSERT_EQ(0, err); + + err = listen(self->sk, 8); + EXPECT_EQ(-1, err); + EXPECT_EQ(EINVAL, errno); +} + +TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/net/netfilter/nft_flowtable.sh b/tools/testing/selftests/net/netfilter/nft_flowtable.sh index fb1c59d45567..449c518bd947 100755 --- a/tools/testing/selftests/net/netfilter/nft_flowtable.sh +++ b/tools/testing/selftests/net/netfilter/nft_flowtable.sh @@ -617,7 +617,11 @@ ip -6 -net "$nsr2" route add default via fee1:3::1 ip -net "$ns2" route add default via 10.0.2.1 ip -6 -net "$ns2" route add default via dead:2::1 +ip netns exec "$nsr1" nft -a insert rule inet filter forward \ + 'meta oif tun0 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept' ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun0 accept' +ip netns exec "$nsr1" nft -a insert rule inet filter forward \ + 'meta oif tun6 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept' ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun6 accept' ip netns exec "$nsr1" nft -a insert rule inet filter forward \ 'meta oif "veth0" tcp sport 12345 ct mark set 1 flow add @f1 counter name routed_repl accept' @@ -629,7 +633,7 @@ if ! test_tcp_forwarding_nat "$ns1" "$ns2" 1 "IPIP tunnel"; then fi if test_tcp_forwarding "$ns1" "$ns2" 1 6 "[dead:2::99]" 12345; then - echo "PASS: flow offload for ns1/ns2 IP6IP6 tunnel" + check_counters "flow offload for ns1/ns2 IP6IP6 tunnel" else echo "FAIL: flow offload for ns1/ns2 with IP6IP6 tunnel" 1>&2 ip netns exec "$nsr1" nft list ruleset @@ -642,6 +646,8 @@ ip -net "$nsr1" link set veth1.10 up ip -net "$nsr1" addr add 192.168.20.1/24 dev veth1.10 ip -net "$nsr1" addr add fee1:4::1/64 dev veth1.10 nodad ip netns exec "$nsr1" sysctl net.ipv4.conf.veth1/10.forwarding=1 > /dev/null +ip netns exec "$nsr1" nft -a insert rule inet filter forward \ + 'meta oif veth1.10 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept' ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif veth1.10 accept' ip -net "$nsr1" link add name tun0.10 type ipip local 192.168.20.1 remote 192.168.20.2 @@ -649,6 +655,8 @@ ip -net "$nsr1" link set tun0.10 up ip -net "$nsr1" addr add 192.168.200.1/24 dev tun0.10 ip -net "$nsr1" route change default via 192.168.200.2 ip netns exec "$nsr1" sysctl net.ipv4.conf.tun0/10.forwarding=1 > /dev/null +ip netns exec "$nsr1" nft -a insert rule inet filter forward \ + 'meta oif tun0.10 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept' ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun0.10 accept' ip -net "$nsr1" link add name tun6.10 type ip6tnl local fee1:4::1 remote fee1:4::2 encaplimit none @@ -656,6 +664,8 @@ ip -net "$nsr1" link set tun6.10 up ip -net "$nsr1" addr add fee1:5::1/64 dev tun6.10 nodad ip -6 -net "$nsr1" route delete default ip -6 -net "$nsr1" route add default via fee1:5::2 +ip netns exec "$nsr1" nft -a insert rule inet filter forward \ + 'meta oif tun6.10 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept' ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun6.10 accept' ip -net "$nsr2" link add link veth0 name veth0.10 type vlan id 10 @@ -683,7 +693,7 @@ if ! test_tcp_forwarding_nat "$ns1" "$ns2" 1 "IPIP tunnel over vlan"; then fi if test_tcp_forwarding "$ns1" "$ns2" 1 6 "[dead:2::99]" 12345; then - echo "PASS: flow offload for ns1/ns2 IP6IP6 tunnel over vlan" + check_counters "flow offload for ns1/ns2 IP6IP6 tunnel over vlan" else echo "FAIL: flow offload for ns1/ns2 with IP6IP6 tunnel over vlan" 1>&2 ip netns exec "$nsr1" nft list ruleset |
