[ovs-dev] [PATCH 2/2] datapath: Add conntrack zone support

Thomas Graf tgraf at noironetworks.com
Wed Sep 17 12:46:39 UTC 2014


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




More information about the dev mailing list