* [PATCH net-next v2 0/4] net: selftests: adjustments to ipv6_flowlabel_mgr
@ 2026-08-04 2:59 Marcelo Mendes Spessoto Junior
2026-08-04 2:59 ` [PATCH net-next v2 1/4] selftests: net: test IPV6_FL_A_RENEW Marcelo Mendes Spessoto Junior
` (3 more replies)
0 siblings, 4 replies; 9+ messages in thread
From: Marcelo Mendes Spessoto Junior @ 2026-08-04 2:59 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, shuah, linux-kselftest,
linux-kernel, Marcelo Mendes Spessoto Junior
The ipv6_flowlabel_mgr test file was lacking coverage for the IPV6_FL_A_RENEW
action, and the IPV6_FL_F_REMOTE and IPV6_FL_F_REFLECT flags. The first
three patches from this set aim to add a proper test case for each.
The fourth patch proposes the adoption of "kselftest_harness.h" helpers,
improving code readability and conforming to the implementation of the most
recent selftests.
---
v2:
- Apply missing scripts/checkpath.pl fixes and other formatting
enhancements
- Add new basic IPV6_FL_A_RENEW tests
- Put label after IPV6_FL_F_REMOTE test, freeing the value for a
subsequent test
- Delegate flowlabel_consistency configuration to wrapper sh script
instead of doing it on the main test code
- Skip IPV6_FL_F_REFLECT tests if flowlabel_consistency is set,
instead of failing the test or attempting to change it
- Compare resulting error for failure assertions
v1:
- https://lore.kernel.org/all/20260727043516.93101-1-marcelospe@proton.me/
Marcelo Mendes Spessoto Junior (4):
selftests: net: test IPV6_FL_A_RENEW
selftests: net: test IPV6_FL_F_REMOTE
selftests: net: test IPV6_FL_F_REFLECT
selftests: net: adopt harness for flow label mgr
tools/testing/selftests/net/ipv6_flowlabel.sh | 3 +-
.../selftests/net/ipv6_flowlabel_mgr.c | 504 +++++++++++++-----
2 files changed, 387 insertions(+), 120 deletions(-)
--
2.55.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH net-next v2 1/4] selftests: net: test IPV6_FL_A_RENEW 2026-08-04 2:59 [PATCH net-next v2 0/4] net: selftests: adjustments to ipv6_flowlabel_mgr Marcelo Mendes Spessoto Junior @ 2026-08-04 2:59 ` Marcelo Mendes Spessoto Junior 2026-08-06 19:22 ` Jakub Kicinski 2026-08-04 2:59 ` [PATCH net-next v2 2/4] selftests: net: test IPV6_FL_F_REMOTE Marcelo Mendes Spessoto Junior ` (2 subsequent siblings) 3 siblings, 1 reply; 9+ messages in thread From: Marcelo Mendes Spessoto Junior @ 2026-08-04 2:59 UTC (permalink / raw) To: netdev Cc: davem, edumazet, kuba, pabeni, horms, shuah, linux-kselftest, linux-kernel, Marcelo Mendes Spessoto Junior RENEW was the only flow label action without selftests coverage. Assert renew returns no error on correct usage and fails for labels that do not exist. Based on the previously implemented EXCL share test, that demonstrates that a new flow label with same label can be created after the linger period, use renew to show that the flow label can last longer and block a new flow label creation after the previous linger time. This test, however, demands sleep during execution, and should be placed as a conditional test under the -l option. The addition of expect_fail_errno helper is necessary to assert the corresponding error when a function can fail on multiple ways. Signed-off-by: Marcelo Mendes Spessoto Junior <marcelomspessoto@gmail.com> --- .../selftests/net/ipv6_flowlabel_mgr.c | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tools/testing/selftests/net/ipv6_flowlabel_mgr.c b/tools/testing/selftests/net/ipv6_flowlabel_mgr.c index af95b48acea9..01fab414895c 100644 --- a/tools/testing/selftests/net/ipv6_flowlabel_mgr.c +++ b/tools/testing/selftests/net/ipv6_flowlabel_mgr.c @@ -27,6 +27,7 @@ /* from net/ipv6/ip6_flowlabel.c */ #define FL_MIN_LINGER 6 +#define FL_MAX_LINGER 150 #define explain(x) \ do { if (cfg_verbose) fprintf(stderr, " " x "\n"); } while (0) @@ -42,6 +43,18 @@ #define expect_pass(x) __expect(x) #define expect_fail(x) __expect(!(x)) +#define expect_fail_errno(x, e) \ + do { \ + int __exp = (e); \ + int __ret = (x); \ + int __err = errno; \ + if (__ret && __err == __exp) \ + fprintf(stderr, "[OK] " #x "\n"); \ + else \ + error(1, 0, "[ERR] " #x " (line %d): expected errno %d, got %d", \ + __LINE__, __exp, __err); \ + } while (0) + static bool cfg_long_running; static bool cfg_verbose; @@ -71,6 +84,17 @@ static int flowlabel_put(int fd, uint32_t label) return setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &req, sizeof(req)); } +static int flowlabel_renew(int fd, uint32_t label, uint16_t linger) +{ + struct in6_flowlabel_req req = { + .flr_action = IPV6_FL_A_RENEW, + .flr_label = htonl(label), + .flr_linger = linger, + }; + + return setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &req, sizeof(req)); +} + static void run_tests(int fd) { int wstatus; @@ -160,6 +184,26 @@ static void run_tests(int fd) error(1, errno, "wait"); if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) error(1, errno, "wait: unexpected child result"); + + explain("It is not possible to renew a label that does not exist"); + expect_fail_errno(flowlabel_renew(fd, 5, 2 * (FL_MIN_LINGER * 2 + 1)), ESRCH); + + explain("Create a label for basic renew validation"); + expect_pass(flowlabel_get(fd, 5, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE)); + explain("Check if renew does not return errors for existing and valid label"); + expect_pass(flowlabel_renew(fd, 5, 2 * (FL_MIN_LINGER * 2 + 1))); + + if (cfg_long_running) { + explain("create a new label with FL_MIN_LINGER linger time"); + expect_pass(flowlabel_get(fd, 6, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE)); + explain("renew the label to increase its linger time and put it"); + expect_pass(flowlabel_renew(fd, 6, 2 * (FL_MIN_LINGER * 2 + 1))); + expect_pass(flowlabel_put(fd, 6)); + sleep(FL_MIN_LINGER * 2 + 1); + explain("The label cannot be created because the new linger time is not over yet"); + expect_fail_errno(flowlabel_get(fd, 6, IPV6_FL_S_ANY, IPV6_FL_F_CREATE), EPERM); + } + } static void parse_opts(int argc, char **argv) -- 2.55.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH net-next v2 1/4] selftests: net: test IPV6_FL_A_RENEW 2026-08-04 2:59 ` [PATCH net-next v2 1/4] selftests: net: test IPV6_FL_A_RENEW Marcelo Mendes Spessoto Junior @ 2026-08-06 19:22 ` Jakub Kicinski 0 siblings, 0 replies; 9+ messages in thread From: Jakub Kicinski @ 2026-08-06 19:22 UTC (permalink / raw) To: marcelomspessoto Cc: Jakub Kicinski, netdev, davem, edumazet, pabeni, horms, shuah, linux-kselftest, linux-kernel This is an AI-generated review of your patch. The human sending this email has considered the AI review valid, or at least plausible. --- selftests: net: test IPV6_FL_A_RENEW This adds selftest coverage for the IPV6_FL_A_RENEW flow label action: a new flowlabel_renew() helper, an expect_fail_errno() assertion macro, and tests that renew fails with ESRCH for a nonexistent label and succeeds for an existing one. A long-running case verifies that renewing extends the linger period so a new label cannot be created after the original linger time. A couple of questions below. > diff --git a/tools/testing/selftests/net/ipv6_flowlabel_mgr.c b/tools/testing/selftests/net/ipv6_flowlabel_mgr.c > index af95b48acea90..01fab414895c5 100644 > --- a/tools/testing/selftests/net/ipv6_flowlabel_mgr.c > +++ b/tools/testing/selftests/net/ipv6_flowlabel_mgr.c > @@ -27,6 +27,7 @@ > > /* from net/ipv6/ip6_flowlabel.c */ > #define FL_MIN_LINGER 6 > +#define FL_MAX_LINGER 150 [Severity: Low] Is FL_MAX_LINGER used anywhere? Grepping the file, the identifier appears only on this definition line, both in this commit and at the end of the series. The only linger values the test passes are 2 * (FL_MIN_LINGER * 2 + 1) and the implicit 0. The kernel path this constant mirrors is the one RENEW error case the new tests do not exercise: net/ipv6/ip6_flowlabel.c:check_linger() { if (ttl > FL_MAX_LINGER && !capable(CAP_NET_ADMIN)) return 0; ... } and fl6_renew() turns that 0 into -EPERM. Since the selftest is driven through in_netns.sh as root, an over-max linger value would not reach that branch anyway. Would it make sense to either drop the define, or add an unprivileged over-max linger case that expects EPERM? As it stands an unreferenced object-like macro produces no warning, so it suggests coverage that is not there. Also, this isn't a bug, but the new line uses a single tab while the adjacent #define FL_MIN_LINGER uses two, so the values no longer line up. [ ... ] > @@ -71,6 +84,17 @@ static int flowlabel_put(int fd, uint32_t label) > return setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &req, sizeof(req)); > } > > +static int flowlabel_renew(int fd, uint32_t label, uint16_t linger) > +{ > + struct in6_flowlabel_req req = { > + .flr_action = IPV6_FL_A_RENEW, > + .flr_label = htonl(label), > + .flr_linger = linger, > + }; > + > + return setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &req, sizeof(req)); > +} [Severity: Low] Should flr_share be set explicitly here? Leaving it out zeroes it, and 0 is IPV6_FL_S_NONE, which selects a second renew path in the kernel: net/ipv6/ip6_flowlabel.c:ipv6_flowlabel_renew() { rcu_read_lock(); for_each_sk_fl_rcu(sk, sfl) { if (sfl->fl->label == freq->flr_label) { err = fl6_renew(sfl->fl, freq->flr_linger, freq->flr_expires); ... if (freq->flr_share == IPV6_FL_S_NONE && ns_capable(net->user_ns, CAP_NET_ADMIN)) { struct ip6_flowlabel *fl = fl_lookup(net, freq->flr_label); if (fl) { err = fl6_renew(fl, freq->flr_linger, freq->flr_expires); ... return -ESRCH; } The test runs as root via ipv6_flowlabel.sh -> in_netns.sh, so the CAP_NET_ADMIN fallback is always available, and labels 5 and 6 are also present in the per-netns hash because the same socket created them. If the for_each_sk_fl_rcu() matching branch ever stopped matching, would fl_lookup() not still find the label, so fl6_renew() returns 0 and both the "renew succeeds" and the long-running EPERM linger-extension assertions still pass? Would setting flr_share to the label's share (IPV6_FL_S_EXCL for these labels), or adding a case using a label the socket does not own, separate the two branches? Cross-instance finding from sashiko-gemini (2a95a5b15b2d5ec9fd64306a937e0876383a2116fb10cbee36476a293b84adb4): [Severity: Medium] The `expect_fail_errno` macro incorrectly formats the error message when the tested function unexpectedly succeeds, printing a stale `errno` value rather than clearly indicating unexpected success. -- pw-bot: cr ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH net-next v2 2/4] selftests: net: test IPV6_FL_F_REMOTE 2026-08-04 2:59 [PATCH net-next v2 0/4] net: selftests: adjustments to ipv6_flowlabel_mgr Marcelo Mendes Spessoto Junior 2026-08-04 2:59 ` [PATCH net-next v2 1/4] selftests: net: test IPV6_FL_A_RENEW Marcelo Mendes Spessoto Junior @ 2026-08-04 2:59 ` Marcelo Mendes Spessoto Junior 2026-08-04 2:59 ` [PATCH net-next v2 3/4] selftests: net: test IPV6_FL_F_REFLECT Marcelo Mendes Spessoto Junior 2026-08-04 2:59 ` [PATCH net-next v2 4/4] selftests: net: adopt harness for flow label mgr Marcelo Mendes Spessoto Junior 3 siblings, 0 replies; 9+ messages in thread From: Marcelo Mendes Spessoto Junior @ 2026-08-04 2:59 UTC (permalink / raw) To: netdev Cc: davem, edumazet, kuba, pabeni, horms, shuah, linux-kselftest, linux-kernel, Marcelo Mendes Spessoto Junior This flag retrieves the flow label seen by the socket at connection setup via a getsockopt query. Therefore, the validation of this flag requires a brief connection setup (source code for flow label shows it must be TCP). The simple TCP connection logic was wrapped inside two simple helpers, because there are other uncovered features of flow label mgr that could benefit from it (such as IPV6_FL_F_REFLECT). Signed-off-by: Marcelo Mendes Spessoto Junior <marcelomspessoto@gmail.com> --- .../selftests/net/ipv6_flowlabel_mgr.c | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/tools/testing/selftests/net/ipv6_flowlabel_mgr.c b/tools/testing/selftests/net/ipv6_flowlabel_mgr.c index 01fab414895c..d482be2e9f9f 100644 --- a/tools/testing/selftests/net/ipv6_flowlabel_mgr.c +++ b/tools/testing/selftests/net/ipv6_flowlabel_mgr.c @@ -24,6 +24,9 @@ #ifndef IPV6_FLOWLABEL_MGR #define IPV6_FLOWLABEL_MGR 32 #endif +#ifndef IPV6_FLOWINFO_SEND +#define IPV6_FLOWINFO_SEND 33 +#endif /* from net/ipv6/ip6_flowlabel.c */ #define FL_MIN_LINGER 6 @@ -95,6 +98,66 @@ static int flowlabel_renew(int fd, uint32_t label, uint16_t linger) return setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &req, sizeof(req)); } +static struct sockaddr_in6 loopback_addr(void) +{ + struct sockaddr_in6 addr = { + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_LOOPBACK_INIT, + .sin6_port = htons(8888), + }; + + return addr; +} + +static int tcp_listen(void) +{ + struct sockaddr_in6 addr = loopback_addr(); + const int one = 1; + int fd; + + fd = socket(PF_INET6, SOCK_STREAM, 0); + if (fd == -1) + error(1, errno, "socket listener"); + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) + error(1, errno, "setsockopt SO_REUSEADDR"); + if (bind(fd, (void *)&addr, sizeof(addr))) + error(1, errno, "bind"); + if (listen(fd, 1)) + error(1, errno, "listen"); + + return fd; +} + +static void tcp_connect(int listener, uint32_t flowlabel, int *client, int *accepted) +{ + struct sockaddr_in6 addr = loopback_addr(); + const int one = 1; + int cfd, afd; + + cfd = socket(PF_INET6, SOCK_STREAM, 0); + if (cfd == -1) + error(1, errno, "socket client"); + + if (flowlabel_get(cfd, flowlabel, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE)) + error(1, errno, "flowlabel_get"); + if (setsockopt(cfd, SOL_IPV6, IPV6_FLOWINFO_SEND, &one, sizeof(one))) + error(1, errno, "setsockopt flowinfo_send"); + addr.sin6_flowinfo = htonl(flowlabel); + + if (connect(cfd, (void *)&addr, sizeof(addr))) + error(1, errno, "connect"); + + afd = accept(listener, NULL, NULL); + if (afd == -1) + error(1, errno, "accept"); + + if (flowlabel_put(cfd, flowlabel)) + error(1, errno, "flowlabel_put"); + + *client = cfd; + *accepted = afd; +} + static void run_tests(int fd) { int wstatus; @@ -204,6 +267,27 @@ static void run_tests(int fd) expect_fail_errno(flowlabel_get(fd, 6, IPV6_FL_S_ANY, IPV6_FL_F_CREATE), EPERM); } + { + struct in6_flowlabel_req freq = { + .flr_action = IPV6_FL_A_GET, + .flr_flags = IPV6_FL_F_REMOTE, + }; + socklen_t freq_len = sizeof(freq); + int remote_listener = tcp_listen(); + int remote_cfd, remote_afd; + + explain("Prepare TCP SYN for REMOTE flag validation"); + tcp_connect(remote_listener, 7, &remote_cfd, &remote_afd); + + explain("Query for label sent by client with IPV6_FL_F_REMOTE"); + expect_pass(getsockopt(remote_afd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &freq, &freq_len)); + if (ntohl(freq.flr_label) != 7) + error(1, 0, "unexpected remote flowlabel %u", ntohl(freq.flr_label)); + + close(remote_afd); + close(remote_cfd); + close(remote_listener); + } } static void parse_opts(int argc, char **argv) -- 2.55.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH net-next v2 3/4] selftests: net: test IPV6_FL_F_REFLECT 2026-08-04 2:59 [PATCH net-next v2 0/4] net: selftests: adjustments to ipv6_flowlabel_mgr Marcelo Mendes Spessoto Junior 2026-08-04 2:59 ` [PATCH net-next v2 1/4] selftests: net: test IPV6_FL_A_RENEW Marcelo Mendes Spessoto Junior 2026-08-04 2:59 ` [PATCH net-next v2 2/4] selftests: net: test IPV6_FL_F_REMOTE Marcelo Mendes Spessoto Junior @ 2026-08-04 2:59 ` Marcelo Mendes Spessoto Junior 2026-08-06 19:24 ` Jakub Kicinski 2026-08-04 2:59 ` [PATCH net-next v2 4/4] selftests: net: adopt harness for flow label mgr Marcelo Mendes Spessoto Junior 3 siblings, 1 reply; 9+ messages in thread From: Marcelo Mendes Spessoto Junior @ 2026-08-04 2:59 UTC (permalink / raw) To: netdev Cc: davem, edumazet, kuba, pabeni, horms, shuah, linux-kselftest, linux-kernel, Marcelo Mendes Spessoto Junior According to source code, flowlabel_consistency must be deactivated for the IPV6_FL_F_REFLECT flag to work. Therefore, take the following measures: deactivate it on the wrapper .sh script, and skip this test if it is run on an environment that does not correspond. The previously defined tcp_listen and tcp_connect helpers were reused, since the connection flow required for REFLECT validation is very similar to REMOTE. Signed-off-by: Marcelo Mendes Spessoto Junior <marcelomspessoto@gmail.com> --- tools/testing/selftests/net/ipv6_flowlabel.sh | 3 +- .../selftests/net/ipv6_flowlabel_mgr.c | 61 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/ipv6_flowlabel.sh b/tools/testing/selftests/net/ipv6_flowlabel.sh index cee95e252bee..4c3de3a27807 100755 --- a/tools/testing/selftests/net/ipv6_flowlabel.sh +++ b/tools/testing/selftests/net/ipv6_flowlabel.sh @@ -8,7 +8,8 @@ set -e echo "TEST management" -./in_netns.sh ./ipv6_flowlabel_mgr +./in_netns.sh \ + sh -c 'sysctl -q -w net.ipv6.flowlabel_consistency=0 && ./ipv6_flowlabel_mgr' echo "TEST datapath" ./in_netns.sh \ diff --git a/tools/testing/selftests/net/ipv6_flowlabel_mgr.c b/tools/testing/selftests/net/ipv6_flowlabel_mgr.c index d482be2e9f9f..af87eec799c8 100644 --- a/tools/testing/selftests/net/ipv6_flowlabel_mgr.c +++ b/tools/testing/selftests/net/ipv6_flowlabel_mgr.c @@ -6,6 +6,7 @@ #include <arpa/inet.h> #include <error.h> #include <errno.h> +#include <fcntl.h> #include <limits.h> #include <linux/in6.h> #include <stdbool.h> @@ -158,6 +159,22 @@ static void tcp_connect(int listener, uint32_t flowlabel, int *client, int *acce *accepted = afd; } +static bool flowlabel_consistency_enabled(void) +{ + char buf[2] = {}; + int fd; + + fd = open("/proc/sys/net/ipv6/flowlabel_consistency", O_RDONLY); + if (fd == -1) + return true; + + if (read(fd, buf, sizeof(buf) - 1) < 0) + buf[0] = '1'; + close(fd); + + return buf[0] != '0'; +} + static void run_tests(int fd) { int wstatus; @@ -288,6 +305,50 @@ static void run_tests(int fd) close(remote_cfd); close(remote_listener); } + + if (flowlabel_consistency_enabled()) { + fprintf(stderr, + "[INFO] skip REFLECT flag validation (net.ipv6.flowlabel_consistency must be 0)\n"); + } else { + struct in6_flowlabel_req reflect_query = { + .flr_action = IPV6_FL_A_GET, + }; + struct in6_flowlabel_req reflect_off = { + .flr_action = IPV6_FL_A_PUT, + .flr_flags = IPV6_FL_F_REFLECT, + }; + struct in6_flowlabel_req reflect_on = { + .flr_action = IPV6_FL_A_GET, + .flr_flags = IPV6_FL_F_REFLECT, + }; + socklen_t reflect_query_len = sizeof(reflect_query); + int reflect_listener = tcp_listen(); + int reflect_cfd, reflect_afd; + + explain("Enable REFLECT on the listener before the client connects"); + expect_pass(setsockopt(reflect_listener, SOL_IPV6, IPV6_FLOWLABEL_MGR, + &reflect_on, sizeof(reflect_on))); + + tcp_connect(reflect_listener, 8, &reflect_cfd, &reflect_afd); + + explain("Query the accepted socket's outgoing label, should be reflected"); + expect_pass(getsockopt(reflect_afd, SOL_IPV6, IPV6_FLOWLABEL_MGR, + &reflect_query, &reflect_query_len)); + if (ntohl(reflect_query.flr_label) != 8) + error(1, 0, "unexpected reflected flowlabel %u", + ntohl(reflect_query.flr_label)); + + explain("PUT+REFLECT disables reflection on the accepted socket"); + expect_pass(setsockopt(reflect_afd, SOL_IPV6, IPV6_FLOWLABEL_MGR, + &reflect_off, sizeof(reflect_off))); + explain("cannot disable reflection twice"); + expect_fail(setsockopt(reflect_afd, SOL_IPV6, IPV6_FLOWLABEL_MGR, + &reflect_off, sizeof(reflect_off))); + + close(reflect_afd); + close(reflect_cfd); + close(reflect_listener); + } } static void parse_opts(int argc, char **argv) -- 2.55.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH net-next v2 3/4] selftests: net: test IPV6_FL_F_REFLECT 2026-08-04 2:59 ` [PATCH net-next v2 3/4] selftests: net: test IPV6_FL_F_REFLECT Marcelo Mendes Spessoto Junior @ 2026-08-06 19:24 ` Jakub Kicinski 0 siblings, 0 replies; 9+ messages in thread From: Jakub Kicinski @ 2026-08-06 19:24 UTC (permalink / raw) To: Marcelo Mendes Spessoto Junior Cc: netdev, davem, edumazet, pabeni, horms, shuah, linux-kselftest, linux-kernel On Mon, 3 Aug 2026 23:59:09 -0300 Marcelo Mendes Spessoto Junior wrote: > echo "TEST management" > -./in_netns.sh ./ipv6_flowlabel_mgr > +./in_netns.sh \ > + sh -c 'sysctl -q -w net.ipv6.flowlabel_consistency=0 && ./ipv6_flowlabel_mgr' Would it be possible to make the netns prep part of the test itself? We can use the setup function in the harness to make each case run, or do it once. ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH net-next v2 4/4] selftests: net: adopt harness for flow label mgr 2026-08-04 2:59 [PATCH net-next v2 0/4] net: selftests: adjustments to ipv6_flowlabel_mgr Marcelo Mendes Spessoto Junior ` (2 preceding siblings ...) 2026-08-04 2:59 ` [PATCH net-next v2 3/4] selftests: net: test IPV6_FL_F_REFLECT Marcelo Mendes Spessoto Junior @ 2026-08-04 2:59 ` Marcelo Mendes Spessoto Junior 2026-08-06 19:22 ` Jakub Kicinski 2026-08-06 19:25 ` Jakub Kicinski 3 siblings, 2 replies; 9+ messages in thread From: Marcelo Mendes Spessoto Junior @ 2026-08-04 2:59 UTC (permalink / raw) To: netdev Cc: davem, edumazet, kuba, pabeni, horms, shuah, linux-kselftest, linux-kernel, Marcelo Mendes Spessoto Junior The kselftest_harness.h file contains modern helpers to build tests for kselftest. Dropping current test helpers for ipv6_flowlabel_mgr to use harness helps tests to be more legible and conform to the structure of the latest tests. It also enforces TAP standard. Another change made to the structure of ipv6_flowlabel_mgr test file was the removal of parse_opts. The supported opts were already unused: the binary is listed in TEST_GEN_FILES, and is driven solely by ipv6_flowlabel.sh via "./in_netns.sh ./ipv6_flowlabel_mgr", which never passed -l or -v. Dropping the -l gate means the two checks it previously guarded (each with a 13-second sleep, ~26 seconds total) are now unconditionally enabled on every run instead of never running at all. The TH_LOG and code comments cover the information obtainable from (-v). Signed-off-by: Marcelo Mendes Spessoto Junior <marcelomspessoto@gmail.com> --- .../selftests/net/ipv6_flowlabel_mgr.c | 521 ++++++++++-------- 1 file changed, 299 insertions(+), 222 deletions(-) diff --git a/tools/testing/selftests/net/ipv6_flowlabel_mgr.c b/tools/testing/selftests/net/ipv6_flowlabel_mgr.c index af87eec799c8..482921f7ee11 100644 --- a/tools/testing/selftests/net/ipv6_flowlabel_mgr.c +++ b/tools/testing/selftests/net/ipv6_flowlabel_mgr.c @@ -20,6 +20,7 @@ #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> +#include "kselftest_harness.h" /* uapi/glibc weirdness may leave this undefined */ #ifndef IPV6_FLOWLABEL_MGR @@ -33,35 +34,6 @@ #define FL_MIN_LINGER 6 #define FL_MAX_LINGER 150 -#define explain(x) \ - do { if (cfg_verbose) fprintf(stderr, " " x "\n"); } while (0) - -#define __expect(x) \ - do { \ - if (!(x)) \ - fprintf(stderr, "[OK] " #x "\n"); \ - else \ - error(1, 0, "[ERR] " #x " (line %d)", __LINE__); \ - } while (0) - -#define expect_pass(x) __expect(x) -#define expect_fail(x) __expect(!(x)) - -#define expect_fail_errno(x, e) \ - do { \ - int __exp = (e); \ - int __ret = (x); \ - int __err = errno; \ - if (__ret && __err == __exp) \ - fprintf(stderr, "[OK] " #x "\n"); \ - else \ - error(1, 0, "[ERR] " #x " (line %d): expected errno %d, got %d", \ - __LINE__, __exp, __err); \ - } while (0) - -static bool cfg_long_running; -static bool cfg_verbose; - static int flowlabel_get(int fd, uint32_t label, uint8_t share, uint16_t flags) { struct in6_flowlabel_req req = { @@ -159,230 +131,335 @@ static void tcp_connect(int listener, uint32_t flowlabel, int *client, int *acce *accepted = afd; } -static bool flowlabel_consistency_enabled(void) +TEST(cannot_get_non_existent_label) { - char buf[2] = {}; - int fd; + int fd, err; - fd = open("/proc/sys/net/ipv6/flowlabel_consistency", O_RDONLY); - if (fd == -1) - return true; + fd = socket(PF_INET6, SOCK_DGRAM, 0); + ASSERT_GE(fd, 0) TH_LOG("socket failed"); - if (read(fd, buf, sizeof(buf) - 1) < 0) - buf[0] = '1'; - close(fd); + err = flowlabel_get(fd, 9, IPV6_FL_S_ANY, 0); + ASSERT_TRUE(err) TH_LOG("expected get of a non-existent label to fail"); + ASSERT_EQ(ENOENT, errno) TH_LOG("expected ENOENT, got %d", errno); - return buf[0] != '0'; + ASSERT_EQ(0, close(fd)); +} + +TEST(cannot_put_non_existent_label) +{ + int fd, err; + + fd = socket(PF_INET6, SOCK_DGRAM, 0); + ASSERT_GE(fd, 0) TH_LOG("socket failed"); + + err = flowlabel_put(fd, 10); + ASSERT_TRUE(err) TH_LOG("expected put of a non-existent label to fail"); + ASSERT_EQ(ESRCH, errno) TH_LOG("expected ESRCH, got %d", errno); + + ASSERT_EQ(0, close(fd)); +} + +TEST(cannot_create_label_greater_than_20_bits) +{ + int fd, err; + + fd = socket(PF_INET6, SOCK_DGRAM, 0); + ASSERT_GE(fd, 0) TH_LOG("socket failed"); + + err = flowlabel_get(fd, 0x1FFFFF, IPV6_FL_S_ANY, IPV6_FL_F_CREATE); + ASSERT_TRUE(err) TH_LOG("expected label > 20 bits to be rejected"); + ASSERT_EQ(EINVAL, errno) TH_LOG("expected EINVAL, got %d", errno); + + ASSERT_EQ(0, close(fd)); +} + +TEST(can_create_and_get_and_put_labels) +{ + int fd, err; + + fd = socket(PF_INET6, SOCK_DGRAM, 0); + ASSERT_GE(fd, 0) TH_LOG("socket failed"); + + err = flowlabel_get(fd, 1, IPV6_FL_S_ANY, IPV6_FL_F_CREATE); + ASSERT_TRUE(!err) TH_LOG("failed to create label (FL_F_CREATE)"); + + err = flowlabel_get(fd, 1, IPV6_FL_S_ANY, 0); + ASSERT_TRUE(!err) TH_LOG("failed to get the label without FL_F_CREATE"); + + err = flowlabel_get(fd, 1, IPV6_FL_S_ANY, IPV6_FL_F_CREATE); + ASSERT_TRUE(!err) TH_LOG("failed to get it again with create flag set, too"); + + err = flowlabel_get(fd, 1, IPV6_FL_S_ANY, IPV6_FL_F_CREATE | IPV6_FL_F_EXCL); + ASSERT_TRUE(err) TH_LOG("expected FL_F_EXCL to reject an already-existing label"); + ASSERT_EQ(EEXIST, errno) TH_LOG("expected EEXIST, got %d", errno); + + err = flowlabel_put(fd, 1); + ASSERT_TRUE(!err) TH_LOG("failed to put first reference"); + err = flowlabel_put(fd, 1); + ASSERT_TRUE(!err) TH_LOG("failed to put second reference"); + err = flowlabel_put(fd, 1); + ASSERT_TRUE(!err) TH_LOG("failed to put third reference"); + err = flowlabel_put(fd, 1); + ASSERT_TRUE(err) TH_LOG("expected fourth put to fail, no references left"); + ASSERT_EQ(ESRCH, errno) TH_LOG("expected ESRCH, got %d", errno); + + ASSERT_EQ(0, close(fd)); +} + +TEST(exclusive_label_share) +{ + int fd, err; + + fd = socket(PF_INET6, SOCK_DGRAM, 0); + ASSERT_GE(fd, 0) TH_LOG("socket failed"); + + err = flowlabel_get(fd, 2, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE); + ASSERT_TRUE(!err) TH_LOG("failed to create a new exclusive label (FL_S_EXCL)"); + + err = flowlabel_get(fd, 2, IPV6_FL_S_ANY, IPV6_FL_F_CREATE); + ASSERT_TRUE(err) TH_LOG("expected reuse in non-exclusive mode to fail"); + ASSERT_EQ(EPERM, errno) TH_LOG("expected EPERM, got %d", errno); + + err = flowlabel_get(fd, 2, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE); + ASSERT_TRUE(err) TH_LOG("expected reuse in exclusive mode to fail too"); + ASSERT_EQ(EPERM, errno) TH_LOG("expected EPERM, got %d", errno); + + err = flowlabel_put(fd, 2); + ASSERT_TRUE(!err) TH_LOG("failed to put the exclusive label"); + + err = flowlabel_get(fd, 2, IPV6_FL_S_ANY, IPV6_FL_F_CREATE); + ASSERT_TRUE(err) TH_LOG("expected reuse to fail, due to linger"); + ASSERT_EQ(EPERM, errno) TH_LOG("expected EPERM, got %d", errno); + + sleep(FL_MIN_LINGER * 2 + 1); + + err = flowlabel_get(fd, 2, IPV6_FL_S_ANY, IPV6_FL_F_CREATE); + ASSERT_TRUE(!err) TH_LOG("expected reuse to succeed after linger"); + + ASSERT_EQ(0, close(fd)); } -static void run_tests(int fd) +TEST(user_private_label_share) { - int wstatus; + int fd, err, wstatus; pid_t pid; - explain("cannot get non-existent label"); - expect_fail(flowlabel_get(fd, 1, IPV6_FL_S_ANY, 0)); - - explain("cannot put non-existent label"); - expect_fail(flowlabel_put(fd, 1)); - - explain("cannot create label greater than 20 bits"); - expect_fail(flowlabel_get(fd, 0x1FFFFF, IPV6_FL_S_ANY, - IPV6_FL_F_CREATE)); - - explain("create a new label (FL_F_CREATE)"); - expect_pass(flowlabel_get(fd, 1, IPV6_FL_S_ANY, IPV6_FL_F_CREATE)); - explain("can get the label (without FL_F_CREATE)"); - expect_pass(flowlabel_get(fd, 1, IPV6_FL_S_ANY, 0)); - explain("can get it again with create flag set, too"); - expect_pass(flowlabel_get(fd, 1, IPV6_FL_S_ANY, IPV6_FL_F_CREATE)); - explain("cannot get it again with the exclusive (FL_FL_EXCL) flag"); - expect_fail(flowlabel_get(fd, 1, IPV6_FL_S_ANY, - IPV6_FL_F_CREATE | IPV6_FL_F_EXCL)); - explain("can now put exactly three references"); - expect_pass(flowlabel_put(fd, 1)); - expect_pass(flowlabel_put(fd, 1)); - expect_pass(flowlabel_put(fd, 1)); - expect_fail(flowlabel_put(fd, 1)); - - explain("create a new exclusive label (FL_S_EXCL)"); - expect_pass(flowlabel_get(fd, 2, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE)); - explain("cannot get it again in non-exclusive mode"); - expect_fail(flowlabel_get(fd, 2, IPV6_FL_S_ANY, IPV6_FL_F_CREATE)); - explain("cannot get it again in exclusive mode either"); - expect_fail(flowlabel_get(fd, 2, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE)); - expect_pass(flowlabel_put(fd, 2)); - - if (cfg_long_running) { - explain("cannot reuse the label, due to linger"); - expect_fail(flowlabel_get(fd, 2, IPV6_FL_S_ANY, - IPV6_FL_F_CREATE)); - explain("after sleep, can reuse"); - sleep(FL_MIN_LINGER * 2 + 1); - expect_pass(flowlabel_get(fd, 2, IPV6_FL_S_ANY, - IPV6_FL_F_CREATE)); - } + fd = socket(PF_INET6, SOCK_DGRAM, 0); + ASSERT_GE(fd, 0) TH_LOG("socket failed"); + + err = flowlabel_get(fd, 3, IPV6_FL_S_USER, IPV6_FL_F_CREATE); + ASSERT_TRUE(!err) TH_LOG("failed to create a new user-private label (FL_S_USER)"); + + err = flowlabel_get(fd, 3, IPV6_FL_S_ANY, 0); + ASSERT_TRUE(err) TH_LOG("expected get in non-exclusive mode to fail"); + ASSERT_EQ(EPERM, errno) TH_LOG("expected EPERM, got %d", errno); + + err = flowlabel_get(fd, 3, IPV6_FL_S_EXCL, 0); + ASSERT_TRUE(err) TH_LOG("expected get in exclusive mode to fail"); + ASSERT_EQ(EPERM, errno) TH_LOG("expected EPERM, got %d", errno); + + err = flowlabel_get(fd, 3, IPV6_FL_S_USER, 0); + ASSERT_TRUE(!err) TH_LOG("failed to get it again in user mode"); - explain("create a new user-private label (FL_S_USER)"); - expect_pass(flowlabel_get(fd, 3, IPV6_FL_S_USER, IPV6_FL_F_CREATE)); - explain("cannot get it again in non-exclusive mode"); - expect_fail(flowlabel_get(fd, 3, IPV6_FL_S_ANY, 0)); - explain("cannot get it again in exclusive mode"); - expect_fail(flowlabel_get(fd, 3, IPV6_FL_S_EXCL, 0)); - explain("can get it again in user mode"); - expect_pass(flowlabel_get(fd, 3, IPV6_FL_S_USER, 0)); - explain("child process can get it too, but not after setuid(nobody)"); pid = fork(); - if (pid == -1) - error(1, errno, "fork"); + ASSERT_NE(-1, pid) TH_LOG("fork failed"); if (!pid) { - expect_pass(flowlabel_get(fd, 3, IPV6_FL_S_USER, 0)); - if (setuid(USHRT_MAX)) + err = flowlabel_get(fd, 3, IPV6_FL_S_USER, 0); + ASSERT_TRUE(!err) TH_LOG("child failed to get the user-private label"); + + if (setuid(USHRT_MAX)) { fprintf(stderr, "[INFO] skip setuid child test\n"); - else - expect_fail(flowlabel_get(fd, 3, IPV6_FL_S_USER, 0)); + exit(0); + } + + err = flowlabel_get(fd, 3, IPV6_FL_S_USER, 0); + ASSERT_TRUE(err) TH_LOG("child unexpectedly got the label after setuid(nobody)"); + ASSERT_EQ(EPERM, errno) TH_LOG("expected EPERM, got %d", errno); exit(0); } - if (wait(&wstatus) == -1) - error(1, errno, "wait"); - if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) - error(1, errno, "wait: unexpected child result"); - - explain("create a new process-private label (FL_S_PROCESS)"); - expect_pass(flowlabel_get(fd, 4, IPV6_FL_S_PROCESS, IPV6_FL_F_CREATE)); - explain("can get it again"); - expect_pass(flowlabel_get(fd, 4, IPV6_FL_S_PROCESS, 0)); - explain("child process cannot can get it"); + ASSERT_EQ(pid, wait(&wstatus)) TH_LOG("wait failed"); + ASSERT_TRUE(WIFEXITED(wstatus)) TH_LOG("child did not exit normally"); + ASSERT_EQ(0, WEXITSTATUS(wstatus)) TH_LOG("child reported unexpected result"); + + ASSERT_EQ(0, close(fd)); +} + +TEST(process_private_label_share) +{ + int fd, err, wstatus; + pid_t pid; + + fd = socket(PF_INET6, SOCK_DGRAM, 0); + ASSERT_GE(fd, 0) TH_LOG("socket failed"); + + err = flowlabel_get(fd, 4, IPV6_FL_S_PROCESS, IPV6_FL_F_CREATE); + ASSERT_TRUE(!err) TH_LOG("failed to create a new process-private label (FL_S_PROCESS)"); + + err = flowlabel_get(fd, 4, IPV6_FL_S_PROCESS, 0); + ASSERT_TRUE(!err) TH_LOG("failed to get it again"); + pid = fork(); - if (pid == -1) - error(1, errno, "fork"); + ASSERT_NE(-1, pid) TH_LOG("fork failed"); if (!pid) { - expect_fail(flowlabel_get(fd, 4, IPV6_FL_S_PROCESS, 0)); + err = flowlabel_get(fd, 4, IPV6_FL_S_PROCESS, 0); + ASSERT_TRUE(err) TH_LOG("child unexpectedly got the process-private label"); + ASSERT_EQ(EPERM, errno) TH_LOG("expected EPERM, got %d", errno); exit(0); } - if (wait(&wstatus) == -1) - error(1, errno, "wait"); - if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) - error(1, errno, "wait: unexpected child result"); - - explain("It is not possible to renew a label that does not exist"); - expect_fail_errno(flowlabel_renew(fd, 5, 2 * (FL_MIN_LINGER * 2 + 1)), ESRCH); - - explain("Create a label for basic renew validation"); - expect_pass(flowlabel_get(fd, 5, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE)); - explain("Check if renew does not return errors for existing and valid label"); - expect_pass(flowlabel_renew(fd, 5, 2 * (FL_MIN_LINGER * 2 + 1))); - - if (cfg_long_running) { - explain("create a new label with FL_MIN_LINGER linger time"); - expect_pass(flowlabel_get(fd, 6, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE)); - explain("renew the label to increase its linger time and put it"); - expect_pass(flowlabel_renew(fd, 6, 2 * (FL_MIN_LINGER * 2 + 1))); - expect_pass(flowlabel_put(fd, 6)); - sleep(FL_MIN_LINGER * 2 + 1); - explain("The label cannot be created because the new linger time is not over yet"); - expect_fail_errno(flowlabel_get(fd, 6, IPV6_FL_S_ANY, IPV6_FL_F_CREATE), EPERM); - } + ASSERT_EQ(pid, wait(&wstatus)) TH_LOG("wait failed"); + ASSERT_TRUE(WIFEXITED(wstatus)) TH_LOG("child did not exit normally"); + ASSERT_EQ(0, WEXITSTATUS(wstatus)) TH_LOG("child reported unexpected result"); - { - struct in6_flowlabel_req freq = { - .flr_action = IPV6_FL_A_GET, - .flr_flags = IPV6_FL_F_REMOTE, - }; - socklen_t freq_len = sizeof(freq); - int remote_listener = tcp_listen(); - int remote_cfd, remote_afd; - - explain("Prepare TCP SYN for REMOTE flag validation"); - tcp_connect(remote_listener, 7, &remote_cfd, &remote_afd); - - explain("Query for label sent by client with IPV6_FL_F_REMOTE"); - expect_pass(getsockopt(remote_afd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &freq, &freq_len)); - if (ntohl(freq.flr_label) != 7) - error(1, 0, "unexpected remote flowlabel %u", ntohl(freq.flr_label)); - - close(remote_afd); - close(remote_cfd); - close(remote_listener); - } - - if (flowlabel_consistency_enabled()) { - fprintf(stderr, - "[INFO] skip REFLECT flag validation (net.ipv6.flowlabel_consistency must be 0)\n"); - } else { - struct in6_flowlabel_req reflect_query = { - .flr_action = IPV6_FL_A_GET, - }; - struct in6_flowlabel_req reflect_off = { - .flr_action = IPV6_FL_A_PUT, - .flr_flags = IPV6_FL_F_REFLECT, - }; - struct in6_flowlabel_req reflect_on = { - .flr_action = IPV6_FL_A_GET, - .flr_flags = IPV6_FL_F_REFLECT, - }; - socklen_t reflect_query_len = sizeof(reflect_query); - int reflect_listener = tcp_listen(); - int reflect_cfd, reflect_afd; - - explain("Enable REFLECT on the listener before the client connects"); - expect_pass(setsockopt(reflect_listener, SOL_IPV6, IPV6_FLOWLABEL_MGR, - &reflect_on, sizeof(reflect_on))); - - tcp_connect(reflect_listener, 8, &reflect_cfd, &reflect_afd); - - explain("Query the accepted socket's outgoing label, should be reflected"); - expect_pass(getsockopt(reflect_afd, SOL_IPV6, IPV6_FLOWLABEL_MGR, - &reflect_query, &reflect_query_len)); - if (ntohl(reflect_query.flr_label) != 8) - error(1, 0, "unexpected reflected flowlabel %u", - ntohl(reflect_query.flr_label)); - - explain("PUT+REFLECT disables reflection on the accepted socket"); - expect_pass(setsockopt(reflect_afd, SOL_IPV6, IPV6_FLOWLABEL_MGR, - &reflect_off, sizeof(reflect_off))); - explain("cannot disable reflection twice"); - expect_fail(setsockopt(reflect_afd, SOL_IPV6, IPV6_FLOWLABEL_MGR, - &reflect_off, sizeof(reflect_off))); - - close(reflect_afd); - close(reflect_cfd); - close(reflect_listener); - } + ASSERT_EQ(0, close(fd)); } -static void parse_opts(int argc, char **argv) +TEST(cannot_renew_non_existent_label) { - int c; - - while ((c = getopt(argc, argv, "lv")) != -1) { - switch (c) { - case 'l': - cfg_long_running = true; - break; - case 'v': - cfg_verbose = true; - break; - default: - error(1, 0, "%s: parse error", argv[0]); - } - } + int fd, err; + + fd = socket(PF_INET6, SOCK_DGRAM, 0); + ASSERT_GE(fd, 0) TH_LOG("socket failed"); + + err = flowlabel_renew(fd, 5, 2 * (FL_MIN_LINGER * 2 + 1)); + ASSERT_TRUE(err) TH_LOG("expected renew of a non-existent label to fail"); + ASSERT_EQ(ESRCH, errno) TH_LOG("expected ESRCH, got %d", errno); + + ASSERT_EQ(0, close(fd)); } -int main(int argc, char **argv) +TEST(can_renew_existing_label) { - int fd; + int fd, err; + + fd = socket(PF_INET6, SOCK_DGRAM, 0); + ASSERT_GE(fd, 0) TH_LOG("socket failed"); + + err = flowlabel_get(fd, 5, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE); + ASSERT_TRUE(!err) TH_LOG("failed to create a new label for renew validation"); - parse_opts(argc, argv); + err = flowlabel_renew(fd, 5, 2 * (FL_MIN_LINGER * 2 + 1)); + ASSERT_TRUE(!err) TH_LOG("failed to renew an existing valid label"); + + err = flowlabel_put(fd, 5); + ASSERT_TRUE(!err) TH_LOG("failed to put the label"); + + ASSERT_EQ(0, close(fd)); +} + +TEST(renew_label_linger) +{ + /* RENEW must extend a label's linger period: putting a renewed + * label and waiting out its original linger time must not be + * enough to allow the label to be recreated. + */ + int fd, err; fd = socket(PF_INET6, SOCK_DGRAM, 0); + ASSERT_GE(fd, 0) TH_LOG("socket failed"); + + err = flowlabel_get(fd, 6, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE); + ASSERT_TRUE(!err) TH_LOG("failed to create a new label with FL_MIN_LINGER linger time"); + + err = flowlabel_renew(fd, 6, 2 * (FL_MIN_LINGER * 2 + 1)); + ASSERT_TRUE(!err) TH_LOG("failed to renew the label to increase its linger time"); + + err = flowlabel_put(fd, 6); + ASSERT_TRUE(!err) TH_LOG("failed to put the label"); + + sleep(FL_MIN_LINGER * 2 + 1); + + err = flowlabel_get(fd, 6, IPV6_FL_S_ANY, IPV6_FL_F_CREATE); + ASSERT_TRUE(err) TH_LOG("expected reuse to fail, new linger time not over yet"); + ASSERT_EQ(EPERM, errno) TH_LOG("expected EPERM, got %d", errno); + + ASSERT_EQ(0, close(fd)); +} + +TEST(remote_flag) +{ + /* The REMOTE flag, used for getsockopt, is expected to retrieve the + * label from the latest received header. + */ + struct in6_flowlabel_req freq = { + .flr_action = IPV6_FL_A_GET, + .flr_flags = IPV6_FL_F_REMOTE, + }; + socklen_t freq_len = sizeof(freq); + int listener, cfd, afd, err; + + listener = tcp_listen(); + tcp_connect(listener, 7, &cfd, &afd); + + err = getsockopt(afd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &freq, &freq_len); + ASSERT_TRUE(!err) TH_LOG("getsockopt with IPV6_FL_F_REMOTE failed"); + ASSERT_EQ(7, ntohl(freq.flr_label)) TH_LOG("unexpected remote flow label"); + + ASSERT_EQ(0, close(afd)); + ASSERT_EQ(0, close(cfd)); + ASSERT_EQ(0, close(listener)); +} + +static bool flowlabel_consistency_enabled(void) +{ + char buf[2] = {}; + int fd; + + fd = open("/proc/sys/net/ipv6/flowlabel_consistency", O_RDONLY); if (fd == -1) - error(1, errno, "socket"); + return true; + + if (read(fd, buf, sizeof(buf) - 1) < 0) + buf[0] = '1'; + close(fd); - run_tests(fd); + return buf[0] != '0'; +} + +TEST(reflect_flag) +{ + /* The REFLECT flag acts as a trigger to the REPFLOW bit. When REPFLOW + * is triggered for a socket, it adopts the label received from the + * connected socket. + */ + struct in6_flowlabel_req reflect_on = { + .flr_action = IPV6_FL_A_GET, + .flr_flags = IPV6_FL_F_REFLECT, + }; + struct in6_flowlabel_req reflect_query = { + .flr_action = IPV6_FL_A_GET, + }; + struct in6_flowlabel_req reflect_off = { + .flr_action = IPV6_FL_A_PUT, + .flr_flags = IPV6_FL_F_REFLECT, + }; + socklen_t reflect_query_len = sizeof(reflect_query); + int listener, cfd, afd, err; + + if (flowlabel_consistency_enabled()) + SKIP(return, + "net.ipv6.flowlabel_consistency must be 0 (run via ipv6_flowlabel.sh)"); + + listener = tcp_listen(); + err = setsockopt(listener, SOL_IPV6, IPV6_FLOWLABEL_MGR, &reflect_on, sizeof(reflect_on)); + ASSERT_TRUE(!err) TH_LOG("failed to enable REFLECT on the listener"); - if (close(fd)) - error(1, errno, "close"); + tcp_connect(listener, 8, &cfd, &afd); - return 0; + err = getsockopt(afd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &reflect_query, &reflect_query_len); + ASSERT_TRUE(!err) TH_LOG("failed to query the accepted socket's outgoing label"); + ASSERT_EQ(8, ntohl(reflect_query.flr_label)) + TH_LOG("accepted socket did not reflect client's label"); + + err = setsockopt(afd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &reflect_off, sizeof(reflect_off)); + ASSERT_TRUE(!err) TH_LOG("failed to disable REFLECT on the accepted socket"); + + err = setsockopt(afd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &reflect_off, sizeof(reflect_off)); + ASSERT_TRUE(err) TH_LOG("expected disabling REFLECT twice to fail"); + ASSERT_EQ(ESRCH, errno) TH_LOG("expected ESRCH, got %d", errno); + + ASSERT_EQ(0, close(afd)); + ASSERT_EQ(0, close(cfd)); + ASSERT_EQ(0, close(listener)); } + +TEST_HARNESS_MAIN -- 2.55.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH net-next v2 4/4] selftests: net: adopt harness for flow label mgr 2026-08-04 2:59 ` [PATCH net-next v2 4/4] selftests: net: adopt harness for flow label mgr Marcelo Mendes Spessoto Junior @ 2026-08-06 19:22 ` Jakub Kicinski 2026-08-06 19:25 ` Jakub Kicinski 1 sibling, 0 replies; 9+ messages in thread From: Jakub Kicinski @ 2026-08-06 19:22 UTC (permalink / raw) To: marcelomspessoto Cc: Jakub Kicinski, netdev, davem, edumazet, pabeni, horms, shuah, linux-kselftest, linux-kernel This is an AI-generated review of your patch. The human sending this email has considered the AI review valid, or at least plausible. --- selftests: net: adopt harness for flow label mgr This converts tools/testing/selftests/net/ipv6_flowlabel_mgr.c from the local expect_pass/expect_fail/explain helpers to kselftest_harness.h, splitting run_tests() into individual TEST() cases. It also removes parse_opts(), so the two previously -l gated checks now always run. > diff --git a/tools/testing/selftests/net/ipv6_flowlabel_mgr.c b/tools/testing/selftests/net/ipv6_flowlabel_mgr.c > index af87eec799c80..482921f7ee119 100644 > --- a/tools/testing/selftests/net/ipv6_flowlabel_mgr.c > +++ b/tools/testing/selftests/net/ipv6_flowlabel_mgr.c [ ... ] > @@ -159,230 +131,335 @@ static void tcp_connect(int listener, uint32_t flowlabel, int *client, int *acce [ ... ] > pid = fork(); > - if (pid == -1) > - error(1, errno, "fork"); > + ASSERT_NE(-1, pid) TH_LOG("fork failed"); > if (!pid) { > - expect_pass(flowlabel_get(fd, 3, IPV6_FL_S_USER, 0)); > - if (setuid(USHRT_MAX)) > + err = flowlabel_get(fd, 3, IPV6_FL_S_USER, 0); > + ASSERT_TRUE(!err) TH_LOG("child failed to get the user-private label"); > + > + if (setuid(USHRT_MAX)) { > fprintf(stderr, "[INFO] skip setuid child test\n"); > - else > - expect_fail(flowlabel_get(fd, 3, IPV6_FL_S_USER, 0)); > + exit(0); > + } [Severity: Medium] Should this path report a skip rather than a pass? In TEST(user_private_label_share), when setuid(USHRT_MAX) fails the child prints to stderr and calls exit(0). The parent then runs: ASSERT_EQ(pid, wait(&wstatus)) TH_LOG("wait failed"); ASSERT_TRUE(WIFEXITED(wstatus)) TH_LOG("child did not exit normally"); ASSERT_EQ(0, WEXITSTATUS(wstatus)) TH_LOG("child reported unexpected result"); All three succeed, so the harness emits "ok user_private_label_share" even though the check the test is named for, that an IPV6_FL_S_USER label cannot be obtained after switching uid, never executed. setuid(USHRT_MAX) fails when the binary runs without CAP_SETUID, or in a user namespace where uid 65535 is unmapped, which is reachable since ipv6_flowlabel.sh drives this through ./in_netns.sh. Would propagating KSFT_SKIP work better here, for example the child doing exit(KSFT_SKIP) and the parent turning that into SKIP(return, ...)? The silent-skip logic itself predates this patch, but before the conversion there were no per-test TAP results; now the test individually reports "ok" instead of "# SKIP". The raw fprintf(stderr, "[INFO] ...") is also the only diagnostic left in the converted file that does not go through TH_LOG, which prefixes output with "# ". [ ... ] > +TEST(reflect_flag) > +{ [ ... ] > + if (flowlabel_consistency_enabled()) > + SKIP(return, > + "net.ipv6.flowlabel_consistency must be 0 (run via ipv6_flowlabel.sh)"); This is the idiom the setuid path above could use: SKIP() sets _metadata->exit_code = KSFT_SKIP and prints a "# SKIP" diagnostic, so an unmet precondition is not reported as a pass. Could both unmet-precondition sites in this file use it consistently? [ ... ] ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH net-next v2 4/4] selftests: net: adopt harness for flow label mgr 2026-08-04 2:59 ` [PATCH net-next v2 4/4] selftests: net: adopt harness for flow label mgr Marcelo Mendes Spessoto Junior 2026-08-06 19:22 ` Jakub Kicinski @ 2026-08-06 19:25 ` Jakub Kicinski 1 sibling, 0 replies; 9+ messages in thread From: Jakub Kicinski @ 2026-08-06 19:25 UTC (permalink / raw) To: Marcelo Mendes Spessoto Junior Cc: netdev, davem, edumazet, pabeni, horms, shuah, linux-kselftest, linux-kernel On Mon, 3 Aug 2026 23:59:10 -0300 Marcelo Mendes Spessoto Junior wrote: > +TEST(cannot_put_non_existent_label) > +{ > + int fd, err; > + > + fd = socket(PF_INET6, SOCK_DGRAM, 0); > + ASSERT_GE(fd, 0) TH_LOG("socket failed"); > + > + err = flowlabel_put(fd, 10); > + ASSERT_TRUE(err) TH_LOG("expected put of a non-existent label to fail"); > + ASSERT_EQ(ESRCH, errno) TH_LOG("expected ESRCH, got %d", errno); nit: I think you're using ASSERTS where EXPECT should be used. My understanding is that EXPECT is better if the test can continue. > + ASSERT_EQ(0, close(fd)); ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-08-06 19:25 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-08-04 2:59 [PATCH net-next v2 0/4] net: selftests: adjustments to ipv6_flowlabel_mgr Marcelo Mendes Spessoto Junior 2026-08-04 2:59 ` [PATCH net-next v2 1/4] selftests: net: test IPV6_FL_A_RENEW Marcelo Mendes Spessoto Junior 2026-08-06 19:22 ` Jakub Kicinski 2026-08-04 2:59 ` [PATCH net-next v2 2/4] selftests: net: test IPV6_FL_F_REMOTE Marcelo Mendes Spessoto Junior 2026-08-04 2:59 ` [PATCH net-next v2 3/4] selftests: net: test IPV6_FL_F_REFLECT Marcelo Mendes Spessoto Junior 2026-08-06 19:24 ` Jakub Kicinski 2026-08-04 2:59 ` [PATCH net-next v2 4/4] selftests: net: adopt harness for flow label mgr Marcelo Mendes Spessoto Junior 2026-08-06 19:22 ` Jakub Kicinski 2026-08-06 19:25 ` Jakub Kicinski
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox