[ovs-dev,RFC,v2,6/8] netdev-offload-tc: Add conntrack support
diff mbox series

Message ID 1562250507-20335-7-git-send-email-paulb@mellanox.com
State New
Headers show
Series
  • Introduce connection tracking tc offload
Related show

Commit Message

Paul Blakey July 4, 2019, 2:28 p.m. UTC
Zone and ct_state first.

Signed-off-by: Paul Blakey <paulb@mellanox.com>
---
 lib/dpif-netlink.c      |   2 +
 lib/netdev-offload-tc.c | 133 ++++++++++++++++++++++++++++++++++++++++++++----
 lib/tc.c                | 108 +++++++++++++++++++++++++++++++++++++++
 lib/tc.h                |  11 ++++
 4 files changed, 244 insertions(+), 10 deletions(-)

Patch
diff mbox series

diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
index 3fd94bb..30378d3 100644
--- a/lib/dpif-netlink.c
+++ b/lib/dpif-netlink.c
@@ -1599,6 +1599,8 @@  dpif_netlink_netdev_match_to_dpif_flow(struct match *match,
         .support = {
             .max_vlan_headers = 2,
             .recirc = true,
+            .ct_state = true,
+            .ct_zone = true,
         },
     };
     size_t offset;
diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c
index 7bcedc8..1071b9a 100644
--- a/lib/netdev-offload-tc.c
+++ b/lib/netdev-offload-tc.c
@@ -600,6 +600,32 @@  parse_tc_flower_to_match(struct tc_flower *flower,
             match_set_tp_dst_masked(match, key->sctp_dst, mask->sctp_dst);
             match_set_tp_src_masked(match, key->sctp_src, mask->sctp_src);
         }
+
+        if (mask->ct_state) {
+            uint8_t ct_statev = 0, ct_statem = 0;
+
+            if (mask->ct_state & TCA_FLOWER_KEY_CT_FLAGS_NEW) {
+                if (key->ct_state & TCA_FLOWER_KEY_CT_FLAGS_NEW)
+                    ct_statev |= OVS_CS_F_NEW;
+                ct_statem |= OVS_CS_F_NEW;
+            }
+
+            if (mask->ct_state & TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED) {
+                if (key->ct_state & TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED)
+                    ct_statev |= OVS_CS_F_ESTABLISHED;
+                ct_statem |= OVS_CS_F_ESTABLISHED;
+            }
+
+            if (mask->ct_state & TCA_FLOWER_KEY_CT_FLAGS_TRACKED) {
+                if (key->ct_state & TCA_FLOWER_KEY_CT_FLAGS_TRACKED)
+                    ct_statev |= OVS_CS_F_TRACKED;
+                ct_statem |= OVS_CS_F_TRACKED;
+            }
+
+            match_set_ct_state_masked(match, ct_statev, ct_statem);
+        }
+
+        match_set_ct_zone_masked(match, key->ct_zone, mask->ct_zone);
     }
 
     if (flower->tunnel) {
@@ -713,6 +739,27 @@  parse_tc_flower_to_match(struct tc_flower *flower,
                 nl_msg_put_u32(buf, OVS_ACTION_ATTR_OUTPUT, odp_to_u32(outport));
             }
             break;
+            case TC_ACT_CT: {
+                size_t ct_offset;
+
+                if (action->ct.clear) {
+                    nl_msg_put_flag(buf, OVS_ACTION_ATTR_CT_CLEAR);
+                    break;
+                }
+
+                ct_offset = nl_msg_start_nested(buf, OVS_ACTION_ATTR_CT);
+
+                if (action->ct.commit) {
+                    nl_msg_put_flag(buf, OVS_CT_ATTR_COMMIT);
+                }
+
+                if (action->ct.zone) {
+                    nl_msg_put_u16(buf, OVS_CT_ATTR_ZONE, action->ct.zone);
+                }
+
+                nl_msg_end_nested(buf, ct_offset);
+            }
+            break;
             case TC_ACT_GOTO: {
                 nl_msg_put_u32(buf, OVS_ACTION_ATTR_RECIRC, action->chain);
             }
@@ -785,6 +832,34 @@  netdev_tc_flow_dump_next(struct netdev_flow_dump *dump,
 }
 
 static int
+parse_put_flow_ct_action(struct tc_flower *flower,
+                         struct tc_action *action,
+                         const struct nlattr *ct,
+                         size_t ct_len)
+{
+        const struct nlattr *ct_attr;
+        size_t ct_left;
+        int err;
+
+        NL_ATTR_FOR_EACH_UNSAFE(ct_attr, ct_left, ct, ct_len) {
+            switch (nl_attr_type(ct_attr)) {
+                case OVS_CT_ATTR_COMMIT: {
+                        action->ct.commit = true;
+                }
+                break;
+                case OVS_CT_ATTR_ZONE: {
+                    action->ct.zone = nl_attr_get_u16(ct_attr);
+                }
+                break;
+            }
+        }
+
+        action->type = TC_ACT_CT;
+        flower->action_count++;
+        return 0;
+}
+
+static int
 parse_put_flow_set_masked_action(struct tc_flower *flower,
                                  struct tc_action *action,
                                  const struct nlattr *set,
@@ -962,16 +1037,6 @@  test_key_and_mask(struct match *match)
         return EOPNOTSUPP;
     }
 
-    if (mask->ct_state) {
-        VLOG_DBG_RL(&rl, "offloading attribute ct_state isn't supported");
-        return EOPNOTSUPP;
-    }
-
-    if (mask->ct_zone) {
-        VLOG_DBG_RL(&rl, "offloading attribute ct_zone isn't supported");
-        return EOPNOTSUPP;
-    }
-
     if (mask->ct_mark) {
         VLOG_DBG_RL(&rl, "offloading attribute ct_mark isn't supported");
         return EOPNOTSUPP;
@@ -1306,6 +1371,42 @@  netdev_tc_flow_put(struct netdev *netdev, struct match *match,
         }
     }
 
+    if (mask->ct_state) {
+        if (mask->ct_state & OVS_CS_F_NEW) {
+            if (key->ct_state & OVS_CS_F_NEW) {
+                flower.key.ct_state |= TCA_FLOWER_KEY_CT_FLAGS_NEW;
+            }
+            flower.mask.ct_state |= TCA_FLOWER_KEY_CT_FLAGS_NEW;
+        }
+
+        if (mask->ct_state & OVS_CS_F_ESTABLISHED) {
+            if (key->ct_state & OVS_CS_F_ESTABLISHED) {
+                flower.key.ct_state |= TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED;
+            }
+            flower.mask.ct_state |= TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED;
+        }
+
+        if (mask->ct_state & OVS_CS_F_TRACKED) {
+            if (key->ct_state & OVS_CS_F_TRACKED) {
+                flower.key.ct_state |= TCA_FLOWER_KEY_CT_FLAGS_TRACKED;
+            }
+            flower.mask.ct_state |= TCA_FLOWER_KEY_CT_FLAGS_TRACKED;
+        }
+
+        if (flower.key.ct_state & TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED) {
+            flower.key.ct_state &= ~(TCA_FLOWER_KEY_CT_FLAGS_NEW);
+            flower.mask.ct_state &= ~(TCA_FLOWER_KEY_CT_FLAGS_NEW);
+        }
+
+        mask->ct_state = 0;
+    }
+
+    if (mask->ct_zone) {
+        flower.key.ct_zone = key->ct_zone;
+        flower.mask.ct_zone = mask->ct_zone;
+        mask->ct_zone = 0;
+    }
+
     err = test_key_and_mask(match);
     if (err) {
         return err;
@@ -1358,6 +1459,18 @@  netdev_tc_flow_put(struct netdev *netdev, struct match *match,
             if (err) {
                 return err;
             }
+        } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_CT) {
+            const struct nlattr *ct = nl_attr_get(nla);
+            const size_t ct_len = nl_attr_get_size(nla);
+
+            err = parse_put_flow_ct_action(&flower, action, ct, ct_len);
+            if (err) {
+                return err;
+            }
+        } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_CT_CLEAR) {
+            action->type = TC_ACT_CT;
+            action->ct.clear = true;
+            flower.action_count++;
         } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_RECIRC) {
             action->type = TC_ACT_GOTO;
             action->chain = nl_attr_get_u32(nla);
diff --git a/lib/tc.c b/lib/tc.c
index fa23545..99d65a0 100644
--- a/lib/tc.c
+++ b/lib/tc.c
@@ -29,6 +29,7 @@ 
 #include <linux/tc_act/tc_skbedit.h>
 #include <linux/tc_act/tc_tunnel_key.h>
 #include <linux/tc_act/tc_vlan.h>
+#include <linux/tc_act/tc_ct.h>
 #include <linux/gen_stats.h>
 #include <net/if.h>
 #include <unistd.h>
@@ -398,6 +399,10 @@  static const struct nl_policy tca_flower_policy[] = {
     [TCA_FLOWER_KEY_ENC_OPTS] = { .type = NL_A_NESTED, .optional = true, },
     [TCA_FLOWER_KEY_ENC_OPTS_MASK] = { .type = NL_A_NESTED,
                                        .optional = true, },
+    [TCA_FLOWER_KEY_CT_STATE] = { .type = NL_A_U16, .optional = true, },
+    [TCA_FLOWER_KEY_CT_STATE_MASK] = { .type = NL_A_U16, .optional = true, },
+    [TCA_FLOWER_KEY_CT_ZONE] = { .type = NL_A_U16, .optional = true, },
+    [TCA_FLOWER_KEY_CT_ZONE_MASK] = { .type = NL_A_U16, .optional = true, },
 };
 
 static void
@@ -798,6 +803,16 @@  nl_parse_flower_ip(struct nlattr **attrs, struct tc_flower *flower) {
         key->ip_tos = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_TOS]);
         mask->ip_tos = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_TOS_MASK]);
     }
+
+    if (attrs[TCA_FLOWER_KEY_CT_STATE_MASK]) {
+        key->ct_state = nl_attr_get_u16(attrs[TCA_FLOWER_KEY_CT_STATE]);
+        mask->ct_state = nl_attr_get_u16(attrs[TCA_FLOWER_KEY_CT_STATE_MASK]);
+    }
+
+    if (attrs[TCA_FLOWER_KEY_CT_ZONE_MASK]) {
+        key->ct_zone = nl_attr_get_u16(attrs[TCA_FLOWER_KEY_CT_ZONE]);
+        mask->ct_zone = nl_attr_get_u16(attrs[TCA_FLOWER_KEY_CT_ZONE_MASK]);
+    }
 }
 
 static enum tc_offloaded_state
@@ -1218,6 +1233,53 @@  nl_parse_act_mirred(struct nlattr *options, struct tc_flower *flower)
     return 0;
 }
 
+static const struct nl_policy ct_policy[] = {
+    [TCA_CT_PARMS] = { .type = NL_A_UNSPEC,
+                              .min_len = sizeof(struct tc_ct),
+                              .optional = false, },
+    [TCA_CT_ACTION] = { .type = NL_A_U16,
+                         .optional = true, },
+    [TCA_CT_ZONE] = { .type = NL_A_U16,
+                      .optional = true, },
+};
+
+static int
+nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower)
+{
+    struct nlattr *ct_attrs[ARRAY_SIZE(ct_policy)];
+    const struct nlattr *ct_parms;
+    struct tc_action *action;
+    const struct tc_ct *ct;
+    uint16_t ct_action = 0;
+
+    if (!nl_parse_nested(options, ct_policy, ct_attrs,
+                         ARRAY_SIZE(ct_policy))) {
+        VLOG_ERR_RL(&error_rl, "failed to parse ct action options");
+        return EPROTO;
+    }
+
+    ct_parms = ct_attrs[TCA_CT_PARMS];
+    ct = nl_attr_get_unspec(ct_parms, sizeof *ct);
+
+    if (ct_attrs[TCA_CT_ACTION])
+        ct_action = nl_attr_get_u16(ct_attrs[TCA_CT_ACTION]);
+
+    action = &flower->actions[flower->action_count++];
+    action->ct.clear = ct_action & TCA_CT_ACT_CLEAR;
+    if (!action->ct.clear) {
+        struct nlattr *zone = ct_attrs[TCA_CT_ZONE];
+
+        action->ct.commit = ct_action & TCA_CT_ACT_COMMIT;
+        action->ct.force = ct_action & TCA_CT_ACT_FORCE;
+
+        action->ct.zone = zone ? nl_attr_get_u16(zone) : 0;
+
+    }
+    action->type = TC_ACT_CT;
+
+    return 0;
+}
+
 static const struct nl_policy vlan_policy[] = {
     [TCA_VLAN_PARMS] = { .type = NL_A_UNSPEC,
                          .min_len = sizeof(struct tc_vlan),
@@ -1353,6 +1415,8 @@  nl_parse_single_action(struct nlattr *action, struct tc_flower *flower)
         nl_parse_act_csum(act_options, flower);
     } else if (!strcmp(act_kind, "skbedit")) {
         /* Added for TC rule only (not in OvS rule) so ignore. */
+    } else if (!strcmp(act_kind, "ct")) {
+        nl_parse_act_ct(act_options, flower);
     } else {
         VLOG_ERR_RL(&error_rl, "unknown tc action kind: %s", act_kind);
         err = EINVAL;
@@ -1748,6 +1812,40 @@  nl_msg_put_act_gact(struct ofpbuf *request, uint32_t chain)
 }
 
 static void
+nl_msg_put_act_ct(struct ofpbuf *request, struct tc_action *action)
+{
+    uint16_t ct_action = 0;
+    size_t offset;
+
+    nl_msg_put_string(request, TCA_ACT_KIND, "ct");
+    offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED);
+    {
+        struct tc_ct ct = {
+                .action = TC_ACT_PIPE,
+        };
+
+        if (!action->ct.clear) {
+            if (action->ct.zone) {
+                nl_msg_put_u16(request, TCA_CT_ZONE, action->ct.zone);
+            }
+
+            if (action->ct.commit) {
+                ct_action = TCA_CT_ACT_COMMIT;
+                if (action->ct.force) {
+                    ct_action |= TCA_CT_ACT_FORCE;
+                }
+            }
+        } else {
+            ct_action = TCA_CT_ACT_CLEAR;
+        }
+
+        nl_msg_put_u16(request, TCA_CT_ACTION, ct_action);
+        nl_msg_put_unspec(request, TCA_CT_PARMS, &ct, sizeof ct);
+    }
+    nl_msg_end_nested(request, offset);
+}
+
+static void
 nl_msg_put_act_skbedit_to_host(struct ofpbuf *request)
 {
     size_t offset;
@@ -2058,6 +2156,13 @@  nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
                 nl_msg_end_nested(request, act_offset);
             }
             break;
