On 2/6/26 02:24, Emil Tsalapatis wrote:
> On Mon Jun 1, 2026 at 11:02 AM EDT, Leon Hwang wrote:
[...]
>> @@ -35,6 +38,10 @@
>> #define IP6_ADDR_SRC IP6_ADDR_1
>> #define IP6_ADDR_DST IP6_ADDR_4
>>
>> +/* VxLAN tunnel endpoints, reachable via the bottom route (veth5/6/7/8). */
>> +#define IP4_ADDR_VXLAN "172.16.17.100"
>> +#define IP6_ADDR_VXLAN "fb20::1"
>
> There's a whole series of existing IP definitions at the top of the file, we
> should put those new ones right below them. And fb20 -> fb11 to keep
> with the pattern imo.
>
Ack.
>> +
>> /* Setup/topology:
>> *
>> * NS1 NS2 NS3
>> @@ -538,3 +545,160 @@ void test_lwt_ip_encap_ipv4(void)
>> if (test__start_subtest("ingress"))
>> lwt_ip_encap(IPV4_ENCAP, INGRESS, "");
>> }
>> +
>> +/*
>> + * VxLAN Setup/topology:
>> + *
>> + * NS1 (IP*_ADDR_1) NS2 NS3 (IP*_ADDR_4)
>> + * [ping src]
>> + * | top route
>> + * veth1 (LWT encap) <<-- veth2 veth3 <<-- veth4 (ping dst)
>> + * | ^
>> + * (bottom route) | (inner pkt)
>> + * v bottom route |
>> + * veth5 -->> veth6 veth7 -->> veth8 (vxlan
>> decap)
>> + * (IP*_ADDR_VXLAN)
>> + *
> Not sure if this is rendering weird for me but NS2 could be tabbed over
> once more for clarity.
>
NS2 here is to make sure the LWT-encap VxLAN packets can be routed
correctly like other lwt tests.
>> + * Add the VxLAN endpoint addresses to NS3's veth8, create standard
>> + * VxLAN decap devices bound to those addresses, and install routes so
>> + * NS1/NS2 can reach the endpoints via the bottom route.
>> + */
[...]
>> +fail_close:
>> + close_netns(nstoken);
>> +fail:
>> + return -1;
>> +}
>> +
>> +/*
>> + * VxLAN encap tests (IPv4-outer and IPv6-outer variants).
>> + *
>> + * Test 1 - functional: the BPF LWT xmit program encapsulates the packet
>> + * (protocol=UDP, port=4789) and re-routes it without dropping it.
>> + * Verified by ping success.
>> + *
>> + * Test 2 - fix verification: after bpf_lwt_push_ip_encap() the
>> + * skb->transport_header must point at the outer UDP header, i.e.
>> + * transport_header - network_header == sizeof(outer IP header).
>> + * Without the fix the transport_header still points at the inner
>> + * transport layer, giving a wrong (larger) offset.
>> + */
>
> The Test 2 bullet regurgitates the AI's context, can you rephrase it so
> that it's not framed as a fix but as a test?
>
Hmm, better to drop this comment, I think.
>> +static void lwt_ip_encap_vxlan(bool ipv4_encap)
>> +{
>> + char ns1[NETNS_NAME_SIZE] = NETNS_BASE "-1-";
>> + char ns2[NETNS_NAME_SIZE] = NETNS_BASE "-2-";
>> + char ns3[NETNS_NAME_SIZE] = NETNS_BASE "-3-";
>> + const char *sec = ipv4_encap ? "encap_vxlan" : "encap_vxlan6";
[...]
>> +
>> +SEC("encap_vxlan")
>> +int bpf_lwt_encap_vxlan(struct __sk_buff *skb)
>> +{
>> + struct encap_hdr {
>> + struct iphdr iph;
>> + struct udphdr udph;
>> + struct vxlanhdr vxh;
>> + struct ethhdr eth;
>> + } __attribute__((__packed__)) /* packed is required to avoid padding */
>> hdr;
>
> Comment is unnecessary here.
>
Ack.
>> + int err;
>> +
>> + memset(&hdr, 0, sizeof(hdr));
>> +
>> + hdr.iph.ihl = 5;
>> + hdr.iph.version = 4;
>> + hdr.iph.ttl = 0x40;
>> + hdr.iph.protocol = 17; /* IPPROTO_UDP */
>> + hdr.iph.tot_len = bpf_htons(skb->len + sizeof(hdr));
>> +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
>> + hdr.iph.saddr = 0x640510ac; /* 172.16.5.100 */
>> + hdr.iph.daddr = 0x641110ac; /* 172.16.17.100 */
>
> Ideally want to keep the addresses we are hardcoding here and the
> the addresses we're declaring in the userspace part in sync. Here
> we've hardcoded the addresses three multiple ways (be, le, and string
> in the userspace part).
>
I think it's OK to hardcode them here like the gre tests.
>> +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
>> + hdr.iph.saddr = 0xac100564; /* 172.16.5.100 */
>> + hdr.iph.daddr = 0xac101164; /* 172.16.17.100 */
>> +#else
>> +#error "Fix your compiler's __BYTE_ORDER__?!"
>> +#endif
>> +
>> + hdr.udph.source = bpf_htons(VXLAN_PORT);
>> + hdr.udph.dest = bpf_htons(VXLAN_PORT);
>> + hdr.udph.len = bpf_htons(skb->len + sizeof(hdr.udph) +
>> sizeof(hdr.vxh) +
>> + sizeof(hdr.eth));
>> +
>> + hdr.vxh.vx_flags = bpf_htonl(VXLAN_FLAGS);
>> + hdr.vxh.vx_vni = bpf_htonl(VXLAN_VNI << 8);
>> +
>> + __builtin_memcpy(hdr.eth.h_dest, bcast, ETH_ALEN);
>> + __builtin_memcpy(hdr.eth.h_source, srcmac, ETH_ALEN);
>> + hdr.eth.h_proto = bpf_htons(ETH_P_IP);
>> +
>> + err = bpf_lwt_push_encap(skb, BPF_LWT_ENCAP_IP, &hdr, sizeof(hdr));
>> + if (err)
>> + return BPF_DROP;
>> +
>> + return BPF_LWT_REROUTE;
>> +}
>> +
>> +SEC("encap_vxlan6")
>> +int bpf_lwt_encap_vxlan6(struct __sk_buff *skb)
>> +{
>> + struct encap_hdr {
>> + struct ipv6hdr ip6hdr;
>> + struct udphdr udph;
>> + struct vxlanhdr vxh;
>> + struct ethhdr eth;
>> + } __attribute__((__packed__)) /* packed is required to avoid padding */
>> hdr;
Will drop the comment.
>> + int err;
>> +
[...]
>> + return BPF_LWT_REROUTE;
>> +}
>> +
>> char _license[] SEC("license") = "GPL";
>> diff --git a/tools/testing/selftests/bpf/progs/test_lwt_ip_encap_fix.c
>> b/tools/testing/selftests/bpf/progs/test_lwt_ip_encap_fix.c
>
> We definitely need to change the name here this is straight from the AI.
>
Will fold it into test_lwt_ip_encap.c to avoid creating a new file.
>> new file mode 100644
>> index 000000000000..6945f83b94f2
>> --- /dev/null
>> +++ b/tools/testing/selftests/bpf/progs/test_lwt_ip_encap_fix.c
>> @@ -0,0 +1,44 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +#include "vmlinux.h"
>> +#include <bpf/bpf_helpers.h>
>> +#include <bpf/bpf_tracing.h>
>> +#include <bpf/bpf_core_read.h>
>> +
>> +#define NEXTHDR_UDP 17 /* UDP message. */
>
> Unnecessary, we hardcode 17 in the other test.
>
Ack.
Thanks,
Leon
[...]