diff options
Diffstat (limited to 'net/openvswitch')
| -rw-r--r-- | net/openvswitch/actions.c | 52 | ||||
| -rw-r--r-- | net/openvswitch/conntrack.c | 118 | ||||
| -rw-r--r-- | net/openvswitch/datapath.c | 18 | ||||
| -rw-r--r-- | net/openvswitch/datapath.h | 2 | ||||
| -rw-r--r-- | net/openvswitch/flow.c | 143 | ||||
| -rw-r--r-- | net/openvswitch/flow.h | 14 | ||||
| -rw-r--r-- | net/openvswitch/flow_netlink.c | 142 | ||||
| -rw-r--r-- | net/openvswitch/vport.c | 2 | 
8 files changed, 397 insertions, 94 deletions
| diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 076774034bb9..1b5d73079dc9 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -423,12 +423,43 @@ static void set_ipv6_addr(struct sk_buff *skb, u8 l4_proto,  	memcpy(addr, new_addr, sizeof(__be32[4]));  } -static void set_ipv6_fl(struct ipv6hdr *nh, u32 fl, u32 mask) +static void set_ipv6_dsfield(struct sk_buff *skb, struct ipv6hdr *nh, u8 ipv6_tclass, u8 mask)  { +	u8 old_ipv6_tclass = ipv6_get_dsfield(nh); + +	ipv6_tclass = OVS_MASKED(old_ipv6_tclass, ipv6_tclass, mask); + +	if (skb->ip_summed == CHECKSUM_COMPLETE) +		csum_replace(&skb->csum, (__force __wsum)(old_ipv6_tclass << 12), +			     (__force __wsum)(ipv6_tclass << 12)); + +	ipv6_change_dsfield(nh, ~mask, ipv6_tclass); +} + +static void set_ipv6_fl(struct sk_buff *skb, struct ipv6hdr *nh, u32 fl, u32 mask) +{ +	u32 ofl; + +	ofl = nh->flow_lbl[0] << 16 |  nh->flow_lbl[1] << 8 |  nh->flow_lbl[2]; +	fl = OVS_MASKED(ofl, fl, mask); +  	/* Bits 21-24 are always unmasked, so this retains their values. */ -	OVS_SET_MASKED(nh->flow_lbl[0], (u8)(fl >> 16), (u8)(mask >> 16)); -	OVS_SET_MASKED(nh->flow_lbl[1], (u8)(fl >> 8), (u8)(mask >> 8)); -	OVS_SET_MASKED(nh->flow_lbl[2], (u8)fl, (u8)mask); +	nh->flow_lbl[0] = (u8)(fl >> 16); +	nh->flow_lbl[1] = (u8)(fl >> 8); +	nh->flow_lbl[2] = (u8)fl; + +	if (skb->ip_summed == CHECKSUM_COMPLETE) +		csum_replace(&skb->csum, (__force __wsum)htonl(ofl), (__force __wsum)htonl(fl)); +} + +static void set_ipv6_ttl(struct sk_buff *skb, struct ipv6hdr *nh, u8 new_ttl, u8 mask) +{ +	new_ttl = OVS_MASKED(nh->hop_limit, new_ttl, mask); + +	if (skb->ip_summed == CHECKSUM_COMPLETE) +		csum_replace(&skb->csum, (__force __wsum)(nh->hop_limit << 8), +			     (__force __wsum)(new_ttl << 8)); +	nh->hop_limit = new_ttl;  }  static void set_ip_ttl(struct sk_buff *skb, struct iphdr *nh, u8 new_ttl, @@ -546,18 +577,17 @@ static int set_ipv6(struct sk_buff *skb, struct sw_flow_key *flow_key,  		}  	}  	if (mask->ipv6_tclass) { -		ipv6_change_dsfield(nh, ~mask->ipv6_tclass, key->ipv6_tclass); +		set_ipv6_dsfield(skb, nh, key->ipv6_tclass, mask->ipv6_tclass);  		flow_key->ip.tos = ipv6_get_dsfield(nh);  	}  	if (mask->ipv6_label) { -		set_ipv6_fl(nh, ntohl(key->ipv6_label), +		set_ipv6_fl(skb, nh, ntohl(key->ipv6_label),  			    ntohl(mask->ipv6_label));  		flow_key->ipv6.label =  		    *(__be32 *)nh & htonl(IPV6_FLOWINFO_FLOWLABEL);  	}  	if (mask->ipv6_hlimit) { -		OVS_SET_MASKED(nh->hop_limit, key->ipv6_hlimit, -			       mask->ipv6_hlimit); +		set_ipv6_ttl(skb, nh, key->ipv6_hlimit, mask->ipv6_hlimit);  		flow_key->ip.ttl = nh->hop_limit;  	}  	return 0; @@ -1021,7 +1051,7 @@ static int clone(struct datapath *dp, struct sk_buff *skb,  	int rem = nla_len(attr);  	bool dont_clone_flow_key; -	/* The first action is always 'OVS_CLONE_ATTR_ARG'. */ +	/* The first action is always 'OVS_CLONE_ATTR_EXEC'. */  	clone_arg = nla_data(attr);  	dont_clone_flow_key = nla_get_u32(clone_arg);  	actions = nla_next(clone_arg, &rem); @@ -1509,8 +1539,8 @@ static int clone_execute(struct datapath *dp, struct sk_buff *skb,  				pr_warn("%s: deferred action limit reached, drop sample action\n",  					ovs_dp_name(dp));  			} else {  /* Recirc action */ -				pr_warn("%s: deferred action limit reached, drop recirc action\n", -					ovs_dp_name(dp)); +				pr_warn("%s: deferred action limit reached, drop recirc action (recirc_id=%#x)\n", +					ovs_dp_name(dp), recirc_id);  			}  		}  	} diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index c07afff57dd3..4a947c13c813 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -734,6 +734,57 @@ static bool skb_nfct_cached(struct net *net,  }  #if IS_ENABLED(CONFIG_NF_NAT) +static void ovs_nat_update_key(struct sw_flow_key *key, +			       const struct sk_buff *skb, +			       enum nf_nat_manip_type maniptype) +{ +	if (maniptype == NF_NAT_MANIP_SRC) { +		__be16 src; + +		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)) +			memcpy(&key->ipv6.addr.src, &ipv6_hdr(skb)->saddr, +			       sizeof(key->ipv6.addr.src)); +		else +			return; + +		if (key->ip.proto == IPPROTO_UDP) +			src = udp_hdr(skb)->source; +		else if (key->ip.proto == IPPROTO_TCP) +			src = tcp_hdr(skb)->source; +		else if (key->ip.proto == IPPROTO_SCTP) +			src = sctp_hdr(skb)->source; +		else +			return; + +		key->tp.src = src; +	} else { +		__be16 dst; + +		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)) +			memcpy(&key->ipv6.addr.dst, &ipv6_hdr(skb)->daddr, +			       sizeof(key->ipv6.addr.dst)); +		else +			return; + +		if (key->ip.proto == IPPROTO_UDP) +			dst = udp_hdr(skb)->dest; +		else if (key->ip.proto == IPPROTO_TCP) +			dst = tcp_hdr(skb)->dest; +		else if (key->ip.proto == IPPROTO_SCTP) +			dst = sctp_hdr(skb)->dest; +		else +			return; + +		key->tp.dst = dst; +	} +} +  /* Modelled after nf_nat_ipv[46]_fn().   * range is only used for new, uninitialized NAT state.   * Returns either NF_ACCEPT or NF_DROP. @@ -741,7 +792,7 @@ static bool skb_nfct_cached(struct net *net,  static int ovs_ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct,  			      enum ip_conntrack_info ctinfo,  			      const struct nf_nat_range2 *range, -			      enum nf_nat_manip_type maniptype) +			      enum nf_nat_manip_type maniptype, struct sw_flow_key *key)  {  	int hooknum, nh_off, err = NF_ACCEPT; @@ -813,58 +864,11 @@ static int ovs_ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct,  push:  	skb_push_rcsum(skb, nh_off); -	return err; -} - -static void ovs_nat_update_key(struct sw_flow_key *key, -			       const struct sk_buff *skb, -			       enum nf_nat_manip_type maniptype) -{ -	if (maniptype == NF_NAT_MANIP_SRC) { -		__be16 src; - -		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)) -			memcpy(&key->ipv6.addr.src, &ipv6_hdr(skb)->saddr, -			       sizeof(key->ipv6.addr.src)); -		else -			return; - -		if (key->ip.proto == IPPROTO_UDP) -			src = udp_hdr(skb)->source; -		else if (key->ip.proto == IPPROTO_TCP) -			src = tcp_hdr(skb)->source; -		else if (key->ip.proto == IPPROTO_SCTP) -			src = sctp_hdr(skb)->source; -		else -			return; - -		key->tp.src = src; -	} else { -		__be16 dst; - -		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)) -			memcpy(&key->ipv6.addr.dst, &ipv6_hdr(skb)->daddr, -			       sizeof(key->ipv6.addr.dst)); -		else -			return; - -		if (key->ip.proto == IPPROTO_UDP) -			dst = udp_hdr(skb)->dest; -		else if (key->ip.proto == IPPROTO_TCP) -			dst = tcp_hdr(skb)->dest; -		else if (key->ip.proto == IPPROTO_SCTP) -			dst = sctp_hdr(skb)->dest; -		else -			return; +	/* Update the flow key if NAT successful. */ +	if (err == NF_ACCEPT) +		ovs_nat_update_key(key, skb, maniptype); -		key->tp.dst = dst; -	} +	return err;  }  /* Returns NF_DROP if the packet should be dropped, NF_ACCEPT otherwise. */ @@ -906,7 +910,7 @@ static int ovs_ct_nat(struct net *net, struct sw_flow_key *key,  	} else {  		return NF_ACCEPT; /* Connection is not NATed. */  	} -	err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, maniptype); +	err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, maniptype, key);  	if (err == NF_ACCEPT && ct->status & IPS_DST_NAT) {  		if (ct->status & IPS_SRC_NAT) { @@ -916,17 +920,13 @@ static int ovs_ct_nat(struct net *net, struct sw_flow_key *key,  				maniptype = NF_NAT_MANIP_SRC;  			err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, -						 maniptype); +						 maniptype, key);  		} else if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) {  			err = ovs_ct_nat_execute(skb, ct, ctinfo, NULL, -						 NF_NAT_MANIP_SRC); +						 NF_NAT_MANIP_SRC, key);  		}  	} -	/* Mark NAT done if successful and update the flow key. */ -	if (err == NF_ACCEPT) -		ovs_nat_update_key(key, skb, maniptype); -  	return err;  }  #else /* !CONFIG_NF_NAT */ diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 67ad08320886..7e8a39a35627 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -37,6 +37,7 @@  #include <net/genetlink.h>  #include <net/net_namespace.h>  #include <net/netns/generic.h> +#include <net/pkt_cls.h>  #include "datapath.h"  #include "flow.h" @@ -1601,8 +1602,6 @@ static void ovs_dp_reset_user_features(struct sk_buff *skb,  	dp->user_features = 0;  } -DEFINE_STATIC_KEY_FALSE(tc_recirc_sharing_support); -  static int ovs_dp_set_upcall_portids(struct datapath *dp,  			      const struct nlattr *ids)  { @@ -1657,7 +1656,7 @@ u32 ovs_dp_get_upcall_portid(const struct datapath *dp, uint32_t cpu_id)  static int ovs_dp_change(struct datapath *dp, struct nlattr *a[])  { -	u32 user_features = 0; +	u32 user_features = 0, old_features = dp->user_features;  	int err;  	if (a[OVS_DP_ATTR_USER_FEATURES]) { @@ -1696,10 +1695,12 @@ static int ovs_dp_change(struct datapath *dp, struct nlattr *a[])  			return err;  	} -	if (dp->user_features & OVS_DP_F_TC_RECIRC_SHARING) -		static_branch_enable(&tc_recirc_sharing_support); -	else -		static_branch_disable(&tc_recirc_sharing_support); +	if ((dp->user_features & OVS_DP_F_TC_RECIRC_SHARING) && +	    !(old_features & OVS_DP_F_TC_RECIRC_SHARING)) +		tc_skb_ext_tc_enable(); +	else if (!(dp->user_features & OVS_DP_F_TC_RECIRC_SHARING) && +		 (old_features & OVS_DP_F_TC_RECIRC_SHARING)) +		tc_skb_ext_tc_disable();  	return 0;  } @@ -1839,6 +1840,9 @@ static void __dp_destroy(struct datapath *dp)  	struct flow_table *table = &dp->table;  	int i; +	if (dp->user_features & OVS_DP_F_TC_RECIRC_SHARING) +		tc_skb_ext_tc_disable(); +  	for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) {  		struct vport *vport;  		struct hlist_node *n; diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h index fcfe6cb46441..0cd29971a907 100644 --- a/net/openvswitch/datapath.h +++ b/net/openvswitch/datapath.h @@ -253,8 +253,6 @@ static inline struct datapath *get_dp(struct net *net, int dp_ifindex)  extern struct notifier_block ovs_dp_device_notifier;  extern struct genl_family dp_vport_genl_family; -DECLARE_STATIC_KEY_FALSE(tc_recirc_sharing_support); -  void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key);  void ovs_dp_detach_port(struct vport *);  int ovs_dp_upcall(struct datapath *, struct sk_buff *, diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index 02096f2ec678..372bf54a0ca9 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c @@ -34,6 +34,7 @@  #include <net/mpls.h>  #include <net/ndisc.h>  #include <net/nsh.h> +#include <net/pkt_cls.h>  #include <net/netfilter/nf_conntrack_zones.h>  #include "conntrack.h" @@ -240,6 +241,144 @@ static bool icmphdr_ok(struct sk_buff *skb)  				  sizeof(struct icmphdr));  } +/** + * get_ipv6_ext_hdrs() - Parses packet and sets IPv6 extension header flags. + * + * @skb: buffer where extension header data starts in packet + * @nh: ipv6 header + * @ext_hdrs: flags are stored here + * + * OFPIEH12_UNREP is set if more than one of a given IPv6 extension header + * is unexpectedly encountered. (Two destination options headers may be + * expected and would not cause this bit to be set.) + * + * OFPIEH12_UNSEQ is set if IPv6 extension headers were not in the order + * preferred (but not required) by RFC 2460: + * + * When more than one extension header is used in the same packet, it is + * recommended that those headers appear in the following order: + *      IPv6 header + *      Hop-by-Hop Options header + *      Destination Options header + *      Routing header + *      Fragment header + *      Authentication header + *      Encapsulating Security Payload header + *      Destination Options header + *      upper-layer header + */ +static void get_ipv6_ext_hdrs(struct sk_buff *skb, struct ipv6hdr *nh, +			      u16 *ext_hdrs) +{ +	u8 next_type = nh->nexthdr; +	unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr); +	int dest_options_header_count = 0; + +	*ext_hdrs = 0; + +	while (ipv6_ext_hdr(next_type)) { +		struct ipv6_opt_hdr _hdr, *hp; + +		switch (next_type) { +		case IPPROTO_NONE: +			*ext_hdrs |= OFPIEH12_NONEXT; +			/* stop parsing */ +			return; + +		case IPPROTO_ESP: +			if (*ext_hdrs & OFPIEH12_ESP) +				*ext_hdrs |= OFPIEH12_UNREP; +			if ((*ext_hdrs & ~(OFPIEH12_HOP | OFPIEH12_DEST | +					   OFPIEH12_ROUTER | IPPROTO_FRAGMENT | +					   OFPIEH12_AUTH | OFPIEH12_UNREP)) || +			    dest_options_header_count >= 2) { +				*ext_hdrs |= OFPIEH12_UNSEQ; +			} +			*ext_hdrs |= OFPIEH12_ESP; +			break; + +		case IPPROTO_AH: +			if (*ext_hdrs & OFPIEH12_AUTH) +				*ext_hdrs |= OFPIEH12_UNREP; +			if ((*ext_hdrs & +			     ~(OFPIEH12_HOP | OFPIEH12_DEST | OFPIEH12_ROUTER | +			       IPPROTO_FRAGMENT | OFPIEH12_UNREP)) || +			    dest_options_header_count >= 2) { +				*ext_hdrs |= OFPIEH12_UNSEQ; +			} +			*ext_hdrs |= OFPIEH12_AUTH; +			break; + +		case IPPROTO_DSTOPTS: +			if (dest_options_header_count == 0) { +				if (*ext_hdrs & +				    ~(OFPIEH12_HOP | OFPIEH12_UNREP)) +					*ext_hdrs |= OFPIEH12_UNSEQ; +				*ext_hdrs |= OFPIEH12_DEST; +			} else if (dest_options_header_count == 1) { +				if (*ext_hdrs & +				    ~(OFPIEH12_HOP | OFPIEH12_DEST | +				      OFPIEH12_ROUTER | OFPIEH12_FRAG | +				      OFPIEH12_AUTH | OFPIEH12_ESP | +				      OFPIEH12_UNREP)) { +					*ext_hdrs |= OFPIEH12_UNSEQ; +				} +			} else { +				*ext_hdrs |= OFPIEH12_UNREP; +			} +			dest_options_header_count++; +			break; + +		case IPPROTO_FRAGMENT: +			if (*ext_hdrs & OFPIEH12_FRAG) +				*ext_hdrs |= OFPIEH12_UNREP; +			if ((*ext_hdrs & ~(OFPIEH12_HOP | +					   OFPIEH12_DEST | +					   OFPIEH12_ROUTER | +					   OFPIEH12_UNREP)) || +			    dest_options_header_count >= 2) { +				*ext_hdrs |= OFPIEH12_UNSEQ; +			} +			*ext_hdrs |= OFPIEH12_FRAG; +			break; + +		case IPPROTO_ROUTING: +			if (*ext_hdrs & OFPIEH12_ROUTER) +				*ext_hdrs |= OFPIEH12_UNREP; +			if ((*ext_hdrs & ~(OFPIEH12_HOP | +					   OFPIEH12_DEST | +					   OFPIEH12_UNREP)) || +			    dest_options_header_count >= 2) { +				*ext_hdrs |= OFPIEH12_UNSEQ; +			} +			*ext_hdrs |= OFPIEH12_ROUTER; +			break; + +		case IPPROTO_HOPOPTS: +			if (*ext_hdrs & OFPIEH12_HOP) +				*ext_hdrs |= OFPIEH12_UNREP; +			/* OFPIEH12_HOP is set to 1 if a hop-by-hop IPv6 +			 * extension header is present as the first +			 * extension header in the packet. +			 */ +			if (*ext_hdrs == 0) +				*ext_hdrs |= OFPIEH12_HOP; +			else +				*ext_hdrs |= OFPIEH12_UNSEQ; +			break; + +		default: +			return; +		} + +		hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); +		if (!hp) +			break; +		next_type = hp->nexthdr; +		start += ipv6_optlen(hp); +	} +} +  static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key)  {  	unsigned short frag_off; @@ -255,6 +394,8 @@ static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key)  	nh = ipv6_hdr(skb); +	get_ipv6_ext_hdrs(skb, nh, &key->ipv6.exthdrs); +  	key->ip.proto = NEXTHDR_NONE;  	key->ip.tos = ipv6_get_dsfield(nh);  	key->ip.ttl = nh->hop_limit; @@ -895,7 +1036,7 @@ int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info,  	key->mac_proto = res;  #if IS_ENABLED(CONFIG_NET_TC_SKB_EXT) -	if (static_branch_unlikely(&tc_recirc_sharing_support)) { +	if (tc_skb_ext_tc_enabled()) {  		tc_ext = skb_ext_find(skb, TC_SKB_EXT);  		key->recirc_id = tc_ext ? tc_ext->chain : 0;  		OVS_CB(skb)->mru = tc_ext ? tc_ext->mru : 0; diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h index 758a8c77f736..073ab73ffeaa 100644 --- a/net/openvswitch/flow.h +++ b/net/openvswitch/flow.h @@ -32,6 +32,19 @@ enum sw_flow_mac_proto {  #define SW_FLOW_KEY_INVALID	0x80  #define MPLS_LABEL_DEPTH       3 +/* Bit definitions for IPv6 Extension Header pseudo-field. */ +enum ofp12_ipv6exthdr_flags { +	OFPIEH12_NONEXT = 1 << 0,   /* "No next header" encountered. */ +	OFPIEH12_ESP    = 1 << 1,   /* Encrypted Sec Payload header present. */ +	OFPIEH12_AUTH   = 1 << 2,   /* Authentication header present. */ +	OFPIEH12_DEST   = 1 << 3,   /* 1 or 2 dest headers present. */ +	OFPIEH12_FRAG   = 1 << 4,   /* Fragment header present. */ +	OFPIEH12_ROUTER = 1 << 5,   /* Router header present. */ +	OFPIEH12_HOP    = 1 << 6,   /* Hop-by-hop header present. */ +	OFPIEH12_UNREP  = 1 << 7,   /* Unexpected repeats encountered. */ +	OFPIEH12_UNSEQ  = 1 << 8    /* Unexpected sequencing encountered. */ +}; +  /* Store options at the end of the array if they are less than the   * maximum size. This allows us to get the benefits of variable length   * matching for small options. @@ -121,6 +134,7 @@ struct sw_flow_key {  				struct in6_addr dst;	/* IPv6 destination address. */  			} addr;  			__be32 label;			/* IPv6 flow label. */ +			u16 exthdrs;	/* IPv6 extension header flags */  			union {  				struct {  					struct in6_addr src; diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index fd1f809e9bc1..4c09cf8a0ab2 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -346,7 +346,7 @@ size_t ovs_key_attr_size(void)  	/* Whenever adding new OVS_KEY_ FIELDS, we should consider  	 * updating this function.  	 */ -	BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 29); +	BUILD_BUG_ON(OVS_KEY_ATTR_MAX != 32);  	return    nla_total_size(4)   /* OVS_KEY_ATTR_PRIORITY */  		+ nla_total_size(0)   /* OVS_KEY_ATTR_TUNNEL */ @@ -369,7 +369,8 @@ size_t ovs_key_attr_size(void)  		+ nla_total_size(2)   /* OVS_KEY_ATTR_ETHERTYPE */  		+ nla_total_size(40)  /* OVS_KEY_ATTR_IPV6 */  		+ nla_total_size(2)   /* OVS_KEY_ATTR_ICMPV6 */ -		+ nla_total_size(28); /* OVS_KEY_ATTR_ND */ +		+ nla_total_size(28)  /* OVS_KEY_ATTR_ND */ +		+ nla_total_size(2);  /* OVS_KEY_ATTR_IPV6_EXTHDRS */  }  static const struct ovs_len_tbl ovs_vxlan_ext_key_lens[OVS_VXLAN_EXT_MAX + 1] = { @@ -437,6 +438,8 @@ static const struct ovs_len_tbl ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {  		.len = sizeof(struct ovs_key_ct_tuple_ipv6) },  	[OVS_KEY_ATTR_NSH]       = { .len = OVS_ATTR_NESTED,  				     .next = ovs_nsh_key_attr_lens, }, +	[OVS_KEY_ATTR_IPV6_EXTHDRS] = { +		.len = sizeof(struct ovs_key_ipv6_exthdrs) },  };  static bool check_attr_len(unsigned int attr_len, unsigned int expected_len) @@ -479,7 +482,14 @@ static int __parse_flow_nlattrs(const struct nlattr *attr,  			return -EINVAL;  		} -		if (attrs & (1 << type)) { +		if (type == OVS_KEY_ATTR_PACKET_TYPE || +		    type == OVS_KEY_ATTR_ND_EXTENSIONS || +		    type == OVS_KEY_ATTR_TUNNEL_INFO) { +			OVS_NLERR(log, "Key type %d is not supported", type); +			return -EINVAL; +		} + +		if (attrs & (1ULL << type)) {  			OVS_NLERR(log, "Duplicate key (type %d).", type);  			return -EINVAL;  		} @@ -492,7 +502,7 @@ static int __parse_flow_nlattrs(const struct nlattr *attr,  		}  		if (!nz || !is_all_zero(nla_data(nla), nla_len(nla))) { -			attrs |= 1 << type; +			attrs |= 1ULL << type;  			a[type] = nla;  		}  	} @@ -1597,6 +1607,17 @@ static int ovs_key_from_nlattrs(struct net *net, struct sw_flow_match *match,  		attrs &= ~(1 << OVS_KEY_ATTR_IPV6);  	} +	if (attrs & (1ULL << OVS_KEY_ATTR_IPV6_EXTHDRS)) { +		const struct ovs_key_ipv6_exthdrs *ipv6_exthdrs_key; + +		ipv6_exthdrs_key = nla_data(a[OVS_KEY_ATTR_IPV6_EXTHDRS]); + +		SW_FLOW_KEY_PUT(match, ipv6.exthdrs, +				ipv6_exthdrs_key->hdrs, is_mask); + +		attrs &= ~(1ULL << OVS_KEY_ATTR_IPV6_EXTHDRS); +	} +  	if (attrs & (1 << OVS_KEY_ATTR_ARP)) {  		const struct ovs_key_arp *arp_key; @@ -2099,6 +2120,7 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey,  		ipv4_key->ipv4_frag = output->ip.frag;  	} else if (swkey->eth.type == htons(ETH_P_IPV6)) {  		struct ovs_key_ipv6 *ipv6_key; +		struct ovs_key_ipv6_exthdrs *ipv6_exthdrs_key;  		nla = nla_reserve(skb, OVS_KEY_ATTR_IPV6, sizeof(*ipv6_key));  		if (!nla) @@ -2113,6 +2135,13 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey,  		ipv6_key->ipv6_tclass = output->ip.tos;  		ipv6_key->ipv6_hlimit = output->ip.ttl;  		ipv6_key->ipv6_frag = output->ip.frag; + +		nla = nla_reserve(skb, OVS_KEY_ATTR_IPV6_EXTHDRS, +				  sizeof(*ipv6_exthdrs_key)); +		if (!nla) +			goto nla_put_failure; +		ipv6_exthdrs_key = nla_data(nla); +		ipv6_exthdrs_key->hdrs = output->ipv6.exthdrs;  	} else if (swkey->eth.type == htons(ETH_P_NSH)) {  		if (nsh_key_to_nlattr(&output->nsh, is_mask, skb))  			goto nla_put_failure; @@ -2201,8 +2230,8 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey,  			icmpv6_key->icmpv6_type = ntohs(output->tp.src);  			icmpv6_key->icmpv6_code = ntohs(output->tp.dst); -			if (icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_SOLICITATION || -			    icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_ADVERTISEMENT) { +			if (swkey->tp.src == htons(NDISC_NEIGHBOUR_SOLICITATION) || +			    swkey->tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) {  				struct ovs_key_nd *nd_key;  				nla = nla_reserve(skb, OVS_KEY_ATTR_ND, sizeof(*nd_key)); @@ -2288,6 +2317,62 @@ static struct sw_flow_actions *nla_alloc_flow_actions(int size)  	return sfa;  } +static void ovs_nla_free_nested_actions(const struct nlattr *actions, int len); + +static void ovs_nla_free_check_pkt_len_action(const struct nlattr *action) +{ +	const struct nlattr *a; +	int rem; + +	nla_for_each_nested(a, action, rem) { +		switch (nla_type(a)) { +		case OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL: +		case OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER: +			ovs_nla_free_nested_actions(nla_data(a), nla_len(a)); +			break; +		} +	} +} + +static void ovs_nla_free_clone_action(const struct nlattr *action) +{ +	const struct nlattr *a = nla_data(action); +	int rem = nla_len(action); + +	switch (nla_type(a)) { +	case OVS_CLONE_ATTR_EXEC: +		/* The real list of actions follows this attribute. */ +		a = nla_next(a, &rem); +		ovs_nla_free_nested_actions(a, rem); +		break; +	} +} + +static void ovs_nla_free_dec_ttl_action(const struct nlattr *action) +{ +	const struct nlattr *a = nla_data(action); + +	switch (nla_type(a)) { +	case OVS_DEC_TTL_ATTR_ACTION: +		ovs_nla_free_nested_actions(nla_data(a), nla_len(a)); +		break; +	} +} + +static void ovs_nla_free_sample_action(const struct nlattr *action) +{ +	const struct nlattr *a = nla_data(action); +	int rem = nla_len(action); + +	switch (nla_type(a)) { +	case OVS_SAMPLE_ATTR_ARG: +		/* The real list of actions follows this attribute. */ +		a = nla_next(a, &rem); +		ovs_nla_free_nested_actions(a, rem); +		break; +	} +} +  static void ovs_nla_free_set_action(const struct nlattr *a)  {  	const struct nlattr *ovs_key = nla_data(a); @@ -2301,25 +2386,54 @@ static void ovs_nla_free_set_action(const struct nlattr *a)  	}  } -void ovs_nla_free_flow_actions(struct sw_flow_actions *sf_acts) +static void ovs_nla_free_nested_actions(const struct nlattr *actions, int len)  {  	const struct nlattr *a;  	int rem; -	if (!sf_acts) +	/* Whenever new actions are added, the need to update this +	 * function should be considered. +	 */ +	BUILD_BUG_ON(OVS_ACTION_ATTR_MAX != 23); + +	if (!actions)  		return; -	nla_for_each_attr(a, sf_acts->actions, sf_acts->actions_len, rem) { +	nla_for_each_attr(a, actions, len, rem) {  		switch (nla_type(a)) { -		case OVS_ACTION_ATTR_SET: -			ovs_nla_free_set_action(a); +		case OVS_ACTION_ATTR_CHECK_PKT_LEN: +			ovs_nla_free_check_pkt_len_action(a);  			break; + +		case OVS_ACTION_ATTR_CLONE: +			ovs_nla_free_clone_action(a); +			break; +  		case OVS_ACTION_ATTR_CT:  			ovs_ct_free_action(a);  			break; + +		case OVS_ACTION_ATTR_DEC_TTL: +			ovs_nla_free_dec_ttl_action(a); +			break; + +		case OVS_ACTION_ATTR_SAMPLE: +			ovs_nla_free_sample_action(a); +			break; + +		case OVS_ACTION_ATTR_SET: +			ovs_nla_free_set_action(a); +			break;  		}  	} +} + +void ovs_nla_free_flow_actions(struct sw_flow_actions *sf_acts) +{ +	if (!sf_acts) +		return; +	ovs_nla_free_nested_actions(sf_acts->actions, sf_acts->actions_len);  	kfree(sf_acts);  } @@ -2351,7 +2465,7 @@ static struct nlattr *reserve_sfa_size(struct sw_flow_actions **sfa,  	new_acts_size = max(next_offset + req_size, ksize(*sfa) * 2);  	if (new_acts_size > MAX_ACTIONS_BUFSIZE) { -		if ((MAX_ACTIONS_BUFSIZE - next_offset) < req_size) { +		if ((next_offset + req_size) > MAX_ACTIONS_BUFSIZE) {  			OVS_NLERR(log, "Flow action size exceeds max %u",  				  MAX_ACTIONS_BUFSIZE);  			return ERR_PTR(-EMSGSIZE); @@ -3429,7 +3543,9 @@ static int clone_action_to_attr(const struct nlattr *attr,  	if (!start)  		return -EMSGSIZE; -	err = ovs_nla_put_actions(nla_data(attr), rem, skb); +	/* Skipping the OVS_CLONE_ATTR_EXEC that is always the first attribute. */ +	attr = nla_next(nla_data(attr), &rem); +	err = ovs_nla_put_actions(attr, rem, skb);  	if (err)  		nla_nest_cancel(skb, start); diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index cf2ce5812489..82a74f998966 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -507,7 +507,7 @@ void ovs_vport_send(struct vport *vport, struct sk_buff *skb, u8 mac_proto)  	}  	skb->dev = vport->dev; -	skb->tstamp = 0; +	skb_clear_tstamp(skb);  	vport->ops->send(skb);  	return; | 
