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