BPF List
 help / color / mirror / Atom feed
* [PATCH bpf-next] selftests/bpf: Wait for links to come up in lwt_ip_encap
@ 2026-09-16 23:24 Alexei Starovoitov
  2026-09-16 23:51 ` Emil Tsalapatis
  2026-09-17  3:50 ` patchwork-bot+netdevbpf
  0 siblings, 2 replies; 3+ messages in thread
From: Alexei Starovoitov @ 2026-09-16 23:24 UTC (permalink / raw)
  To: bpf; +Cc: daniel, andrii, eddyz87, bastien.curutchet, Alexei Starovoitov

From: Alexei Starovoitov <ast@kernel.org>

lwt_ip_encap creates three fresh netns with veth pairs across them and
then immediately pings over IPv6. The peer ends of the first veth pair
end up with the same ifindex in their respective netns, so linkwatch
does not treat the carrier-on as urgent and may deliver NETDEV_CHANGE up
to one second later. Until that happens addrconf considers the link not
ready: no ff00::/8 multicast route and no link-local address are
installed, and the neighbour solicitation for the next hop is dropped
as a no-route packet. The NS retransmit a second later succeeds, but
ping -W1 has already given up:

  check_ping_ok:FAIL:ip netns exec ns-lwt-ip-encap-1-... ping -6 -c 1 -W1 -I veth1 fb04::1 > /dev/null unexpected error: 256 (errno 2)
  lwt_ip_encap:FAIL:ping OK unexpected error: -1 (errno 2)
  #226/1   lwt_ip_encap_ipv4/egress:FAIL

This reproduces reliably when the test is run standalone on an idle
system where setup completes well within a second. The original shell
script had a "sleep 1 # reduce flakiness" after setup which was lost in
the conversion to test_progs.

Instead of sleeping, poll SIOCGIFFLAGS for IFF_RUNNING on all veths at
the end of setup_network() so traffic is only sent once every link is
operationally up.

Fixes: f5e288943e2c ("selftests/bpf: Move test_lwt_ip_encap to test_progs")
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
---
 .../selftests/bpf/prog_tests/lwt_ip_encap.c   | 53 +++++++++++++++++++
 1 file changed, 53 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/lwt_ip_encap.c b/tools/testing/selftests/bpf/prog_tests/lwt_ip_encap.c
index 39e8a3b8b6af..5c5560d45c5b 100644
--- a/tools/testing/selftests/bpf/prog_tests/lwt_ip_encap.c
+++ b/tools/testing/selftests/bpf/prog_tests/lwt_ip_encap.c
@@ -1,5 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-only
+#include <net/if.h>
 #include <netinet/in.h>
+#include <sys/ioctl.h>
 
 #include "network_helpers.h"
 #include "test_progs.h"
@@ -141,6 +143,54 @@ static int set_bottom_addr(const char *ns1, const char *ns2, const char *ns3)
 	return 1;
 }
 
+/*
+ * A veth whose peer sits in another netns with the same ifindex gets its
+ * carrier-on handled as a non-urgent linkwatch event, i.e. up to 1s late.
+ * Until then IPv6 considers the link not ready (no ff00::/8 route, no
+ * link-local address) and silently drops neighbour solicitations, so wait
+ * for all links to be operationally up (IFF_RUNNING) before sending traffic.
+ */
+static int wait_for_oper_up(const char *ns, const char *dev)
+{
+	struct nstoken *nstoken;
+	struct ifreq ifr = {};
+	int i, fd, ret = -1;
+
+	nstoken = open_netns(ns);
+	if (!ASSERT_OK_PTR(nstoken, "open ns"))
+		return -1;
+
+	fd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (!ASSERT_OK_FD(fd, "socket"))
+		goto out;
+
+	strncpy(ifr.ifr_name, dev, IFNAMSIZ - 1);
+	for (i = 0; i < 50; i++) {
+		if (!ASSERT_OK(ioctl(fd, SIOCGIFFLAGS, &ifr), "SIOCGIFFLAGS"))
+			break;
+		if (ifr.ifr_flags & IFF_RUNNING) {
+			ret = 0;
+			break;
+		}
+		usleep(100000);
+	}
+	close(fd);
+out:
+	ASSERT_OK(ret, dev);
+	close_netns(nstoken);
+	return ret;
+}
+
+static int wait_for_links(const char *ns1, const char *ns2, const char *ns3)
+{
+	if (wait_for_oper_up(ns1, "veth1") || wait_for_oper_up(ns1, "veth5") ||
+	    wait_for_oper_up(ns2, "veth2") || wait_for_oper_up(ns2, "veth3") ||
+	    wait_for_oper_up(ns2, "veth6") || wait_for_oper_up(ns2, "veth7") ||
+	    wait_for_oper_up(ns3, "veth4") || wait_for_oper_up(ns3, "veth8"))
+		return -1;
+	return 0;
+}
+
 static int configure_vrf(const char *ns1, const char *ns2)
 {
 	if (!ns1 || !ns2)
@@ -304,6 +354,9 @@ static int setup_network(char *ns1, char *ns2, char *ns3, const char *vrf)
 	if (!ASSERT_OK(configure_ns3(ns3), "configure ns3 routes"))
 		goto fail;
 
+	if (!ASSERT_OK(wait_for_links(ns1, ns2, ns3), "wait for links"))
+		goto fail;
+
 	/* Link bottom route to the GRE tunnels */
 	SYS(fail, "ip -n %s route add %s/32 dev veth5 via %s %s",
 	    ns1, IP4_ADDR_GRE, IP4_ADDR_6, vrf);
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH bpf-next] selftests/bpf: Wait for links to come up in lwt_ip_encap
  2026-09-16 23:24 [PATCH bpf-next] selftests/bpf: Wait for links to come up in lwt_ip_encap Alexei Starovoitov
