From nobody Mon Jun 8 10:56:11 2026 Received: from out-170.mta1.migadu.com (out-170.mta1.migadu.com [95.215.58.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CDD2433FE0D for ; Fri, 29 May 2026 15:14:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780067693; cv=none; b=iCVwk5QVOZSs9RDi1T3VXKIeZ0C3+JFLAmsZ+3HqXZF86vO14u2R+AkOcVTFNHt0+YQjcXa0rtL2bzCxBQYylnglh8v9oqunv0TqfNrQuiYr50oI4HO/cuOUbfgXyQ/FVzqztZJ22LdGBLEv2ZGajDBskXR+23rzNSQQWpgBpdE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780067693; c=relaxed/simple; bh=XSCO5CSVCnzEfuZMtOvLgrM/19YC/n7GmkNMoqE9KlA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=nGYJXMh5jYgyKouK0yyU6JrqZNxTrNKujmGP+GDDYAp62HL+3tHHW/qQkMH4yrQtmMDSbr90QKBhGUI4982ZlciM1uD0iS/R1Qv1tGdvBLjS1TWUQ5zReDqN/S/CvtjLo79QP4hEK9ylACNVdVdFXrV+b/67tnGmiQKrE3iaFSM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=VTHlJIA7; arc=none smtp.client-ip=95.215.58.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="VTHlJIA7" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1780067689; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=QOd4RhJzsfLP0kB2sEsBt46YxR8Ub6JYiYOL9mjFn8M=; b=VTHlJIA7y2WLB3S+aHCKwgmOPsRftcpaWhLdgola1SXKsAl8YWC5Pd7e6NtuZ6RjbN72l+ y0smBbjxQid+H9wDKtT+m+0bkdtOzPD2hlaqUc2ukzPI8645nXAHZiQEF8ia5Fqtz0Logm P1baahfKfWKu3ozjRLZosLg2Sfhy4aw= From: Leon Hwang To: bpf@vger.kernel.org Cc: "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Andrii Nakryiko , Eduard Zingerman , Alexei Starovoitov , Daniel Borkmann , Martin KaFai Lau , Kumar Kartikeya Dwivedi , Song Liu , Yonghong Song , Jiri Olsa , Shuah Khan , Guillaume Nault , Leon Hwang , Ido Schimmel , Fernando Fernandez Mancera , Peter Oskolkov , linux-kernel@vger.kernel.org, netdev@vger.kernel.org, linux-kselftest@vger.kernel.org, kernel-patches-bot@fb.com Subject: [PATCH bpf v2 1/4] bpf: Fix TOCTOU issue in lwt Date: Fri, 29 May 2026 23:13:48 +0800 Message-ID: <20260529151351.69911-2-leon.hwang@linux.dev> In-Reply-To: <20260529151351.69911-1-leon.hwang@linux.dev> References: <20260529151351.69911-1-leon.hwang@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Sashiko pointed out [1]: The hdr pointer passed to bpf_lwt_push_ip_encap() can point to concurrently mutable memory such as a BPF map value. So, the memory of hdr pointer can be updated after skb_postpush_rcsum(). To fix it, memcpy() the hdr to a local buffer, which will be used for the following checks and updates. [1] https://lore.kernel.org/bpf/20260525150010.CDEBA1F000E9@smtp.kernel.org/ Fixes: 52f278774e79 ("bpf: implement BPF_LWT_ENCAP_IP mode in bpf_lwt_push_= encap") Signed-off-by: Leon Hwang --- net/core/lwt_bpf.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/core/lwt_bpf.c b/net/core/lwt_bpf.c index f71ef82a5f3d..8009e427851f 100644 --- a/net/core/lwt_bpf.c +++ b/net/core/lwt_bpf.c @@ -599,6 +599,7 @@ static int handle_gso_encap(struct sk_buff *skb, bool i= pv4, int encap_len) =20 int bpf_lwt_push_ip_encap(struct sk_buff *skb, void *hdr, u32 len, bool in= gress) { + u8 buff[LWT_BPF_MAX_HEADROOM]; struct iphdr *iph; bool ipv4; int err; @@ -606,8 +607,10 @@ int bpf_lwt_push_ip_encap(struct sk_buff *skb, void *h= dr, u32 len, bool ingress) if (unlikely(len < sizeof(struct iphdr) || len > LWT_BPF_MAX_HEADROOM)) return -EINVAL; =20 + memcpy(buff, hdr, len); + /* validate protocol and length */ - iph =3D (struct iphdr *)hdr; + iph =3D (struct iphdr *)buff; if (iph->version =3D=3D 4) { ipv4 =3D true; if (unlikely(len < iph->ihl * 4)) @@ -637,7 +640,7 @@ int bpf_lwt_push_ip_encap(struct sk_buff *skb, void *hd= r, u32 len, bool ingress) if (ingress) skb_postpush_rcsum(skb, iph, len); skb_reset_network_header(skb); - memcpy(skb_network_header(skb), hdr, len); + memcpy(skb_network_header(skb), buff, len); bpf_compute_data_pointers(skb); skb_clear_hash(skb); =20 --=20 2.54.0 From nobody Mon Jun 8 10:56:11 2026 Received: from out-178.mta1.migadu.com (out-178.mta1.migadu.com [95.215.58.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6270C36654C; Fri, 29 May 2026 15:15:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780067702; cv=none; b=VMXCVDiMCD8f5lpf4I6hYbjPiDRUFW59U+Q0FiQCBVacUGpGr38pvhP8IOWYUKgedXQwL7BTbGIchB44DyPFU2RU0i5tQNQJo5fkx3XuOjkjSu5ltZlYKl8wPO5+0r/pcjscu7rvh7zj7VQ9BA2FuBpw/0CiYIlWrh/1pbBBhpU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780067702; c=relaxed/simple; bh=BD+fGjyph/ospc62O/7eSoVQx4RfgaqTgXjRSZqH8PU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=WoDIt7QREGW9aJdoOxTxaZUuVtkEl2Z5e5Nv4VDwbwcsIKg9SXncjAMFsTFdJlBiycjdSWEigMUpnt5rci7vgSTIgc53H396FD/Ip10T5hBLCqvPqf3RwXfdC5+bRJoW6ruln9GcIJkNIKZ0Ye/ShuFnM1rSqajjQ/q4g0WqdUk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=g8e/X6nd; arc=none smtp.client-ip=95.215.58.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="g8e/X6nd" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1780067699; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=JLT2gH39dUAUDzhjvHd6hBoqXWfY6t5vMPhXRheOmRk=; b=g8e/X6ndtNpsyjwOlQgixqBdWaSfqqVmOam9aZw8oRbAy8svbWxY05Rtam7AiK5irxSqM4 S6bLRHqkMv1EkH/Lrj2zeAcFUm2GGUT9MKuITvz23aXIjqgH3Zl1iEkX9Rm0qcOOqCE98z I3KpZZz0ZQ+8ctnKTJzZFFuH2tCwr8A= From: Leon Hwang To: bpf@vger.kernel.org Cc: "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Andrii Nakryiko , Eduard Zingerman , Alexei Starovoitov , Daniel Borkmann , Martin KaFai Lau , Kumar Kartikeya Dwivedi , Song Liu , Yonghong Song , Jiri Olsa , Shuah Khan , Guillaume Nault , Leon Hwang , Ido Schimmel , Fernando Fernandez Mancera , Peter Oskolkov , linux-kernel@vger.kernel.org, netdev@vger.kernel.org, linux-kselftest@vger.kernel.org, kernel-patches-bot@fb.com Subject: [PATCH bpf v2 2/4] bpf: Add check iph->ihl < 5 in lwt Date: Fri, 29 May 2026 23:13:49 +0800 Message-ID: <20260529151351.69911-3-leon.hwang@linux.dev> In-Reply-To: <20260529151351.69911-1-leon.hwang@linux.dev> References: <20260529151351.69911-1-leon.hwang@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Sashiko pointed out [1]: On architectures like MIPS, the while-loop won't stop in ip_fast_csum(). To avoid such issues caused by invalid iph->ihl in lwt, add check "iph->ihl < 5" in bpf_lwt_push_ip_encap() to make sure iph->ihl is valid. [1] https://lore.kernel.org/bpf/20260525150010.CDEBA1F000E9@smtp.kernel.org/ Fixes: 52f278774e79 ("bpf: implement BPF_LWT_ENCAP_IP mode in bpf_lwt_push_= encap") Signed-off-by: Leon Hwang --- net/core/lwt_bpf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/lwt_bpf.c b/net/core/lwt_bpf.c index 8009e427851f..c306120e11d2 100644 --- a/net/core/lwt_bpf.c +++ b/net/core/lwt_bpf.c @@ -613,7 +613,7 @@ int bpf_lwt_push_ip_encap(struct sk_buff *skb, void *hd= r, u32 len, bool ingress) iph =3D (struct iphdr *)buff; if (iph->version =3D=3D 4) { ipv4 =3D true; - if (unlikely(len < iph->ihl * 4)) + if (unlikely(iph->ihl < 5 || len < iph->ihl * 4)) return -EINVAL; } else if (iph->version =3D=3D 6) { ipv4 =3D false; --=20 2.54.0 From nobody Mon Jun 8 10:56:11 2026 Received: from out-172.mta1.migadu.com (out-172.mta1.migadu.com [95.215.58.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5C8B21607A4; Fri, 29 May 2026 15:15:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780067711; cv=none; b=hQ+7NAJBb6wY7aXxHFIcdhesy/BzA3KaUP3aWYKbE1yWdLICREvmtzw6x24Lx25rloqk6ZqLFclXeF2eQtr26DGOa6bNbFQ/1NUbg6pkeBoBk5S+dTXHdj2V82bbBlDwGn0Dw5ZJrZbGbIbGRPWnyLTPbaw9+y9OU4Oj8tu3ZrA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780067711; c=relaxed/simple; bh=SKAT7hw/8Orv13PHCSnTgIBz2Ipz4cxEP3jGinHwIm4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cdELZBN8DO7onfOCcfQLP3M1zH2DdH+T2P1rGvTHH6/YmuImiF5CdVPgmodAbULdDKVnHQPqE4eqw1ORBM90+Vv5X10RGvS6ITyYIkLzJEnwrHoSgwO5wYWzZj02IljBf5VtKYa7u/yb/oA9fe6a7mJI/hAG4k/T1h80KBhVzDk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=Yf7aKi3K; arc=none smtp.client-ip=95.215.58.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="Yf7aKi3K" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1780067708; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=BNYctVoQYshrW+FHoERDUeT3GUi8gIpDSH2odCxQVMM=; b=Yf7aKi3K+7XdNVq/QbZlpU2cDoF8mDqYwrJlXFI2qk54YQPT0RTJ0nWeDP+/wvdTMYTBr6 PKYmV062GNM1PDVt0YGBTv0VXUvnTpa0UH5j5Os6RIM08zKsNxCm9DGpa2mla4I0ESnT1Y kGQmIgto4txNLwJrXUK8jFYOBWTfpsA= From: Leon Hwang To: bpf@vger.kernel.org Cc: "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Andrii Nakryiko , Eduard Zingerman , Alexei Starovoitov , Daniel Borkmann , Martin KaFai Lau , Kumar Kartikeya Dwivedi , Song Liu , Yonghong Song , Jiri Olsa , Shuah Khan , Guillaume Nault , Leon Hwang , Ido Schimmel , Fernando Fernandez Mancera , Peter Oskolkov , linux-kernel@vger.kernel.org, netdev@vger.kernel.org, linux-kselftest@vger.kernel.org, kernel-patches-bot@fb.com, Leon Hwang Subject: [PATCH bpf v2 3/4] bpf: Update transport_header when encapsulating UDP tunnel in lwt Date: Fri, 29 May 2026 23:13:50 +0800 Message-ID: <20260529151351.69911-4-leon.hwang@linux.dev> In-Reply-To: <20260529151351.69911-1-leon.hwang@linux.dev> References: <20260529151351.69911-1-leon.hwang@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Currently, bpf_lwt_push_ip_encap() does not update skb->transport_header. When a driver, e.g. ice, reuses the stale skb->transport_header to offload checksum computation to NIC hardware, VxLAN packets encapsulated by bpf_lwt_push_encap() helper may be dropped due to incorrect checksum. Update skb->transport_header in bpf_lwt_push_ip_encap() whenever the encapsulated packet uses UDP, so checksum offload works correctly. Fixes: 52f278774e79 ("bpf: implement BPF_LWT_ENCAP_IP mode in bpf_lwt_push_= encap") Cc: Leon Hwang Signed-off-by: Leon Hwang --- net/core/lwt_bpf.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/core/lwt_bpf.c b/net/core/lwt_bpf.c index c306120e11d2..1d556dec94b4 100644 --- a/net/core/lwt_bpf.c +++ b/net/core/lwt_bpf.c @@ -600,6 +600,7 @@ static int handle_gso_encap(struct sk_buff *skb, bool i= pv4, int encap_len) int bpf_lwt_push_ip_encap(struct sk_buff *skb, void *hdr, u32 len, bool in= gress) { u8 buff[LWT_BPF_MAX_HEADROOM]; + bool is_udp_tunnel; struct iphdr *iph; bool ipv4; int err; @@ -615,10 +616,16 @@ int bpf_lwt_push_ip_encap(struct sk_buff *skb, void *= hdr, u32 len, bool ingress) ipv4 =3D true; if (unlikely(iph->ihl < 5 || len < iph->ihl * 4)) return -EINVAL; + is_udp_tunnel =3D iph->protocol =3D=3D IPPROTO_UDP; + if (unlikely(is_udp_tunnel && len < iph->ihl * 4 + sizeof(struct udphdr)= )) + return -EINVAL; } else if (iph->version =3D=3D 6) { ipv4 =3D false; if (unlikely(len < sizeof(struct ipv6hdr))) return -EINVAL; + is_udp_tunnel =3D ((struct ipv6hdr *)iph)->nexthdr =3D=3D NEXTHDR_UDP; + if (unlikely(is_udp_tunnel && len < sizeof(struct ipv6hdr) + sizeof(stru= ct udphdr))) + return -EINVAL; } else { return -EINVAL; } @@ -641,6 +648,10 @@ int bpf_lwt_push_ip_encap(struct sk_buff *skb, void *h= dr, u32 len, bool ingress) skb_postpush_rcsum(skb, iph, len); skb_reset_network_header(skb); memcpy(skb_network_header(skb), buff, len); + if (ipv4 && is_udp_tunnel) + skb_set_transport_header(skb, skb_network_offset(skb) + iph->ihl * 4); + else if (!ipv4 && is_udp_tunnel) + skb_set_transport_header(skb, skb_network_offset(skb) + sizeof(struct ip= v6hdr)); bpf_compute_data_pointers(skb); skb_clear_hash(skb); =20 --=20 2.54.0 From nobody Mon Jun 8 10:56:11 2026 Received: from out-172.mta1.migadu.com (out-172.mta1.migadu.com [95.215.58.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0E88B3469F6 for ; Fri, 29 May 2026 15:15:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780067721; cv=none; b=LA7egnp+s0m2+O/o4tJ/4bzokNvdMKD6hMYseyPzYD4h0B0LbqHChw45OqcOFppB58iD+oueQ8pmXyFHZmZ2abY9e1Eox0pCPdTL6YdhxhwY/c2OgtiFmVDk9ffZv2IY9sM2m6x703bAj3e5wcCrSCLIpjYDqPoNHI+StKUtyM8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780067721; c=relaxed/simple; bh=fx2Dy9tCi4RnUy9orAYiUR3uS3pPniY/2y/d7zAdTtw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=HdLuaKEPYaKpnSGZid15cBYe8M/GHDN6QFjQP3a9wdTdgms3qBaqOWbTlcVCvj6LLxIbJYmszK5rlWtSpMEF/m4sejm3KuvjiZ1+T1rcSeEVW2KUbA08JFD1bDAXkN5FYIUaa//W1Mh319obwJP8gyRQNl45Zz++1RRKxuwhsW4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=RBQpmo7a; arc=none smtp.client-ip=95.215.58.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="RBQpmo7a" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1780067717; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=VglQtV/dDfp6gWpnOxAVU64Cr5RpzqFiUFyq2nDL3QU=; b=RBQpmo7axT66xQHuz3rK+2+2utTQBb2V4oC8lSOA2A2fytV73+KQcCMeI/wIqmx030uGTP uOoCjx1rCGCA0mTNVKs/U7iiVWj9nPqijayHkr7gMO/aIZrCsEyKvwg1/PbFM/5cLni1VO wSa8k6+oKGbiXynRNDAbwPlXf2OdX/M= From: Leon Hwang To: bpf@vger.kernel.org Cc: "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Andrii Nakryiko , Eduard Zingerman , Alexei Starovoitov , Daniel Borkmann , Martin KaFai Lau , Kumar Kartikeya Dwivedi , Song Liu , Yonghong Song , Jiri Olsa , Shuah Khan , Guillaume Nault , Leon Hwang , Ido Schimmel , Fernando Fernandez Mancera , Peter Oskolkov , linux-kernel@vger.kernel.org, netdev@vger.kernel.org, linux-kselftest@vger.kernel.org, kernel-patches-bot@fb.com, Leon Hwang Subject: [PATCH bpf v2 4/4] selftests/bpf: Add tests to verify the fix of encapsulating VxLAN in lwt Date: Fri, 29 May 2026 23:13:51 +0800 Message-ID: <20260529151351.69911-5-leon.hwang@linux.dev> In-Reply-To: <20260529151351.69911-1-leon.hwang@linux.dev> References: <20260529151351.69911-1-leon.hwang@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Add two tests to verify the transport header of skb has been set when encapsulate VxLAN using bpf_lwt_push_encap() helper. 1. VxLAN over IPv4. 2. VxLAN over IPv6. Without the fix, the tests would fail: lwt_ip_encap_vxlan:FAIL:transport_hdr offset unexpected transport_hdr offs= et: actual 70 !=3D expected 20 #208 lwt_ip_encap_vxlan_ipv4:FAIL lwt_ip_encap_vxlan:FAIL:transport_hdr offset unexpected transport_hdr offs= et: actual 110 !=3D expected 40 #209 lwt_ip_encap_vxlan_ipv6:FAIL Assisted-by: Claude:claude-sonnet-4-6 Cc: Leon Hwang Signed-off-by: Leon Hwang --- .../selftests/bpf/prog_tests/lwt_ip_encap.c | 158 ++++++++++++++++++ .../selftests/bpf/progs/test_lwt_ip_encap.c | 112 +++++++++++++ .../bpf/progs/test_lwt_ip_encap_fix.c | 36 ++++ 3 files changed, 306 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/test_lwt_ip_encap_fix= .c 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 b6391af5f6f9..3d79e3b47f89 100644 --- a/tools/testing/selftests/bpf/prog_tests/lwt_ip_encap.c +++ b/tools/testing/selftests/bpf/prog_tests/lwt_ip_encap.c @@ -1,8 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-only +#include +#include #include =20 #include "network_helpers.h" #include "test_progs.h" +#include "test_lwt_ip_encap_fix.skel.h" =20 #define BPF_FILE "test_lwt_ip_encap.bpf.o" =20 @@ -35,6 +38,10 @@ #define IP6_ADDR_SRC IP6_ADDR_1 #define IP6_ADDR_DST IP6_ADDR_4 =20 +/* 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" + /* Setup/topology: * * NS1 NS2 NS3 @@ -538,3 +545,154 @@ 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 -X- veth4 (ping ds= t) + * | ^ + * (bottom route) | (inner pkt) + * v bottom route | + * veth5 -->> veth6 veth7 -->> veth8 (vxlan d= ecap) + * (IP*_ADDR_VXLA= N) + * + * 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. + */ +static int setup_vxlan_routes(const char *ns3, const char *ns1, const char= *ns2, + const char *vrf) +{ + struct nstoken *nstoken; + + nstoken =3D open_netns(ns3); + if (!ASSERT_OK_PTR(nstoken, "open ns3 for vxlan")) + return -1; + + SYS(fail_close, "ip a add %s/32 dev veth8", IP4_ADDR_VXLAN); + SYS(fail_close, "ip -6 a add %s/128 dev veth8", IP6_ADDR_VXLAN); + /* + * Standard VxLAN devices to decap the encapsulated packets. The inner + * Ethernet frame uses a broadcast dst MAC so the IP stack accepts it + * without ARP or FDB configuration. + */ + SYS(fail_close, "ip link add vxlan4 type vxlan id 1 dstport 4789 local %s= dev veth8 nolearning noudpcsum", + IP4_ADDR_VXLAN); + SYS(fail_close, "ip link set vxlan4 up"); + SYS(fail_close, "ip link add vxlan6 type vxlan id 1 dstport 4789 local %s= dev veth8 nolearning udp6zerocsumrx", + IP6_ADDR_VXLAN); + SYS(fail_close, "ip link set vxlan6 up"); + close_netns(nstoken); + + SYS(fail, "ip -n %s route add %s/32 dev veth5 via %s %s", + ns1, IP4_ADDR_VXLAN, IP4_ADDR_6, vrf); + SYS(fail, "ip -n %s route add %s/32 dev veth7 via %s %s", + ns2, IP4_ADDR_VXLAN, IP4_ADDR_8, vrf); + SYS(fail, "ip -n %s -6 route add %s/128 dev veth5 via %s %s", + ns1, IP6_ADDR_VXLAN, IP6_ADDR_6, vrf); + SYS(fail, "ip -n %s -6 route add %s/128 dev veth7 via %s %s", + ns2, IP6_ADDR_VXLAN, IP6_ADDR_8, vrf); + return 0; + +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=3DUDP, port=3D4789) 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 =3D=3D sizeof(outer IP header). + * Without the fix the transport_header still points at the inner + * transport layer, giving a wrong (larger) offset. + */ +static void lwt_ip_encap_vxlan(bool ipv4_encap) +{ + char ns1[NETNS_NAME_SIZE] =3D NETNS_BASE "-1-"; + char ns2[NETNS_NAME_SIZE] =3D NETNS_BASE "-2-"; + char ns3[NETNS_NAME_SIZE] =3D NETNS_BASE "-3-"; + const char *sec =3D ipv4_encap ? "encap_vxlan" : "encap_vxlan6"; + int expected_offset =3D ipv4_encap ? (int)sizeof(struct iphdr) + : (int)sizeof(struct ipv6hdr); + struct test_lwt_ip_encap_fix *skel =3D NULL; + int thdr_offset; + + if (!ASSERT_OK(create_ns(ns1, NETNS_NAME_SIZE), "create ns1")) + goto out; + if (!ASSERT_OK(create_ns(ns2, NETNS_NAME_SIZE), "create ns2")) + goto out; + if (!ASSERT_OK(create_ns(ns3, NETNS_NAME_SIZE), "create ns3")) + goto out; + + if (!ASSERT_OK(setup_network(ns1, ns2, ns3, ""), "setup network")) + goto out; + + if (!ASSERT_OK(setup_vxlan_routes(ns3, ns1, ns2, ""), "setup vxlan routes= ")) + goto out; + + /* + * Attach fexit to bpf_lwt_push_ip_encap() before installing the + * LWT route so we don't miss the first encap call. + */ + skel =3D test_lwt_ip_encap_fix__open_and_load(); + if (!ASSERT_OK_PTR(skel, "test_lwt_ip_encap_fix__open_and_load")) + goto out; + + if (!ASSERT_OK(test_lwt_ip_encap_fix__attach(skel), "test_lwt_ip_encap_fi= x__attach")) + goto out; + + /* Remove the direct NS2->DST route so packets must go via LWT encap. */ + SYS(out, "ip -n %s route del %s/32 dev veth3", ns2, IP4_ADDR_DST); + SYS(out, "ip -n %s -6 route del %s/128 dev veth3", ns2, IP6_ADDR_DST); + + /* Install the VxLAN BPF LWT xmit route. */ + if (ipv4_encap) + SYS(out, "ip -n %s route add %s encap bpf xmit obj %s sec %s dev veth1", + ns1, IP4_ADDR_DST, BPF_FILE, sec); + else + SYS(out, "ip -n %s -6 route add %s encap bpf xmit obj %s sec %s dev veth= 1", + ns1, IP6_ADDR_DST, BPF_FILE, sec); + + skel->bss->fexit_triggered =3D false; + if (ipv4_encap) + SYS(out, "ip netns exec %s ping -c 1 -W1 %s", ns1, IP4_ADDR_DST); + else + SYS(out, "ip netns exec %s ping6 -c 1 -W1 %s", ns1, IP6_ADDR_DST); + + /* Test 1: fexit triggered means bpf_lwt_push_ip_encap() succeeded. */ + if (!ASSERT_TRUE(skel->bss->fexit_triggered, "fexit_triggered")) + goto out; + + /* + * Test 2: transport_header must sit immediately after the outer IP + * header, pointing at the UDP header of the VxLAN encap. + */ + thdr_offset =3D (int)skel->bss->transport_hdr - (int)skel->bss->network_h= dr; + ASSERT_EQ(thdr_offset, expected_offset, "transport_hdr offset"); + +out: + test_lwt_ip_encap_fix__destroy(skel); + SYS_NOFAIL("ip netns del %s", ns1); + SYS_NOFAIL("ip netns del %s", ns2); + SYS_NOFAIL("ip netns del %s", ns3); +} + +void test_lwt_ip_encap_vxlan_ipv4(void) +{ + lwt_ip_encap_vxlan(IPV4_ENCAP); +} + +void test_lwt_ip_encap_vxlan_ipv6(void) +{ + lwt_ip_encap_vxlan(IPV6_ENCAP); +} diff --git a/tools/testing/selftests/bpf/progs/test_lwt_ip_encap.c b/tools/= testing/selftests/bpf/progs/test_lwt_ip_encap.c index d6cb986e7533..36f0fc682ffb 100644 --- a/tools/testing/selftests/bpf/progs/test_lwt_ip_encap.c +++ b/tools/testing/selftests/bpf/progs/test_lwt_ip_encap.c @@ -2,8 +2,10 @@ #include #include #include +#include #include #include +#include #include #include =20 @@ -82,4 +84,114 @@ int bpf_lwt_encap_gre6(struct __sk_buff *skb) return BPF_LWT_REROUTE; } =20 +struct vxlanhdr { + __be32 vx_flags; /* I flag =3D 0x08000000 (valid VNI) */ + __be32 vx_vni; /* VNI in top 24 bits */ +}; + +#define VXLAN_PORT 4789 +#define VXLAN_FLAGS 0x08000000 +#define VXLAN_VNI 1 + +static const __u8 bcast[ETH_ALEN] =3D { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const __u8 srcmac[ETH_ALEN] =3D { + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, +}; + +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 */ h= dr; + int err; + + memset(&hdr, 0, sizeof(hdr)); + + hdr.iph.ihl =3D 5; + hdr.iph.version =3D 4; + hdr.iph.ttl =3D 0x40; + hdr.iph.protocol =3D 17; /* IPPROTO_UDP */ + hdr.iph.tot_len =3D bpf_htons(skb->len + sizeof(hdr)); +#if __BYTE_ORDER__ =3D=3D __ORDER_LITTLE_ENDIAN__ + hdr.iph.saddr =3D 0x640510ac; /* 172.16.5.100 */ + hdr.iph.daddr =3D 0x641110ac; /* 172.16.17.100 */ +#elif __BYTE_ORDER__ =3D=3D __ORDER_BIG_ENDIAN__ + hdr.iph.saddr =3D 0xac100564; /* 172.16.5.100 */ + hdr.iph.daddr =3D 0xac101164; /* 172.16.17.100 */ +#else +#error "Fix your compiler's __BYTE_ORDER__?!" +#endif + + hdr.udph.source =3D bpf_htons(VXLAN_PORT); + hdr.udph.dest =3D bpf_htons(VXLAN_PORT); + hdr.udph.len =3D bpf_htons(skb->len + sizeof(hdr.udph) + sizeof(hdr.vx= h) + + sizeof(hdr.eth)); + + hdr.vxh.vx_flags =3D bpf_htonl(VXLAN_FLAGS); + hdr.vxh.vx_vni =3D 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 =3D bpf_htons(ETH_P_IP); + + err =3D 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 */ h= dr; + int err; + + memset(&hdr, 0, sizeof(hdr)); + + hdr.ip6hdr.version =3D 6; + hdr.ip6hdr.nexthdr =3D 17; /* IPPROTO_UDP */ + hdr.ip6hdr.hop_limit =3D 0x40; + hdr.ip6hdr.payload_len =3D bpf_htons(skb->len + sizeof(hdr.udph) + sizeof= (hdr.vxh) + + sizeof(hdr.eth)); + /* fb05::1 */ + hdr.ip6hdr.saddr.s6_addr[0] =3D 0xfb; + hdr.ip6hdr.saddr.s6_addr[1] =3D 0x05; + hdr.ip6hdr.saddr.s6_addr[15] =3D 1; + /* fb20::1 */ + hdr.ip6hdr.daddr.s6_addr[0] =3D 0xfb; + hdr.ip6hdr.daddr.s6_addr[1] =3D 0x20; + hdr.ip6hdr.daddr.s6_addr[15] =3D 1; + + hdr.udph.source =3D bpf_htons(VXLAN_PORT); + hdr.udph.dest =3D bpf_htons(VXLAN_PORT); + hdr.udph.len =3D bpf_htons(skb->len + sizeof(hdr.udph) + sizeof(hdr.vx= h) + + sizeof(hdr.eth)); + + hdr.vxh.vx_flags =3D bpf_htonl(VXLAN_FLAGS); + hdr.vxh.vx_vni =3D 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 =3D bpf_htons(ETH_P_IPV6); + + err =3D bpf_lwt_push_encap(skb, BPF_LWT_ENCAP_IP, &hdr, sizeof(hdr)); + if (err) + return BPF_DROP; + + return BPF_LWT_REROUTE; +} + char _license[] SEC("license") =3D "GPL"; diff --git a/tools/testing/selftests/bpf/progs/test_lwt_ip_encap_fix.c b/to= ols/testing/selftests/bpf/progs/test_lwt_ip_encap_fix.c new file mode 100644 index 000000000000..e9043fe654eb --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_lwt_ip_encap_fix.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * fexit on bpf_lwt_push_ip_encap() to verify skb->transport_header is + * correctly updated when a UDP-based tunnel (e.g. VxLAN) is pushed. + */ +#include "vmlinux.h" +#include +#include +#include + +/* Written by fexit, read by the user-space test via skeleton BSS. */ +__u16 transport_hdr =3D 0; +__u16 network_hdr =3D 0; +bool fexit_triggered =3D false; + +/* + * bpf_lwt_push_ip_encap(struct sk_buff *skb, void *hdr, u32 len, bool ing= ress) + * + * After a successful push the transport_header must point at the outer + * transport header (UDP for VxLAN), i.e. + * transport_header - network_header =3D=3D sizeof(outer IP header) + */ +SEC("fexit/bpf_lwt_push_ip_encap") +int BPF_PROG(fexit_lwt_push_ip_encap, struct sk_buff *skb, void *hdr, u32 = len, bool ingress, + int retval) +{ + if (retval || fexit_triggered) + return 0; + + fexit_triggered =3D true; + transport_hdr =3D BPF_CORE_READ(skb, transport_header); + network_hdr =3D BPF_CORE_READ(skb, network_header); + return 0; +} + +char _license[] SEC("license") =3D "GPL"; --=20 2.54.0