diff mbox series

[net-next,v2,04/12] flow_dissector: UDP encap infrastructure

Message ID 20240815214527.2100137-5-tom@herbertland.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series flow_dissector: Dissect UDP encapsulation protocols | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next, async
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 72 this patch: 72
netdev/build_tools success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers warning 4 maintainers not CCed: pabeni@redhat.com bpf@vger.kernel.org ast@fiberby.net dcaratti@redhat.com
netdev/build_clang success Errors and warnings before: 131 this patch: 131
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 4976 this patch: 4976
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 158 lines checked
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 47 this patch: 47
netdev/source_inline success Was 0 now: 0

Commit Message

Tom Herbert Aug. 15, 2024, 9:45 p.m. UTC
Add infrastructure for parsing into UDP encapsulations

Add function __skb_flow_dissect_udp that is called for IPPROTO_UDP.
The flag FLOW_DISSECTOR_F_PARSE_UDP_ENCAPS enables parsing of UDP
encapsulations. If the flag is set when parsing a UDP packet then
a socket lookup is performed. The offset of the base network header,
either an IPv4 or IPv6 header, is tracked and passed to
__skb_flow_dissect_udp so that it can perform the socket lookup

If a socket is found and it's for a UDP encapsulation (encap_type is
set in the UDP socket) then a switch is performed on the encap_type
value (cases are UDP_ENCAP_* values)

An encapsulated packet in UDP can either be indicated by an
EtherType or IP protocol. The processing for dissecting a UDP encap
protocol returns a flow dissector return code. If
FLOW_DISSECT_RET_PROTO_AGAIN or FLOW_DISSECT_RET_IPPROTO_AGAIN is
returned then the corresponding  encapsulated protocol is dissected.
The nhoff is set to point to the header to process.  In the case
FLOW_DISSECT_RET_PROTO_AGAIN the EtherType protocol is returned and
the IP protocol is set to zero. In the case of
FLOW_DISSECT_RET_IPPROTO_AGAIN, the IP protocol is returned and
the EtherType protocol is returned unchanged

Signed-off-by: Tom Herbert <tom@herbertland.com>
---
 include/net/flow_dissector.h |   1 +
 net/core/flow_dissector.c    | 121 +++++++++++++++++++++++++++++++++++
 2 files changed, 122 insertions(+)

Comments

