Add per protocol offload callbacks for flow_dissect to UDP for
IPv4 and IPv6. The callback functions extract the port number
information and with the packet addresses (given in an argument with
type flow_dissector_key_addrs) it performs a lookup on the UDP
socket. If a socket is found and flow_dissect is set for the
socket then that function is called.

Signed-off-by: Tom Herbert <t...@herbertland.com>
---
 net/ipv4/udp_offload.c | 39 +++++++++++++++++++++++++++++++++++++++
 net/ipv6/udp_offload.c | 38 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 77 insertions(+)

diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index f9333c9..c7753ba 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -377,11 +377,50 @@ static int udp4_gro_complete(struct sk_buff *skb, int 
nhoff)
        return udp_gro_complete(skb, nhoff, udp4_lib_lookup_skb);
 }
 
+/* Assumes rcu lock is held */
+static int udp4_flow_dissect(const struct sk_buff *skb, void *data, int hlen,
+                            int *nhoff, u8 *ip_proto, __be16 *proto,
+                            struct flow_dissector_key_addrs *key_addrs)
+{
+       u16 _ports[2], *ports;
+       struct net *net;
+       struct sock *sk;
+       int dif = -1;
+
+       /* See if there is a flow dissector in the UDP socket */
+
+       if (skb->dev) {
+               net = dev_net(skb->dev);
+               dif = skb->dev->ifindex;
+       } else if (skb->sk) {
+               net = sock_net(skb->sk);
+       } else {
+               return FLOW_DIS_RET_PASS;
+       }
+
+       ports = __skb_header_pointer(skb, *nhoff, sizeof(_ports),
+                                    data, hlen, &_ports);
+       if (!ports)
+               return FLOW_DIS_RET_BAD;
+
+       sk = udp4_lib_lookup_noref(net,
+                                  key_addrs->v4addrs.src, ports[0],
+                                  key_addrs->v4addrs.dst, ports[1],
+                                  dif);
+
+       if (sk && udp_sk(sk)->flow_dissect)
+               return udp_sk(sk)->flow_dissect(sk, skb, data, hlen, nhoff,
+                                               ip_proto, proto);
+       else
+               return FLOW_DIS_RET_PASS;
+}
+
 static const struct net_offload udpv4_offload = {
        .callbacks = {
                .gso_segment = udp4_ufo_fragment,
                .gro_receive  = udp4_gro_receive,
                .gro_complete = udp4_gro_complete,
+               .flow_dissect = udp4_flow_dissect,
        },
 };
 
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c
index ac858c4..b3f4a6c 100644
--- a/net/ipv6/udp_offload.c
+++ b/net/ipv6/udp_offload.c
@@ -163,11 +163,49 @@ static int udp6_gro_complete(struct sk_buff *skb, int 
nhoff)
        return udp_gro_complete(skb, nhoff, udp6_lib_lookup_skb);
 }
 
+/* Assumes rcu lock is held */
+static int udp6_flow_dissect(const struct sk_buff *skb, void *data, int hlen,
+                            int *nhoff, u8 *ip_proto, __be16 *proto,
+                            const struct flow_dissector_key_addrs *key_addrs)
+{
+       u16 _ports[2], *ports;
+       struct net *net;
+       struct sock *sk;
+       int dif = -1;
+
+       /* See if there is a flow dissector in the UDP socket */
+
+       if (skb->dev) {
+               net = dev_net(skb->dev);
+               dif = skb->dev->ifindex;
+       } else if (skb->sk) {
+               net = sock_net(skb->sk);
+       } else {
+               return FLOW_DIS_RET_PASS;
+       }
+
+       ports = __skb_header_pointer(skb, *nhoff, sizeof(_ports),
+                                    data, hlen, &_ports);
+       if (!ports)
+               return FLOW_DIS_RET_BAD;
+
+       sk = udp6_lib_lookup_noref(net,
+                                  &key_addrs->v6addrs.src, ports[0],
+                                  &key_addrs->v6addrs.dst, ports[1],
+                                  dif);
+
+       if (sk && udp_sk(sk)->flow_dissect)
+               return udp_sk(sk)->flow_dissect(sk, skb, data, hlen, nhoff,
+                                               ip_proto, proto);
+       return FLOW_DIS_RET_PASS;
+}
+
 static const struct net_offload udpv6_offload = {
        .callbacks = {
                .gso_segment    =       udp6_ufo_fragment,
                .gro_receive    =       udp6_gro_receive,
                .gro_complete   =       udp6_gro_complete,
+               .flow_dissect   =       udp6_flow_dissect,
        },
 };
 
-- 
2.9.3

Reply via email to