@ 2026-09-16 23:51 ` Emil Tsalapatis
  2026-09-17  3:50 ` patchwork-bot+netdevbpf
  1 sibling, 0 replies; 3+ messages in thread
From: Emil Tsalapatis @ 2026-09-16 23:51 UTC (permalink / raw)
  To: Alexei Starovoitov, bpf
  Cc: daniel, andrii, eddyz87, bastien.curutchet, Alexei Starovoitov

On Wed Sep 16, 2026 at 11:24 PM UTC, Alexei Starovoitov wrote:
> From: Alexei Starovoitov <ast@kernel.org>
>
> lwt_ip_encap creates three fresh netns with veth pairs across them and
> then immediately pings over IPv6. The peer ends of the first veth pair
> end up with the same ifindex in their respective netns, so linkwatch
> does not treat the carrier-on as urgent and may deliver NETDEV_CHANGE up
> to one second later. Until that happens addrconf considers the link not
> ready: no ff00::/8 multicast route and no link-local address are
> installed, and the neighbour solicitation for the next hop is dropped
> as a no-route packet. The NS retransmit a second later succeeds, but
> ping -W1 has already given up:
>
>   check_ping_ok:FAIL:ip netns exec ns-lwt-ip-encap-1-... ping -6 -c 1 -W1 -I veth1 fb04::1 > /dev/null unexpected error: 256 (errno 2)
>   lwt_ip_encap:FAIL:ping OK unexpected error: -1 (errno 2)
>   #226/1   lwt_ip_encap_ipv4/egress:FAIL
>
> This reproduces reliably when the test is run standalone on an idle
> system where setup completes well within a second. The original shell
> script had a "sleep 1 # reduce flakiness" after setup which was lost in
> the conversion to test_progs.
>
> Instead of sleeping, poll SIOCGIFFLAGS for IFF_RUNNING on all veths at
> the end of setup_network() so traffic is only sent once every link is
> operationally up.
>
> Fixes: f5e288943e2c ("selftests/bpf: Move test_lwt_ip_encap to test_progs")
> Signed-off-by: Alexei Starovoitov <ast@kernel.org>

Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>

