Commit 41747509 authored by David S. Miller's avatar David S. Miller

Merge branch 'ovs-ct-fixes'

Joe Stringer says:

====================
OVS conntrack fixes for net

The userspace side of the Open vSwitch conntrack changes is currently
undergoing review, which has highlighted some minor bugs in the existing
conntrack implementation in the kernel, as well as pointing out some
future-proofing that can be done on the interface to reduce the need for
additional compatibility code in future.

The biggest changes here are to the userspace API for the ct_state match
field and the CT action. This series proposes to firstly extend the ct_state
match field to 32 bits, ensuring to reject any currently unsupported bits.
Secondly, rather than representing CT action flags within a 32-bit field,
simply use a netlink attribute as presence of the single flag that is
defined today. This also serves to reject unsupported ct action flag bits.

v4: Use 12-character abbreviated hashes in commit messages.
v3: Fully acked.
v2: Address minor style feedback, add acks.
v1: Initial post.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 60872867 ab38a7b5
...@@ -323,7 +323,7 @@ enum ovs_key_attr { ...@@ -323,7 +323,7 @@ enum ovs_key_attr {
OVS_KEY_ATTR_MPLS, /* array of struct ovs_key_mpls. OVS_KEY_ATTR_MPLS, /* array of struct ovs_key_mpls.
* The implementation may restrict * The implementation may restrict
* the accepted length of the array. */ * the accepted length of the array. */
OVS_KEY_ATTR_CT_STATE, /* u8 bitmask of OVS_CS_F_* */ OVS_KEY_ATTR_CT_STATE, /* u32 bitmask of OVS_CS_F_* */
OVS_KEY_ATTR_CT_ZONE, /* u16 connection tracking zone. */ OVS_KEY_ATTR_CT_ZONE, /* u16 connection tracking zone. */
OVS_KEY_ATTR_CT_MARK, /* u32 connection tracking mark */ OVS_KEY_ATTR_CT_MARK, /* u32 connection tracking mark */
OVS_KEY_ATTR_CT_LABELS, /* 16-octet connection tracking label */ OVS_KEY_ATTR_CT_LABELS, /* 16-octet connection tracking label */
...@@ -449,9 +449,9 @@ struct ovs_key_ct_labels { ...@@ -449,9 +449,9 @@ struct ovs_key_ct_labels {
#define OVS_CS_F_ESTABLISHED 0x02 /* Part of an existing connection. */ #define OVS_CS_F_ESTABLISHED 0x02 /* Part of an existing connection. */
#define OVS_CS_F_RELATED 0x04 /* Related to an established #define OVS_CS_F_RELATED 0x04 /* Related to an established
* connection. */ * connection. */
#define OVS_CS_F_INVALID 0x20 /* Could not track connection. */ #define OVS_CS_F_REPLY_DIR 0x08 /* Flow is in the reply direction. */
#define OVS_CS_F_REPLY_DIR 0x40 /* Flow is in the reply direction. */ #define OVS_CS_F_INVALID 0x10 /* Could not track connection. */
#define OVS_CS_F_TRACKED 0x80 /* Conntrack has occurred. */ #define OVS_CS_F_TRACKED 0x20 /* Conntrack has occurred. */
/** /**
* enum ovs_flow_attr - attributes for %OVS_FLOW_* commands. * enum ovs_flow_attr - attributes for %OVS_FLOW_* commands.
...@@ -618,7 +618,9 @@ struct ovs_action_hash { ...@@ -618,7 +618,9 @@ struct ovs_action_hash {
/** /**
* enum ovs_ct_attr - Attributes for %OVS_ACTION_ATTR_CT action. * enum ovs_ct_attr - Attributes for %OVS_ACTION_ATTR_CT action.
* @OVS_CT_ATTR_FLAGS: u32 connection tracking flags. * @OVS_CT_ATTR_COMMIT: If present, commits the connection to the conntrack
* table. This allows future packets for the same connection to be identified
* as 'established' or 'related'.
* @OVS_CT_ATTR_ZONE: u16 connection tracking zone. * @OVS_CT_ATTR_ZONE: u16 connection tracking zone.
* @OVS_CT_ATTR_MARK: u32 value followed by u32 mask. For each bit set in the * @OVS_CT_ATTR_MARK: u32 value followed by u32 mask. For each bit set in the
* mask, the corresponding bit in the value is copied to the connection * mask, the corresponding bit in the value is copied to the connection
...@@ -630,7 +632,7 @@ struct ovs_action_hash { ...@@ -630,7 +632,7 @@ struct ovs_action_hash {
*/ */
enum ovs_ct_attr { enum ovs_ct_attr {
OVS_CT_ATTR_UNSPEC, OVS_CT_ATTR_UNSPEC,
OVS_CT_ATTR_FLAGS, /* u8 bitmask of OVS_CT_F_*. */ OVS_CT_ATTR_COMMIT, /* No argument, commits connection. */
OVS_CT_ATTR_ZONE, /* u16 zone id. */ OVS_CT_ATTR_ZONE, /* u16 zone id. */
OVS_CT_ATTR_MARK, /* mark to associate with this connection. */ OVS_CT_ATTR_MARK, /* mark to associate with this connection. */
OVS_CT_ATTR_LABELS, /* labels to associate with this connection. */ OVS_CT_ATTR_LABELS, /* labels to associate with this connection. */
...@@ -641,14 +643,6 @@ enum ovs_ct_attr { ...@@ -641,14 +643,6 @@ enum ovs_ct_attr {
#define OVS_CT_ATTR_MAX (__OVS_CT_ATTR_MAX - 1) #define OVS_CT_ATTR_MAX (__OVS_CT_ATTR_MAX - 1)
/*
* OVS_CT_ATTR_FLAGS flags - bitmask of %OVS_CT_F_*
* @OVS_CT_F_COMMIT: Commits the flow to the conntrack table. This allows
* future packets for the same connection to be identified as 'established'
* or 'related'.
*/
#define OVS_CT_F_COMMIT 0x01
/** /**
* enum ovs_action_attr - Action types. * enum ovs_action_attr - Action types.
* *
...@@ -705,7 +699,7 @@ enum ovs_action_attr { ...@@ -705,7 +699,7 @@ enum ovs_action_attr {
* data immediately followed by a mask. * data immediately followed by a mask.
* The data must be zero for the unmasked * The data must be zero for the unmasked
* bits. */ * bits. */
OVS_ACTION_ATTR_CT, /* One nested OVS_CT_ATTR_* . */ OVS_ACTION_ATTR_CT, /* Nested OVS_CT_ATTR_* . */
__OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted __OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted
* from userspace. */ * from userspace. */
......
...@@ -684,7 +684,7 @@ static void ovs_fragment(struct vport *vport, struct sk_buff *skb, u16 mru, ...@@ -684,7 +684,7 @@ static void ovs_fragment(struct vport *vport, struct sk_buff *skb, u16 mru,
{ {
if (skb_network_offset(skb) > MAX_L2_LEN) { if (skb_network_offset(skb) > MAX_L2_LEN) {
OVS_NLERR(1, "L2 header too long to fragment"); OVS_NLERR(1, "L2 header too long to fragment");
return; goto err;
} }
if (ethertype == htons(ETH_P_IP)) { if (ethertype == htons(ETH_P_IP)) {
...@@ -708,8 +708,7 @@ static void ovs_fragment(struct vport *vport, struct sk_buff *skb, u16 mru, ...@@ -708,8 +708,7 @@ static void ovs_fragment(struct vport *vport, struct sk_buff *skb, u16 mru,
struct rt6_info ovs_rt; struct rt6_info ovs_rt;
if (!v6ops) { if (!v6ops) {
kfree_skb(skb); goto err;
return;
} }
prepare_frag(vport, skb); prepare_frag(vport, skb);
...@@ -728,8 +727,12 @@ static void ovs_fragment(struct vport *vport, struct sk_buff *skb, u16 mru, ...@@ -728,8 +727,12 @@ static void ovs_fragment(struct vport *vport, struct sk_buff *skb, u16 mru,
WARN_ONCE(1, "Failed fragment ->%s: eth=%04x, MRU=%d, MTU=%d.", WARN_ONCE(1, "Failed fragment ->%s: eth=%04x, MRU=%d, MTU=%d.",
ovs_vport_name(vport), ntohs(ethertype), mru, ovs_vport_name(vport), ntohs(ethertype), mru,
vport->dev->mtu); vport->dev->mtu);
kfree_skb(skb); goto err;
} }
return;
err:
kfree_skb(skb);
} }
static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port, static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port,
...@@ -1099,6 +1102,12 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, ...@@ -1099,6 +1102,12 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
break; break;
case OVS_ACTION_ATTR_CT: case OVS_ACTION_ATTR_CT:
if (!is_flow_key_valid(key)) {
err = ovs_flow_key_update(skb, key);
if (err)
return err;
}
err = ovs_ct_execute(ovs_dp_get_net(dp), skb, key, err = ovs_ct_execute(ovs_dp_get_net(dp), skb, key,
nla_data(a)); nla_data(a));
......
...@@ -47,7 +47,7 @@ struct ovs_conntrack_info { ...@@ -47,7 +47,7 @@ struct ovs_conntrack_info {
struct nf_conntrack_helper *helper; struct nf_conntrack_helper *helper;
struct nf_conntrack_zone zone; struct nf_conntrack_zone zone;
struct nf_conn *ct; struct nf_conn *ct;
u32 flags; u8 commit : 1;
u16 family; u16 family;
struct md_mark mark; struct md_mark mark;
struct md_labels labels; struct md_labels labels;
...@@ -167,7 +167,7 @@ void ovs_ct_fill_key(const struct sk_buff *skb, struct sw_flow_key *key) ...@@ -167,7 +167,7 @@ void ovs_ct_fill_key(const struct sk_buff *skb, struct sw_flow_key *key)
int ovs_ct_put_key(const struct sw_flow_key *key, struct sk_buff *skb) int ovs_ct_put_key(const struct sw_flow_key *key, struct sk_buff *skb)
{ {
if (nla_put_u8(skb, OVS_KEY_ATTR_CT_STATE, key->ct.state)) if (nla_put_u32(skb, OVS_KEY_ATTR_CT_STATE, key->ct.state))
return -EMSGSIZE; return -EMSGSIZE;
if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) && if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) &&
...@@ -493,7 +493,7 @@ int ovs_ct_execute(struct net *net, struct sk_buff *skb, ...@@ -493,7 +493,7 @@ int ovs_ct_execute(struct net *net, struct sk_buff *skb,
return err; return err;
} }
if (info->flags & OVS_CT_F_COMMIT) if (info->commit)
err = ovs_ct_commit(net, key, info, skb); err = ovs_ct_commit(net, key, info, skb);
else else
err = ovs_ct_lookup(net, key, info, skb); err = ovs_ct_lookup(net, key, info, skb);
...@@ -539,8 +539,7 @@ static int ovs_ct_add_helper(struct ovs_conntrack_info *info, const char *name, ...@@ -539,8 +539,7 @@ static int ovs_ct_add_helper(struct ovs_conntrack_info *info, const char *name,
} }
static const struct ovs_ct_len_tbl ovs_ct_attr_lens[OVS_CT_ATTR_MAX + 1] = { static const struct ovs_ct_len_tbl ovs_ct_attr_lens[OVS_CT_ATTR_MAX + 1] = {
[OVS_CT_ATTR_FLAGS] = { .minlen = sizeof(u32), [OVS_CT_ATTR_COMMIT] = { .minlen = 0, .maxlen = 0 },
.maxlen = sizeof(u32) },
[OVS_CT_ATTR_ZONE] = { .minlen = sizeof(u16), [OVS_CT_ATTR_ZONE] = { .minlen = sizeof(u16),
.maxlen = sizeof(u16) }, .maxlen = sizeof(u16) },
[OVS_CT_ATTR_MARK] = { .minlen = sizeof(struct md_mark), [OVS_CT_ATTR_MARK] = { .minlen = sizeof(struct md_mark),
...@@ -576,8 +575,8 @@ static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info, ...@@ -576,8 +575,8 @@ static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info,
} }
switch (type) { switch (type) {
case OVS_CT_ATTR_FLAGS: case OVS_CT_ATTR_COMMIT:
info->flags = nla_get_u32(a); info->commit = true;
break; break;
#ifdef CONFIG_NF_CONNTRACK_ZONES #ifdef CONFIG_NF_CONNTRACK_ZONES
case OVS_CT_ATTR_ZONE: case OVS_CT_ATTR_ZONE:
...@@ -701,7 +700,7 @@ int ovs_ct_action_to_attr(const struct ovs_conntrack_info *ct_info, ...@@ -701,7 +700,7 @@ int ovs_ct_action_to_attr(const struct ovs_conntrack_info *ct_info,
if (!start) if (!start)
return -EMSGSIZE; return -EMSGSIZE;
if (nla_put_u32(skb, OVS_CT_ATTR_FLAGS, ct_info->flags)) if (ct_info->commit && nla_put_flag(skb, OVS_CT_ATTR_COMMIT))
return -EMSGSIZE; return -EMSGSIZE;
if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) && if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) &&
nla_put_u16(skb, OVS_CT_ATTR_ZONE, ct_info->zone.id)) nla_put_u16(skb, OVS_CT_ATTR_ZONE, ct_info->zone.id))
......
...@@ -34,6 +34,13 @@ int ovs_ct_execute(struct net *, struct sk_buff *, struct sw_flow_key *, ...@@ -34,6 +34,13 @@ int ovs_ct_execute(struct net *, struct sk_buff *, struct sw_flow_key *,
void ovs_ct_fill_key(const struct sk_buff *skb, struct sw_flow_key *key); void ovs_ct_fill_key(const struct sk_buff *skb, struct sw_flow_key *key);
int ovs_ct_put_key(const struct sw_flow_key *key, struct sk_buff *skb); int ovs_ct_put_key(const struct sw_flow_key *key, struct sk_buff *skb);
void ovs_ct_free_action(const struct nlattr *a); void ovs_ct_free_action(const struct nlattr *a);
static inline bool ovs_ct_state_supported(u32 state)
{
return !(state & ~(OVS_CS_F_NEW | OVS_CS_F_ESTABLISHED |
OVS_CS_F_RELATED | OVS_CS_F_REPLY_DIR |
OVS_CS_F_INVALID | OVS_CS_F_TRACKED));
}
#else #else
#include <linux/errno.h> #include <linux/errno.h>
...@@ -46,6 +53,11 @@ static inline bool ovs_ct_verify(struct net *net, int attr) ...@@ -46,6 +53,11 @@ static inline bool ovs_ct_verify(struct net *net, int attr)
return false; return false;
} }
static inline bool ovs_ct_state_supported(u32 state)
{
return false;
}
static inline int ovs_ct_copy_action(struct net *net, const struct nlattr *nla, static inline int ovs_ct_copy_action(struct net *net, const struct nlattr *nla,
const struct sw_flow_key *key, const struct sw_flow_key *key,
struct sw_flow_actions **acts, bool log) struct sw_flow_actions **acts, bool log)
......
...@@ -291,7 +291,7 @@ size_t ovs_key_attr_size(void) ...@@ -291,7 +291,7 @@ size_t ovs_key_attr_size(void)
+ nla_total_size(4) /* OVS_KEY_ATTR_SKB_MARK */ + nla_total_size(4) /* OVS_KEY_ATTR_SKB_MARK */
+ nla_total_size(4) /* OVS_KEY_ATTR_DP_HASH */ + nla_total_size(4) /* OVS_KEY_ATTR_DP_HASH */
+ nla_total_size(4) /* OVS_KEY_ATTR_RECIRC_ID */ + nla_total_size(4) /* OVS_KEY_ATTR_RECIRC_ID */
+ nla_total_size(1) /* OVS_KEY_ATTR_CT_STATE */ + nla_total_size(4) /* OVS_KEY_ATTR_CT_STATE */
+ nla_total_size(2) /* OVS_KEY_ATTR_CT_ZONE */ + nla_total_size(2) /* OVS_KEY_ATTR_CT_ZONE */
+ nla_total_size(4) /* OVS_KEY_ATTR_CT_MARK */ + nla_total_size(4) /* OVS_KEY_ATTR_CT_MARK */
+ nla_total_size(16) /* OVS_KEY_ATTR_CT_LABELS */ + nla_total_size(16) /* OVS_KEY_ATTR_CT_LABELS */
...@@ -349,7 +349,7 @@ static const struct ovs_len_tbl ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = { ...@@ -349,7 +349,7 @@ static const struct ovs_len_tbl ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
[OVS_KEY_ATTR_TUNNEL] = { .len = OVS_ATTR_NESTED, [OVS_KEY_ATTR_TUNNEL] = { .len = OVS_ATTR_NESTED,
.next = ovs_tunnel_key_lens, }, .next = ovs_tunnel_key_lens, },
[OVS_KEY_ATTR_MPLS] = { .len = sizeof(struct ovs_key_mpls) }, [OVS_KEY_ATTR_MPLS] = { .len = sizeof(struct ovs_key_mpls) },
[OVS_KEY_ATTR_CT_STATE] = { .len = sizeof(u8) }, [OVS_KEY_ATTR_CT_STATE] = { .len = sizeof(u32) },
[OVS_KEY_ATTR_CT_ZONE] = { .len = sizeof(u16) }, [OVS_KEY_ATTR_CT_ZONE] = { .len = sizeof(u16) },
[OVS_KEY_ATTR_CT_MARK] = { .len = sizeof(u32) }, [OVS_KEY_ATTR_CT_MARK] = { .len = sizeof(u32) },
[OVS_KEY_ATTR_CT_LABELS] = { .len = sizeof(struct ovs_key_ct_labels) }, [OVS_KEY_ATTR_CT_LABELS] = { .len = sizeof(struct ovs_key_ct_labels) },
...@@ -814,7 +814,13 @@ static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match, ...@@ -814,7 +814,13 @@ static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match,
if (*attrs & (1 << OVS_KEY_ATTR_CT_STATE) && if (*attrs & (1 << OVS_KEY_ATTR_CT_STATE) &&
ovs_ct_verify(net, OVS_KEY_ATTR_CT_STATE)) { ovs_ct_verify(net, OVS_KEY_ATTR_CT_STATE)) {
u8 ct_state = nla_get_u8(a[OVS_KEY_ATTR_CT_STATE]); u32 ct_state = nla_get_u32(a[OVS_KEY_ATTR_CT_STATE]);
if (!is_mask && !ovs_ct_state_supported(ct_state)) {
OVS_NLERR(log, "ct_state flags %08x unsupported",
ct_state);
return -EINVAL;
}
SW_FLOW_KEY_PUT(match, ct.state, ct_state, is_mask); SW_FLOW_KEY_PUT(match, ct.state, ct_state, is_mask);
*attrs &= ~(1ULL << OVS_KEY_ATTR_CT_STATE); *attrs &= ~(1ULL << OVS_KEY_ATTR_CT_STATE);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment