diff options
Diffstat (limited to 'net/mpls/af_mpls.c')
| -rw-r--r-- | net/mpls/af_mpls.c | 48 | 
1 files changed, 25 insertions, 23 deletions
| diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 15fe97644ffe..5b77377e5a15 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -98,18 +98,19 @@ bool mpls_pkt_too_big(const struct sk_buff *skb, unsigned int mtu)  }  EXPORT_SYMBOL_GPL(mpls_pkt_too_big); -static u32 mpls_multipath_hash(struct mpls_route *rt, -			       struct sk_buff *skb, bool bos) +static u32 mpls_multipath_hash(struct mpls_route *rt, struct sk_buff *skb)  {  	struct mpls_entry_decoded dec; +	unsigned int mpls_hdr_len = 0;  	struct mpls_shim_hdr *hdr;  	bool eli_seen = false;  	int label_index;  	u32 hash = 0; -	for (label_index = 0; label_index < MAX_MP_SELECT_LABELS && !bos; +	for (label_index = 0; label_index < MAX_MP_SELECT_LABELS;  	     label_index++) { -		if (!pskb_may_pull(skb, sizeof(*hdr) * label_index)) +		mpls_hdr_len += sizeof(*hdr); +		if (!pskb_may_pull(skb, mpls_hdr_len))  			break;  		/* Read and decode the current label */ @@ -134,37 +135,38 @@ static u32 mpls_multipath_hash(struct mpls_route *rt,  			eli_seen = true;  		} -		bos = dec.bos; -		if (bos && pskb_may_pull(skb, sizeof(*hdr) * label_index + -					 sizeof(struct iphdr))) { +		if (!dec.bos) +			continue; + +		/* found bottom label; does skb have room for a header? */ +		if (pskb_may_pull(skb, mpls_hdr_len + sizeof(struct iphdr))) {  			const struct iphdr *v4hdr; -			v4hdr = (const struct iphdr *)(mpls_hdr(skb) + -						       label_index); +			v4hdr = (const struct iphdr *)(hdr + 1);  			if (v4hdr->version == 4) {  				hash = jhash_3words(ntohl(v4hdr->saddr),  						    ntohl(v4hdr->daddr),  						    v4hdr->protocol, hash);  			} else if (v4hdr->version == 6 && -				pskb_may_pull(skb, sizeof(*hdr) * label_index + -					      sizeof(struct ipv6hdr))) { +				   pskb_may_pull(skb, mpls_hdr_len + +						 sizeof(struct ipv6hdr))) {  				const struct ipv6hdr *v6hdr; -				v6hdr = (const struct ipv6hdr *)(mpls_hdr(skb) + -								label_index); - +				v6hdr = (const struct ipv6hdr *)(hdr + 1);  				hash = __ipv6_addr_jhash(&v6hdr->saddr, hash);  				hash = __ipv6_addr_jhash(&v6hdr->daddr, hash);  				hash = jhash_1word(v6hdr->nexthdr, hash);  			}  		} + +		break;  	}  	return hash;  }  static struct mpls_nh *mpls_select_multipath(struct mpls_route *rt, -					     struct sk_buff *skb, bool bos) +					     struct sk_buff *skb)  {  	int alive = ACCESS_ONCE(rt->rt_nhn_alive);  	u32 hash = 0; @@ -180,7 +182,7 @@ static struct mpls_nh *mpls_select_multipath(struct mpls_route *rt,  	if (alive <= 0)  		return NULL; -	hash = mpls_multipath_hash(rt, skb, bos); +	hash = mpls_multipath_hash(rt, skb);  	nh_index = hash % alive;  	if (alive == rt->rt_nhn)  		goto out; @@ -278,17 +280,11 @@ static int mpls_forward(struct sk_buff *skb, struct net_device *dev,  	hdr = mpls_hdr(skb);  	dec = mpls_entry_decode(hdr); -	/* Pop the label */ -	skb_pull(skb, sizeof(*hdr)); -	skb_reset_network_header(skb); - -	skb_orphan(skb); -  	rt = mpls_route_input_rcu(net, dec.label);  	if (!rt)  		goto drop; -	nh = mpls_select_multipath(rt, skb, dec.bos); +	nh = mpls_select_multipath(rt, skb);  	if (!nh)  		goto drop; @@ -297,6 +293,12 @@ static int mpls_forward(struct sk_buff *skb, struct net_device *dev,  	if (!mpls_output_possible(out_dev))  		goto drop; +	/* Pop the label */ +	skb_pull(skb, sizeof(*hdr)); +	skb_reset_network_header(skb); + +	skb_orphan(skb); +  	if (skb_warn_if_lro(skb))  		goto drop; |