> ---
>  .../selftests/bpf/prog_tests/lwt_ip_encap.c   | 53 +++++++++++++++++++
>  1 file changed, 53 insertions(+)
>
> diff --git a/tools/testing/selftests/bpf/prog_tests/lwt_ip_encap.c b/tools/testing/selftests/bpf/prog_tests/lwt_ip_encap.c
> index 39e8a3b8b6af..5c5560d45c5b 100644
> --- a/tools/testing/selftests/bpf/prog_tests/lwt_ip_encap.c
> +++ b/tools/testing/selftests/bpf/prog_tests/lwt_ip_encap.c
> @@ -1,5 +1,7 @@
>  // SPDX-License-Identifier: GPL-2.0-only
> +#include <net/if.h>
>  #include <netinet/in.h>
> +#include <sys/ioctl.h>
>  
>  #include "network_helpers.h"
>  #include "test_progs.h"
> @@ -141,6 +143,54 @@ static int set_bottom_addr(const char *ns1, const char *ns2, const char *ns3)
>  	return 1;
>  }
>  
> +/*
> + * A veth whose peer sits in another netns with the same ifindex gets its
> + * carrier-on handled as a non-urgent linkwatch event, i.e. up to 1s late.
> + * Until then IPv6 considers the link not ready (no ff00::/8 route, no
> + * link-local address) and silently drops neighbour solicitations, so wait
> + * for all links to be operationally up (IFF_RUNNING) before sending traffic.
> + */
> +static int wait_for_oper_up(const char *ns, const char *dev)
> +{
> +	struct nstoken *nstoken;
> +	struct ifreq ifr = {};
> +	int i, fd, ret = -1;
> +
> +	nstoken = open_netns(ns);
> +	if (!ASSERT_OK_PTR(nstoken, "open ns"))
> +		return -1;
> +
> +	fd = socket(AF_INET, SOCK_DGRAM, 0);
> +	if (!ASSERT_OK_FD(fd, "socket"))
> +		goto out;
> +
> +	strncpy(ifr.ifr_name, dev, IFNAMSIZ - 1);
> +	for (i = 0; i < 50; i++) {
> +		if (!ASSERT_OK(ioctl(fd, SIOCGIFFLAGS, &ifr), "SIOCGIFFLAGS"))
> +			break;
> +		if (ifr.ifr_flags & IFF_RUNNING) {
> +			ret = 0;
> +			break;
> +		}
> +		usleep(100000);
> +	}
> +	close(fd);
> +out:
> +	ASSERT_OK(ret, dev);
> +	close_netns(nstoken);
> +	return ret;
> +}
> +
> +static int wait_for_links(const char *ns1, const char *ns2, const char *ns3)
> +{
> +	if (wait_for_oper_up(ns1, "veth1") || wait_for_oper_up(ns1, "veth5") ||
> +	    wait_for_oper_up(ns2, "veth2") || wait_for_oper_up(ns2, "veth3") ||
> +	    wait_for_oper_up(ns2, "veth6") || wait_for_oper_up(ns2, "veth7") ||
> +	    wait_for_oper_up(ns3, "veth4") || wait_for_oper_up(ns3, "veth8"))
> +		return -1;
> +	return 0;
> +}
> +
>  static int configure_vrf(const char *ns1, const char *ns2)
>  {
>  	if (!ns1 || !ns2)
> @@ -304,6 +354,9 @@ static int setup_network(char *ns1, char *ns2, char *ns3, const char *vrf)
>  	if (!ASSERT_OK(configure_ns3(ns3), "configure ns3 routes"))
>  		goto fail;
>  
> +	if (!ASSERT_OK(wait_for_links(ns1, ns2, ns3), "wait for links"))
> +		goto fail;
> +
>  	/* Link bottom route to the GRE tunnels */
>  	SYS(fail, "ip -n %s route add %s/32 dev veth5 via %s %s",
>  	    ns1, IP4_ADDR_GRE, IP4_ADDR_6, vrf);


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH bpf-next] selftests/bpf: Wait for links to come up in lwt_ip_encap
  2026-09-16 23:24 [PATCH bpf-next] selftests/bpf: Wait for links to come up in lwt_ip_encap Alexei Starovoitov
  2026-09-16 23:51 ` Emil Tsalapatis
@ 2026-09-17  3:50 ` patchwork-bot+netdevbpf
  1 sibling, 0 replies; 3+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-09-17  3:50 UTC (permalink / raw)
  To: Alexei Starovoitov; +Cc: bpf, daniel, andrii, eddyz87, bastien.curutchet, ast

Hello:

This patch was applied to bpf/bpf-next.git (master)
by Alexei Starovoitov <ast@kernel.org>:

On Wed, 16 Sep 2026 23:24:07 +0000 you wrote:
> From: Alexei Starovoitov <ast@kernel.org>
> 
> lwt_ip_encap creates three fresh netns with veth pairs across them and
> then immediately pings over IPv6. The peer ends of the first veth pair
> end up with the same ifindex in their respective netns, so linkwatch
> does not treat the carrier-on as urgent and may deliver NETDEV_CHANGE up
> to one second later. Until that happens addrconf considers the link not
> ready: no ff00::/8 multicast route and no link-local address are
> installed, and the neighbour solicitation for the next hop is dropped
> as a no-route packet. The NS retransmit a second later succeeds, but
> ping -W1 has already given up:
> 
> [...]

Here is the summary with links:
  - [bpf-next] selftests/bpf: Wait for links to come up in lwt_ip_encap
    https://git.kernel.org/bpf/bpf-next/c/71b75648d1a2

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-09-17  3:51 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-16 23:24 [PATCH bpf-next] selftests/bpf: Wait for links to come up in lwt_ip_encap Alexei Starovoitov
2026-09-16 23:51 ` Emil Tsalapatis
2026-09-17  3:50 ` patchwork-bot+netdevbpf

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox