* [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
* [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
* [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 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
* 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 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
* 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