netfilter: flowtable: add tunnel match offload support
authorwenxu <wenxu@ucloud.cn>
Mon, 24 Feb 2020 04:22:54 +0000 (05:22 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Sun, 15 Mar 2020 14:26:17 +0000 (15:26 +0100)
This patch support both ipv4 and ipv6 tunnel_id, tunnel_src and
tunnel_dst match for flowtable offload

Signed-off-by: wenxu <wenxu@ucloud.cn>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/net/netfilter/nf_flow_table.h
net/netfilter/nf_flow_table_offload.c

index 6890f1ca3e31b5b6a1f570d1c0ef422e821356b9..f523ea87b6ae7e325b05d41570e30f7d21e9f819 100644 (file)
@@ -19,11 +19,17 @@ enum flow_offload_tuple_dir;
 struct nf_flow_key {
        struct flow_dissector_key_meta                  meta;
        struct flow_dissector_key_control               control;
+       struct flow_dissector_key_control               enc_control;
        struct flow_dissector_key_basic                 basic;
        union {
                struct flow_dissector_key_ipv4_addrs    ipv4;
                struct flow_dissector_key_ipv6_addrs    ipv6;
        };
+       struct flow_dissector_key_keyid                 enc_key_id;
+       union {
+               struct flow_dissector_key_ipv4_addrs    enc_ipv4;
+               struct flow_dissector_key_ipv6_addrs    enc_ipv6;
+       };
        struct flow_dissector_key_tcp                   tcp;
        struct flow_dissector_key_ports                 tp;
 } __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */
index f60f01e929b83d34a760846e039b8c8fbd82999a..3101b35eac80a674abfcb4ef4221a35ff01c30cf 100644 (file)
@@ -28,11 +28,61 @@ struct flow_offload_work {
        (__match)->dissector.offset[__type] =           \
                offsetof(struct nf_flow_key, __field)
 
+static void nf_flow_rule_lwt_match(struct nf_flow_match *match,
+                                  struct ip_tunnel_info *tun_info)
+{
+       struct nf_flow_key *mask = &match->mask;
+       struct nf_flow_key *key = &match->key;
+       unsigned int enc_keys;
+
+       if (!tun_info || !(tun_info->mode & IP_TUNNEL_INFO_TX))
+               return;
+
+       NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_ENC_CONTROL, enc_control);
+       NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_ENC_KEYID, enc_key_id);
+       key->enc_key_id.keyid = tunnel_id_to_key32(tun_info->key.tun_id);
+       mask->enc_key_id.keyid = 0xffffffff;
+       enc_keys = BIT(FLOW_DISSECTOR_KEY_ENC_KEYID) |
+                  BIT(FLOW_DISSECTOR_KEY_ENC_CONTROL);
+
+       if (ip_tunnel_info_af(tun_info) == AF_INET) {
+               NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS,
+                                 enc_ipv4);
+               key->enc_ipv4.src = tun_info->key.u.ipv4.dst;
+               key->enc_ipv4.dst = tun_info->key.u.ipv4.src;
+               if (key->enc_ipv4.src)
+                       mask->enc_ipv4.src = 0xffffffff;
+               if (key->enc_ipv4.dst)
+                       mask->enc_ipv4.dst = 0xffffffff;
+               enc_keys |= BIT(FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS);
+               key->enc_control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
+       } else {
+               memcpy(&key->enc_ipv6.src, &tun_info->key.u.ipv6.dst,
+                      sizeof(struct in6_addr));
+               memcpy(&key->enc_ipv6.dst, &tun_info->key.u.ipv6.src,
+                      sizeof(struct in6_addr));
+               if (memcmp(&key->enc_ipv6.src, &in6addr_any,
+                          sizeof(struct in6_addr)))
+                       memset(&key->enc_ipv6.src, 0xff,
+                              sizeof(struct in6_addr));
+               if (memcmp(&key->enc_ipv6.dst, &in6addr_any,
+                          sizeof(struct in6_addr)))
+                       memset(&key->enc_ipv6.dst, 0xff,
+                              sizeof(struct in6_addr));
+               enc_keys |= BIT(FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS);
+               key->enc_control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
+       }
+
+       match->dissector.used_keys |= enc_keys;
+}
+
 static int nf_flow_rule_match(struct nf_flow_match *match,
-                             const struct flow_offload_tuple *tuple)
+                             const struct flow_offload_tuple *tuple,
+                             struct dst_entry *other_dst)
 {
        struct nf_flow_key *mask = &match->mask;
        struct nf_flow_key *key = &match->key;
+       struct ip_tunnel_info *tun_info;
 
        NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_META, meta);
        NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_CONTROL, control);
@@ -42,6 +92,11 @@ static int nf_flow_rule_match(struct nf_flow_match *match,
        NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_TCP, tcp);
        NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_PORTS, tp);
 
+       if (other_dst->lwtstate) {
+               tun_info = lwt_tun_info(other_dst->lwtstate);
+               nf_flow_rule_lwt_match(match, tun_info);
+       }
+
        key->meta.ingress_ifindex = tuple->iifidx;
        mask->meta.ingress_ifindex = 0xffffffff;
 
@@ -480,6 +535,7 @@ nf_flow_offload_rule_alloc(struct net *net,
        const struct flow_offload *flow = offload->flow;
        const struct flow_offload_tuple *tuple;
        struct nf_flow_rule *flow_rule;
+       struct dst_entry *other_dst;
        int err = -ENOMEM;
 
        flow_rule = kzalloc(sizeof(*flow_rule), GFP_KERNEL);
@@ -495,7 +551,8 @@ nf_flow_offload_rule_alloc(struct net *net,
        flow_rule->rule->match.key = &flow_rule->match.key;
 
        tuple = &flow->tuplehash[dir].tuple;
-       err = nf_flow_rule_match(&flow_rule->match, tuple);
+       other_dst = flow->tuplehash[!dir].tuple.dst_cache;
+       err = nf_flow_rule_match(&flow_rule->match, tuple, other_dst);
        if (err < 0)
                goto err_flow_match;