Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a507c346 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'openvswitch-Conntrack-integration-improvements'

parents 9878f602 316d4d78
Loading
Loading
Loading
Loading
+30 −3
Original line number Diff line number Diff line

/*
 * Copyright (c) 2007-2013 Nicira, Inc.
 * Copyright (c) 2007-2017 Nicira, Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of version 2 of the GNU General Public
@@ -331,6 +331,8 @@ enum ovs_key_attr {
	OVS_KEY_ATTR_CT_ZONE,	/* u16 connection tracking zone. */
	OVS_KEY_ATTR_CT_MARK,	/* u32 connection tracking mark */
	OVS_KEY_ATTR_CT_LABELS,	/* 16-octet connection tracking label */
	OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4,   /* struct ovs_key_ct_tuple_ipv4 */
	OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6,   /* struct ovs_key_ct_tuple_ipv6 */

#ifdef __KERNEL__
	OVS_KEY_ATTR_TUNNEL_INFO,  /* struct ip_tunnel_info */
@@ -446,9 +448,13 @@ struct ovs_key_nd {
	__u8	nd_tll[ETH_ALEN];
};

#define OVS_CT_LABELS_LEN	16
#define OVS_CT_LABELS_LEN_32	4
#define OVS_CT_LABELS_LEN	(OVS_CT_LABELS_LEN_32 * sizeof(__u32))
struct ovs_key_ct_labels {
	union {
		__u8	ct_labels[OVS_CT_LABELS_LEN];
		__u32	ct_labels_32[OVS_CT_LABELS_LEN_32];
	};
};