+            case TC_ACT_CT: {
+                act_offset = nl_msg_start_nested(request, act_index++);
+                nl_msg_put_act_ct(request, action);
+                nl_msg_put_act_cookie(request, &flower->act_cookie);
+                nl_msg_end_nested(request, act_offset);
+            }
+            break;
             }
         }
     }
@@ -2224,6 +2329,9 @@  nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower)
             FLOWER_PUT_MASKED_VALUE(sctp_src, TCA_FLOWER_KEY_SCTP_SRC);
             FLOWER_PUT_MASKED_VALUE(sctp_dst, TCA_FLOWER_KEY_SCTP_DST);
         }
+
+        FLOWER_PUT_MASKED_VALUE(ct_state, TCA_FLOWER_KEY_CT_STATE);
+        FLOWER_PUT_MASKED_VALUE(ct_zone, TCA_FLOWER_KEY_CT_ZONE);
     }
 
     if (host_eth_type == ETH_P_IP) {
diff --git a/lib/tc.h b/lib/tc.h
index fe8da4a..9ff9c0b 100644
--- a/lib/tc.h
+++ b/lib/tc.h
@@ -116,6 +116,9 @@  struct tc_flower_key {
     uint8_t ip_ttl;
     uint8_t ip_tos;
 
+    uint16_t ct_state;
+    uint16_t ct_zone;
+
     struct {
         ovs_be32 ipv4_src;
         ovs_be32 ipv4_dst;
@@ -154,6 +157,7 @@  enum tc_action_type {
     TC_ACT_VLAN_POP,
     TC_ACT_VLAN_PUSH,
     TC_ACT_GOTO,
+    TC_ACT_CT,
 };
 
 struct tc_action {
@@ -189,6 +193,13 @@  struct tc_action {
             } ipv6;
             struct tun_metadata data;
         } encap;
+
+        struct {
+            uint16_t zone;
+            bool clear;
+            bool force;
+            bool commit;
+        } ct;
      };
 
      enum tc_action_type type;