* [PATCH net-next v3 0/5] net: selftests: adjustments to ipv6_flowlabel_mgr
@ 2026-08-07 22:09 Marcelo Mendes Spessoto Junior
2026-08-07 22:09 ` [PATCH net-next v3 1/5] selftests: net: test IPV6_FL_A_RENEW Marcelo Mendes Spessoto Junior
` (4 more replies)
0 siblings, 5 replies; 6+ messages in thread
From: Marcelo Mendes Spessoto Junior @ 2026-08-07 22:09 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Shuah Khan
Cc: netdev, 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 two patches from this set aim to add a proper test
case for RENEW and REMOTE.
The third patch was added to insert network namespace creation inside
the test suite, instead of relying on external wrapper scripts. This
change conforms to other net test implementations, such as
tools/testing/selftests/net/icmp_rfc4884.c, and it is important for
the fourth and fifth patches.
The fourth patch adds the IPV6_FL_F_REFLECT test.
The fifth patch proposes the adoption of "kselftest_harness.h" helpers,
improving code readability and conforming to the implementation of the most
recent selftests.
---
v3:
- Remove unused FL_MAX_LINGER variable
- Set flr_share explicitly for renew tests
- Add corner case for expect_fail_errno macro where test succeeds when
it should've failed
- Add a new intermediate patch, between remote test patch and reflect test
patch, placing the netns setup inside the test file
- Add missing skip for setuid fail, addressing stale code from harness
migration
- Delimit EXPECT and ASSERT usage (use former when test can continue)
- Minor commit message rewording
- Apply more missing scripts/checkpath.pl fixes
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
- https://lore.kernel.org/all/20260804025910.50145-1-marcelomspessoto@gmail.com/
v1:
- https://lore.kernel.org/all/20260727043516.93101-1-marcelospe@proton.me/
Marcelo Mendes Spessoto Junior (5):
selftests: net: test IPV6_FL_A_RENEW
selftests: net: test IPV6_FL_F_REMOTE
selftests: net: create own netns in ipv6_flowlabel_mgr
selftests: net: test IPV6_FL_F_REFLECT
selftests: net: adopt harness for flow label mgr
tools/testing/selftests/net/ipv6_flowlabel.sh | 2 +-
.../selftests/net/ipv6_flowlabel_mgr.c | 584 ++++++++++++++----
2 files changed, 466 insertions(+), 120 deletions(-)
--
2.55.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH net-next v3 1/5] selftests: net: test IPV6_FL_A_RENEW
2026-08-07 22:09 [PATCH net-next v3 0/5] net: selftests: adjustments to ipv6_flowlabel_mgr Marcelo Mendes Spessoto Junior
@ 2026-08-07 22:09 ` Marcelo Mendes Spessoto Junior
2026-08-07 22:09 ` [PATCH net-next v3 2/5] selftests: net: test IPV6_FL_F_REMOTE Marcelo Mendes Spessoto Junior
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Marcelo Mendes Spessoto Junior @ 2026-08-07 22:09 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Shuah Khan
Cc: netdev, 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.
This test is based on the previously implemented EXCL share test,
which demonstrates that a new flow label with the same value can be
created after the linger period. Renew is used here to show that a
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 the expect_fail_errno helper is necessary to assert
the corresponding error when a function can fail in multiple ways.
Signed-off-by: Marcelo Mendes Spessoto Junior <marcelomspessoto@gmail.com>
---
.../selftests/net/ipv6_flowlabel_mgr.c | 58 ++++++++++++++++++-
1 file changed, 57 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/net/ipv6_flowlabel_mgr.c b/tools/testing/selftests/net/ipv6_flowlabel_mgr.c
index af95b48acea9..ec187890a1ed 100644
--- a/tools/testing/selftests/net/ipv6_flowlabel_mgr.c
+++ b/tools/testing/selftests/net/ipv6_flowlabel_mgr.c
@@ -42,6 +42,23 @@
#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 if (!__ret) \
+ error(1, 0, "[ERR] " #x \
+ " (line %d): unexpectedly succeeded", \
+ __LINE__); \
+ 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 +88,19 @@ 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, uint8_t share,
+ uint16_t linger)
+{
+ struct in6_flowlabel_req req = {
+ .flr_action = IPV6_FL_A_RENEW,
+ .flr_label = htonl(label),
+ .flr_share = share,
+ .flr_linger = linger,
+ };
+
+ return setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &req, sizeof(req));
+}
+
static void run_tests(int fd)
{
int wstatus;
@@ -94,7 +124,7 @@ static void run_tests(int fd)
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));
+ 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));
@@ -160,6 +190,32 @@ 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, IPV6_FL_S_EXCL,
+ 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("renew does not error for an existing, valid label");
+ expect_pass(flowlabel_renew(fd, 5, IPV6_FL_S_EXCL,
+ 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 extend linger, then put it");
+ expect_pass(flowlabel_renew(fd, 6, IPV6_FL_S_EXCL,
+ 2 * (FL_MIN_LINGER * 2 + 1)));
+ expect_pass(flowlabel_put(fd, 6));
+ sleep(FL_MIN_LINGER * 2 + 1);
+ explain("cannot create: new linger time 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] 6+ messages in thread
* [PATCH net-next v3 2/5] selftests: net: test IPV6_FL_F_REMOTE
2026-08-07 22:09 [PATCH net-next v3 0/5] net: selftests: adjustments to ipv6_flowlabel_mgr Marcelo Mendes Spessoto Junior
2026-08-07 22:09 ` [PATCH net-next v3 1/5] selftests: net: test IPV6_FL_A_RENEW Marcelo Mendes Spessoto Junior
@ 2026-08-07 22:09 ` Marcelo Mendes Spessoto Junior
2026-08-07 22:09 ` [PATCH net-next v3 3/5] selftests: net: create own netns in ipv6_flowlabel_mgr Marcelo Mendes Spessoto Junior
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Marcelo Mendes Spessoto Junior @ 2026-08-07 22:09 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Shuah Khan
Cc: netdev, 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 | 88 +++++++++++++++++++
1 file changed, 88 insertions(+)
diff --git a/tools/testing/selftests/net/ipv6_flowlabel_mgr.c b/tools/testing/selftests/net/ipv6_flowlabel_mgr.c
index ec187890a1ed..51541e792257 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
@@ -101,6 +104,67 @@ static int flowlabel_renew(int fd, uint32_t label, uint8_t share,
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;
@@ -216,6 +280,30 @@ static void run_tests(int fd)
IPV6_FL_F_CREATE),
EPERM);
}
+
+ {
+ struct in6_flowlabel_req freq = {
+ .flr_action = IPV6_FL_A_GET,
+ .flr_flags = IPV6_FL_F_REMOTE,
+ };
+ int remote_listener = tcp_listen();
+ socklen_t freq_len = sizeof(freq);
+ 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] 6+ messages in thread
* [PATCH net-next v3 3/5] selftests: net: create own netns in ipv6_flowlabel_mgr
2026-08-07 22:09 [PATCH net-next v3 0/5] net: selftests: adjustments to ipv6_flowlabel_mgr Marcelo Mendes Spessoto Junior
2026-08-07 22:09 ` [PATCH net-next v3 1/5] selftests: net: test IPV6_FL_A_RENEW Marcelo Mendes Spessoto Junior
2026-08-07 22:09 ` [PATCH net-next v3 2/5] selftests: net: test IPV6_FL_F_REMOTE Marcelo Mendes Spessoto Junior
@ 2026-08-07 22:09 ` Marcelo Mendes Spessoto Junior
2026-08-07 22:09 ` [PATCH net-next v3 4/5] selftests: net: test IPV6_FL_F_REFLECT Marcelo Mendes Spessoto Junior
2026-08-07 22:09 ` [PATCH net-next v3 5/5] selftests: net: adopt harness for flow label mgr Marcelo Mendes Spessoto Junior
4 siblings, 0 replies; 6+ messages in thread
From: Marcelo Mendes Spessoto Junior @ 2026-08-07 22:09 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Shuah Khan
Cc: netdev, linux-kselftest, linux-kernel,
Marcelo Mendes Spessoto Junior
Have ipv6_flowlabel_mgr create and configure its own network
namespace (unshare(CLONE_NEWNET) + bring up lo), the same way
ipv6_fragmentation.c and icmp_rfc4884.c already do, instead of
relying on the in_netns.sh wrapper script.
The setup can then be reused across tests through fixtures and
provide isolated network environments for each test in the case
of a future adoption of kselftest_harness.
It also avoids the leak of modifications to the netns in case the
user runs the test file directly, outside the wrapper and without
the in_netns.sh file.
Signed-off-by: Marcelo Mendes Spessoto Junior <marcelomspessoto@gmail.com>
---
tools/testing/selftests/net/ipv6_flowlabel.sh | 2 +-
.../selftests/net/ipv6_flowlabel_mgr.c | 28 +++++++++++++++++++
2 files changed, 29 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/net/ipv6_flowlabel.sh b/tools/testing/selftests/net/ipv6_flowlabel.sh
index cee95e252bee..2eeda39bf64c 100755
--- a/tools/testing/selftests/net/ipv6_flowlabel.sh
+++ b/tools/testing/selftests/net/ipv6_flowlabel.sh
@@ -8,7 +8,7 @@
set -e
echo "TEST management"
-./in_netns.sh ./ipv6_flowlabel_mgr
+./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 51541e792257..f8d8b09b9d86 100644
--- a/tools/testing/selftests/net/ipv6_flowlabel_mgr.c
+++ b/tools/testing/selftests/net/ipv6_flowlabel_mgr.c
@@ -8,11 +8,14 @@
#include <errno.h>
#include <limits.h>
#include <linux/in6.h>
+#include <net/if.h>
+#include <sched.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
@@ -306,6 +309,30 @@ static void run_tests(int fd)
}
}
+static void setup(void)
+{
+ struct ifreq ifr = {
+ .ifr_name = "lo"
+ };
+ int ctl;
+
+ if (unshare(CLONE_NEWNET))
+ error(1, errno, "unshare");
+
+ ctl = socket(AF_LOCAL, SOCK_STREAM, 0);
+ if (ctl == -1)
+ error(1, errno, "socket");
+
+ if (ioctl(ctl, SIOCGIFFLAGS, &ifr))
+ error(1, errno, "ioctl SIOCGIFFLAGS");
+ ifr.ifr_flags |= IFF_UP;
+ if (ioctl(ctl, SIOCSIFFLAGS, &ifr))
+ error(1, errno, "ioctl: bring lo up");
+
+ if (close(ctl))
+ error(1, errno, "close");
+}
+
static void parse_opts(int argc, char **argv)
{
int c;
@@ -329,6 +356,7 @@ int main(int argc, char **argv)
int fd;
parse_opts(argc, argv);
+ setup();
fd = socket(PF_INET6, SOCK_DGRAM, 0);
if (fd == -1)
--
2.55.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH net-next v3 4/5] selftests: net: test IPV6_FL_F_REFLECT
2026-08-07 22:09 [PATCH net-next v3 0/5] net: selftests: adjustments to ipv6_flowlabel_mgr Marcelo Mendes Spessoto Junior
` (2 preceding siblings ...)
2026-08-07 22:09 ` [PATCH net-next v3 3/5] selftests: net: create own netns in ipv6_flowlabel_mgr Marcelo Mendes Spessoto Junior
@ 2026-08-07 22:09 ` Marcelo Mendes Spessoto Junior
2026-08-07 22:09 ` [PATCH net-next v3 5/5] selftests: net: adopt harness for flow label mgr Marcelo Mendes Spessoto Junior
4 siblings, 0 replies; 6+ messages in thread
From: Marcelo Mendes Spessoto Junior @ 2026-08-07 22:09 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Shuah Khan
Cc: netdev, linux-kselftest, linux-kernel,
Marcelo Mendes Spessoto Junior
According to the source code, flowlabel_consistency must be
deactivated for the IPV6_FL_F_REFLECT flag to work. Since
ipv6_flowlabel_mgr now runs in its own network namespace, do this
directly from the test binary. Attempt to disable
net.ipv6.flowlabel_consistency and skip the reflect test if that
fails. A disabled flowlabel_consistency does not affect the remaining
features being tested on the file, and failing to disable is not fatal
and skips the reflect test only.
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>
---
.../selftests/net/ipv6_flowlabel_mgr.c | 66 +++++++++++++++++++
1 file changed, 66 insertions(+)
diff --git a/tools/testing/selftests/net/ipv6_flowlabel_mgr.c b/tools/testing/selftests/net/ipv6_flowlabel_mgr.c
index f8d8b09b9d86..d32150abd8ff 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 <net/if.h>
@@ -168,6 +169,23 @@ static void tcp_connect(int listener, uint32_t flowlabel,
*accepted = afd;
}
+static bool disable_flowlabel_consistency(void)
+{
+ int fd;
+
+ fd = open("/proc/sys/net/ipv6/flowlabel_consistency", O_WRONLY);
+ if (fd == -1)
+ return false;
+
+ if (write(fd, "0", 1) != 1) {
+ close(fd);
+ return false;
+ }
+ close(fd);
+
+ return true;
+}
+
static void run_tests(int fd)
{
int wstatus;
@@ -307,6 +325,54 @@ static void run_tests(int fd)
close(remote_cfd);
close(remote_listener);
}
+
+ if (!disable_flowlabel_consistency()) {
+ fprintf(stderr,
+ "[INFO] skip REFLECT: cannot disable net.ipv6.flowlabel_consistency\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 listener before 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("accepted socket's 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 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 setup(void)
--
2.55.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH net-next v3 5/5] selftests: net: adopt harness for flow label mgr
2026-08-07 22:09 [PATCH net-next v3 0/5] net: selftests: adjustments to ipv6_flowlabel_mgr Marcelo Mendes Spessoto Junior
` (3 preceding siblings ...)
2026-08-07 22:09 ` [PATCH net-next v3 4/5] selftests: net: test IPV6_FL_F_REFLECT Marcelo Mendes Spessoto Junior
@ 2026-08-07 22:09 ` Marcelo Mendes Spessoto Junior
4 siblings, 0 replies; 6+ messages in thread
From: Marcelo Mendes Spessoto Junior @ 2026-08-07 22:09 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Shuah Khan
Cc: netdev, linux-kselftest, linux-kernel,
Marcelo Mendes Spessoto Junior
The kselftest_harness.h file contains modern helpers to build tests
for kselftest. Dropping the custom test helpers in ipv6_flowlabel_mgr
in favor of the harness makes tests more legible and conforms to the
structure of the latest selftests. It also enforces the TAP standard.
Another change made to the structure of the 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 "./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 calls and code comments now cover the information that the
removed, custom -v flag used to print.
Finally, FIXTURE_SETUP(flowlabel) ensures each test gets its own
isolated network namespace. The previously added setup() helper was
dropped to conform to the netns setup pattern used in icmp_rfc4884.c.
disable_flowlabel_consistency() was moved next to reflect_flag, the
only test that calls it, and now uses SKIP() instead of an ad hoc
[INFO] message when the sysctl cannot be disabled.
Signed-off-by: Marcelo Mendes Spessoto Junior <marcelomspessoto@gmail.com>
---
.../selftests/net/ipv6_flowlabel_mgr.c | 622 ++++++++++--------
1 file changed, 365 insertions(+), 257 deletions(-)
diff --git a/tools/testing/selftests/net/ipv6_flowlabel_mgr.c b/tools/testing/selftests/net/ipv6_flowlabel_mgr.c
index d32150abd8ff..072fb3a9b121 100644
--- a/tools/testing/selftests/net/ipv6_flowlabel_mgr.c
+++ b/tools/testing/selftests/net/ipv6_flowlabel_mgr.c
@@ -23,6 +23,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
@@ -35,40 +36,6 @@
/* from net/ipv6/ip6_flowlabel.c */
#define FL_MIN_LINGER 6
-#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 if (!__ret) \
- error(1, 0, "[ERR] " #x \
- " (line %d): unexpectedly succeeded", \
- __LINE__); \
- 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 = {
@@ -169,269 +136,410 @@ static void tcp_connect(int listener, uint32_t flowlabel,
*accepted = afd;
}
-static bool disable_flowlabel_consistency(void)
+static int bringup_loopback(void)
{
+ struct ifreq ifr = {
+ .ifr_name = "lo"
+ };
int fd;
- fd = open("/proc/sys/net/ipv6/flowlabel_consistency", O_WRONLY);
- if (fd == -1)
- return false;
+ fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+ if (fd < 0)
+ return -1;
+
+ if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0)
+ goto err;
+
+ ifr.ifr_flags = ifr.ifr_flags | IFF_UP;
+
+ if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0)
+ goto err;
- if (write(fd, "0", 1) != 1) {
- close(fd);
- return false;
- }
close(fd);
+ return 0;
- return true;
+err:
+ close(fd);
+ return -1;
}
-static void run_tests(int fd)
+FIXTURE(flowlabel) {};
+
+FIXTURE_SETUP(flowlabel)
{
- int wstatus;
- pid_t pid;
+ int ret;
- 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));
+ ret = unshare(CLONE_NEWNET);
+ ASSERT_EQ(ret, 0) {
+ TH_LOG("unshare(CLONE_NEWNET) failed: %s", strerror(errno));
}
- 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)");
+ ret = bringup_loopback();
+ ASSERT_EQ(ret, 0) TH_LOG("Failed to bring up loopback interface");
+}
+
+FIXTURE_TEARDOWN(flowlabel)
+{
+}
+
+TEST_F(flowlabel, cannot_get_non_existent_label)
+{
+ int fd, err;
+
+ fd = socket(PF_INET6, SOCK_DGRAM, 0);
+ ASSERT_GE(fd, 0) TH_LOG("socket failed");
+
+ err = flowlabel_get(fd, 9, IPV6_FL_S_ANY, 0);
+ EXPECT_TRUE(err) TH_LOG("expected get of a non-existent label to fail");
+ EXPECT_EQ(ENOENT, errno) TH_LOG("expected ENOENT, got %d", errno);
+
+ EXPECT_EQ(0, close(fd));
+}
+
+TEST_F(flowlabel, 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);
+ EXPECT_TRUE(err) TH_LOG("expected put of a non-existent label to fail");
+ EXPECT_EQ(ESRCH, errno) TH_LOG("expected ESRCH, got %d", errno);
+
+ EXPECT_EQ(0, close(fd));
+}
+
+TEST_F(flowlabel, 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);
+ EXPECT_TRUE(err) TH_LOG("expected label > 20 bits to be rejected");
+ EXPECT_EQ(EINVAL, errno) TH_LOG("expected EINVAL, got %d", errno);
+
+ EXPECT_EQ(0, close(fd));
+}
+
+TEST_F(flowlabel, 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);
+ EXPECT_TRUE(!err) TH_LOG("failed to create label (FL_F_CREATE)");
+
+ err = flowlabel_get(fd, 1, IPV6_FL_S_ANY, 0);
+ EXPECT_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);
+ EXPECT_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);
+ EXPECT_TRUE(err)
+ TH_LOG("expected FL_F_EXCL to reject existing label");
+ EXPECT_EQ(EEXIST, errno) TH_LOG("expected EEXIST, got %d", errno);
+
+ err = flowlabel_put(fd, 1);
+ EXPECT_TRUE(!err) TH_LOG("failed to put first reference");
+ err = flowlabel_put(fd, 1);
+ EXPECT_TRUE(!err) TH_LOG("failed to put second reference");
+ err = flowlabel_put(fd, 1);
+ EXPECT_TRUE(!err) TH_LOG("failed to put third reference");
+ err = flowlabel_put(fd, 1);
+ EXPECT_TRUE(err)
+ TH_LOG("expected fourth put to fail, no references left");
+ EXPECT_EQ(ESRCH, errno) TH_LOG("expected ESRCH, got %d", errno);
+
+ EXPECT_EQ(0, close(fd));
+}
+
+TEST_F(flowlabel, 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);
+ EXPECT_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);
+ EXPECT_TRUE(err) TH_LOG("expected reuse in non-exclusive mode to fail");
+ EXPECT_EQ(EPERM, errno) TH_LOG("expected EPERM, got %d", errno);
+
+ err = flowlabel_get(fd, 2, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE);
+ EXPECT_TRUE(err) TH_LOG("expected reuse in exclusive mode to fail too");
+ EXPECT_EQ(EPERM, errno) TH_LOG("expected EPERM, got %d", errno);
+
+ err = flowlabel_put(fd, 2);
+ EXPECT_TRUE(!err) TH_LOG("failed to put the exclusive label");
+
+ err = flowlabel_get(fd, 2, IPV6_FL_S_ANY, IPV6_FL_F_CREATE);
+ EXPECT_TRUE(err) TH_LOG("expected reuse to fail, due to linger");
+ EXPECT_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);
+ EXPECT_TRUE(!err) TH_LOG("expected reuse to succeed after linger");
+
+ EXPECT_EQ(0, close(fd));
+}
+
+TEST_F(flowlabel, user_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, 3, IPV6_FL_S_USER, IPV6_FL_F_CREATE);
+ EXPECT_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);
+ EXPECT_TRUE(err) TH_LOG("expected get in non-exclusive mode to fail");
+ EXPECT_EQ(EPERM, errno) TH_LOG("expected EPERM, got %d", errno);
+
+ err = flowlabel_get(fd, 3, IPV6_FL_S_EXCL, 0);
+ EXPECT_TRUE(err) TH_LOG("expected get in exclusive mode to fail");
+ EXPECT_EQ(EPERM, errno) TH_LOG("expected EPERM, got %d", errno);
+
+ err = flowlabel_get(fd, 3, IPV6_FL_S_USER, 0);
+ EXPECT_TRUE(!err) TH_LOG("failed to get it again in user mode");
+
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));
+ err = flowlabel_get(fd, 3, IPV6_FL_S_USER, 0);
+ EXPECT_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(KSFT_SKIP);
+
+ err = flowlabel_get(fd, 3, IPV6_FL_S_USER, 0);
+ EXPECT_TRUE(err)
+ TH_LOG("child unexpectedly got label after setuid");
+ EXPECT_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");
+ if (WEXITSTATUS(wstatus) == KSFT_SKIP)
+ SKIP(return,
+ "setuid(USHRT_MAX) unavailable (no CAP_SETUID or uid unmapped)");
+ EXPECT_EQ(0, WEXITSTATUS(wstatus))
+ TH_LOG("child reported unexpected result");
+
+ EXPECT_EQ(0, close(fd));
+}
+
+TEST_F(flowlabel, 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);
+ EXPECT_TRUE(!err)
+ TH_LOG("failed to create a new process-private label");
+
+ err = flowlabel_get(fd, 4, IPV6_FL_S_PROCESS, 0);
+ EXPECT_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);
+ EXPECT_TRUE(err)
+ TH_LOG("child unexpectedly got process-private label");
+ EXPECT_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, IPV6_FL_S_EXCL,
- 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("renew does not error for an existing, valid label");
- expect_pass(flowlabel_renew(fd, 5, IPV6_FL_S_EXCL,
- 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 extend linger, then put it");
- expect_pass(flowlabel_renew(fd, 6, IPV6_FL_S_EXCL,
- 2 * (FL_MIN_LINGER * 2 + 1)));
- expect_pass(flowlabel_put(fd, 6));
- sleep(FL_MIN_LINGER * 2 + 1);
- explain("cannot create: new linger time 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");
+ EXPECT_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,
- };
- int remote_listener = tcp_listen();
- socklen_t freq_len = sizeof(freq);
- 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);
- }
+ EXPECT_EQ(0, close(fd));
+}
- if (!disable_flowlabel_consistency()) {
- fprintf(stderr,
- "[INFO] skip REFLECT: cannot disable net.ipv6.flowlabel_consistency\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 listener before 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("accepted socket's 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 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);
- }
+TEST_F(flowlabel, cannot_renew_non_existent_label)
+{
+ int fd, err;
+
+ fd = socket(PF_INET6, SOCK_DGRAM, 0);
+ ASSERT_GE(fd, 0) TH_LOG("socket failed");
+
+ err = flowlabel_renew(fd, 5, IPV6_FL_S_EXCL,
+ 2 * (FL_MIN_LINGER * 2 + 1));
+ EXPECT_TRUE(err)
+ TH_LOG("expected renew of a non-existent label to fail");
+ EXPECT_EQ(ESRCH, errno) TH_LOG("expected ESRCH, got %d", errno);
+
+ EXPECT_EQ(0, close(fd));
}
-static void setup(void)
+TEST_F(flowlabel, can_renew_existing_label)
{
- struct ifreq ifr = {
- .ifr_name = "lo"
- };
- int ctl;
+ int fd, err;
+
+ fd = socket(PF_INET6, SOCK_DGRAM, 0);
+ ASSERT_GE(fd, 0) TH_LOG("socket failed");
- if (unshare(CLONE_NEWNET))
- error(1, errno, "unshare");
+ err = flowlabel_get(fd, 5, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE);
+ EXPECT_TRUE(!err)
+ TH_LOG("failed to create a new label for renew validation");
- ctl = socket(AF_LOCAL, SOCK_STREAM, 0);
- if (ctl == -1)
- error(1, errno, "socket");
+ err = flowlabel_renew(fd, 5, IPV6_FL_S_EXCL,
+ 2 * (FL_MIN_LINGER * 2 + 1));
+ EXPECT_TRUE(!err) TH_LOG("failed to renew an existing valid label");
- if (ioctl(ctl, SIOCGIFFLAGS, &ifr))
- error(1, errno, "ioctl SIOCGIFFLAGS");
- ifr.ifr_flags |= IFF_UP;
- if (ioctl(ctl, SIOCSIFFLAGS, &ifr))
- error(1, errno, "ioctl: bring lo up");
+ err = flowlabel_put(fd, 5);
+ EXPECT_TRUE(!err) TH_LOG("failed to put the label");
- if (close(ctl))
- error(1, errno, "close");
+ EXPECT_EQ(0, close(fd));
}
-static void parse_opts(int argc, char **argv)
+TEST_F(flowlabel, renew_label_linger)
{
- 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]);
- }
- }
+ /* 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);
+ EXPECT_TRUE(!err)
+ TH_LOG("failed to create label with FL_MIN_LINGER linger time");
+
+ err = flowlabel_renew(fd, 6, IPV6_FL_S_EXCL,
+ 2 * (FL_MIN_LINGER * 2 + 1));
+ EXPECT_TRUE(!err)
+ TH_LOG("failed to renew the label to increase its linger time");
+
+ err = flowlabel_put(fd, 6);
+ EXPECT_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);
+ EXPECT_TRUE(err)
+ TH_LOG("expected reuse to fail, new linger time not over yet");
+ EXPECT_EQ(EPERM, errno) TH_LOG("expected EPERM, got %d", errno);
+
+ EXPECT_EQ(0, close(fd));
}
-int main(int argc, char **argv)
+TEST_F(flowlabel, remote_flag)
{
- int fd;
+ /* 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;
- parse_opts(argc, argv);
- setup();
+ listener = tcp_listen();
+ tcp_connect(listener, 7, &cfd, &afd);
- fd = socket(PF_INET6, SOCK_DGRAM, 0);
+ err = getsockopt(afd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &freq, &freq_len);
+ EXPECT_TRUE(!err) TH_LOG("getsockopt with IPV6_FL_F_REMOTE failed");
+ EXPECT_EQ(7, ntohl(freq.flr_label))
+ TH_LOG("unexpected remote flow label");
+
+ EXPECT_EQ(0, close(afd));
+ EXPECT_EQ(0, close(cfd));
+ EXPECT_EQ(0, close(listener));
+}
+
+static bool disable_flowlabel_consistency(void)
+{
+ int fd;
+
+ fd = open("/proc/sys/net/ipv6/flowlabel_consistency", O_WRONLY);
if (fd == -1)
- error(1, errno, "socket");
+ return false;
- run_tests(fd);
+ if (write(fd, "0", 1) != 1) {
+ close(fd);
+ return false;
+ }
+ close(fd);
- if (close(fd))
- error(1, errno, "close");
+ return true;
+}
- return 0;
+TEST_F(flowlabel, 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 (!disable_flowlabel_consistency())
+ SKIP(return,
+ "cannot disable net.ipv6.flowlabel_consistency");
+
+ listener = tcp_listen();
+ err = setsockopt(listener, SOL_IPV6, IPV6_FLOWLABEL_MGR,
+ &reflect_on, sizeof(reflect_on));
+ EXPECT_TRUE(!err) TH_LOG("failed to enable REFLECT on the listener");
+
+ tcp_connect(listener, 8, &cfd, &afd);
+
+ err = getsockopt(afd, SOL_IPV6, IPV6_FLOWLABEL_MGR,
+ &reflect_query, &reflect_query_len);
+ EXPECT_TRUE(!err)
+ TH_LOG("failed to query the accepted socket's outgoing label");
+ EXPECT_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));
+ EXPECT_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));
+ EXPECT_TRUE(err) TH_LOG("expected disabling REFLECT twice to fail");
+ EXPECT_EQ(ESRCH, errno) TH_LOG("expected ESRCH, got %d", errno);
+
+ EXPECT_EQ(0, close(afd));
+ EXPECT_EQ(0, close(cfd));
+ EXPECT_EQ(0, close(listener));
}
+
+TEST_HARNESS_MAIN
--
2.55.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-08-07 22:10 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-07 22:09 [PATCH net-next v3 0/5] net: selftests: adjustments to ipv6_flowlabel_mgr Marcelo Mendes Spessoto Junior
2026-08-07 22:09 ` [PATCH net-next v3 1/5] selftests: net: test IPV6_FL_A_RENEW Marcelo Mendes Spessoto Junior
2026-08-07 22:09 ` [PATCH net-next v3 2/5] selftests: net: test IPV6_FL_F_REMOTE Marcelo Mendes Spessoto Junior
2026-08-07 22:09 ` [PATCH net-next v3 3/5] selftests: net: create own netns in ipv6_flowlabel_mgr Marcelo Mendes Spessoto Junior
2026-08-07 22:09 ` [PATCH net-next v3 4/5] selftests: net: test IPV6_FL_F_REFLECT Marcelo Mendes Spessoto Junior
2026-08-07 22:09 ` [PATCH net-next v3 5/5] selftests: net: adopt harness for flow label mgr Marcelo Mendes Spessoto Junior
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox