act_ct: Support GRE offload
authorToshiaki Makita <toshiaki.makita1@gmail.com>
Fri, 25 Feb 2022 01:53:08 +0000 (10:53 +0900)
committerPablo Neira Ayuso <pablo@netfilter.org>
Thu, 3 Mar 2022 14:20:51 +0000 (15:20 +0100)
Support GREv0 without NAT.

Signed-off-by: Toshiaki Makita <toshiaki.makita1@gmail.com>
Acked-by: Paul Blakey <paulb@nvidia.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
net/sched/act_ct.c

index 5234e8b..bed0b10 100644 (file)
@@ -415,6 +415,19 @@ static void tcf_ct_flow_table_process_conn(struct tcf_ct_flow_table *ct_ft,
                break;
        case IPPROTO_UDP:
                break;
+#ifdef CONFIG_NF_CT_PROTO_GRE
+       case IPPROTO_GRE: {
+               struct nf_conntrack_tuple *tuple;
+
+               if (ct->status & IPS_NAT_MASK)
+                       return;
+               tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
+               /* No support for GRE v1 */
+               if (tuple->src.u.gre.key || tuple->dst.u.gre.key)
+                       return;
+               break;
+       }
+#endif
        default:
                return;
        }
@@ -434,6 +447,8 @@ tcf_ct_flow_table_fill_tuple_ipv4(struct sk_buff *skb,
        struct flow_ports *ports;
        unsigned int thoff;
        struct iphdr *iph;
+       size_t hdrsize;
+       u8 ipproto;
 
        if (!pskb_network_may_pull(skb, sizeof(*iph)))
                return false;
@@ -445,29 +460,54 @@ tcf_ct_flow_table_fill_tuple_ipv4(struct sk_buff *skb,
            unlikely(thoff != sizeof(struct iphdr)))
                return false;
 
-       if (iph->protocol != IPPROTO_TCP &&
-           iph->protocol != IPPROTO_UDP)
+       ipproto = iph->protocol;
+       switch (ipproto) {
+       case IPPROTO_TCP:
+               hdrsize = sizeof(struct tcphdr);
+               break;
+       case IPPROTO_UDP:
+               hdrsize = sizeof(*ports);
+               break;
+#ifdef CONFIG_NF_CT_PROTO_GRE
+       case IPPROTO_GRE:
+               hdrsize = sizeof(struct gre_base_hdr);
+               break;
+#endif
+       default:
                return false;
+       }
 
        if (iph->ttl <= 1)
                return false;
 
-       if (!pskb_network_may_pull(skb, iph->protocol == IPPROTO_TCP ?
-                                       thoff + sizeof(struct tcphdr) :
-                                       thoff + sizeof(*ports)))
+       if (!pskb_network_may_pull(skb, thoff + hdrsize))
                return false;
 
-       iph = ip_hdr(skb);
-       if (iph->protocol == IPPROTO_TCP)
+       switch (ipproto) {
+       case IPPROTO_TCP:
                *tcph = (void *)(skb_network_header(skb) + thoff);
+               fallthrough;
+       case IPPROTO_UDP:
+               ports = (struct flow_ports *)(skb_network_header(skb) + thoff);
+               tuple->src_port = ports->source;
+               tuple->dst_port = ports->dest;
+               break;
+       case IPPROTO_GRE: {
+               struct gre_base_hdr *greh;
+
+               greh = (struct gre_base_hdr *)(skb_network_header(skb) + thoff);
+               if ((greh->flags & GRE_VERSION) != GRE_VERSION_0)
+                       return false;
+               break;
+       }
+       }
+
+       iph = ip_hdr(skb);
 
-       ports = (struct flow_ports *)(skb_network_header(skb) + thoff);
        tuple->src_v4.s_addr = iph->saddr;
        tuple->dst_v4.s_addr = iph->daddr;
-       tuple->src_port = ports->source;
-       tuple->dst_port = ports->dest;
        tuple->l3proto = AF_INET;
-       tuple->l4proto = iph->protocol;
+       tuple->l4proto = ipproto;
 
        return true;
 }
@@ -480,36 +520,63 @@ tcf_ct_flow_table_fill_tuple_ipv6(struct sk_buff *skb,
        struct flow_ports *ports;
        struct ipv6hdr *ip6h;
        unsigned int thoff;
+       size_t hdrsize;
+       u8 nexthdr;
 
        if (!pskb_network_may_pull(skb, sizeof(*ip6h)))
                return false;
 
        ip6h = ipv6_hdr(skb);
+       thoff = sizeof(*ip6h);
 
-       if (ip6h->nexthdr != IPPROTO_TCP &&
-           ip6h->nexthdr != IPPROTO_UDP)
-               return false;
+       nexthdr = ip6h->nexthdr;
+       switch (nexthdr) {
+       case IPPROTO_TCP:
+               hdrsize = sizeof(struct tcphdr);
+               break;
+       case IPPROTO_UDP:
+               hdrsize = sizeof(*ports);
+               break;
+#ifdef CONFIG_NF_CT_PROTO_GRE
+       case IPPROTO_GRE:
+               hdrsize = sizeof(struct gre_base_hdr);
+               break;
+#endif
+       default:
+               return -1;
+       }
 
        if (ip6h->hop_limit <= 1)
                return false;
 
-       thoff = sizeof(*ip6h);
-       if (!pskb_network_may_pull(skb, ip6h->nexthdr == IPPROTO_TCP ?
-                                       thoff + sizeof(struct tcphdr) :
-                                       thoff + sizeof(*ports)))
+       if (!pskb_network_may_pull(skb, thoff + hdrsize))
                return false;
 
-       ip6h = ipv6_hdr(skb);
-       if (ip6h->nexthdr == IPPROTO_TCP)
+       switch (nexthdr) {
+       case IPPROTO_TCP:
                *tcph = (void *)(skb_network_header(skb) + thoff);
+               fallthrough;
+       case IPPROTO_UDP:
+               ports = (struct flow_ports *)(skb_network_header(skb) + thoff);
+               tuple->src_port = ports->source;
+               tuple->dst_port = ports->dest;
+               break;
+       case IPPROTO_GRE: {
+               struct gre_base_hdr *greh;
+
+               greh = (struct gre_base_hdr *)(skb_network_header(skb) + thoff);
+               if ((greh->flags & GRE_VERSION) != GRE_VERSION_0)
+                       return false;
+               break;
+       }
+       }
+
+       ip6h = ipv6_hdr(skb);
 
-       ports = (struct flow_ports *)(skb_network_header(skb) + thoff);
        tuple->src_v6 = ip6h->saddr;
        tuple->dst_v6 = ip6h->daddr;
-       tuple->src_port = ports->source;
-       tuple->dst_port = ports->dest;
        tuple->l3proto = AF_INET6;
-       tuple->l4proto = ip6h->nexthdr;
+       tuple->l4proto = nexthdr;
 
        return true;
 }