Work-in-Progress

Attach conntrack template to packet if a non standard zone setting has
been provided. Conntrack template is stored in action.
---
 datapath/actions.c                                | 12 ++-
 datapath/datapath.c                               | 18 +++--
 datapath/flow.h                                   |  6 ++
 datapath/flow_netlink.c                           | 98 ++++++++++++++++++++---
 datapath/flow_netlink.h                           |  3 +-
 datapath/flow_table.c                             |  3 +-
 datapath/linux/compat/include/linux/openvswitch.h | 14 +++-
 lib/odp-util.c                                    | 29 ++++++-
 8 files changed, 157 insertions(+), 26 deletions(-)

diff --git a/datapath/actions.c b/datapath/actions.c
index 46bdf53..81cc8d2 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -825,10 +825,11 @@ static void execute_hash(struct sk_buff *skb, const 
struct nlattr *attr)
        key->ovs_flow_hash = hash;
 }
 
-static int conntrack(struct sk_buff *skb, uint16_t zone)
+static int conntrack(struct sk_buff *skb, const struct ovs_conntrack_info 
*info)
 {
        struct sw_flow_key *key = OVS_CB(skb)->pkt_key;
        int nh_ofs = skb_network_offset(skb);
+       struct nf_conn *ct = info->ct;
        struct vport *vport;
        struct net *net;
 
@@ -850,6 +851,13 @@ static int conntrack(struct sk_buff *skb, uint16_t zone)
        /* The conntrack module expects to be working at L3. */
        skb_pull(skb, nh_ofs);
 
+       /* Associate skb with specified zone */
+       if (ct) {
+               atomic_inc(&ct->ct_general.use);
+               skb->nfct = &ct->ct_general;
+               skb->nfctinfo = IP_CT_NEW;
+       }
+
        /* xxx What's the best return val? */
        if (nf_conntrack_in(net, PF_INET, NF_INET_PRE_ROUTING, skb) != 
NF_ACCEPT)
                return EINVAL;
@@ -1029,7 +1037,7 @@ static int do_execute_actions(struct datapath *dp, struct 
sk_buff *skb,
                        break;
 
                case OVS_ACTION_ATTR_CONNTRACK:
-                       err = conntrack(skb, nla_get_u16(a));
+                       err = conntrack(skb, nla_data(a));
                        break;
                }
 
diff --git a/datapath/datapath.c b/datapath/datapath.c
index 982ed9d..f90a34b 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -530,6 +530,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, 
struct genl_info *info)
        struct datapath *dp;
        struct ethhdr *eth;
        struct vport *input_vport;
+       struct net *net = sock_net(skb->sk);
        int len;
        int err;
 
@@ -569,7 +570,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, 
struct genl_info *info)
        if (err)
                goto err_flow_free;
 
-       err = ovs_nla_copy_actions(a[OVS_PACKET_ATTR_ACTIONS],
+       err = ovs_nla_copy_actions(net, a[OVS_PACKET_ATTR_ACTIONS],
                                   &flow->key, &acts);
        if (err)
                goto err_flow_free;
@@ -861,6 +862,7 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct 
genl_info *info)
        struct datapath *dp;
        struct sw_flow_actions *acts;
        struct sw_flow_match match;
+       struct net *net = sock_net(skb->sk);
        int error;
 
        /* Must have key and actions. */
@@ -892,7 +894,7 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct 
genl_info *info)
        ovs_flow_mask_key(&new_flow->key, &new_flow->unmasked_key, &mask);
 
        /* Validate actions. */
