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 |
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; > +} > +
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
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.
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 --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,
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(+)