[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