-       error = ovs_nla_copy_actions(a[OVS_FLOW_ATTR_ACTIONS], &new_flow->key,
+       error = ovs_nla_copy_actions(net, a[OVS_FLOW_ATTR_ACTIONS], 
&new_flow->key,
                                     &acts);
        if (error) {
                OVS_NLERR("Flow actions may not be safe on all matching 
packets.\n");
@@ -981,7 +983,7 @@ err_unlock_ovs:
        ovs_unlock();
        kfree_skb(reply);
 err_kfree_acts:
-       kfree(acts);
+       __ovs_nla_free_flow_actions(acts);
 err_kfree_flow:
        ovs_flow_free(new_flow, false);
 error:
@@ -989,7 +991,8 @@ error:
 }
 
 /* Factor out action copy to avoid "Wframe-larger-than=1024" warning. */
-static struct sw_flow_actions *get_flow_actions(const struct nlattr *a,
+static struct sw_flow_actions *get_flow_actions(struct net *net,
+                                               const struct nlattr *a,
                                                const struct sw_flow_key *key,
                                                const struct sw_flow_mask *mask)
 {
@@ -998,7 +1001,7 @@ static struct sw_flow_actions *get_flow_actions(const 
struct nlattr *a,
        int error;
 
        ovs_flow_mask_key(&masked_key, key, mask);
-       error = ovs_nla_copy_actions(a, &masked_key, &acts);
+       error = ovs_nla_copy_actions(net, a, &masked_key, &acts);
        if (error) {
                OVS_NLERR("Actions may not be safe on all matching packets.\n");
                return ERR_PTR(error);
@@ -1018,6 +1021,7 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct 
genl_info *info)
        struct datapath *dp;
        struct sw_flow_actions *old_acts = NULL, *acts = NULL;
        struct sw_flow_match match;
+       struct net *net = sock_net(skb->sk);
        int error;
 
        /* Extract key. */
@@ -1035,7 +1039,7 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct 
genl_info *info)
 
        /* Validate actions. */
        if (a[OVS_FLOW_ATTR_ACTIONS]) {
-               acts = get_flow_actions(a[OVS_FLOW_ATTR_ACTIONS], &key, &mask);
+               acts = get_flow_actions(net, a[OVS_FLOW_ATTR_ACTIONS], &key, 
&mask);
                if (IS_ERR(acts)) {
                        error = PTR_ERR(acts);
                        goto error;
@@ -1101,7 +1105,7 @@ err_unlock_ovs:
        ovs_unlock();
        kfree_skb(reply);
 err_kfree_acts:
-       kfree(acts);
+       __ovs_nla_free_flow_actions(acts);
 error:
        return error;
 }
diff --git a/datapath/flow.h b/datapath/flow.h
index 22f2c83..a4b7043 100644
--- a/datapath/flow.h
+++ b/datapath/flow.h
@@ -34,6 +34,7 @@
 
 #include <net/inet_ecn.h>
 #include <net/ip_tunnels.h>
+#include <net/netfilter/nf_conntrack.h>
 
 struct sk_buff;
 
@@ -118,6 +119,11 @@ static inline void ovs_flow_tun_info_init(struct 
ovs_tunnel_info *tun_info,
                                 opts, opts_len);
 }
 
+struct ovs_conntrack_info {
+       u16 zone;
+       struct nf_conn *ct;
+};
+
 #define OVS_SW_FLOW_KEY_METADATA_SIZE                  \
        (offsetof(struct sw_flow_key, recirc_id) +      \
        FIELD_SIZEOF(struct sw_flow_key, recirc_id))
diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c
index e0ed56b..1769c0f 100644
--- a/datapath/flow_netlink.c
+++ b/datapath/flow_netlink.c
@@ -1350,12 +1350,32 @@ static struct sw_flow_actions 
*nla_alloc_flow_actions(int size)
        return sfa;
 }
 
+void __ovs_nla_free_flow_actions(struct sw_flow_actions *acts)
+{
+       int rem, len = acts->actions_len;
+       struct nlattr *a;
+       struct ovs_conntrack_info *ct_info;
+
+       for (a = acts->actions, rem = len; rem > 0;
+            a = nla_next(a, &rem)) {
+               switch (nla_type(a)) {
+               case OVS_ACTION_ATTR_CONNTRACK:
+                       ct_info = nla_data(a);
+                       if (ct_info->ct)
+                               nf_ct_put(ct_info->ct);
+                       break;
+               }
+       }
+
+       kfree(acts);
+}
+
 /* RCU callback used by ovs_nla_free_flow_actions. */
 static void rcu_free_acts_callback(struct rcu_head *rcu)
 {
        struct sw_flow_actions *sf_acts = container_of(rcu,
                        struct sw_flow_actions, rcu);
-       kfree(sf_acts);
+       __ovs_nla_free_flow_actions(sf_acts);
 }
 
 /* Schedules 'sf_acts' to be freed after the next RCU grace period.
@@ -1453,12 +1473,69 @@ static inline void add_nested_action_end(struct 
sw_flow_actions *sfa,
        a->nla_len = sfa->actions_len - st_offset;
 }
 
-static int __ovs_nla_copy_actions(const struct nlattr *attr,
+static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
                                  const struct sw_flow_key *key,
                                  int depth, struct sw_flow_actions **sfa,
                                  __be16 eth_type, __be16 vlan_tci);
 
-static int validate_and_copy_sample(const struct nlattr *attr,
+static int validate_and_copy_conntrack(struct net *net,
+                                      const struct nlattr *attr,
+                                      const struct sw_flow_key *key,
+                                      struct sw_flow_actions **sfa)
+{
+       struct ovs_conntrack_info ct_info;
+       struct nf_conntrack_tuple t;
+       struct nlattr *a;
+       int rem;
+
+       memset(&ct_info, 0, sizeof(ct_info));
+
+       nla_for_each_nested(a, attr, rem) {
+               int type = nla_type(a);
+               static const u32 ovs_ct_attr_lens[OVS_CT_ATTR_MAX + 1] = {
+                       [OVS_CT_ATTR_ZONE] = sizeof(u16),
+               };
+
+               if (type > OVS_CT_ATTR_MAX) {
+                       OVS_NLERR("Unknown conntrack attribute (type=%d, 
max=%d).\n",
+                       type, OVS_CT_ATTR_MAX);
+                       return -EINVAL;
+               }
+
+               if (ovs_ct_attr_lens[type] != nla_len(a) &&
+                   ovs_ct_attr_lens[type] != -1) {
+                       OVS_NLERR("Conntrack attribute type has unexpected "
+                                 " length (type=%d, length=%d, 
expected=%d).\n",
+                                 type, nla_len(a), ovs_ct_attr_lens[type]);
+                       return -EINVAL;
+               }
+
+               switch (type) {
+               case OVS_CT_ATTR_ZONE:
+                       memset(&t, 0, sizeof(t));
+                       ct_info.zone = nla_get_u16(a);
+                       ct_info.ct = nf_conntrack_alloc(net, ct_info.zone, &t, 
&t, GFP_KERNEL);
+                       if (!ct_info.ct)
+                               return -ENOMEM;
+
+                       nf_conntrack_tmpl_insert(net, ct_info.ct);
+                       break;
+               default:
+                       OVS_NLERR("Unknown conntrack attribute (%d).\n", type);
+                       return -EINVAL;
+               }
+       }
+
+       if (rem > 0) {
+               OVS_NLERR("Conntrack attribute has %d unknown bytes.\n", rem);
+               return -EINVAL;
+       }
+
+       return add_action(sfa, OVS_ACTION_ATTR_CONNTRACK, &ct_info,
+                         sizeof(ct_info));
+}
+
+static int validate_and_copy_sample(struct net *net, const struct nlattr *attr,
                                    const struct sw_flow_key *key, int depth,
                                    struct sw_flow_actions **sfa,
                                    __be16 eth_type, __be16 vlan_tci)
@@ -1498,7 +1575,7 @@ static int validate_and_copy_sample(const struct nlattr 
*attr,
        if (st_acts < 0)
                return st_acts;
 
-       err = __ovs_nla_copy_actions(actions, key, depth + 1, sfa,
+       err = __ovs_nla_copy_actions(net, actions, key, depth + 1, sfa,
                                     eth_type, vlan_tci);
        if (err)
                return err;
@@ -1740,7 +1817,7 @@ static int copy_action(const struct nlattr *from,
        return 0;
 }
 
-static int __ovs_nla_copy_actions(const struct nlattr *attr,
+static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
                                  const struct sw_flow_key *key,
                                  int depth, struct sw_flow_actions **sfa,
                                  __be16 eth_type, __be16 vlan_tci)
@@ -1764,7 +1841,7 @@ static int __ovs_nla_copy_actions(const struct nlattr 
*attr,
                        [OVS_ACTION_ATTR_SET] = (u32)-1,
                        [OVS_ACTION_ATTR_SAMPLE] = (u32)-1,
                        [OVS_ACTION_ATTR_HASH] = sizeof(struct ovs_action_hash),
-                       [OVS_ACTION_ATTR_CONNTRACK] = sizeof(u16)
+                       [OVS_ACTION_ATTR_CONNTRACK] = (u32)-1,
                };
                const struct ovs_action_push_vlan *vlan;
                int type = nla_type(a);
@@ -1863,7 +1940,7 @@ static int __ovs_nla_copy_actions(const struct nlattr 
*attr,
                        break;
 
                case OVS_ACTION_ATTR_SAMPLE:
-                       err = validate_and_copy_sample(a, key, depth, sfa,
+                       err = validate_and_copy_sample(net, a, key, depth, sfa,
                                                       eth_type, vlan_tci);
                        if (err)
                                return err;
@@ -1871,6 +1948,7 @@ static int __ovs_nla_copy_actions(const struct nlattr 
*attr,
                        break;
 
                case OVS_ACTION_ATTR_CONNTRACK:
+                       err = validate_and_copy_conntrack(net, a, key, sfa);
                        break;
 
                default:
@@ -1889,7 +1967,7 @@ static int __ovs_nla_copy_actions(const struct nlattr 
*attr,
        return 0;
 }
 
-int ovs_nla_copy_actions(const struct nlattr *attr,
+int ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
                         const struct sw_flow_key *key,
                         struct sw_flow_actions **sfa)
 {
@@ -1899,10 +1977,10 @@ int ovs_nla_copy_actions(const struct nlattr *attr,
        if (IS_ERR(*sfa))
                return PTR_ERR(*sfa);
 
-       err = __ovs_nla_copy_actions(attr, key, 0, sfa, key->eth.type,
+       err = __ovs_nla_copy_actions(net, attr, key, 0, sfa, key->eth.type,
                                     key->eth.tci);
        if (err)
-               kfree(*sfa);
+               __ovs_nla_free_flow_actions(*sfa);
 
        return err;
 }
diff --git a/datapath/flow_netlink.h b/datapath/flow_netlink.h
index 90bbe37..be6bee8 100644
--- a/datapath/flow_netlink.h
+++ b/datapath/flow_netlink.h
@@ -53,12 +53,13 @@ int ovs_nla_get_match(struct sw_flow_match *match,
 int ovs_nla_put_egress_tunnel_key(struct sk_buff *,
                                  const struct ovs_tunnel_info *);
 
-int ovs_nla_copy_actions(const struct nlattr *attr,
+int ovs_nla_copy_actions(struct net*net, const struct nlattr *attr,
                         const struct sw_flow_key *key,
                         struct sw_flow_actions **sfa);
 int ovs_nla_put_actions(const struct nlattr *attr,
                        int len, struct sk_buff *skb);
 
+void __ovs_nla_free_flow_actions(struct sw_flow_actions *);
 void ovs_nla_free_flow_actions(struct sw_flow_actions *);
 
 #endif /* flow_netlink.h */
diff --git a/datapath/flow_table.c b/datapath/flow_table.c
index 10bf830..ea551df 100644
--- a/datapath/flow_table.c
+++ b/datapath/flow_table.c
@@ -45,6 +45,7 @@
 #include <net/ndisc.h>
 
 #include "vlan.h"
+#include "flow_netlink.h"
 
 #define TBL_MIN_BUCKETS                1024
 #define MASK_ARRAY_SIZE_MIN    16
@@ -146,7 +147,7 @@ static void flow_free(struct sw_flow *flow)
 {
        int node;
 
-       kfree((struct sw_flow_actions __force *)flow->sf_acts);
+       __ovs_nla_free_flow_actions((struct sw_flow_actions __force 
*)flow->sf_acts);
        for_each_node(node)
                if (flow->stats[node])
                        kmem_cache_free(flow_stats_cache,
diff --git a/datapath/linux/compat/include/linux/openvswitch.h 
b/datapath/linux/compat/include/linux/openvswitch.h
index 08372c4..f3654de 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -580,6 +580,18 @@ struct ovs_action_hash {
 };
 
 /**
+ * enum ovs_conntrack_attr - Attributes for %OVS_ACTION_ATTR_CONNTRACK action.
+ * @OVS_CT_ATTR_ZONE: u16 connection tracking zone.
+ */
+enum ovs_conntrack_attr {
+       OVS_CT_ATTR_UNSPEC,
+       OVS_CT_ATTR_ZONE,
+       __OVS_CT_ATTR_MAX
+};
+
+#define OVS_CT_ATTR_MAX (__OVS_CT_ATTR_MAX - 1)
+
+/**
  * enum ovs_action_attr - Action types.
  *
  * @OVS_ACTION_ATTR_OUTPUT: Output packet to port.
@@ -633,7 +645,7 @@ enum ovs_action_attr {
                                       * data immediately followed by a mask.
                                       * The data must be zero for the unmasked
                                       * bits. */
-       OVS_ACTION_ATTR_CONNTRACK,    /* u16 zone. */
+       OVS_ACTION_ATTR_CONNTRACK,    /* One nested OVS_CT_ATTR_* */
        __OVS_ACTION_ATTR_MAX
 };
 
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 420017c..061c8d6 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -84,7 +84,7 @@ odp_action_len(uint16_t type)
     case OVS_ACTION_ATTR_SET: return -2;
     case OVS_ACTION_ATTR_SET_MASKED: return -2;
     case OVS_ACTION_ATTR_SAMPLE: return -2;
-    case OVS_ACTION_ATTR_CONNTRACK: return sizeof(uint16_t);
+    case OVS_ACTION_ATTR_CONNTRACK: return -2;
 
     case OVS_ACTION_ATTR_UNSPEC:
     case __OVS_ACTION_ATTR_MAX:
@@ -509,6 +509,23 @@ format_odp_hash_action(struct ds *ds, const struct 
ovs_action_hash *hash_act)
 }
 
 static void
+format_odp_conntrack_action(struct ds *ds, const struct nlattr *attr)
+{
+    static const struct nl_policy ovs_conntrack_policy[] = {
+        [OVS_CT_ATTR_ZONE] = { .type = NL_A_U16 },
+    };
+    struct nlattr *a[ARRAY_SIZE(ovs_conntrack_policy)];
+
+    if (!nl_parse_nested(attr, ovs_conntrack_policy, a, ARRAY_SIZE(a))) {
+        ds_put_cstr(ds, "conntrack(error)");
+        return;
+    }
+
+    ds_put_format(ds, "conntrack(zone=%"PRIu16")",
+                  nl_attr_get_u16(a[OVS_CT_ATTR_ZONE]));
+}
+
+static void
 format_odp_action(struct ds *ds, const struct nlattr *a)
 {
     int expected_len;
@@ -592,8 +609,7 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
         format_odp_sample_action(ds, a);
         break;
     case OVS_ACTION_ATTR_CONNTRACK: {
-        uint16_t zone = nl_attr_get_u16(a);
-        ds_put_format(ds, "conntrack(zone=%"PRIu16")", zone);
+        format_odp_conntrack_action(ds,a);
         break;
     }
     case OVS_ACTION_ATTR_UNSPEC:
@@ -889,7 +905,12 @@ parse_odp_action(const char *s, const struct simap 
*port_names,
         int n = -1;
 
         if (ovs_scan(s, "conntrack(zone=%i)%n", &zone, &n)) {
-            nl_msg_put_u16(actions, OVS_ACTION_ATTR_CONNTRACK, zone);
+            size_t ct_ofs;
+
+            ct_ofs = nl_msg_start_nested(actions, OVS_ACTION_ATTR_CONNTRACK);
+            nl_msg_put_u16(actions, OVS_CT_ATTR_ZONE, zone);
+            nl_msg_end_nested(actions, ct_ofs);
+
             return n;
         }
     }
-- 
1.9.3

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to