Willem de Bruijn Aug. 16, 2024, 7:09 p.m. UTC | #1
Tom Herbert wrote:
> Add infrastructure for parsing into UDP encapsulations
> 
> Add function __skb_flow_dissect_udp that is called for IPPROTO_UDP.
> The flag FLOW_DISSECTOR_F_PARSE_UDP_ENCAPS enables parsing of UDP
> encapsulations. If the flag is set when parsing a UDP packet then
> a socket lookup is performed. The offset of the base network header,
> either an IPv4 or IPv6 header, is tracked and passed to
> __skb_flow_dissect_udp so that it can perform the socket lookup
> 
> If a socket is found and it's for a UDP encapsulation (encap_type is
> set in the UDP socket) then a switch is performed on the encap_type
> value (cases are UDP_ENCAP_* values)
> 
> An encapsulated packet in UDP can either be indicated by an
> EtherType or IP protocol. The processing for dissecting a UDP encap
> protocol returns a flow dissector return code. If
> FLOW_DISSECT_RET_PROTO_AGAIN or FLOW_DISSECT_RET_IPPROTO_AGAIN is
> returned then the corresponding  encapsulated protocol is dissected.
> The nhoff is set to point to the header to process.  In the case
> FLOW_DISSECT_RET_PROTO_AGAIN the EtherType protocol is returned and
> the IP protocol is set to zero. In the case of
> FLOW_DISSECT_RET_IPPROTO_AGAIN, the IP protocol is returned and
> the EtherType protocol is returned unchanged
> 
> Signed-off-by: Tom Herbert <tom@herbertland.com>
> ---
>  include/net/flow_dissector.h |   1 +
>  net/core/flow_dissector.c    | 121 +++++++++++++++++++++++++++++++++++
>  2 files changed, 122 insertions(+)
> 
> diff --git a/include/net/flow_dissector.h b/include/net/flow_dissector.h
> index ced79dc8e856..8a868a88a6f1 100644
> --- a/include/net/flow_dissector.h
> +++ b/include/net/flow_dissector.h
> @@ -384,6 +384,7 @@ enum flow_dissector_key_id {
>  #define FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL	BIT(1)
>  #define FLOW_DISSECTOR_F_STOP_AT_ENCAP		BIT(2)
>  #define FLOW_DISSECTOR_F_STOP_BEFORE_ENCAP	BIT(3)
> +#define FLOW_DISSECTOR_F_PARSE_UDP_ENCAPS	BIT(4)
>  
>  struct flow_dissector_key {
>  	enum flow_dissector_key_id key_id;
> diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c
> index 4b116119086a..160801b83d54 100644
> --- a/net/core/flow_dissector.c
> +++ b/net/core/flow_dissector.c
> @@ -13,6 +13,7 @@
>  #include <net/gre.h>
>  #include <net/pptp.h>
>  #include <net/tipc.h>
> +#include <net/udp.h>
>  #include <linux/igmp.h>
>  #include <linux/icmp.h>
>  #include <linux/sctp.h>
> @@ -806,6 +807,117 @@ __skb_flow_dissect_batadv(const struct sk_buff *skb,
>  	return FLOW_DISSECT_RET_PROTO_AGAIN;
>  }
>  
> +static enum flow_dissect_ret
> +__skb_flow_dissect_udp(const struct sk_buff *skb, const struct net *net,
> +		       struct flow_dissector *flow_dissector,
> +		       void *target_container, const void *data,
> +		       int *p_nhoff, int hlen, __be16 *p_proto,
> +		       u8 *p_ip_proto, int base_nhoff, unsigned int flags)
> +{
> +	enum flow_dissect_ret ret;
> +	const struct udphdr *udph;
> +	struct udphdr _udph;
> +	struct sock *sk;
> +	__u8 encap_type;
> +	int nhoff;
> +
> +	if (!(flags & FLOW_DISSECTOR_F_PARSE_UDP_ENCAPS))
> +		return FLOW_DISSECT_RET_OUT_GOOD;
> +
> +	switch (*p_proto) {
> +	case htons(ETH_P_IP): {
> +		const struct iphdr *iph;
> +		struct iphdr _iph;
> +
> +		iph = __skb_header_pointer(skb, base_nhoff, sizeof(_iph), data,
> +					   hlen, &_iph);
> +		if (!iph)
> +			return FLOW_DISSECT_RET_OUT_BAD;
> +
> +		udph = __skb_header_pointer(skb, *p_nhoff, sizeof(_udph), data,
> +					    hlen, &_udph);
> +		if (!udph)
> +			return FLOW_DISSECT_RET_OUT_BAD;
> +
> +		rcu_read_lock();
> +		/* Look up the UDPv4 socket and get the encap_type */
> +		sk = __udp4_lib_lookup(net, iph->saddr, udph->source,
> +				       iph->daddr, udph->dest,
> +				       inet_iif(skb), inet_sdif(skb),
> +				       net->ipv4.udp_table, NULL);
> +		if (!sk || !udp_sk(sk)->encap_type) {
> +			rcu_read_unlock();
> +			return FLOW_DISSECT_RET_OUT_GOOD;
> +		}
> +
> +		encap_type = udp_sk(sk)->encap_type;
> +		rcu_read_unlock();
> +
> +		break;
> +	}
> +#if IS_ENABLED(CONFIG_IPV6)
> +	case htons(ETH_P_IPV6): {
> +		const struct ipv6hdr *iph;
> +		struct ipv6hdr _iph;
> +
> +		if (!likely(ipv6_bpf_stub))
> +			return FLOW_DISSECT_RET_OUT_GOOD;
> +
> +		iph = __skb_header_pointer(skb, base_nhoff, sizeof(_iph), data,
> +					   hlen, &_iph);
> +		if (!iph)
> +			return FLOW_DISSECT_RET_OUT_BAD;
> +
> +		udph = __skb_header_pointer(skb, *p_nhoff, sizeof(_udph), data,
> +					    hlen, &_udph);
> +		if (!udph)
> +			return FLOW_DISSECT_RET_OUT_BAD;
> +
> +		rcu_read_lock();
> +		/* Look up the UDPv6 socket and get the encap_type */
> +		sk = ipv6_bpf_stub->udp6_lib_lookup(net,

Should this use ipv6_stub?
> +				&iph->saddr, udph->source,
> +				&iph->daddr, udph->dest,
> +				inet_iif(skb), inet_sdif(skb),
> +				net->ipv4.udp_table, NULL);
> +
> +		if (!sk || !udp_sk(sk)->encap_type) {
> +			rcu_read_unlock();
> +			return FLOW_DISSECT_RET_OUT_GOOD;
> +		}
> +
> +		encap_type = udp_sk(sk)->encap_type;
> +		rcu_read_unlock();
> +
> +		break;
> +	}
> +#endif /* CONFIG_IPV6 */
> +	default:
> +		return FLOW_DISSECT_RET_OUT_GOOD;
> +	}
> +
> +	nhoff = *p_nhoff + sizeof(struct udphdr);

maybe sizeof(_udph) for consistency

> +	ret = FLOW_DISSECT_RET_OUT_GOOD;
> +
> +	switch (encap_type) {
> +	default:
> +		break;
> +	}
> +
> +	switch (ret) {
> +	case FLOW_DISSECT_RET_PROTO_AGAIN:
> +		*p_ip_proto = 0;
> +		fallthrough;
> +	case FLOW_DISSECT_RET_IPPROTO_AGAIN:
> +		*p_nhoff = nhoff;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
kernel test robot Aug. 17, 2024, 2:01 p.m. UTC | #2
Hi Tom,

kernel test robot noticed the following build errors:

[auto build test ERROR on net-next/main]

url:    https://github.com/intel-lab-lkp/linux/commits/Tom-Herbert/flow_dissector-Parse-ETH_P_TEB-and-move-out-of-GRE/20240816-102659
base:   net-next/main
patch link:    https://lore.kernel.org/r/20240815214527.2100137-5-tom%40herbertland.com
patch subject: [PATCH net-next v2 04/12] flow_dissector: UDP encap infrastructure
config: loongarch-randconfig-002-20240817 (https://download.01.org/0day-ci/archive/20240817/202408172101.UDWfVPns-lkp@intel.com/config)
compiler: loongarch64-linux-gcc (GCC) 14.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240817/202408172101.UDWfVPns-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202408172101.UDWfVPns-lkp@intel.com/

All errors (new ones prefixed by >>):

   loongarch64-linux-ld: net/core/flow_dissector.o: in function `__skb_flow_dissect_udp':
>> net/core/flow_dissector.c:844:(.text+0x13e8): undefined reference to `__udp4_lib_lookup'


vim +844 net/core/flow_dissector.c

   809	
   810	static enum flow_dissect_ret
   811	__skb_flow_dissect_udp(const struct sk_buff *skb, const struct net *net,
   812			       struct flow_dissector *flow_dissector,
   813			       void *target_container, const void *data,
   814			       int *p_nhoff, int hlen, __be16 *p_proto,
   815			       u8 *p_ip_proto, int base_nhoff, unsigned int flags)
   816	{
   817		enum flow_dissect_ret ret;
   818		const struct udphdr *udph;
   819		struct udphdr _udph;
   820		struct sock *sk;
   821		__u8 encap_type;
   822		int nhoff;
   823	
   824		if (!(flags & FLOW_DISSECTOR_F_PARSE_UDP_ENCAPS))
   825			return FLOW_DISSECT_RET_OUT_GOOD;
   826	
   827		switch (*p_proto) {
   828		case htons(ETH_P_IP): {
   829			const struct iphdr *iph;
   830			struct iphdr _iph;
   831	
   832			iph = __skb_header_pointer(skb, base_nhoff, sizeof(_iph), data,
   833						   hlen, &_iph);
   834			if (!iph)
   835				return FLOW_DISSECT_RET_OUT_BAD;
   836	
   837			udph = __skb_header_pointer(skb, *p_nhoff, sizeof(_udph), data,
   838						    hlen, &_udph);
   839			if (!udph)
   840				return FLOW_DISSECT_RET_OUT_BAD;
   841	
   842			rcu_read_lock();
   843			/* Look up the UDPv4 socket and get the encap_type */
 > 844			sk = __udp4_lib_lookup(net, iph->saddr, udph->source,
   845					       iph->daddr, udph->dest,
   846					       inet_iif(skb), inet_sdif(skb),
   847					       net->ipv4.udp_table, NULL);
   848			if (!sk || !udp_sk(sk)->encap_type) {
   849				rcu_read_unlock();
   850				return FLOW_DISSECT_RET_OUT_GOOD;
   851			}
   852	
   853			encap_type = udp_sk(sk)->encap_type;
   854			rcu_read_unlock();
   855	
   856			break;
   857		}
   858	#if IS_ENABLED(CONFIG_IPV6)
   859		case htons(ETH_P_IPV6): {
   860			const struct ipv6hdr *iph;
   861			struct ipv6hdr _iph;
   862	
   863			if (!likely(ipv6_bpf_stub))
   864				return FLOW_DISSECT_RET_OUT_GOOD;
   865	
   866			iph = __skb_header_pointer(skb, base_nhoff, sizeof(_iph), data,
   867						   hlen, &_iph);
   868			if (!iph)
   869				return FLOW_DISSECT_RET_OUT_BAD;
   870	
   871			udph = __skb_header_pointer(skb, *p_nhoff, sizeof(_udph), data,
   872						    hlen, &_udph);
   873			if (!udph)
   874				return FLOW_DISSECT_RET_OUT_BAD;
   875	
   876			rcu_read_lock();
   877			/* Look up the UDPv6 socket and get the encap_type */
   878			sk = ipv6_bpf_stub->udp6_lib_lookup(net,
   879					&iph->saddr, udph->source,
   880					&iph->daddr, udph->dest,
   881					inet_iif(skb), inet_sdif(skb),
   882					net->ipv4.udp_table, NULL);
   883	
   884			if (!sk || !udp_sk(sk)->encap_type) {
   885				rcu_read_unlock();
   886				return FLOW_DISSECT_RET_OUT_GOOD;
   887			}
   888	
   889			encap_type = udp_sk(sk)->encap_type;
   890			rcu_read_unlock();
   891	
   892			break;
   893		}
   894	#endif /* CONFIG_IPV6 */
   895		default:
   896			return FLOW_DISSECT_RET_OUT_GOOD;
   897		}
   898	
   899		nhoff = *p_nhoff + sizeof(struct udphdr);
   900		ret = FLOW_DISSECT_RET_OUT_GOOD;
   901	
   902		switch (encap_type) {
   903		default:
   904			break;
   905		}
   906	
   907		switch (ret) {
   908		case FLOW_DISSECT_RET_PROTO_AGAIN:
   909			*p_ip_proto = 0;
   910			fallthrough;
   911		case FLOW_DISSECT_RET_IPPROTO_AGAIN:
   912			*p_nhoff = nhoff;
   913			break;
   914		default:
   915			break;
   916		}
   917	
   918		return ret;
   919	}
   920
Eric Dumazet Aug. 20, 2024, 6:52 p.m. UTC | #3
On Thu, Aug 15, 2024 at 11:46 PM Tom Herbert <tom@herbertland.com> wrote:
>
> Add infrastructure for parsing into UDP encapsulations
>
> Add function __skb_flow_dissect_udp that is called for IPPROTO_UDP.
> The flag FLOW_DISSECTOR_F_PARSE_UDP_ENCAPS enables parsing of UDP
> encapsulations. If the flag is set when parsing a UDP packet then
> a socket lookup is performed. The offset of the base network header,
> either an IPv4 or IPv6 header, is tracked and passed to
> __skb_flow_dissect_udp so that it can perform the socket lookup
>
> If a socket is found and it's for a UDP encapsulation (encap_type is
> set in the UDP socket) then a switch is performed on the encap_type
> value (cases are UDP_ENCAP_* values)
>
> An encapsulated packet in UDP can either be indicated by an
> EtherType or IP protocol. The processing for dissecting a UDP encap
> protocol returns a flow dissector return code. If
> FLOW_DISSECT_RET_PROTO_AGAIN or FLOW_DISSECT_RET_IPPROTO_AGAIN is
> returned then the corresponding  encapsulated protocol is dissected.
> The nhoff is set to point to the header to process.  In the case
> FLOW_DISSECT_RET_PROTO_AGAIN the EtherType protocol is returned and
> the IP protocol is set to zero. In the case of
> FLOW_DISSECT_RET_IPPROTO_AGAIN, the IP protocol is returned and
> the EtherType protocol is returned unchanged
>
> Signed-off-by: Tom Herbert <tom@herbertland.com>

I am a bit confused.

How is this series netns ready ?

tunnel decap devices can be in different netns from the lower device.

socket lookups need the correct net pointer.
Tom Herbert Aug. 20, 2024, 7 p.m. UTC | #4
On Tue, Aug 20, 2024 at 11:52 AM Eric Dumazet <edumazet@google.com> wrote:
>
> On Thu, Aug 15, 2024 at 11:46 PM Tom Herbert <tom@herbertland.com> wrote:
> >
> > Add infrastructure for parsing into UDP encapsulations
> >
> > Add function __skb_flow_dissect_udp that is called for IPPROTO_UDP.
> > The flag FLOW_DISSECTOR_F_PARSE_UDP_ENCAPS enables parsing of UDP
> > encapsulations. If the flag is set when parsing a UDP packet then
> > a socket lookup is performed. The offset of the base network header,
> > either an IPv4 or IPv6 header, is tracked and passed to
> > __skb_flow_dissect_udp so that it can perform the socket lookup
> >
> > If a socket is found and it's for a UDP encapsulation (encap_type is
> > set in the UDP socket) then a switch is performed on the encap_type
> > value (cases are UDP_ENCAP_* values)
> >
> > An encapsulated packet in UDP can either be indicated by an
> > EtherType or IP protocol. The processing for dissecting a UDP encap
> > protocol returns a flow dissector return code. If
> > FLOW_DISSECT_RET_PROTO_AGAIN or FLOW_DISSECT_RET_IPPROTO_AGAIN is
> > returned then the corresponding  encapsulated protocol is dissected.
> > The nhoff is set to point to the header to process.  In the case
> > FLOW_DISSECT_RET_PROTO_AGAIN the EtherType protocol is returned and
> > the IP protocol is set to zero. In the case of
> > FLOW_DISSECT_RET_IPPROTO_AGAIN, the IP protocol is returned and
> > the EtherType protocol is returned unchanged
> >
> > Signed-off-by: Tom Herbert <tom@herbertland.com>
>
> I am a bit confused.
>
> How is this series netns ready ?
>
> tunnel decap devices can be in different netns from the lower device.
>
> socket lookups need the correct net pointer.

Hi Eric,

How would we know what the correct net pointer is? Seems like there
could be multiple choices if netns were nested. Maybe best effort is
sufficient here?

Tom
diff mbox series

Patch

diff --git a/include/net/flow_dissector.h b/include/net/flow_dissector.h
index ced79dc8e856..8a868a88a6f1 100644
--- a/include/net/flow_dissector.h
+++ b/include/net/flow_dissector.h
@@ -384,6 +384,7 @@  enum flow_dissector_key_id {
 #define FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL	BIT(1)
 #define FLOW_DISSECTOR_F_STOP_AT_ENCAP		BIT(2)
 #define FLOW_DISSECTOR_F_STOP_BEFORE_ENCAP	BIT(3)
+#define FLOW_DISSECTOR_F_PARSE_UDP_ENCAPS	BIT(4)
 
 struct flow_dissector_key {
 	enum flow_dissector_key_id key_id;
diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c
index 4b116119086a..160801b83d54 100644
--- a/net/core/flow_dissector.c
+++ b/net/core/flow_dissector.c
@@ -13,6 +13,7 @@ 
 #include <net/gre.h>
 #include <net/pptp.h>
 #include <net/tipc.h>
+#include <net/udp.h>
 #include <linux/igmp.h>
 #include <linux/icmp.h>
 #include <linux/sctp.h>
@@ -806,6 +807,117 @@  __skb_flow_dissect_batadv(const struct sk_buff *skb,
 	return FLOW_DISSECT_RET_PROTO_AGAIN;
 }
 
+static enum flow_dissect_ret
+__skb_flow_dissect_udp(const struct sk_buff *skb, const struct net *net,
+		       struct flow_dissector *flow_dissector,
+		       void *target_container, const void *data,
+		       int *p_nhoff, int hlen, __be16 *p_proto,
+		       u8 *p_ip_proto, int base_nhoff, unsigned int flags)
+{
+	enum flow_dissect_ret ret;
+	const struct udphdr *udph;
+	struct udphdr _udph;
+	struct sock *sk;
+	__u8 encap_type;
+	int nhoff;
+
+	if (!(flags & FLOW_DISSECTOR_F_PARSE_UDP_ENCAPS))
+		return FLOW_DISSECT_RET_OUT_GOOD;
+
+	switch (*p_proto) {
+	case htons(ETH_P_IP): {
+		const struct iphdr *iph;
+		struct iphdr _iph;
+
+		iph = __skb_header_pointer(skb, base_nhoff, sizeof(_iph), data,
+					   hlen, &_iph);
+		if (!iph)
+			return FLOW_DISSECT_RET_OUT_BAD;
+
+		udph = __skb_header_pointer(skb, *p_nhoff, sizeof(_udph), data,
+					    hlen, &_udph);
+		if (!udph)
+			return FLOW_DISSECT_RET_OUT_BAD;
+
+		rcu_read_lock();
+		/* Look up the UDPv4 socket and get the encap_type */
+		sk = __udp4_lib_lookup(net, iph->saddr, udph->source,
+				       iph->daddr, udph->dest,
+				       inet_iif(skb), inet_sdif(skb),
+				       net->ipv4.udp_table, NULL);
+		if (!sk || !udp_sk(sk)->encap_type) {
+			rcu_read_unlock();
+			return FLOW_DISSECT_RET_OUT_GOOD;
+		}
+
+		encap_type = udp_sk(sk)->encap_type;
+		rcu_read_unlock();
+
+		break;
+	}
+#if IS_ENABLED(CONFIG_IPV6)
+	case htons(ETH_P_IPV6): {
+		const struct ipv6hdr *iph;
+		struct ipv6hdr _iph;
+
+		if (!likely(ipv6_bpf_stub))
+			return FLOW_DISSECT_RET_OUT_GOOD;
+
+		iph = __skb_header_pointer(skb, base_nhoff, sizeof(_iph), data,
+					   hlen, &_iph);
+		if (!iph)
+			return FLOW_DISSECT_RET_OUT_BAD;
+
+		udph = __skb_header_pointer(skb, *p_nhoff, sizeof(_udph), data,
+					    hlen, &_udph);
+		if (!udph)
+			return FLOW_DISSECT_RET_OUT_BAD;
+
+		rcu_read_lock();
+		/* Look up the UDPv6 socket and get the encap_type */
+		sk = ipv6_bpf_stub->udp6_lib_lookup(net,
+				&iph->saddr, udph->source,
+				&iph->daddr, udph->dest,
+				inet_iif(skb), inet_sdif(skb),
+				net->ipv4.udp_table, NULL);
+
+		if (!sk || !udp_sk(sk)->encap_type) {
+			rcu_read_unlock();
+			return FLOW_DISSECT_RET_OUT_GOOD;
+		}
+
+		encap_type = udp_sk(sk)->encap_type;
+		rcu_read_unlock();
+
+		break;
+	}
+#endif /* CONFIG_IPV6 */
+	default:
+		return FLOW_DISSECT_RET_OUT_GOOD;
+	}
+
+	nhoff = *p_nhoff + sizeof(struct udphdr);
+	ret = FLOW_DISSECT_RET_OUT_GOOD;
+
+	switch (encap_type) {
+	default:
+		break;
+	}
+
+	switch (ret) {
+	case FLOW_DISSECT_RET_PROTO_AGAIN:
+		*p_ip_proto = 0;
+		fallthrough;
+	case FLOW_DISSECT_RET_IPPROTO_AGAIN:
+		*p_nhoff = nhoff;
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
 static void
 __skb_flow_dissect_tcp(const struct sk_buff *skb,
 		       struct flow_dissector *flow_dissector,
@@ -1046,6 +1158,7 @@  bool __skb_flow_dissect(const struct net *net,
 	int mpls_lse = 0;
 	int num_hdrs = 0;
 	u8 ip_proto = 0;
+	int base_nhoff;
 	bool ret;
 
 	if (!data) {
@@ -1168,6 +1281,7 @@  bool __skb_flow_dissect(const struct net *net,
 
 proto_again:
 	fdret = FLOW_DISSECT_RET_CONTINUE;
+	base_nhoff = nhoff;
 
 	switch (proto) {
 	case htons(ETH_P_IP): {
@@ -1635,6 +1749,13 @@  bool __skb_flow_dissect(const struct net *net,
 				       data, nhoff, hlen);
 		break;
 
+	case IPPROTO_UDP:
+		fdret = __skb_flow_dissect_udp(skb, net, flow_dissector,
+					       target_container, data, &nhoff,
+					       hlen, &proto, &ip_proto,
+					       base_nhoff, flags);
+		break;
+
 	case IPPROTO_ICMP:
 	case IPPROTO_ICMPV6:
 		__skb_flow_dissect_icmp(skb, flow_dissector, target_container,