/* OVS_KEY_ATTR_CT_STATE flags */
@@ -468,6 +474,22 @@ struct ovs_key_ct_labels {

#define OVS_CS_F_NAT_MASK (OVS_CS_F_SRC_NAT | OVS_CS_F_DST_NAT)

struct ovs_key_ct_tuple_ipv4 {
	__be32 ipv4_src;
	__be32 ipv4_dst;
	__be16 src_port;
	__be16 dst_port;
	__u8   ipv4_proto;
};

struct ovs_key_ct_tuple_ipv6 {
	__be32 ipv6_src[4];
	__be32 ipv6_dst[4];
	__be16 src_port;
	__be16 dst_port;
	__u8   ipv6_proto;
};

/**
 * enum ovs_flow_attr - attributes for %OVS_FLOW_* commands.
 * @OVS_FLOW_ATTR_KEY: Nested %OVS_KEY_ATTR_* attributes specifying the flow
@@ -652,6 +674,10 @@ struct ovs_action_hash {
 * @OVS_CT_ATTR_HELPER: variable length string defining conntrack ALG.
 * @OVS_CT_ATTR_NAT: Nested OVS_NAT_ATTR_* for performing L3 network address
 * translation (NAT) on the packet.
 * @OVS_CT_ATTR_FORCE_COMMIT: Like %OVS_CT_ATTR_COMMIT, but instead of doing
 * nothing if the connection is already committed will check that the current
 * packet is in conntrack entry's original direction.  If directionality does
 * not match, will delete the existing conntrack entry and commit a new one.
 */
enum ovs_ct_attr {
	OVS_CT_ATTR_UNSPEC,
@@ -662,6 +688,7 @@ enum ovs_ct_attr {
	OVS_CT_ATTR_HELPER,     /* netlink helper to assist detection of
				   related connections. */
	OVS_CT_ATTR_NAT,        /* Nested OVS_NAT_ATTR_* */
	OVS_CT_ATTR_FORCE_COMMIT,  /* No argument */
	__OVS_CT_ATTR_MAX
};

+2 −0
Original line number Diff line number Diff line
@@ -1074,6 +1074,8 @@ static int execute_masked_set_action(struct sk_buff *skb,
	case OVS_KEY_ATTR_CT_ZONE:
	case OVS_KEY_ATTR_CT_MARK:
	case OVS_KEY_ATTR_CT_LABELS:
	case OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4:
	case OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6:
		err = -EINVAL;
		break;
	}
+232 −64
Original line number Diff line number Diff line
@@ -65,6 +65,7 @@ struct ovs_conntrack_info {
	struct nf_conn *ct;
	u8 commit : 1;
	u8 nat : 3;                 /* enum ovs_ct_nat */
	u8 force : 1;
	u16 family;
	struct md_mark mark;
	struct md_labels labels;
@@ -73,6 +74,8 @@ struct ovs_conntrack_info {
#endif
};

static bool labels_nonzero(const struct ovs_key_ct_labels *labels);

static void __ovs_ct_free_action(struct ovs_conntrack_info *ct_info);

static u16 key_to_nfproto(const struct sw_flow_key *key)
@@ -129,21 +132,33 @@ static u32 ovs_ct_get_mark(const struct nf_conn *ct)
#endif
}

/* Guard against conntrack labels max size shrinking below 128 bits. */
#if NF_CT_LABELS_MAX_SIZE < 16
#error NF_CT_LABELS_MAX_SIZE must be at least 16 bytes
#endif

static void ovs_ct_get_labels(const struct nf_conn *ct,
			      struct ovs_key_ct_labels *labels)
{
	struct nf_conn_labels *cl = ct ? nf_ct_labels_find(ct) : NULL;

	if (cl) {
		size_t len = sizeof(cl->bits);

		if (len > OVS_CT_LABELS_LEN)
			len = OVS_CT_LABELS_LEN;
		else if (len < OVS_CT_LABELS_LEN)
	if (cl)
		memcpy(labels, cl->bits, OVS_CT_LABELS_LEN);
	else
		memset(labels, 0, OVS_CT_LABELS_LEN);
		memcpy(labels, cl->bits, len);
}

static void __ovs_ct_update_key_orig_tp(struct sw_flow_key *key,
					const struct nf_conntrack_tuple *orig,
					u8 icmp_proto)
{
	key->ct_orig_proto = orig->dst.protonum;
	if (orig->dst.protonum == icmp_proto) {
		key->ct.orig_tp.src = htons(orig->dst.u.icmp.type);
		key->ct.orig_tp.dst = htons(orig->dst.u.icmp.code);
	} else {
		memset(labels, 0, OVS_CT_LABELS_LEN);
		key->ct.orig_tp.src = orig->src.u.all;
		key->ct.orig_tp.dst = orig->dst.u.all;
	}
}

@@ -151,13 +166,42 @@ static void __ovs_ct_update_key(struct sw_flow_key *key, u8 state,
				const struct nf_conntrack_zone *zone,
				const struct nf_conn *ct)
{
	key->ct.state = state;
	key->ct.zone = zone->id;
	key->ct_state = state;
	key->ct_zone = zone->id;
	key->ct.mark = ovs_ct_get_mark(ct);
	ovs_ct_get_labels(ct, &key->ct.labels);

	if (ct) {
		const struct nf_conntrack_tuple *orig;

		/* Use the master if we have one. */
		if (ct->master)
			ct = ct->master;
		orig = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;

		/* IP version must match with the master connection. */
		if (key->eth.type == htons(ETH_P_IP) &&
		    nf_ct_l3num(ct) == NFPROTO_IPV4) {
			key->ipv4.ct_orig.src = orig->src.u3.ip;
			key->ipv4.ct_orig.dst = orig->dst.u3.ip;
			__ovs_ct_update_key_orig_tp(key, orig, IPPROTO_ICMP);
			return;
		} else if (key->eth.type == htons(ETH_P_IPV6) &&
			   !sw_flow_key_is_nd(key) &&
			   nf_ct_l3num(ct) == NFPROTO_IPV6) {
			key->ipv6.ct_orig.src = orig->src.u3.in6;
			key->ipv6.ct_orig.dst = orig->dst.u3.in6;
			__ovs_ct_update_key_orig_tp(key, orig, NEXTHDR_ICMP);
			return;
		}
	}
	/* Clear 'ct_orig_proto' to mark the non-existence of conntrack
	 * original direction key fields.
	 */
	key->ct_orig_proto = 0;
}

/* Update 'key' based on skb->nfct.  If 'post_ct' is true, then OVS has
/* Update 'key' based on skb->_nfct.  If 'post_ct' is true, then OVS has
 * previously sent the packet to conntrack via the ct action.  If
 * 'keep_nat_flags' is true, the existing NAT flags retained, else they are
 * initialized from the connection status.
@@ -184,7 +228,7 @@ static void ovs_ct_update_key(const struct sk_buff *skb,
		if (ct->master)
			state |= OVS_CS_F_RELATED;
		if (keep_nat_flags) {
			state |= key->ct.state & OVS_CS_F_NAT_MASK;
			state |= key->ct_state & OVS_CS_F_NAT_MASK;
		} else {
			if (ct->status & IPS_SRC_NAT)
				state |= OVS_CS_F_SRC_NAT;
@@ -208,43 +252,68 @@ void ovs_ct_fill_key(const struct sk_buff *skb, struct sw_flow_key *key)
	ovs_ct_update_key(skb, NULL, key, false, false);
}

int ovs_ct_put_key(const struct sw_flow_key *key, struct sk_buff *skb)
#define IN6_ADDR_INITIALIZER(ADDR) \
	{ (ADDR).s6_addr32[0], (ADDR).s6_addr32[1], \
	  (ADDR).s6_addr32[2], (ADDR).s6_addr32[3] }

int ovs_ct_put_key(const struct sw_flow_key *swkey,
		   const struct sw_flow_key *output, struct sk_buff *skb)
{
	if (nla_put_u32(skb, OVS_KEY_ATTR_CT_STATE, key->ct.state))
	if (nla_put_u32(skb, OVS_KEY_ATTR_CT_STATE, output->ct_state))
		return -EMSGSIZE;

	if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) &&
	    nla_put_u16(skb, OVS_KEY_ATTR_CT_ZONE, key->ct.zone))
	    nla_put_u16(skb, OVS_KEY_ATTR_CT_ZONE, output->ct_zone))
		return -EMSGSIZE;

	if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) &&
	    nla_put_u32(skb, OVS_KEY_ATTR_CT_MARK, key->ct.mark))
	    nla_put_u32(skb, OVS_KEY_ATTR_CT_MARK, output->ct.mark))
		return -EMSGSIZE;

	if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) &&
	    nla_put(skb, OVS_KEY_ATTR_CT_LABELS, sizeof(key->ct.labels),
		    &key->ct.labels))
	    nla_put(skb, OVS_KEY_ATTR_CT_LABELS, sizeof(output->ct.labels),
		    &output->ct.labels))
		return -EMSGSIZE;

	if (swkey->ct_orig_proto) {
		if (swkey->eth.type == htons(ETH_P_IP)) {
			struct ovs_key_ct_tuple_ipv4 orig = {
				output->ipv4.ct_orig.src,
				output->ipv4.ct_orig.dst,
				output->ct.orig_tp.src,
				output->ct.orig_tp.dst,
				output->ct_orig_proto,
			};
			if (nla_put(skb, OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4,
				    sizeof(orig), &orig))
				return -EMSGSIZE;
		} else if (swkey->eth.type == htons(ETH_P_IPV6)) {
			struct ovs_key_ct_tuple_ipv6 orig = {
				IN6_ADDR_INITIALIZER(output->ipv6.ct_orig.src),
				IN6_ADDR_INITIALIZER(output->ipv6.ct_orig.dst),
				output->ct.orig_tp.src,
				output->ct.orig_tp.dst,
				output->ct_orig_proto,
			};
			if (nla_put(skb, OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6,
				    sizeof(orig), &orig))
				return -EMSGSIZE;
		}
	}

	return 0;
}

static int ovs_ct_set_mark(struct sk_buff *skb, struct sw_flow_key *key,
static int ovs_ct_set_mark(struct nf_conn *ct, struct sw_flow_key *key,
			   u32 ct_mark, u32 mask)
{
#if IS_ENABLED(CONFIG_NF_CONNTRACK_MARK)
	enum ip_conntrack_info ctinfo;
	struct nf_conn *ct;
	u32 new_mark;

	/* The connection could be invalid, in which case set_mark is no-op. */
	ct = nf_ct_get(skb, &ctinfo);
	if (!ct)
		return 0;

	new_mark = ct_mark | (ct->mark & ~(mask));
	if (ct->mark != new_mark) {
		ct->mark = new_mark;
		if (nf_ct_is_confirmed(ct))
			nf_conntrack_event_cache(IPCT_MARK, ct);
		key->ct.mark = new_mark;
	}
@@ -255,34 +324,80 @@ static int ovs_ct_set_mark(struct sk_buff *skb, struct sw_flow_key *key,
#endif
}

static int ovs_ct_set_labels(struct sk_buff *skb, struct sw_flow_key *key,
			     const struct ovs_key_ct_labels *labels,
			     const struct ovs_key_ct_labels *mask)
static struct nf_conn_labels *ovs_ct_get_conn_labels(struct nf_conn *ct)
{
	enum ip_conntrack_info ctinfo;
	struct nf_conn_labels *cl;
	struct nf_conn *ct;
	int err;

	/* The connection could be invalid, in which case set_label is no-op.*/
	ct = nf_ct_get(skb, &ctinfo);
	if (!ct)
		return 0;

	cl = nf_ct_labels_find(ct);
	if (!cl) {
		nf_ct_labels_ext_add(ct);
		cl = nf_ct_labels_find(ct);
	}
	if (!cl || sizeof(cl->bits) < OVS_CT_LABELS_LEN)

	return cl;
}

/* Initialize labels for a new, yet to be committed conntrack entry.  Note that
 * since the new connection is not yet confirmed, and thus no-one else has
 * access to it's labels, we simply write them over.  Also, we refrain from
 * triggering events, as receiving change events before the create event would
 * be confusing.
 */
static int ovs_ct_init_labels(struct nf_conn *ct, struct sw_flow_key *key,
			      const struct ovs_key_ct_labels *labels,
			      const struct ovs_key_ct_labels *mask)
{
	struct nf_conn_labels *cl, *master_cl;
	bool have_mask = labels_nonzero(mask);

	/* Inherit master's labels to the related connection? */
	master_cl = ct->master ? nf_ct_labels_find(ct->master) : NULL;

	if (!master_cl && !have_mask)
		return 0;   /* Nothing to do. */

	cl = ovs_ct_get_conn_labels(ct);
	if (!cl)
		return -ENOSPC;

	err = nf_connlabels_replace(ct, (u32 *)labels, (u32 *)mask,
				    OVS_CT_LABELS_LEN / sizeof(u32));
	/* Inherit the master's labels, if any. */
	if (master_cl)
		*cl = *master_cl;

	if (have_mask) {
		u32 *dst = (u32 *)cl->bits;
		int i;

		for (i = 0; i < OVS_CT_LABELS_LEN_32; i++)
			dst[i] = (dst[i] & ~mask->ct_labels_32[i]) |
				(labels->ct_labels_32[i]
				 & mask->ct_labels_32[i]);
	}

	memcpy(&key->ct.labels, cl->bits, OVS_CT_LABELS_LEN);

	return 0;
}

static int ovs_ct_set_labels(struct nf_conn *ct, struct sw_flow_key *key,
			     const struct ovs_key_ct_labels *labels,
			     const struct ovs_key_ct_labels *mask)
{
	struct nf_conn_labels *cl;
	int err;

	cl = ovs_ct_get_conn_labels(ct);
	if (!cl)
		return -ENOSPC;

	err = nf_connlabels_replace(ct, labels->ct_labels_32,
				    mask->ct_labels_32,
				    OVS_CT_LABELS_LEN_32);
	if (err)
		return err;

	ovs_ct_get_labels(ct, &key->ct.labels);
	memcpy(&key->ct.labels, cl->bits, OVS_CT_LABELS_LEN);

	return 0;
}

@@ -421,16 +536,16 @@ ovs_ct_get_info(const struct nf_conntrack_tuple_hash *h)

/* Find an existing connection which this packet belongs to without
 * re-attributing statistics or modifying the connection state.  This allows an
 * skb->nfct lost due to an upcall to be recovered during actions execution.
 * skb->_nfct lost due to an upcall to be recovered during actions execution.
 *
 * Must be called with rcu_read_lock.
 *
 * On success, populates skb->nfct and skb->nfctinfo, and returns the
 * connection.  Returns NULL if there is no existing entry.
 * On success, populates skb->_nfct and returns the connection.  Returns NULL
 * if there is no existing entry.
 */
static struct nf_conn *
ovs_ct_find_existing(struct net *net, const struct nf_conntrack_zone *zone,
		     u8 l3num, struct sk_buff *skb)
		     u8 l3num, struct sk_buff *skb, bool natted)
{
	struct nf_conntrack_l3proto *l3proto;
	struct nf_conntrack_l4proto *l4proto;
@@ -453,6 +568,17 @@ ovs_ct_find_existing(struct net *net, const struct nf_conntrack_zone *zone,
		return NULL;
	}

	/* Must invert the tuple if skb has been transformed by NAT. */
	if (natted) {
		struct nf_conntrack_tuple inverse;

		if (!nf_ct_invert_tuple(&inverse, &tuple, l3proto, l4proto)) {
			pr_debug("ovs_ct_find_existing: Inversion failed!\n");
			return NULL;
		}
		tuple = inverse;
	}

	/* look for tuple match */
	h = nf_conntrack_find_get(net, zone, &tuple);
	if (!h)
@@ -460,11 +586,18 @@ ovs_ct_find_existing(struct net *net, const struct nf_conntrack_zone *zone,

	ct = nf_ct_tuplehash_to_ctrack(h);

	/* Inverted packet tuple matches the reverse direction conntrack tuple,
	 * select the other tuplehash to get the right 'ctinfo' bits for this
	 * packet.
	 */
	if (natted)
		h = &ct->tuplehash[!h->tuple.dst.dir];

	nf_ct_set(skb, ct, ovs_ct_get_info(h));
	return ct;
}

/* Determine whether skb->nfct is equal to the result of conntrack lookup. */
/* Determine whether skb->_nfct is equal to the result of conntrack lookup. */
static bool skb_nfct_cached(struct net *net,
			    const struct sw_flow_key *key,
			    const struct ovs_conntrack_info *info,
@@ -475,14 +608,19 @@ static bool skb_nfct_cached(struct net *net,

	ct = nf_ct_get(skb, &ctinfo);
	/* If no ct, check if we have evidence that an existing conntrack entry
	 * might be found for this skb.  This happens when we lose a skb->nfct
	 * might be found for this skb.  This happens when we lose a skb->_nfct
	 * due to an upcall.  If the connection was not confirmed, it is not
	 * cached and needs to be run through conntrack again.
	 */
	if (!ct && key->ct.state & OVS_CS_F_TRACKED &&
	    !(key->ct.state & OVS_CS_F_INVALID) &&
	    key->ct.zone == info->zone.id)
		ct = ovs_ct_find_existing(net, &info->zone, info->family, skb);
	if (!ct && key->ct_state & OVS_CS_F_TRACKED &&
	    !(key->ct_state & OVS_CS_F_INVALID) &&
	    key->ct_zone == info->zone.id) {
		ct = ovs_ct_find_existing(net, &info->zone, info->family, skb,
					  !!(key->ct_state
					     & OVS_CS_F_NAT_MASK));
		if (ct)
			nf_ct_get(skb, &ctinfo);
	}
	if (!ct)
		return false;
	if (!net_eq(net, read_pnet(&ct->ct_net)))
@@ -496,6 +634,18 @@ static bool skb_nfct_cached(struct net *net,
		if (help && rcu_access_pointer(help->helper) != info->helper)
			return false;
	}
	/* Force conntrack entry direction to the current packet? */
	if (info->force && CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) {
		/* Delete the conntrack entry if confirmed, else just release
		 * the reference.
		 */
		if (nf_ct_is_confirmed(ct))
			nf_ct_delete(ct, 0, 0);
		else
			nf_conntrack_put(&ct->ct_general);
		nf_ct_set(skb, NULL, 0);
		return false;
	}

	return true;
}
@@ -590,7 +740,7 @@ static void ovs_nat_update_key(struct sw_flow_key *key,
	if (maniptype == NF_NAT_MANIP_SRC) {
		__be16 src;

		key->ct.state |= OVS_CS_F_SRC_NAT;
		key->ct_state |= OVS_CS_F_SRC_NAT;
		if (key->eth.type == htons(ETH_P_IP))
			key->ipv4.addr.src = ip_hdr(skb)->saddr;
		else if (key->eth.type == htons(ETH_P_IPV6))
@@ -612,7 +762,7 @@ static void ovs_nat_update_key(struct sw_flow_key *key,
	} else {
		__be16 dst;

		key->ct.state |= OVS_CS_F_DST_NAT;
		key->ct_state |= OVS_CS_F_DST_NAT;
		if (key->eth.type == htons(ETH_P_IP))
			key->ipv4.addr.dst = ip_hdr(skb)->daddr;
		else if (key->eth.type == htons(ETH_P_IPV6))
@@ -699,7 +849,7 @@ static int ovs_ct_nat(struct net *net, struct sw_flow_key *key,
/* Pass 'skb' through conntrack in 'net', using zone configured in 'info', if
 * not done already.  Update key with new CT state after passing the packet
 * through conntrack.
 * Note that if the packet is deemed invalid by conntrack, skb->nfct will be
 * Note that if the packet is deemed invalid by conntrack, skb->_nfct will be
 * set to NULL and 0 will be returned.
 */
static int __ovs_ct_lookup(struct net *net, struct sw_flow_key *key,
@@ -736,7 +886,7 @@ static int __ovs_ct_lookup(struct net *net, struct sw_flow_key *key,
		 * NAT after the nf_conntrack_in() call.  We can actually clear
		 * the whole state, as it will be re-initialized below.
		 */
		key->ct.state = 0;
		key->ct_state = 0;

		/* Update the key, but keep the NAT flags. */
		ovs_ct_update_key(skb, info, key, true, true);
@@ -752,9 +902,9 @@ static int __ovs_ct_lookup(struct net *net, struct sw_flow_key *key,
		 *
		 * NAT will be done only if the CT action has NAT, and only
		 * once per packet (per zone), as guarded by the NAT bits in
		 * the key->ct.state.
		 * the key->ct_state.
		 */
		if (info->nat && !(key->ct.state & OVS_CS_F_NAT_MASK) &&
		if (info->nat && !(key->ct_state & OVS_CS_F_NAT_MASK) &&
		    (nf_ct_is_confirmed(ct) || info->commit) &&
		    ovs_ct_nat(net, key, info, skb, ct, ctinfo) != NF_ACCEPT) {
			return -EINVAL;
@@ -830,8 +980,8 @@ static bool labels_nonzero(const struct ovs_key_ct_labels *labels)
{
	size_t i;

	for (i = 0; i < sizeof(*labels); i++)
		if (labels->ct_labels[i])
	for (i = 0; i < OVS_CT_LABELS_LEN_32; i++)
		if (labels->ct_labels_32[i])
			return true;

	return false;
@@ -842,24 +992,36 @@ static int ovs_ct_commit(struct net *net, struct sw_flow_key *key,
			 const struct ovs_conntrack_info *info,
			 struct sk_buff *skb)
{
	enum ip_conntrack_info ctinfo;
	struct nf_conn *ct;
	int err;

	err = __ovs_ct_lookup(net, key, info, skb);
	if (err)
		return err;

	/* The connection could be invalid, in which case this is a no-op.*/
	ct = nf_ct_get(skb, &ctinfo);
	if (!ct)
		return 0;

	/* Apply changes before confirming the connection so that the initial
	 * conntrack NEW netlink event carries the values given in the CT
	 * action.
	 */
	if (info->mark.mask) {
		err = ovs_ct_set_mark(skb, key, info->mark.value,
		err = ovs_ct_set_mark(ct, key, info->mark.value,
				      info->mark.mask);
		if (err)
			return err;
	}
	if (labels_nonzero(&info->labels.mask)) {
		err = ovs_ct_set_labels(skb, key, &info->labels.value,
	if (!nf_ct_is_confirmed(ct)) {
		err = ovs_ct_init_labels(ct, key, &info->labels.value,
					 &info->labels.mask);
		if (err)
			return err;
	} else if (labels_nonzero(&info->labels.mask)) {
		err = ovs_ct_set_labels(ct, key, &info->labels.value,
					&info->labels.mask);
		if (err)
			return err;
@@ -1061,6 +1223,7 @@ static int parse_nat(const struct nlattr *attr,

static const struct ovs_ct_len_tbl ovs_ct_attr_lens[OVS_CT_ATTR_MAX + 1] = {
	[OVS_CT_ATTR_COMMIT]	= { .minlen = 0, .maxlen = 0 },
	[OVS_CT_ATTR_FORCE_COMMIT]	= { .minlen = 0, .maxlen = 0 },
	[OVS_CT_ATTR_ZONE]	= { .minlen = sizeof(u16),
				    .maxlen = sizeof(u16) },
	[OVS_CT_ATTR_MARK]	= { .minlen = sizeof(struct md_mark),
@@ -1100,6 +1263,9 @@ static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info,
		}

		switch (type) {
		case OVS_CT_ATTR_FORCE_COMMIT:
			info->force = true;
			/* fall through. */
		case OVS_CT_ATTR_COMMIT:
			info->commit = true;
			break;
@@ -1326,7 +1492,9 @@ int ovs_ct_action_to_attr(const struct ovs_conntrack_info *ct_info,
	if (!start)
		return -EMSGSIZE;

	if (ct_info->commit && nla_put_flag(skb, OVS_CT_ATTR_COMMIT))
	if (ct_info->commit && nla_put_flag(skb, ct_info->force
					    ? OVS_CT_ATTR_FORCE_COMMIT
					    : OVS_CT_ATTR_COMMIT))
		return -EMSGSIZE;
	if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) &&
	    nla_put_u16(skb, OVS_CT_ATTR_ZONE, ct_info->zone.id))
+10 −4
Original line number Diff line number Diff line
@@ -32,7 +32,8 @@ int ovs_ct_execute(struct net *, struct sk_buff *, struct sw_flow_key *,
		   const struct ovs_conntrack_info *);

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 *swkey,
		   const struct sw_flow_key *output, struct sk_buff *skb);
void ovs_ct_free_action(const struct nlattr *a);

#define CT_SUPPORTED_MASK (OVS_CS_F_NEW | OVS_CS_F_ESTABLISHED | \
@@ -75,13 +76,18 @@ static inline int ovs_ct_execute(struct net *net, struct sk_buff *skb,
static inline void ovs_ct_fill_key(const struct sk_buff *skb,
				   struct sw_flow_key *key)
{
	key->ct.state = 0;
	key->ct.zone = 0;
	key->ct_state = 0;
	key->ct_zone = 0;
	key->ct.mark = 0;
	memset(&key->ct.labels, 0, sizeof(key->ct.labels));
	/* Clear 'ct_orig_proto' to mark the non-existence of original
	 * direction key fields.
	 */
	key->ct_orig_proto = 0;
}

static inline int ovs_ct_put_key(const struct sw_flow_key *key,
static inline int ovs_ct_put_key(const struct sw_flow_key *swkey,
				 const struct sw_flow_key *output,
				 struct sk_buff *skb)
{
	return 0;
+29 −5
Original line number Diff line number Diff line
@@ -765,7 +765,7 @@ static int key_extract_mac_proto(struct sk_buff *skb)
int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info,
			 struct sk_buff *skb, struct sw_flow_key *key)
{
	int res;
	int res, err;

	/* Extract metadata from packet. */
	if (tun_info) {
@@ -792,7 +792,6 @@ int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info,
	key->phy.priority = skb->priority;
	key->phy.in_port = OVS_CB(skb)->input_vport->port_no;
	key->phy.skb_mark = skb->mark;
	ovs_ct_fill_key(skb, key);
	key->ovs_flow_hash = 0;
	res = key_extract_mac_proto(skb);
	if (res < 0)
@@ -800,17 +799,26 @@ int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info,
	key->mac_proto = res;
	key->recirc_id = 0;

	return key_extract(skb, key);
	err = key_extract(skb, key);
	if (!err)
		ovs_ct_fill_key(skb, key);   /* Must be after key_extract(). */
	return err;
}

int ovs_flow_key_extract_userspace(struct net *net, const struct nlattr *attr,
				   struct sk_buff *skb,
				   struct sw_flow_key *key, bool log)
{
	const struct nlattr *a[OVS_KEY_ATTR_MAX + 1];
	u64 attrs = 0;
	int err;

	err = parse_flow_nlattrs(attr, a, &attrs, log);
	if (err)
		return -EINVAL;

	/* Extract metadata from netlink attributes. */
	err = ovs_nla_get_flow_metadata(net, attr, key, log);
	err = ovs_nla_get_flow_metadata(net, a, attrs, key, log);
	if (err)
		return err;

@@ -824,5 +832,21 @@ int ovs_flow_key_extract_userspace(struct net *net, const struct nlattr *attr,
	 */

	skb->protocol = key->eth.type;
	return key_extract(skb, key);
	err = key_extract(skb, key);
	if (err)
		return err;

	/* Check that we have conntrack original direction tuple metadata only
	 * for packets for which it makes sense.  Otherwise the key may be
	 * corrupted due to overlapping key fields.
	 */
	if (attrs & (1 << OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4) &&
	    key->eth.type != htons(ETH_P_IP))
		return -EINVAL;
	if (attrs & (1 << OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6) &&
	    (key->eth.type != htons(ETH_P_IPV6) ||
	     sw_flow_key_is_nd(key)))
		return -EINVAL;

	return 0;
}
Loading