diff options
Diffstat (limited to 'net')
55 files changed, 851 insertions, 308 deletions
| diff --git a/net/Kconfig b/net/Kconfig index 6272420a721b..99815b5454bf 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -6,7 +6,7 @@ menuconfig NET  	bool "Networking support"  	select NLATTR  	select GENERIC_NET_UTILS -	select ANON_INODES +	select BPF  	---help---  	  Unless you really know what you are doing, you should say Y here.  	  The reason is that some programs need kernel networking support even diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 992ec49a96aa..44cb786b925a 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -112,6 +112,7 @@ void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)  	kfree_skb(skb);  } +EXPORT_SYMBOL_GPL(br_deliver);  /* called with rcu_read_lock */  void br_forward(const struct net_bridge_port *to, struct sk_buff *skb, struct sk_buff *skb0) diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 1bada53bb195..1a4f32c09ad5 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -192,7 +192,6 @@ static inline void nf_bridge_save_header(struct sk_buff *skb)  static int br_parse_ip_options(struct sk_buff *skb)  { -	struct ip_options *opt;  	const struct iphdr *iph;  	struct net_device *dev = skb->dev;  	u32 len; @@ -201,7 +200,6 @@ static int br_parse_ip_options(struct sk_buff *skb)  		goto inhdr_error;  	iph = ip_hdr(skb); -	opt = &(IPCB(skb)->opt);  	/* Basic sanity checks */  	if (iph->ihl < 5 || iph->version != 4) @@ -227,23 +225,11 @@ static int br_parse_ip_options(struct sk_buff *skb)  	}  	memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); -	if (iph->ihl == 5) -		return 0; - -	opt->optlen = iph->ihl*4 - sizeof(struct iphdr); -	if (ip_options_compile(dev_net(dev), opt, skb)) -		goto inhdr_error; - -	/* Check correct handling of SRR option */ -	if (unlikely(opt->srr)) { -		struct in_device *in_dev = __in_dev_get_rcu(dev); -		if (in_dev && !IN_DEV_SOURCE_ROUTE(in_dev)) -			goto drop; - -		if (ip_options_rcv_srr(skb)) -			goto drop; -	} - +	/* We should really parse IP options here but until +	 * somebody who actually uses IP options complains to +	 * us we'll just silently ignore the options because +	 * we're lazy! +	 */  	return 0;  inhdr_error: diff --git a/net/bridge/netfilter/nf_tables_bridge.c b/net/bridge/netfilter/nf_tables_bridge.c index da17a5eab8b4..074c557ab505 100644 --- a/net/bridge/netfilter/nf_tables_bridge.c +++ b/net/bridge/netfilter/nf_tables_bridge.c @@ -75,9 +75,11 @@ static const struct nf_chain_type filter_bridge = {  	.type		= NFT_CHAIN_T_DEFAULT,  	.family		= NFPROTO_BRIDGE,  	.owner		= THIS_MODULE, -	.hook_mask	= (1 << NF_BR_LOCAL_IN) | +	.hook_mask	= (1 << NF_BR_PRE_ROUTING) | +			  (1 << NF_BR_LOCAL_IN) |  			  (1 << NF_BR_FORWARD) | -			  (1 << NF_BR_LOCAL_OUT), +			  (1 << NF_BR_LOCAL_OUT) | +			  (1 << NF_BR_POST_ROUTING),  };  static int __init nf_tables_bridge_init(void) diff --git a/net/bridge/netfilter/nft_reject_bridge.c b/net/bridge/netfilter/nft_reject_bridge.c index a76479535df2..654c9018e3e7 100644 --- a/net/bridge/netfilter/nft_reject_bridge.c +++ b/net/bridge/netfilter/nft_reject_bridge.c @@ -16,6 +16,238 @@  #include <net/netfilter/nft_reject.h>  #include <net/netfilter/ipv4/nf_reject.h>  #include <net/netfilter/ipv6/nf_reject.h> +#include <linux/ip.h> +#include <net/ip.h> +#include <linux/netfilter_bridge.h> +#include "../br_private.h" + +static void nft_reject_br_push_etherhdr(struct sk_buff *oldskb, +					struct sk_buff *nskb) +{ +	struct ethhdr *eth; + +	eth = (struct ethhdr *)skb_push(nskb, ETH_HLEN); +	skb_reset_mac_header(nskb); +	ether_addr_copy(eth->h_source, eth_hdr(oldskb)->h_dest); +	ether_addr_copy(eth->h_dest, eth_hdr(oldskb)->h_source); +	eth->h_proto = eth_hdr(oldskb)->h_proto; +	skb_pull(nskb, ETH_HLEN); +} + +static int nft_reject_iphdr_validate(struct sk_buff *oldskb) +{ +	struct iphdr *iph; +	u32 len; + +	if (!pskb_may_pull(oldskb, sizeof(struct iphdr))) +		return 0; + +	iph = ip_hdr(oldskb); +	if (iph->ihl < 5 || iph->version != 4) +		return 0; + +	len = ntohs(iph->tot_len); +	if (oldskb->len < len) +		return 0; +	else if (len < (iph->ihl*4)) +		return 0; + +	if (!pskb_may_pull(oldskb, iph->ihl*4)) +		return 0; + +	return 1; +} + +static void nft_reject_br_send_v4_tcp_reset(struct sk_buff *oldskb, int hook) +{ +	struct sk_buff *nskb; +	struct iphdr *niph; +	const struct tcphdr *oth; +	struct tcphdr _oth; + +	if (!nft_reject_iphdr_validate(oldskb)) +		return; + +	oth = nf_reject_ip_tcphdr_get(oldskb, &_oth, hook); +	if (!oth) +		return; + +	nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) + +			 LL_MAX_HEADER, GFP_ATOMIC); +	if (!nskb) +		return; + +	skb_reserve(nskb, LL_MAX_HEADER); +	niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_TCP, +				   sysctl_ip_default_ttl); +	nf_reject_ip_tcphdr_put(nskb, oldskb, oth); +	niph->ttl	= sysctl_ip_default_ttl; +	niph->tot_len	= htons(nskb->len); +	ip_send_check(niph); + +	nft_reject_br_push_etherhdr(oldskb, nskb); + +	br_deliver(br_port_get_rcu(oldskb->dev), nskb); +} + +static void nft_reject_br_send_v4_unreach(struct sk_buff *oldskb, int hook, +					  u8 code) +{ +	struct sk_buff *nskb; +	struct iphdr *niph; +	struct icmphdr *icmph; +	unsigned int len; +	void *payload; +	__wsum csum; + +	if (!nft_reject_iphdr_validate(oldskb)) +		return; + +	/* IP header checks: fragment. */ +	if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET)) +		return; + +	/* RFC says return as much as we can without exceeding 576 bytes. */ +	len = min_t(unsigned int, 536, oldskb->len); + +	if (!pskb_may_pull(oldskb, len)) +		return; + +	if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), 0)) +		return; + +	nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct icmphdr) + +			 LL_MAX_HEADER + len, GFP_ATOMIC); +	if (!nskb) +		return; + +	skb_reserve(nskb, LL_MAX_HEADER); +	niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_ICMP, +				   sysctl_ip_default_ttl); + +	skb_reset_transport_header(nskb); +	icmph = (struct icmphdr *)skb_put(nskb, sizeof(struct icmphdr)); +	memset(icmph, 0, sizeof(*icmph)); +	icmph->type     = ICMP_DEST_UNREACH; +	icmph->code	= code; + +	payload = skb_put(nskb, len); +	memcpy(payload, skb_network_header(oldskb), len); + +	csum = csum_partial((void *)icmph, len + sizeof(struct icmphdr), 0); +	icmph->checksum = csum_fold(csum); + +	niph->tot_len	= htons(nskb->len); +	ip_send_check(niph); + +	nft_reject_br_push_etherhdr(oldskb, nskb); + +	br_deliver(br_port_get_rcu(oldskb->dev), nskb); +} + +static int nft_reject_ip6hdr_validate(struct sk_buff *oldskb) +{ +	struct ipv6hdr *hdr; +	u32 pkt_len; + +	if (!pskb_may_pull(oldskb, sizeof(struct ipv6hdr))) +		return 0; + +	hdr = ipv6_hdr(oldskb); +	if (hdr->version != 6) +		return 0; + +	pkt_len = ntohs(hdr->payload_len); +	if (pkt_len + sizeof(struct ipv6hdr) > oldskb->len) +		return 0; + +	return 1; +} + +static void nft_reject_br_send_v6_tcp_reset(struct net *net, +					    struct sk_buff *oldskb, int hook) +{ +	struct sk_buff *nskb; +	const struct tcphdr *oth; +	struct tcphdr _oth; +	unsigned int otcplen; +	struct ipv6hdr *nip6h; + +	if (!nft_reject_ip6hdr_validate(oldskb)) +		return; + +	oth = nf_reject_ip6_tcphdr_get(oldskb, &_oth, &otcplen, hook); +	if (!oth) +		return; + +	nskb = alloc_skb(sizeof(struct ipv6hdr) + sizeof(struct tcphdr) + +			 LL_MAX_HEADER, GFP_ATOMIC); +	if (!nskb) +		return; + +	skb_reserve(nskb, LL_MAX_HEADER); +	nip6h = nf_reject_ip6hdr_put(nskb, oldskb, IPPROTO_TCP, +				     net->ipv6.devconf_all->hop_limit); +	nf_reject_ip6_tcphdr_put(nskb, oldskb, oth, otcplen); +	nip6h->payload_len = htons(nskb->len - sizeof(struct ipv6hdr)); + +	nft_reject_br_push_etherhdr(oldskb, nskb); + +	br_deliver(br_port_get_rcu(oldskb->dev), nskb); +} + +static void nft_reject_br_send_v6_unreach(struct net *net, +					  struct sk_buff *oldskb, int hook, +					  u8 code) +{ +	struct sk_buff *nskb; +	struct ipv6hdr *nip6h; +	struct icmp6hdr *icmp6h; +	unsigned int len; +	void *payload; + +	if (!nft_reject_ip6hdr_validate(oldskb)) +		return; + +	/* Include "As much of invoking packet as possible without the ICMPv6 +	 * packet exceeding the minimum IPv6 MTU" in the ICMP payload. +	 */ +	len = min_t(unsigned int, 1220, oldskb->len); + +	if (!pskb_may_pull(oldskb, len)) +		return; + +	nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct icmp6hdr) + +			 LL_MAX_HEADER + len, GFP_ATOMIC); +	if (!nskb) +		return; + +	skb_reserve(nskb, LL_MAX_HEADER); +	nip6h = nf_reject_ip6hdr_put(nskb, oldskb, IPPROTO_ICMPV6, +				     net->ipv6.devconf_all->hop_limit); + +	skb_reset_transport_header(nskb); +	icmp6h = (struct icmp6hdr *)skb_put(nskb, sizeof(struct icmp6hdr)); +	memset(icmp6h, 0, sizeof(*icmp6h)); +	icmp6h->icmp6_type = ICMPV6_DEST_UNREACH; +	icmp6h->icmp6_code = code; + +	payload = skb_put(nskb, len); +	memcpy(payload, skb_network_header(oldskb), len); +	nip6h->payload_len = htons(nskb->len - sizeof(struct ipv6hdr)); + +	icmp6h->icmp6_cksum = +		csum_ipv6_magic(&nip6h->saddr, &nip6h->daddr, +				nskb->len - sizeof(struct ipv6hdr), +				IPPROTO_ICMPV6, +				csum_partial(icmp6h, +					     nskb->len - sizeof(struct ipv6hdr), +					     0)); + +	nft_reject_br_push_etherhdr(oldskb, nskb); + +	br_deliver(br_port_get_rcu(oldskb->dev), nskb); +}  static void nft_reject_bridge_eval(const struct nft_expr *expr,  				 struct nft_data data[NFT_REG_MAX + 1], @@ -23,35 +255,46 @@ static void nft_reject_bridge_eval(const struct nft_expr *expr,  {  	struct nft_reject *priv = nft_expr_priv(expr);  	struct net *net = dev_net((pkt->in != NULL) ? pkt->in : pkt->out); +	const unsigned char *dest = eth_hdr(pkt->skb)->h_dest; + +	if (is_broadcast_ether_addr(dest) || +	    is_multicast_ether_addr(dest)) +		goto out;  	switch (eth_hdr(pkt->skb)->h_proto) {  	case htons(ETH_P_IP):  		switch (priv->type) {  		case NFT_REJECT_ICMP_UNREACH: -			nf_send_unreach(pkt->skb, priv->icmp_code); +			nft_reject_br_send_v4_unreach(pkt->skb, +						      pkt->ops->hooknum, +						      priv->icmp_code);  			break;  		case NFT_REJECT_TCP_RST: -			nf_send_reset(pkt->skb, pkt->ops->hooknum); +			nft_reject_br_send_v4_tcp_reset(pkt->skb, +							pkt->ops->hooknum);  			break;  		case NFT_REJECT_ICMPX_UNREACH: -			nf_send_unreach(pkt->skb, -					nft_reject_icmp_code(priv->icmp_code)); +			nft_reject_br_send_v4_unreach(pkt->skb, +						      pkt->ops->hooknum, +						      nft_reject_icmp_code(priv->icmp_code));  			break;  		}  		break;  	case htons(ETH_P_IPV6):  		switch (priv->type) {  		case NFT_REJECT_ICMP_UNREACH: -			nf_send_unreach6(net, pkt->skb, priv->icmp_code, -					 pkt->ops->hooknum); +			nft_reject_br_send_v6_unreach(net, pkt->skb, +						      pkt->ops->hooknum, +						      priv->icmp_code);  			break;  		case NFT_REJECT_TCP_RST: -			nf_send_reset6(net, pkt->skb, pkt->ops->hooknum); +			nft_reject_br_send_v6_tcp_reset(net, pkt->skb, +							pkt->ops->hooknum);  			break;  		case NFT_REJECT_ICMPX_UNREACH: -			nf_send_unreach6(net, pkt->skb, -					 nft_reject_icmpv6_code(priv->icmp_code), -					 pkt->ops->hooknum); +			nft_reject_br_send_v6_unreach(net, pkt->skb, +						      pkt->ops->hooknum, +						      nft_reject_icmpv6_code(priv->icmp_code));  			break;  		}  		break; @@ -59,15 +302,38 @@ static void nft_reject_bridge_eval(const struct nft_expr *expr,  		/* No explicit way to reject this protocol, drop it. */  		break;  	} +out:  	data[NFT_REG_VERDICT].verdict = NF_DROP;  } +static int nft_reject_bridge_validate_hooks(const struct nft_chain *chain) +{ +	struct nft_base_chain *basechain; + +	if (chain->flags & NFT_BASE_CHAIN) { +		basechain = nft_base_chain(chain); + +		switch (basechain->ops[0].hooknum) { +		case NF_BR_PRE_ROUTING: +		case NF_BR_LOCAL_IN: +			break; +		default: +			return -EOPNOTSUPP; +		} +	} +	return 0; +} +  static int nft_reject_bridge_init(const struct nft_ctx *ctx,  				  const struct nft_expr *expr,  				  const struct nlattr * const tb[])  {  	struct nft_reject *priv = nft_expr_priv(expr); -	int icmp_code; +	int icmp_code, err; + +	err = nft_reject_bridge_validate_hooks(ctx->chain); +	if (err < 0) +		return err;  	if (tb[NFTA_REJECT_TYPE] == NULL)  		return -EINVAL; @@ -116,6 +382,13 @@ nla_put_failure:  	return -1;  } +static int nft_reject_bridge_validate(const struct nft_ctx *ctx, +				      const struct nft_expr *expr, +				      const struct nft_data **data) +{ +	return nft_reject_bridge_validate_hooks(ctx->chain); +} +  static struct nft_expr_type nft_reject_bridge_type;  static const struct nft_expr_ops nft_reject_bridge_ops = {  	.type		= &nft_reject_bridge_type, @@ -123,6 +396,7 @@ static const struct nft_expr_ops nft_reject_bridge_ops = {  	.eval		= nft_reject_bridge_eval,  	.init		= nft_reject_bridge_init,  	.dump		= nft_reject_bridge_dump, +	.validate	= nft_reject_bridge_validate,  };  static struct nft_expr_type nft_reject_bridge_type __read_mostly = { diff --git a/net/core/dev.c b/net/core/dev.c index b793e3521a36..945bbd001359 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4157,6 +4157,10 @@ EXPORT_SYMBOL(napi_gro_receive);  static void napi_reuse_skb(struct napi_struct *napi, struct sk_buff *skb)  { +	if (unlikely(skb->pfmemalloc)) { +		consume_skb(skb); +		return; +	}  	__skb_pull(skb, skb_headlen(skb));  	/* restore the reserve we had after netdev_alloc_skb_ip_align() */  	skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN - skb_headroom(skb)); diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 1600aa24d36b..06dfb293e5aa 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -1036,7 +1036,8 @@ static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr)  {  	const struct ethtool_ops *ops = dev->ethtool_ops; -	if (!ops->get_eeprom || !ops->get_eeprom_len) +	if (!ops->get_eeprom || !ops->get_eeprom_len || +	    !ops->get_eeprom_len(dev))  		return -EOPNOTSUPP;  	return ethtool_get_any_eeprom(dev, useraddr, ops->get_eeprom, @@ -1052,7 +1053,8 @@ static int ethtool_set_eeprom(struct net_device *dev, void __user *useraddr)  	u8 *data;  	int ret = 0; -	if (!ops->set_eeprom || !ops->get_eeprom_len) +	if (!ops->set_eeprom || !ops->get_eeprom_len || +	    !ops->get_eeprom_len(dev))  		return -EOPNOTSUPP;  	if (copy_from_user(&eeprom, useraddr, sizeof(eeprom))) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 61059a05ec95..c16615bfb61e 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -4070,15 +4070,22 @@ EXPORT_SYMBOL_GPL(skb_scrub_packet);  unsigned int skb_gso_transport_seglen(const struct sk_buff *skb)  {  	const struct skb_shared_info *shinfo = skb_shinfo(skb); +	unsigned int thlen = 0; -	if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) -		return tcp_hdrlen(skb) + shinfo->gso_size; +	if (skb->encapsulation) { +		thlen = skb_inner_transport_header(skb) - +			skb_transport_header(skb); +		if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) +			thlen += inner_tcp_hdrlen(skb); +	} else if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) { +		thlen = tcp_hdrlen(skb); +	}  	/* UFO sets gso_size to the size of the fragmentation  	 * payload, i.e. the size of the L4 (UDP) header is already  	 * accounted for.  	 */ -	return shinfo->gso_size; +	return thlen + shinfo->gso_size;  }  EXPORT_SYMBOL_GPL(skb_gso_transport_seglen); diff --git a/net/core/tso.c b/net/core/tso.c index 8c3203c585b0..630b30b4fb53 100644 --- a/net/core/tso.c +++ b/net/core/tso.c @@ -1,6 +1,7 @@  #include <linux/export.h>  #include <net/ip.h>  #include <net/tso.h> +#include <asm/unaligned.h>  /* Calculate expected number of TX descriptors */  int tso_count_descs(struct sk_buff *skb) @@ -23,7 +24,7 @@ void tso_build_hdr(struct sk_buff *skb, char *hdr, struct tso_t *tso,  	iph->id = htons(tso->ip_id);  	iph->tot_len = htons(size + hdr_len - mac_hdr_len);  	tcph = (struct tcphdr *)(hdr + skb_transport_offset(skb)); -	tcph->seq = htonl(tso->tcp_seq); +	put_unaligned_be32(tso->tcp_seq, &tcph->seq);  	tso->ip_id++;  	if (!is_last) { diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 22f34cf4cb27..6317b41c99b0 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -174,8 +174,11 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index,  			dst->rcv = brcm_netdev_ops.rcv;  			break;  #endif -		default: +		case DSA_TAG_PROTO_NONE:  			break; +		default: +			ret = -ENOPROTOOPT; +			goto out;  		}  		dst->tag_protocol = drv->tag_protocol; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 92db7a69f2b9..8b7fe5b03906 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1246,7 +1246,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,  	encap = SKB_GSO_CB(skb)->encap_level > 0;  	if (encap) -		features = skb->dev->hw_enc_features & netif_skb_features(skb); +		features &= skb->dev->hw_enc_features;  	SKB_GSO_CB(skb)->encap_level += ihl;  	skb_reset_transport_header(skb); diff --git a/net/ipv4/gre_offload.c b/net/ipv4/gre_offload.c index ccda09628de7..bb5947b0ce2d 100644 --- a/net/ipv4/gre_offload.c +++ b/net/ipv4/gre_offload.c @@ -47,7 +47,7 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb,  	greh = (struct gre_base_hdr *)skb_transport_header(skb); -	ghl = skb_inner_network_header(skb) - skb_transport_header(skb); +	ghl = skb_inner_mac_header(skb) - skb_transport_header(skb);  	if (unlikely(ghl < sizeof(*greh)))  		goto out; @@ -68,7 +68,7 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb,  	skb->mac_len = skb_inner_network_offset(skb);  	/* segment inner packet. */ -	enc_features = skb->dev->hw_enc_features & netif_skb_features(skb); +	enc_features = skb->dev->hw_enc_features & features;  	segs = skb_mac_gso_segment(skb, enc_features);  	if (IS_ERR_OR_NULL(segs)) {  		skb_gso_error_unwind(skb, protocol, ghl, mac_offset, mac_len); diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 9eb89f3f0ee4..19419b60cb37 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -146,7 +146,6 @@ evict_again:  			atomic_inc(&fq->refcnt);  			spin_unlock(&hb->chain_lock);  			del_timer_sync(&fq->timer); -			WARN_ON(atomic_read(&fq->refcnt) != 1);  			inet_frag_put(fq, f);  			goto evict_again;  		} @@ -285,7 +284,8 @@ static inline void fq_unlink(struct inet_frag_queue *fq, struct inet_frags *f)  	struct inet_frag_bucket *hb;  	hb = get_frag_bucket_locked(fq, f); -	hlist_del(&fq->list); +	if (!(fq->flags & INET_FRAG_EVICTED)) +		hlist_del(&fq->list);  	spin_unlock(&hb->chain_lock);  } diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 88e5ef2c7f51..bc6471d4abcd 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -231,7 +231,7 @@ static int ip_finish_output_gso(struct sk_buff *skb)  	 */  	features = netif_skb_features(skb);  	segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK); -	if (IS_ERR(segs)) { +	if (IS_ERR_OR_NULL(segs)) {  		kfree_skb(skb);  		return -ENOMEM;  	} diff --git a/net/ipv4/netfilter/nf_reject_ipv4.c b/net/ipv4/netfilter/nf_reject_ipv4.c index b023b4eb1a96..1baaa83dfe5c 100644 --- a/net/ipv4/netfilter/nf_reject_ipv4.c +++ b/net/ipv4/netfilter/nf_reject_ipv4.c @@ -6,48 +6,45 @@   * published by the Free Software Foundation.   */ +#include <linux/module.h>  #include <net/ip.h>  #include <net/tcp.h>  #include <net/route.h>  #include <net/dst.h>  #include <linux/netfilter_ipv4.h> +#include <net/netfilter/ipv4/nf_reject.h> -/* Send RST reply */ -void nf_send_reset(struct sk_buff *oldskb, int hook) +const struct tcphdr *nf_reject_ip_tcphdr_get(struct sk_buff *oldskb, +					     struct tcphdr *_oth, int hook)  { -	struct sk_buff *nskb; -	const struct iphdr *oiph; -	struct iphdr *niph;  	const struct tcphdr *oth; -	struct tcphdr _otcph, *tcph;  	/* IP header checks: fragment. */  	if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET)) -		return; +		return NULL;  	oth = skb_header_pointer(oldskb, ip_hdrlen(oldskb), -				 sizeof(_otcph), &_otcph); +				 sizeof(struct tcphdr), _oth);  	if (oth == NULL) -		return; +		return NULL;  	/* No RST for RST. */  	if (oth->rst) -		return; - -	if (skb_rtable(oldskb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) -		return; +		return NULL;  	/* Check checksum */  	if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP)) -		return; -	oiph = ip_hdr(oldskb); +		return NULL; -	nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) + -			 LL_MAX_HEADER, GFP_ATOMIC); -	if (!nskb) -		return; +	return oth; +} +EXPORT_SYMBOL_GPL(nf_reject_ip_tcphdr_get); -	skb_reserve(nskb, LL_MAX_HEADER); +struct iphdr *nf_reject_iphdr_put(struct sk_buff *nskb, +				  const struct sk_buff *oldskb, +				  __be16 protocol, int ttl) +{ +	struct iphdr *niph, *oiph = ip_hdr(oldskb);  	skb_reset_network_header(nskb);  	niph = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr)); @@ -56,10 +53,23 @@ void nf_send_reset(struct sk_buff *oldskb, int hook)  	niph->tos	= 0;  	niph->id	= 0;  	niph->frag_off	= htons(IP_DF); -	niph->protocol	= IPPROTO_TCP; +	niph->protocol	= protocol;  	niph->check	= 0;  	niph->saddr	= oiph->daddr;  	niph->daddr	= oiph->saddr; +	niph->ttl	= ttl; + +	nskb->protocol = htons(ETH_P_IP); + +	return niph; +} +EXPORT_SYMBOL_GPL(nf_reject_iphdr_put); + +void nf_reject_ip_tcphdr_put(struct sk_buff *nskb, const struct sk_buff *oldskb, +			  const struct tcphdr *oth) +{ +	struct iphdr *niph = ip_hdr(nskb); +	struct tcphdr *tcph;  	skb_reset_transport_header(nskb);  	tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); @@ -68,9 +78,9 @@ void nf_send_reset(struct sk_buff *oldskb, int hook)  	tcph->dest	= oth->source;  	tcph->doff	= sizeof(struct tcphdr) / 4; -	if (oth->ack) +	if (oth->ack) {  		tcph->seq = oth->ack_seq; -	else { +	} else {  		tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin +  				      oldskb->len - ip_hdrlen(oldskb) -  				      (oth->doff << 2)); @@ -83,16 +93,43 @@ void nf_send_reset(struct sk_buff *oldskb, int hook)  	nskb->ip_summed = CHECKSUM_PARTIAL;  	nskb->csum_start = (unsigned char *)tcph - nskb->head;  	nskb->csum_offset = offsetof(struct tcphdr, check); +} +EXPORT_SYMBOL_GPL(nf_reject_ip_tcphdr_put); + +/* Send RST reply */ +void nf_send_reset(struct sk_buff *oldskb, int hook) +{ +	struct sk_buff *nskb; +	const struct iphdr *oiph; +	struct iphdr *niph; +	const struct tcphdr *oth; +	struct tcphdr _oth; + +	oth = nf_reject_ip_tcphdr_get(oldskb, &_oth, hook); +	if (!oth) +		return; + +	if (skb_rtable(oldskb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) +		return; + +	oiph = ip_hdr(oldskb); + +	nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) + +			 LL_MAX_HEADER, GFP_ATOMIC); +	if (!nskb) +		return;  	/* ip_route_me_harder expects skb->dst to be set */  	skb_dst_set_noref(nskb, skb_dst(oldskb)); -	nskb->protocol = htons(ETH_P_IP); +	skb_reserve(nskb, LL_MAX_HEADER); +	niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_TCP, +				   ip4_dst_hoplimit(skb_dst(nskb))); +	nf_reject_ip_tcphdr_put(nskb, oldskb, oth); +  	if (ip_route_me_harder(nskb, RTN_UNSPEC))  		goto free_nskb; -	niph->ttl	= ip4_dst_hoplimit(skb_dst(nskb)); -  	/* "Never happens" */  	if (nskb->len > dst_mtu(skb_dst(nskb)))  		goto free_nskb; @@ -125,3 +162,5 @@ void nf_send_reset(struct sk_buff *oldskb, int hook)  	kfree_skb(nskb);  }  EXPORT_SYMBOL_GPL(nf_send_reset); + +MODULE_LICENSE("GPL"); diff --git a/net/ipv4/netfilter/nft_masq_ipv4.c b/net/ipv4/netfilter/nft_masq_ipv4.c index 1c636d6b5b50..c1023c445920 100644 --- a/net/ipv4/netfilter/nft_masq_ipv4.c +++ b/net/ipv4/netfilter/nft_masq_ipv4.c @@ -39,6 +39,7 @@ static const struct nft_expr_ops nft_masq_ipv4_ops = {  	.eval		= nft_masq_ipv4_eval,  	.init		= nft_masq_init,  	.dump		= nft_masq_dump, +	.validate	= nft_masq_validate,  };  static struct nft_expr_type nft_masq_ipv4_type __read_mostly = { diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 2d4ae469b471..6a2155b02602 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1798,6 +1798,7 @@ local_input:  no_route:  	RT_CACHE_STAT_INC(in_no_route);  	res.type = RTN_UNREACHABLE; +	res.fi = NULL;  	goto local_input;  	/* diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 1bec4e76d88c..39ec0c379545 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2868,61 +2868,42 @@ EXPORT_SYMBOL(compat_tcp_getsockopt);  #endif  #ifdef CONFIG_TCP_MD5SIG -static struct tcp_md5sig_pool __percpu *tcp_md5sig_pool __read_mostly; +static DEFINE_PER_CPU(struct tcp_md5sig_pool, tcp_md5sig_pool);  static DEFINE_MUTEX(tcp_md5sig_mutex); - -static void __tcp_free_md5sig_pool(struct tcp_md5sig_pool __percpu *pool) -{ -	int cpu; - -	for_each_possible_cpu(cpu) { -		struct tcp_md5sig_pool *p = per_cpu_ptr(pool, cpu); - -		if (p->md5_desc.tfm) -			crypto_free_hash(p->md5_desc.tfm); -	} -	free_percpu(pool); -} +static bool tcp_md5sig_pool_populated = false;  static void __tcp_alloc_md5sig_pool(void)  {  	int cpu; -	struct tcp_md5sig_pool __percpu *pool; - -	pool = alloc_percpu(struct tcp_md5sig_pool); -	if (!pool) -		return;  	for_each_possible_cpu(cpu) { -		struct crypto_hash *hash; - -		hash = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC); -		if (IS_ERR_OR_NULL(hash)) -			goto out_free; +		if (!per_cpu(tcp_md5sig_pool, cpu).md5_desc.tfm) { +			struct crypto_hash *hash; -		per_cpu_ptr(pool, cpu)->md5_desc.tfm = hash; +			hash = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC); +			if (IS_ERR_OR_NULL(hash)) +				return; +			per_cpu(tcp_md5sig_pool, cpu).md5_desc.tfm = hash; +		}  	} -	/* before setting tcp_md5sig_pool, we must commit all writes -	 * to memory. See ACCESS_ONCE() in tcp_get_md5sig_pool() +	/* before setting tcp_md5sig_pool_populated, we must commit all writes +	 * to memory. See smp_rmb() in tcp_get_md5sig_pool()  	 */  	smp_wmb(); -	tcp_md5sig_pool = pool; -	return; -out_free: -	__tcp_free_md5sig_pool(pool); +	tcp_md5sig_pool_populated = true;  }  bool tcp_alloc_md5sig_pool(void)  { -	if (unlikely(!tcp_md5sig_pool)) { +	if (unlikely(!tcp_md5sig_pool_populated)) {  		mutex_lock(&tcp_md5sig_mutex); -		if (!tcp_md5sig_pool) +		if (!tcp_md5sig_pool_populated)  			__tcp_alloc_md5sig_pool();  		mutex_unlock(&tcp_md5sig_mutex);  	} -	return tcp_md5sig_pool != NULL; +	return tcp_md5sig_pool_populated;  }  EXPORT_SYMBOL(tcp_alloc_md5sig_pool); @@ -2936,13 +2917,13 @@ EXPORT_SYMBOL(tcp_alloc_md5sig_pool);   */  struct tcp_md5sig_pool *tcp_get_md5sig_pool(void)  { -	struct tcp_md5sig_pool __percpu *p; -  	local_bh_disable(); -	p = ACCESS_ONCE(tcp_md5sig_pool); -	if (p) -		return raw_cpu_ptr(p); +	if (tcp_md5sig_pool_populated) { +		/* coupled with smp_wmb() in __tcp_alloc_md5sig_pool() */ +		smp_rmb(); +		return this_cpu_ptr(&tcp_md5sig_pool); +	}  	local_bh_enable();  	return NULL;  } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 94d1a7757ff7..9c7d7621466b 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -206,8 +206,6 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)  	inet->inet_dport = usin->sin_port;  	inet->inet_daddr = daddr; -	inet_set_txhash(sk); -  	inet_csk(sk)->icsk_ext_hdr_len = 0;  	if (inet_opt)  		inet_csk(sk)->icsk_ext_hdr_len = inet_opt->opt.optlen; @@ -224,6 +222,8 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)  	if (err)  		goto failure; +	inet_set_txhash(sk); +  	rt = ip_route_newports(fl4, rt, orig_sport, orig_dport,  			       inet->inet_sport, inet->inet_dport, sk);  	if (IS_ERR(rt)) { diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 3af21296d967..a3d453b94747 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2126,7 +2126,7 @@ bool tcp_schedule_loss_probe(struct sock *sk)  static bool skb_still_in_host_queue(const struct sock *sk,  				    const struct sk_buff *skb)  { -	if (unlikely(skb_fclone_busy(skb))) { +	if (unlikely(skb_fclone_busy(sk, skb))) {  		NET_INC_STATS_BH(sock_net(sk),  				 LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES);  		return true; diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index 507310ef4b56..6480cea7aa53 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c @@ -58,7 +58,7 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,  		skb->encap_hdr_csum = 1;  	/* segment inner packet. */ -	enc_features = skb->dev->hw_enc_features & netif_skb_features(skb); +	enc_features = skb->dev->hw_enc_features & features;  	segs = gso_inner_segment(skb, enc_features);  	if (IS_ERR_OR_NULL(segs)) {  		skb_gso_error_unwind(skb, protocol, tnl_hlen, mac_offset, diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 725c763270a0..0169ccf5aa4f 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -4531,6 +4531,7 @@ static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token)  	}  	write_unlock_bh(&idev->lock); +	inet6_ifinfo_notify(RTM_NEWLINK, idev);  	addrconf_verify_rtnl();  	return 0;  } diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c index 91014d32488d..a071563a7e6e 100644 --- a/net/ipv6/ip6_offload.c +++ b/net/ipv6/ip6_offload.c @@ -90,7 +90,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,  	encap = SKB_GSO_CB(skb)->encap_level > 0;  	if (encap) -		features = skb->dev->hw_enc_features & netif_skb_features(skb); +		features &= skb->dev->hw_enc_features;  	SKB_GSO_CB(skb)->encap_level += sizeof(*ipv6h);  	ipv6h = ipv6_hdr(skb); diff --git a/net/ipv6/netfilter/nf_reject_ipv6.c b/net/ipv6/netfilter/nf_reject_ipv6.c index 5f5f0438d74d..015eb8a80766 100644 --- a/net/ipv6/netfilter/nf_reject_ipv6.c +++ b/net/ipv6/netfilter/nf_reject_ipv6.c @@ -5,121 +5,109 @@   * it under the terms of the GNU General Public License version 2 as   * published by the Free Software Foundation.   */ + +#include <linux/module.h>  #include <net/ipv6.h>  #include <net/ip6_route.h>  #include <net/ip6_fib.h>  #include <net/ip6_checksum.h>  #include <linux/netfilter_ipv6.h> +#include <net/netfilter/ipv6/nf_reject.h> -void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook) +const struct tcphdr *nf_reject_ip6_tcphdr_get(struct sk_buff *oldskb, +					      struct tcphdr *otcph, +					      unsigned int *otcplen, int hook)  { -	struct sk_buff *nskb; -	struct tcphdr otcph, *tcph; -	unsigned int otcplen, hh_len; -	int tcphoff, needs_ack;  	const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); -	struct ipv6hdr *ip6h; -#define DEFAULT_TOS_VALUE	0x0U -	const __u8 tclass = DEFAULT_TOS_VALUE; -	struct dst_entry *dst = NULL;  	u8 proto;  	__be16 frag_off; -	struct flowi6 fl6; - -	if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) || -	    (!(ipv6_addr_type(&oip6h->daddr) & IPV6_ADDR_UNICAST))) { -		pr_debug("addr is not unicast.\n"); -		return; -	} +	int tcphoff;  	proto = oip6h->nexthdr; -	tcphoff = ipv6_skip_exthdr(oldskb, ((u8*)(oip6h+1) - oldskb->data), &proto, &frag_off); +	tcphoff = ipv6_skip_exthdr(oldskb, ((u8*)(oip6h+1) - oldskb->data), +				   &proto, &frag_off);  	if ((tcphoff < 0) || (tcphoff > oldskb->len)) {  		pr_debug("Cannot get TCP header.\n"); -		return; +		return NULL;  	} -	otcplen = oldskb->len - tcphoff; +	*otcplen = oldskb->len - tcphoff;  	/* IP header checks: fragment, too short. */ -	if (proto != IPPROTO_TCP || otcplen < sizeof(struct tcphdr)) { -		pr_debug("proto(%d) != IPPROTO_TCP, " -			 "or too short. otcplen = %d\n", -			 proto, otcplen); -		return; +	if (proto != IPPROTO_TCP || *otcplen < sizeof(struct tcphdr)) { +		pr_debug("proto(%d) != IPPROTO_TCP or too short (len = %d)\n", +			 proto, *otcplen); +		return NULL;  	} -	if (skb_copy_bits(oldskb, tcphoff, &otcph, sizeof(struct tcphdr))) -		BUG(); +	otcph = skb_header_pointer(oldskb, tcphoff, sizeof(struct tcphdr), +				   otcph); +	if (otcph == NULL) +		return NULL;  	/* No RST for RST. */ -	if (otcph.rst) { +	if (otcph->rst) {  		pr_debug("RST is set\n"); -		return; +		return NULL;  	}  	/* Check checksum. */  	if (nf_ip6_checksum(oldskb, hook, tcphoff, IPPROTO_TCP)) {  		pr_debug("TCP checksum is invalid\n"); -		return; +		return NULL;  	} -	memset(&fl6, 0, sizeof(fl6)); -	fl6.flowi6_proto = IPPROTO_TCP; -	fl6.saddr = oip6h->daddr; -	fl6.daddr = oip6h->saddr; -	fl6.fl6_sport = otcph.dest; -	fl6.fl6_dport = otcph.source; -	security_skb_classify_flow(oldskb, flowi6_to_flowi(&fl6)); -	dst = ip6_route_output(net, NULL, &fl6); -	if (dst == NULL || dst->error) { -		dst_release(dst); -		return; -	} -	dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0); -	if (IS_ERR(dst)) -		return; - -	hh_len = (dst->dev->hard_header_len + 15)&~15; -	nskb = alloc_skb(hh_len + 15 + dst->header_len + sizeof(struct ipv6hdr) -			 + sizeof(struct tcphdr) + dst->trailer_len, -			 GFP_ATOMIC); - -	if (!nskb) { -		net_dbg_ratelimited("cannot alloc skb\n"); -		dst_release(dst); -		return; -	} - -	skb_dst_set(nskb, dst); +	return otcph; +} +EXPORT_SYMBOL_GPL(nf_reject_ip6_tcphdr_get); -	skb_reserve(nskb, hh_len + dst->header_len); +struct ipv6hdr *nf_reject_ip6hdr_put(struct sk_buff *nskb, +				     const struct sk_buff *oldskb, +				     __be16 protocol, int hoplimit) +{ +	struct ipv6hdr *ip6h; +	const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); +#define DEFAULT_TOS_VALUE	0x0U +	const __u8 tclass = DEFAULT_TOS_VALUE;  	skb_put(nskb, sizeof(struct ipv6hdr));  	skb_reset_network_header(nskb);  	ip6h = ipv6_hdr(nskb);  	ip6_flow_hdr(ip6h, tclass, 0); -	ip6h->hop_limit = ip6_dst_hoplimit(dst); -	ip6h->nexthdr = IPPROTO_TCP; +	ip6h->hop_limit = hoplimit; +	ip6h->nexthdr = protocol;  	ip6h->saddr = oip6h->daddr;  	ip6h->daddr = oip6h->saddr; +	nskb->protocol = htons(ETH_P_IPV6); + +	return ip6h; +} +EXPORT_SYMBOL_GPL(nf_reject_ip6hdr_put); + +void nf_reject_ip6_tcphdr_put(struct sk_buff *nskb, +			      const struct sk_buff *oldskb, +			      const struct tcphdr *oth, unsigned int otcplen) +{ +	struct tcphdr *tcph; +	int needs_ack; +  	skb_reset_transport_header(nskb);  	tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr));  	/* Truncate to length (no data) */  	tcph->doff = sizeof(struct tcphdr)/4; -	tcph->source = otcph.dest; -	tcph->dest = otcph.source; +	tcph->source = oth->dest; +	tcph->dest = oth->source; -	if (otcph.ack) { +	if (oth->ack) {  		needs_ack = 0; -		tcph->seq = otcph.ack_seq; +		tcph->seq = oth->ack_seq;  		tcph->ack_seq = 0;  	} else {  		needs_ack = 1; -		tcph->ack_seq = htonl(ntohl(otcph.seq) + otcph.syn + otcph.fin -				      + otcplen - (otcph.doff<<2)); +		tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + +				      otcplen - (oth->doff<<2));  		tcph->seq = 0;  	} @@ -137,6 +125,63 @@ void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook)  				      sizeof(struct tcphdr), IPPROTO_TCP,  				      csum_partial(tcph,  						   sizeof(struct tcphdr), 0)); +} +EXPORT_SYMBOL_GPL(nf_reject_ip6_tcphdr_put); + +void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook) +{ +	struct sk_buff *nskb; +	struct tcphdr _otcph; +	const struct tcphdr *otcph; +	unsigned int otcplen, hh_len; +	const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); +	struct ipv6hdr *ip6h; +	struct dst_entry *dst = NULL; +	struct flowi6 fl6; + +	if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) || +	    (!(ipv6_addr_type(&oip6h->daddr) & IPV6_ADDR_UNICAST))) { +		pr_debug("addr is not unicast.\n"); +		return; +	} + +	otcph = nf_reject_ip6_tcphdr_get(oldskb, &_otcph, &otcplen, hook); +	if (!otcph) +		return; + +	memset(&fl6, 0, sizeof(fl6)); +	fl6.flowi6_proto = IPPROTO_TCP; +	fl6.saddr = oip6h->daddr; +	fl6.daddr = oip6h->saddr; +	fl6.fl6_sport = otcph->dest; +	fl6.fl6_dport = otcph->source; +	security_skb_classify_flow(oldskb, flowi6_to_flowi(&fl6)); +	dst = ip6_route_output(net, NULL, &fl6); +	if (dst == NULL || dst->error) { +		dst_release(dst); +		return; +	} +	dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0); +	if (IS_ERR(dst)) +		return; + +	hh_len = (dst->dev->hard_header_len + 15)&~15; +	nskb = alloc_skb(hh_len + 15 + dst->header_len + sizeof(struct ipv6hdr) +			 + sizeof(struct tcphdr) + dst->trailer_len, +			 GFP_ATOMIC); + +	if (!nskb) { +		net_dbg_ratelimited("cannot alloc skb\n"); +		dst_release(dst); +		return; +	} + +	skb_dst_set(nskb, dst); + +	skb_reserve(nskb, hh_len + dst->header_len); +	ip6h = nf_reject_ip6hdr_put(nskb, oldskb, IPPROTO_TCP, +				    ip6_dst_hoplimit(dst)); +	nf_reject_ip6_tcphdr_put(nskb, oldskb, otcph, otcplen);  	nf_ct_attach(nskb, oldskb); @@ -161,3 +206,5 @@ void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook)  		ip6_local_out(nskb);  }  EXPORT_SYMBOL_GPL(nf_send_reset6); + +MODULE_LICENSE("GPL"); diff --git a/net/ipv6/netfilter/nft_masq_ipv6.c b/net/ipv6/netfilter/nft_masq_ipv6.c index 556262f40761..8a7ac685076d 100644 --- a/net/ipv6/netfilter/nft_masq_ipv6.c +++ b/net/ipv6/netfilter/nft_masq_ipv6.c @@ -39,6 +39,7 @@ static const struct nft_expr_ops nft_masq_ipv6_ops = {  	.eval		= nft_masq_ipv6_eval,  	.init		= nft_masq_init,  	.dump		= nft_masq_dump, +	.validate	= nft_masq_validate,  };  static struct nft_expr_type nft_masq_ipv6_type __read_mostly = { diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c index fc24c390af05..97f41a3e68d9 100644 --- a/net/ipv6/output_core.c +++ b/net/ipv6/output_core.c @@ -3,11 +3,45 @@   * not configured or static.  These functions are needed by GSO/GRO implementation.   */  #include <linux/export.h> +#include <net/ip.h>  #include <net/ipv6.h>  #include <net/ip6_fib.h>  #include <net/addrconf.h>  #include <net/secure_seq.h> +/* This function exists only for tap drivers that must support broken + * clients requesting UFO without specifying an IPv6 fragment ID. + * + * This is similar to ipv6_select_ident() but we use an independent hash + * seed to limit information leakage. + * + * The network header must be set before calling this. + */ +void ipv6_proxy_select_ident(struct sk_buff *skb) +{ +	static u32 ip6_proxy_idents_hashrnd __read_mostly; +	struct in6_addr buf[2]; +	struct in6_addr *addrs; +	u32 hash, id; + +	addrs = skb_header_pointer(skb, +				   skb_network_offset(skb) + +				   offsetof(struct ipv6hdr, saddr), +				   sizeof(buf), buf); +	if (!addrs) +		return; + +	net_get_random_once(&ip6_proxy_idents_hashrnd, +			    sizeof(ip6_proxy_idents_hashrnd)); + +	hash = __ipv6_addr_jhash(&addrs[1], ip6_proxy_idents_hashrnd); +	hash = __ipv6_addr_jhash(&addrs[0], hash); + +	id = ip_idents_reserve(hash, 1); +	skb_shinfo(skb)->ip6_frag_id = htonl(id); +} +EXPORT_SYMBOL_GPL(ipv6_proxy_select_ident); +  int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)  {  	u16 offset = sizeof(struct ipv6hdr); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 831495529b82..ace29b60813c 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -200,8 +200,6 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,  	sk->sk_v6_daddr = usin->sin6_addr;  	np->flow_label = fl6.flowlabel; -	ip6_set_txhash(sk); -  	/*  	 *	TCP over IPv4  	 */ @@ -297,6 +295,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,  	if (err)  		goto late_failure; +	ip6_set_txhash(sk); +  	if (!tp->write_seq && likely(!tp->repair))  		tp->write_seq = secure_tcpv6_sequence_number(np->saddr.s6_addr32,  							     sk->sk_v6_daddr.s6_addr32, diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index ac49f84fe2c3..5f983644373a 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -170,8 +170,10 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse)  		case IPPROTO_DCCP:  			if (!onlyproto && (nh + offset + 4 < skb->data ||  			     pskb_may_pull(skb, nh + offset + 4 - skb->data))) { -				__be16 *ports = (__be16 *)exthdr; +				__be16 *ports; +				nh = skb_network_header(skb); +				ports = (__be16 *)(nh + offset);  				fl6->fl6_sport = ports[!!reverse];  				fl6->fl6_dport = ports[!reverse];  			} @@ -180,8 +182,10 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse)  		case IPPROTO_ICMPV6:  			if (!onlyproto && pskb_may_pull(skb, nh + offset + 2 - skb->data)) { -				u8 *icmp = (u8 *)exthdr; +				u8 *icmp; +				nh = skb_network_header(skb); +				icmp = (u8 *)(nh + offset);  				fl6->fl6_icmp_type = icmp[0];  				fl6->fl6_icmp_code = icmp[1];  			} @@ -192,8 +196,9 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse)  		case IPPROTO_MH:  			if (!onlyproto && pskb_may_pull(skb, nh + offset + 3 - skb->data)) {  				struct ip6_mh *mh; -				mh = (struct ip6_mh *)exthdr; +				nh = skb_network_header(skb); +				mh = (struct ip6_mh *)(nh + offset);  				fl6->fl6_mh_type = mh->ip6mh_type;  			}  			fl6->flowi6_proto = nexthdr; diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c index 92fafd485deb..3f3a6cbdceb7 100644 --- a/net/irda/af_irda.c +++ b/net/irda/af_irda.c @@ -1064,8 +1064,6 @@ static int irda_connect(struct socket *sock, struct sockaddr *uaddr,  	if (sk->sk_state != TCP_ESTABLISHED) {  		sock->state = SS_UNCONNECTED; -		if (sk->sk_prot->disconnect(sk, flags)) -			sock->state = SS_DISCONNECTING;  		err = sock_error(sk);  		if (!err)  			err = -ECONNRESET; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index fb6a1502b6df..343da1e35025 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -3458,7 +3458,7 @@ static int ieee80211_cfg_get_channel(struct wiphy *wiphy,  	rcu_read_lock();  	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);  	if (chanctx_conf) { -		*chandef = chanctx_conf->def; +		*chandef = sdata->vif.bss_conf.chandef;  		ret = 0;  	} else if (local->open_count > 0 &&  		   local->open_count == local->monitors && diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 8fdadfd94ba8..6081329784dd 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -448,7 +448,7 @@ static void rate_fixup_ratelist(struct ieee80211_vif *vif,  	 */  	if (!(rates[0].flags & IEEE80211_TX_RC_MCS)) {  		u32 basic_rates = vif->bss_conf.basic_rates; -		s8 baserate = basic_rates ? ffs(basic_rates - 1) : 0; +		s8 baserate = basic_rates ? ffs(basic_rates) - 1 : 0;  		rate = &sband->bitrates[rates[0].idx]; diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c index edde723f9f00..2acab1bcaa4b 100644 --- a/net/mac80211/rc80211_minstrel_debugfs.c +++ b/net/mac80211/rc80211_minstrel_debugfs.c @@ -62,14 +62,14 @@ minstrel_stats_open(struct inode *inode, struct file *file)  	unsigned int i, tp, prob, eprob;  	char *p; -	ms = kmalloc(sizeof(*ms) + 4096, GFP_KERNEL); +	ms = kmalloc(2048, GFP_KERNEL);  	if (!ms)  		return -ENOMEM;  	file->private_data = ms;  	p = ms->buf; -	p += sprintf(p, "rate      throughput  ewma prob  this prob  " -			"this succ/attempt   success    attempts\n"); +	p += sprintf(p, "rate          tpt eprob *prob" +			"  *ok(*cum)        ok(      cum)\n");  	for (i = 0; i < mi->n_rates; i++) {  		struct minstrel_rate *mr = &mi->r[i];  		struct minstrel_rate_stats *mrs = &mi->r[i].stats; @@ -86,8 +86,8 @@ minstrel_stats_open(struct inode *inode, struct file *file)  		prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);  		eprob = MINSTREL_TRUNC(mrs->probability * 1000); -		p += sprintf(p, "  %6u.%1u   %6u.%1u   %6u.%1u        " -				"   %3u(%3u)  %8llu    %8llu\n", +		p += sprintf(p, " %4u.%1u %3u.%1u %3u.%1u" +				" %4u(%4u) %9llu(%9llu)\n",  				tp / 10, tp % 10,  				eprob / 10, eprob % 10,  				prob / 10, prob % 10, @@ -102,6 +102,8 @@ minstrel_stats_open(struct inode *inode, struct file *file)  			mi->sample_packets);  	ms->len = p - ms->buf; +	WARN_ON(ms->len + sizeof(*ms) > 2048); +  	return 0;  } diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c index a72ad46f2a04..d537bec93754 100644 --- a/net/mac80211/rc80211_minstrel_ht_debugfs.c +++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c @@ -63,8 +63,8 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)  		prob = MINSTREL_TRUNC(mr->cur_prob * 1000);  		eprob = MINSTREL_TRUNC(mr->probability * 1000); -		p += sprintf(p, "      %6u.%1u   %6u.%1u    %6u.%1u    " -				"%3u            %3u(%3u)  %8llu    %8llu\n", +		p += sprintf(p, " %4u.%1u %3u.%1u %3u.%1u " +				"%3u %4u(%4u) %9llu(%9llu)\n",  				tp / 10, tp % 10,  				eprob / 10, eprob % 10,  				prob / 10, prob % 10, @@ -96,14 +96,15 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)  		return ret;  	} -	ms = kmalloc(sizeof(*ms) + 8192, GFP_KERNEL); +	ms = kmalloc(8192, GFP_KERNEL);  	if (!ms)  		return -ENOMEM;  	file->private_data = ms;  	p = ms->buf; -	p += sprintf(p, "type           rate     throughput  ewma prob   " -		     "this prob  retry   this succ/attempt   success    attempts\n"); +	p += sprintf(p, "type           rate     tpt eprob *prob " +			"ret  *ok(*cum)        ok(      cum)\n"); +  	p = minstrel_ht_stats_dump(mi, max_mcs, p);  	for (i = 0; i < max_mcs; i++) @@ -118,6 +119,8 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)  		MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10);  	ms->len = p - ms->buf; +	WARN_ON(ms->len + sizeof(*ms) > 8192); +  	return nonseekable_open(inode, file);  } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 42f68cb8957e..bcda2ac7d844 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -336,6 +336,7 @@ struct ieee80211_tx_latency_stat {   * @known_smps_mode: the smps_mode the client thinks we are in. Relevant for   *	AP only.   * @cipher_scheme: optional cipher scheme for this station + * @last_tdls_pkt_time: holds the time in jiffies of last TDLS pkt ACKed   */  struct sta_info {  	/* General information, mostly static */ diff --git a/net/mpls/Makefile b/net/mpls/Makefile index 0a3c171be537..6dec088c2d0f 100644 --- a/net/mpls/Makefile +++ b/net/mpls/Makefile @@ -1,4 +1,4 @@  #  # Makefile for MPLS.  # -obj-y += mpls_gso.o +obj-$(CONFIG_NET_MPLS_GSO) += mpls_gso.o diff --git a/net/mpls/mpls_gso.c b/net/mpls/mpls_gso.c index e28ed2ef5b06..e3545f21a099 100644 --- a/net/mpls/mpls_gso.c +++ b/net/mpls/mpls_gso.c @@ -48,7 +48,7 @@ static struct sk_buff *mpls_gso_segment(struct sk_buff *skb,  	__skb_push(skb, skb->mac_len);  	/* Segment inner packet. */ -	mpls_features = skb->dev->mpls_features & netif_skb_features(skb); +	mpls_features = skb->dev->mpls_features & features;  	segs = skb_mac_gso_segment(skb, mpls_features); @@ -59,8 +59,7 @@ static struct sk_buff *mpls_gso_segment(struct sk_buff *skb,  	 * above pulled.  It will be re-pushed after returning  	 * skb_mac_gso_segment(), an indirect caller of this function.  	 */ -	__skb_push(skb, skb->data - skb_mac_header(skb)); - +	__skb_pull(skb, skb->data - skb_mac_header(skb));  out:  	return segs;  } diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 912e5a05b79d..86f9d76b1464 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -659,7 +659,7 @@ ip_set_nfnl_get_byindex(struct net *net, ip_set_id_t index)  	struct ip_set *set;  	struct ip_set_net *inst = ip_set_pernet(net); -	if (index > inst->ip_set_max) +	if (index >= inst->ip_set_max)  		return IPSET_INVALID_ID;  	nfnl_lock(NFNL_SUBSYS_IPSET); diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 91f17c1eb8a2..437a3663ad03 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -316,7 +316,7 @@ __ip_vs_get_out_rt(int skb_af, struct sk_buff *skb, struct ip_vs_dest *dest,  	if (unlikely(crosses_local_route_boundary(skb_af, skb, rt_mode,  						  local))) {  		IP_VS_DBG_RL("We are crossing local and non-local addresses" -			     " daddr=%pI4\n", &dest->addr.ip); +			     " daddr=%pI4\n", &daddr);  		goto err_put;  	} @@ -458,7 +458,7 @@ __ip_vs_get_out_rt_v6(int skb_af, struct sk_buff *skb, struct ip_vs_dest *dest,  	if (unlikely(crosses_local_route_boundary(skb_af, skb, rt_mode,  						  local))) {  		IP_VS_DBG_RL("We are crossing local and non-local addresses" -			     " daddr=%pI6\n", &dest->addr.in6); +			     " daddr=%pI6\n", daddr);  		goto err_put;  	} diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 44d1ea32570a..d87b6423ffb2 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -213,7 +213,7 @@ static const u8 tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = {  	{  /* REPLY */  /* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sS2	*/ -/*syn*/	   { sIV, sS2, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sS2 }, +/*syn*/	   { sIV, sS2, sIV, sIV, sIV, sIV, sIV, sSS, sIV, sS2 },  /*   *	sNO -> sIV	Never reached.   *	sSS -> sS2	Simultaneous open @@ -223,7 +223,7 @@ static const u8 tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = {   *	sFW -> sIV   *	sCW -> sIV   *	sLA -> sIV - *	sTW -> sIV	Reopened connection, but server may not do it. + *	sTW -> sSS	Reopened connection, but server may have switched role   *	sCL -> sIV   */  /* 	     sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sS2	*/ diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 556a0dfa4abc..11ab4b078f3b 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -1328,10 +1328,10 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,  			basechain->stats = stats;  		} else {  			stats = netdev_alloc_pcpu_stats(struct nft_stats); -			if (IS_ERR(stats)) { +			if (stats == NULL) {  				module_put(type->owner);  				kfree(basechain); -				return PTR_ERR(stats); +				return -ENOMEM;  			}  			rcu_assign_pointer(basechain->stats, stats);  		} @@ -3744,6 +3744,20 @@ static const struct nfnetlink_subsystem nf_tables_subsys = {  	.abort		= nf_tables_abort,  }; +int nft_chain_validate_dependency(const struct nft_chain *chain, +				  enum nft_chain_type type) +{ +	const struct nft_base_chain *basechain; + +	if (chain->flags & NFT_BASE_CHAIN) { +		basechain = nft_base_chain(chain); +		if (basechain->type->type != type) +			return -EOPNOTSUPP; +	} +	return 0; +} +EXPORT_SYMBOL_GPL(nft_chain_validate_dependency); +  /*   * Loop detection - walk through the ruleset beginning at the destination chain   * of a new jump until either the source chain is reached (loop) or all diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index b1e3a0579416..5f1be5ba3559 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -43,7 +43,8 @@  #define NFULNL_NLBUFSIZ_DEFAULT	NLMSG_GOODSIZE  #define NFULNL_TIMEOUT_DEFAULT 	100	/* every second */  #define NFULNL_QTHRESH_DEFAULT 	100	/* 100 packets */ -#define NFULNL_COPY_RANGE_MAX	0xFFFF	/* max packet size is limited by 16-bit struct nfattr nfa_len field */ +/* max packet size is limited by 16-bit struct nfattr nfa_len field */ +#define NFULNL_COPY_RANGE_MAX	(0xFFFF - NLA_HDRLEN)  #define PRINTR(x, args...)	do { if (net_ratelimit()) \  				     printk(x, ## args); } while (0); @@ -252,6 +253,8 @@ nfulnl_set_mode(struct nfulnl_instance *inst, u_int8_t mode,  	case NFULNL_COPY_PACKET:  		inst->copy_mode = mode; +		if (range == 0) +			range = NFULNL_COPY_RANGE_MAX;  		inst->copy_range = min_t(unsigned int,  					 range, NFULNL_COPY_RANGE_MAX);  		break; @@ -343,26 +346,25 @@ nfulnl_alloc_skb(struct net *net, u32 peer_portid, unsigned int inst_size,  	return skb;  } -static int +static void  __nfulnl_send(struct nfulnl_instance *inst)  { -	int status = -1; -  	if (inst->qlen > 1) {  		struct nlmsghdr *nlh = nlmsg_put(inst->skb, 0, 0,  						 NLMSG_DONE,  						 sizeof(struct nfgenmsg),  						 0); -		if (!nlh) +		if (WARN_ONCE(!nlh, "bad nlskb size: %u, tailroom %d\n", +			      inst->skb->len, skb_tailroom(inst->skb))) { +			kfree_skb(inst->skb);  			goto out; +		}  	} -	status = nfnetlink_unicast(inst->skb, inst->net, inst->peer_portid, -				   MSG_DONTWAIT); - +	nfnetlink_unicast(inst->skb, inst->net, inst->peer_portid, +			  MSG_DONTWAIT); +out:  	inst->qlen = 0;  	inst->skb = NULL; -out: -	return status;  }  static void @@ -649,7 +651,8 @@ nfulnl_log_packet(struct net *net,  		+ nla_total_size(sizeof(u_int32_t))	/* gid */  		+ nla_total_size(plen)			/* prefix */  		+ nla_total_size(sizeof(struct nfulnl_msg_packet_hw)) -		+ nla_total_size(sizeof(struct nfulnl_msg_packet_timestamp)); +		+ nla_total_size(sizeof(struct nfulnl_msg_packet_timestamp)) +		+ nla_total_size(sizeof(struct nfgenmsg));	/* NLMSG_DONE */  	if (in && skb_mac_header_was_set(skb)) {  		size +=   nla_total_size(skb->dev->hard_header_len) @@ -678,8 +681,7 @@ nfulnl_log_packet(struct net *net,  		break;  	case NFULNL_COPY_PACKET: -		if (inst->copy_range == 0 -		    || inst->copy_range > skb->len) +		if (inst->copy_range > skb->len)  			data_len = skb->len;  		else  			data_len = inst->copy_range; @@ -692,8 +694,7 @@ nfulnl_log_packet(struct net *net,  		goto unlock_and_release;  	} -	if (inst->skb && -	    size > skb_tailroom(inst->skb) - sizeof(struct nfgenmsg)) { +	if (inst->skb && size > skb_tailroom(inst->skb)) {  		/* either the queue len is too high or we don't have  		 * enough room in the skb left. flush to userspace. */  		__nfulnl_flush(inst); diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c index a82077d9f59b..7c60ccd61a3e 100644 --- a/net/netfilter/nfnetlink_queue_core.c +++ b/net/netfilter/nfnetlink_queue_core.c @@ -665,7 +665,7 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)  	 * returned by nf_queue.  For instance, callers rely on -ECANCELED to  	 * mean 'ignore this hook'.  	 */ -	if (IS_ERR(segs)) +	if (IS_ERR_OR_NULL(segs))  		goto out_err;  	queued = 0;  	err = 0; diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c index 7e2683c8a44a..9d6d6f60a80f 100644 --- a/net/netfilter/nft_compat.c +++ b/net/netfilter/nft_compat.c @@ -19,9 +19,52 @@  #include <linux/netfilter/x_tables.h>  #include <linux/netfilter_ipv4/ip_tables.h>  #include <linux/netfilter_ipv6/ip6_tables.h> -#include <asm/uaccess.h> /* for set_fs */  #include <net/netfilter/nf_tables.h> +static const struct { +       const char	*name; +       u8		type; +} table_to_chaintype[] = { +       { "filter",     NFT_CHAIN_T_DEFAULT }, +       { "raw",        NFT_CHAIN_T_DEFAULT }, +       { "security",   NFT_CHAIN_T_DEFAULT }, +       { "mangle",     NFT_CHAIN_T_ROUTE }, +       { "nat",        NFT_CHAIN_T_NAT }, +       { }, +}; + +static int nft_compat_table_to_chaintype(const char *table) +{ +	int i; + +	for (i = 0; table_to_chaintype[i].name != NULL; i++) { +		if (strcmp(table_to_chaintype[i].name, table) == 0) +			return table_to_chaintype[i].type; +	} + +	return -1; +} + +static int nft_compat_chain_validate_dependency(const char *tablename, +						const struct nft_chain *chain) +{ +	enum nft_chain_type type; +	const struct nft_base_chain *basechain; + +	if (!tablename || !(chain->flags & NFT_BASE_CHAIN)) +		return 0; + +	type = nft_compat_table_to_chaintype(tablename); +	if (type < 0) +		return -EINVAL; + +	basechain = nft_base_chain(chain); +	if (basechain->type->type != type) +		return -EINVAL; + +	return 0; +} +  union nft_entry {  	struct ipt_entry e4;  	struct ip6t_entry e6; @@ -95,6 +138,8 @@ nft_target_set_tgchk_param(struct xt_tgchk_param *par,  		const struct nf_hook_ops *ops = &basechain->ops[0];  		par->hook_mask = 1 << ops->hooknum; +	} else { +		par->hook_mask = 0;  	}  	par->family	= ctx->afi->family;  } @@ -151,6 +196,10 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,  	union nft_entry e = {};  	int ret; +	ret = nft_compat_chain_validate_dependency(target->table, ctx->chain); +	if (ret < 0) +		goto err; +  	target_compat_from_user(target, nla_data(tb[NFTA_TARGET_INFO]), info);  	if (ctx->nla[NFTA_RULE_COMPAT]) { @@ -216,6 +265,7 @@ static int nft_target_validate(const struct nft_ctx *ctx,  {  	struct xt_target *target = expr->ops->data;  	unsigned int hook_mask = 0; +	int ret;  	if (ctx->chain->flags & NFT_BASE_CHAIN) {  		const struct nft_base_chain *basechain = @@ -223,11 +273,13 @@ static int nft_target_validate(const struct nft_ctx *ctx,  		const struct nf_hook_ops *ops = &basechain->ops[0];  		hook_mask = 1 << ops->hooknum; -		if (hook_mask & target->hooks) -			return 0; +		if (!(hook_mask & target->hooks)) +			return -EINVAL; -		/* This target is being called from an invalid chain */ -		return -EINVAL; +		ret = nft_compat_chain_validate_dependency(target->table, +							   ctx->chain); +		if (ret < 0) +			return ret;  	}  	return 0;  } @@ -293,6 +345,8 @@ nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx,  		const struct nf_hook_ops *ops = &basechain->ops[0];  		par->hook_mask = 1 << ops->hooknum; +	} else { +		par->hook_mask = 0;  	}  	par->family	= ctx->afi->family;  } @@ -320,6 +374,10 @@ nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,  	union nft_entry e = {};  	int ret; +	ret = nft_compat_chain_validate_dependency(match->name, ctx->chain); +	if (ret < 0) +		goto err; +  	match_compat_from_user(match, nla_data(tb[NFTA_MATCH_INFO]), info);  	if (ctx->nla[NFTA_RULE_COMPAT]) { @@ -379,6 +437,7 @@ static int nft_match_validate(const struct nft_ctx *ctx,  {  	struct xt_match *match = expr->ops->data;  	unsigned int hook_mask = 0; +	int ret;  	if (ctx->chain->flags & NFT_BASE_CHAIN) {  		const struct nft_base_chain *basechain = @@ -386,11 +445,13 @@ static int nft_match_validate(const struct nft_ctx *ctx,  		const struct nf_hook_ops *ops = &basechain->ops[0];  		hook_mask = 1 << ops->hooknum; -		if (hook_mask & match->hooks) -			return 0; +		if (!(hook_mask & match->hooks)) +			return -EINVAL; -		/* This match is being called from an invalid chain */ -		return -EINVAL; +		ret = nft_compat_chain_validate_dependency(match->name, +							   ctx->chain); +		if (ret < 0) +			return ret;  	}  	return 0;  } @@ -611,7 +672,7 @@ nft_target_select_ops(const struct nft_ctx *ctx,  	family = ctx->afi->family;  	/* Re-use the existing target if it's already loaded. */ -	list_for_each_entry(nft_target, &nft_match_list, head) { +	list_for_each_entry(nft_target, &nft_target_list, head) {  		struct xt_target *target = nft_target->ops.data;  		if (strcmp(target->name, tg_name) == 0 && diff --git a/net/netfilter/nft_masq.c b/net/netfilter/nft_masq.c index 6637bab00567..d1ffd5eb3a9b 100644 --- a/net/netfilter/nft_masq.c +++ b/net/netfilter/nft_masq.c @@ -26,6 +26,11 @@ int nft_masq_init(const struct nft_ctx *ctx,  		  const struct nlattr * const tb[])  {  	struct nft_masq *priv = nft_expr_priv(expr); +	int err; + +	err = nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT); +	if (err < 0) +		return err;  	if (tb[NFTA_MASQ_FLAGS] == NULL)  		return 0; @@ -55,5 +60,12 @@ nla_put_failure:  }  EXPORT_SYMBOL_GPL(nft_masq_dump); +int nft_masq_validate(const struct nft_ctx *ctx, const struct nft_expr *expr, +		      const struct nft_data **data) +{ +	return nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT); +} +EXPORT_SYMBOL_GPL(nft_masq_validate); +  MODULE_LICENSE("GPL");  MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>"); diff --git a/net/netfilter/nft_nat.c b/net/netfilter/nft_nat.c index 799550b476fb..afe2b0b45ec4 100644 --- a/net/netfilter/nft_nat.c +++ b/net/netfilter/nft_nat.c @@ -95,7 +95,13 @@ static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr,  	u32 family;  	int err; -	if (tb[NFTA_NAT_TYPE] == NULL) +	err = nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT); +	if (err < 0) +		return err; + +	if (tb[NFTA_NAT_TYPE] == NULL || +	    (tb[NFTA_NAT_REG_ADDR_MIN] == NULL && +	     tb[NFTA_NAT_REG_PROTO_MIN] == NULL))  		return -EINVAL;  	switch (ntohl(nla_get_be32(tb[NFTA_NAT_TYPE]))) { @@ -120,38 +126,44 @@ static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr,  	priv->family = family;  	if (tb[NFTA_NAT_REG_ADDR_MIN]) { -		priv->sreg_addr_min = ntohl(nla_get_be32( -						tb[NFTA_NAT_REG_ADDR_MIN])); +		priv->sreg_addr_min = +			ntohl(nla_get_be32(tb[NFTA_NAT_REG_ADDR_MIN])); +  		err = nft_validate_input_register(priv->sreg_addr_min);  		if (err < 0)  			return err; -	} -	if (tb[NFTA_NAT_REG_ADDR_MAX]) { -		priv->sreg_addr_max = ntohl(nla_get_be32( -						tb[NFTA_NAT_REG_ADDR_MAX])); -		err = nft_validate_input_register(priv->sreg_addr_max); -		if (err < 0) -			return err; -	} else -		priv->sreg_addr_max = priv->sreg_addr_min; +		if (tb[NFTA_NAT_REG_ADDR_MAX]) { +			priv->sreg_addr_max = +				ntohl(nla_get_be32(tb[NFTA_NAT_REG_ADDR_MAX])); + +			err = nft_validate_input_register(priv->sreg_addr_max); +			if (err < 0) +				return err; +		} else { +			priv->sreg_addr_max = priv->sreg_addr_min; +		} +	}  	if (tb[NFTA_NAT_REG_PROTO_MIN]) { -		priv->sreg_proto_min = ntohl(nla_get_be32( -						tb[NFTA_NAT_REG_PROTO_MIN])); +		priv->sreg_proto_min = +			ntohl(nla_get_be32(tb[NFTA_NAT_REG_PROTO_MIN])); +  		err = nft_validate_input_register(priv->sreg_proto_min);  		if (err < 0)  			return err; -	} -	if (tb[NFTA_NAT_REG_PROTO_MAX]) { -		priv->sreg_proto_max = ntohl(nla_get_be32( -						tb[NFTA_NAT_REG_PROTO_MAX])); -		err = nft_validate_input_register(priv->sreg_proto_max); -		if (err < 0) -			return err; -	} else -		priv->sreg_proto_max = priv->sreg_proto_min; +		if (tb[NFTA_NAT_REG_PROTO_MAX]) { +			priv->sreg_proto_max = +				ntohl(nla_get_be32(tb[NFTA_NAT_REG_PROTO_MAX])); + +			err = nft_validate_input_register(priv->sreg_proto_max); +			if (err < 0) +				return err; +		} else { +			priv->sreg_proto_max = priv->sreg_proto_min; +		} +	}  	if (tb[NFTA_NAT_FLAGS]) {  		priv->flags = ntohl(nla_get_be32(tb[NFTA_NAT_FLAGS])); @@ -179,17 +191,19 @@ static int nft_nat_dump(struct sk_buff *skb, const struct nft_expr *expr)  	if (nla_put_be32(skb, NFTA_NAT_FAMILY, htonl(priv->family)))  		goto nla_put_failure; -	if (nla_put_be32(skb, -			 NFTA_NAT_REG_ADDR_MIN, htonl(priv->sreg_addr_min))) -		goto nla_put_failure; -	if (nla_put_be32(skb, -			 NFTA_NAT_REG_ADDR_MAX, htonl(priv->sreg_addr_max))) -		goto nla_put_failure; + +	if (priv->sreg_addr_min) { +		if (nla_put_be32(skb, NFTA_NAT_REG_ADDR_MIN, +				 htonl(priv->sreg_addr_min)) || +		    nla_put_be32(skb, NFTA_NAT_REG_ADDR_MAX, +				 htonl(priv->sreg_addr_max))) +			goto nla_put_failure; +	} +  	if (priv->sreg_proto_min) {  		if (nla_put_be32(skb, NFTA_NAT_REG_PROTO_MIN, -				 htonl(priv->sreg_proto_min))) -			goto nla_put_failure; -		if (nla_put_be32(skb, NFTA_NAT_REG_PROTO_MAX, +				 htonl(priv->sreg_proto_min)) || +		    nla_put_be32(skb, NFTA_NAT_REG_PROTO_MAX,  				 htonl(priv->sreg_proto_max)))  			goto nla_put_failure;  	} @@ -205,6 +219,13 @@ nla_put_failure:  	return -1;  } +static int nft_nat_validate(const struct nft_ctx *ctx, +			    const struct nft_expr *expr, +			    const struct nft_data **data) +{ +	return nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT); +} +  static struct nft_expr_type nft_nat_type;  static const struct nft_expr_ops nft_nat_ops = {  	.type           = &nft_nat_type, @@ -212,6 +233,7 @@ static const struct nft_expr_ops nft_nat_ops = {  	.eval           = nft_nat_eval,  	.init           = nft_nat_init,  	.dump           = nft_nat_dump, +	.validate	= nft_nat_validate,  };  static struct nft_expr_type nft_nat_type __read_mostly = { diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 7a186e74b1b3..f1de72de273e 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -96,6 +96,14 @@ static DECLARE_WAIT_QUEUE_HEAD(nl_table_wait);  static int netlink_dump(struct sock *sk);  static void netlink_skb_destructor(struct sk_buff *skb); +/* nl_table locking explained: + * Lookup and traversal are protected with nl_sk_hash_lock or nl_table_lock + * combined with an RCU read-side lock. Insertion and removal are protected + * with nl_sk_hash_lock while using RCU list modification primitives and may + * run in parallel to nl_table_lock protected lookups. Destruction of the + * Netlink socket may only occur *after* nl_table_lock has been acquired + * either during or after the socket has been removed from the list. + */  DEFINE_RWLOCK(nl_table_lock);  EXPORT_SYMBOL_GPL(nl_table_lock);  static atomic_t nl_table_users = ATOMIC_INIT(0); @@ -109,10 +117,10 @@ EXPORT_SYMBOL_GPL(nl_sk_hash_lock);  static int lockdep_nl_sk_hash_is_held(void)  {  #ifdef CONFIG_LOCKDEP -	return (debug_locks) ? lockdep_is_held(&nl_sk_hash_lock) : 1; -#else -	return 1; +	if (debug_locks) +		return lockdep_is_held(&nl_sk_hash_lock) || lockdep_is_held(&nl_table_lock);  #endif +	return 1;  }  static ATOMIC_NOTIFIER_HEAD(netlink_chain); @@ -1028,11 +1036,13 @@ static struct sock *netlink_lookup(struct net *net, int protocol, u32 portid)  	struct netlink_table *table = &nl_table[protocol];  	struct sock *sk; +	read_lock(&nl_table_lock);  	rcu_read_lock();  	sk = __netlink_lookup(table, portid, net);  	if (sk)  		sock_hold(sk);  	rcu_read_unlock(); +	read_unlock(&nl_table_lock);  	return sk;  } @@ -1257,9 +1267,6 @@ static int netlink_release(struct socket *sock)  	}  	netlink_table_ungrab(); -	/* Wait for readers to complete */ -	synchronize_net(); -  	kfree(nlk->groups);  	nlk->groups = NULL; @@ -1281,6 +1288,7 @@ static int netlink_autobind(struct socket *sock)  retry:  	cond_resched(); +	netlink_table_grab();  	rcu_read_lock();  	if (__netlink_lookup(table, portid, net)) {  		/* Bind collision, search negative portid values. */ @@ -1288,9 +1296,11 @@ retry:  		if (rover > -4097)  			rover = -4097;  		rcu_read_unlock(); +		netlink_table_ungrab();  		goto retry;  	}  	rcu_read_unlock(); +	netlink_table_ungrab();  	err = netlink_insert(sk, net, portid);  	if (err == -EADDRINUSE) @@ -2921,14 +2931,16 @@ static struct sock *netlink_seq_socket_idx(struct seq_file *seq, loff_t pos)  }  static void *netlink_seq_start(struct seq_file *seq, loff_t *pos) -	__acquires(RCU) +	__acquires(nl_table_lock) __acquires(RCU)  { +	read_lock(&nl_table_lock);  	rcu_read_lock();  	return *pos ? netlink_seq_socket_idx(seq, *pos - 1) : SEQ_START_TOKEN;  }  static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos)  { +	struct rhashtable *ht;  	struct netlink_sock *nlk;  	struct nl_seq_iter *iter;  	struct net *net; @@ -2943,19 +2955,19 @@ static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos)  	iter = seq->private;  	nlk = v; -	rht_for_each_entry_rcu(nlk, nlk->node.next, node) +	i = iter->link; +	ht = &nl_table[i].hash; +	rht_for_each_entry(nlk, nlk->node.next, ht, node)  		if (net_eq(sock_net((struct sock *)nlk), net))  			return nlk; -	i = iter->link;  	j = iter->hash_idx + 1;  	do { -		struct rhashtable *ht = &nl_table[i].hash;  		const struct bucket_table *tbl = rht_dereference_rcu(ht->tbl, ht);  		for (; j < tbl->size; j++) { -			rht_for_each_entry_rcu(nlk, tbl->buckets[j], node) { +			rht_for_each_entry(nlk, tbl->buckets[j], ht, node) {  				if (net_eq(sock_net((struct sock *)nlk), net)) {  					iter->link = i;  					iter->hash_idx = j; @@ -2971,9 +2983,10 @@ static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos)  }  static void netlink_seq_stop(struct seq_file *seq, void *v) -	__releases(RCU) +	__releases(RCU) __releases(nl_table_lock)  {  	rcu_read_unlock(); +	read_unlock(&nl_table_lock);  } diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 2e31d9e7f4dc..e6d7255183eb 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -324,6 +324,8 @@ static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb,  	segs = __skb_gso_segment(skb, NETIF_F_SG, false);  	if (IS_ERR(segs))  		return PTR_ERR(segs); +	if (segs == NULL) +		return -EINVAL;  	/* Queue all of the segments. */  	skb = segs; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 2cf61b3e633c..76f402e05bd6 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -947,7 +947,7 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue,  	if (!ops->init || (err = ops->init(sch, tca[TCA_OPTIONS])) == 0) {  		if (qdisc_is_percpu_stats(sch)) {  			sch->cpu_bstats = -				alloc_percpu(struct gnet_stats_basic_cpu); +				netdev_alloc_pcpu_stats(struct gnet_stats_basic_cpu);  			if (!sch->cpu_bstats)  				goto err_out4; diff --git a/net/sched/sch_pie.c b/net/sched/sch_pie.c index 33d7a98a7a97..b783a446d884 100644 --- a/net/sched/sch_pie.c +++ b/net/sched/sch_pie.c @@ -445,7 +445,6 @@ static int pie_init(struct Qdisc *sch, struct nlattr *opt)  	sch->limit = q->params.limit;  	setup_timer(&q->adapt_timer, pie_timer, (unsigned long)sch); -	mod_timer(&q->adapt_timer, jiffies + HZ / 2);  	if (opt) {  		int err = pie_change(sch, opt); @@ -454,6 +453,7 @@ static int pie_init(struct Qdisc *sch, struct nlattr *opt)  			return err;  	} +	mod_timer(&q->adapt_timer, jiffies + HZ / 2);  	return 0;  } diff --git a/net/tipc/node.c b/net/tipc/node.c index 90cee4a6fce4..5781634e957d 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -219,11 +219,11 @@ void tipc_node_abort_sock_conns(struct list_head *conns)  void tipc_node_link_up(struct tipc_node *n_ptr, struct tipc_link *l_ptr)  {  	struct tipc_link **active = &n_ptr->active_links[0]; -	u32 addr = n_ptr->addr;  	n_ptr->working_links++; -	tipc_nametbl_publish(TIPC_LINK_STATE, addr, addr, TIPC_NODE_SCOPE, -			     l_ptr->bearer_id, addr); +	n_ptr->action_flags |= TIPC_NOTIFY_LINK_UP; +	n_ptr->link_id = l_ptr->peer_bearer_id << 16 | l_ptr->bearer_id; +  	pr_info("Established link <%s> on network plane %c\n",  		l_ptr->name, l_ptr->net_plane); @@ -284,10 +284,10 @@ static void node_select_active_links(struct tipc_node *n_ptr)  void tipc_node_link_down(struct tipc_node *n_ptr, struct tipc_link *l_ptr)  {  	struct tipc_link **active; -	u32 addr = n_ptr->addr;  	n_ptr->working_links--; -	tipc_nametbl_withdraw(TIPC_LINK_STATE, addr, l_ptr->bearer_id, addr); +	n_ptr->action_flags |= TIPC_NOTIFY_LINK_DOWN; +	n_ptr->link_id = l_ptr->peer_bearer_id << 16 | l_ptr->bearer_id;  	if (!tipc_link_is_active(l_ptr)) {  		pr_info("Lost standby link <%s> on network plane %c\n", @@ -552,28 +552,30 @@ void tipc_node_unlock(struct tipc_node *node)  	LIST_HEAD(conn_sks);  	struct sk_buff_head waiting_sks;  	u32 addr = 0; -	unsigned int flags = node->action_flags; +	int flags = node->action_flags; +	u32 link_id = 0; -	if (likely(!node->action_flags)) { +	if (likely(!flags)) {  		spin_unlock_bh(&node->lock);  		return;  	} +	addr = node->addr; +	link_id = node->link_id;  	__skb_queue_head_init(&waiting_sks); -	if (node->action_flags & TIPC_WAKEUP_USERS) { + +	if (flags & TIPC_WAKEUP_USERS)  		skb_queue_splice_init(&node->waiting_sks, &waiting_sks); -		node->action_flags &= ~TIPC_WAKEUP_USERS; -	} -	if (node->action_flags & TIPC_NOTIFY_NODE_DOWN) { + +	if (flags & TIPC_NOTIFY_NODE_DOWN) {  		list_replace_init(&node->nsub, &nsub_list);  		list_replace_init(&node->conn_sks, &conn_sks); -		node->action_flags &= ~TIPC_NOTIFY_NODE_DOWN;  	} -	if (node->action_flags & TIPC_NOTIFY_NODE_UP) { -		node->action_flags &= ~TIPC_NOTIFY_NODE_UP; -		addr = node->addr; -	} -	node->action_flags &= ~TIPC_WAKEUP_BCAST_USERS; +	node->action_flags &= ~(TIPC_WAKEUP_USERS | TIPC_NOTIFY_NODE_DOWN | +				TIPC_NOTIFY_NODE_UP | TIPC_NOTIFY_LINK_UP | +				TIPC_NOTIFY_LINK_DOWN | +				TIPC_WAKEUP_BCAST_USERS); +  	spin_unlock_bh(&node->lock);  	while (!skb_queue_empty(&waiting_sks)) @@ -588,6 +590,14 @@ void tipc_node_unlock(struct tipc_node *node)  	if (flags & TIPC_WAKEUP_BCAST_USERS)  		tipc_bclink_wakeup_users(); -	if (addr) +	if (flags & TIPC_NOTIFY_NODE_UP)  		tipc_named_node_up(addr); + +	if (flags & TIPC_NOTIFY_LINK_UP) +		tipc_nametbl_publish(TIPC_LINK_STATE, addr, addr, +				     TIPC_NODE_SCOPE, link_id, addr); + +	if (flags & TIPC_NOTIFY_LINK_DOWN) +		tipc_nametbl_withdraw(TIPC_LINK_STATE, addr, +				      link_id, addr);  } diff --git a/net/tipc/node.h b/net/tipc/node.h index 67513c3c852c..04e91458bb29 100644 --- a/net/tipc/node.h +++ b/net/tipc/node.h @@ -53,6 +53,7 @@   * TIPC_WAIT_OWN_LINKS_DOWN: wait until peer node is declared down   * TIPC_NOTIFY_NODE_DOWN: notify node is down   * TIPC_NOTIFY_NODE_UP: notify node is up + * TIPC_DISTRIBUTE_NAME: publish or withdraw link state name type   */  enum {  	TIPC_WAIT_PEER_LINKS_DOWN	= (1 << 1), @@ -60,7 +61,9 @@ enum {  	TIPC_NOTIFY_NODE_DOWN		= (1 << 3),  	TIPC_NOTIFY_NODE_UP		= (1 << 4),  	TIPC_WAKEUP_USERS		= (1 << 5), -	TIPC_WAKEUP_BCAST_USERS		= (1 << 6) +	TIPC_WAKEUP_BCAST_USERS		= (1 << 6), +	TIPC_NOTIFY_LINK_UP		= (1 << 7), +	TIPC_NOTIFY_LINK_DOWN		= (1 << 8)  };  /** @@ -100,6 +103,7 @@ struct tipc_node_bclink {   * @working_links: number of working links to node (both active and standby)   * @link_cnt: number of links to node   * @signature: node instance identifier + * @link_id: local and remote bearer ids of changing link, if any   * @nsub: list of "node down" subscriptions monitoring node   * @rcu: rcu struct for tipc_node   */ @@ -116,6 +120,7 @@ struct tipc_node {  	int link_cnt;  	int working_links;  	u32 signature; +	u32 link_id;  	struct list_head nsub;  	struct sk_buff_head waiting_sks;  	struct list_head conn_sks; diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 75275c5cf929..51bddc236a15 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -1776,7 +1776,7 @@ int tipc_sk_rcv(struct sk_buff *buf)  	sk = &tsk->sk;  	/* Queue message */ -	bh_lock_sock(sk); +	spin_lock_bh(&sk->sk_lock.slock);  	if (!sock_owned_by_user(sk)) {  		rc = filter_rcv(sk, buf); @@ -1787,7 +1787,7 @@ int tipc_sk_rcv(struct sk_buff *buf)  		if (sk_add_backlog(sk, buf, limit))  			rc = -TIPC_ERR_OVERLOAD;  	} -	bh_unlock_sock(sk); +	spin_unlock_bh(&sk->sk_lock.slock);  	tipc_sk_put(tsk);  	if (likely(!rc))  		return 0; @@ -2673,7 +2673,7 @@ static int tipc_ioctl(struct socket *sk, unsigned int cmd, unsigned long arg)  	case SIOCGETLINKNAME:  		if (copy_from_user(&lnr, argp, sizeof(lnr)))  			return -EFAULT; -		if (!tipc_node_get_linkname(lnr.bearer_id, lnr.peer, +		if (!tipc_node_get_linkname(lnr.bearer_id & 0xffff, lnr.peer,  					    lnr.linkname, TIPC_MAX_LINK_NAME)) {  			if (copy_to_user(argp, &lnr, sizeof(lnr)))  				return -EFAULT; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index cb9f5a44ffad..5839c85075f1 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5927,6 +5927,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)  	int err;  	bool need_new_beacon = false;  	int len, i; +	u32 cs_count;  	if (!rdev->ops->channel_switch ||  	    !(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)) @@ -5963,7 +5964,14 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)  	if (need_new_beacon && !info->attrs[NL80211_ATTR_CSA_IES])  		return -EINVAL; -	params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]); +	/* Even though the attribute is u32, the specification says +	 * u8, so let's make sure we don't overflow. +	 */ +	cs_count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]); +	if (cs_count > 255) +		return -EINVAL; + +	params.count = cs_count;  	if (!need_new_beacon)  		goto skip_beacons; diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index 499d6c18a8ce..7c532856b398 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c @@ -157,6 +157,8 @@ static int xfrm_output_gso(struct sk_buff *skb)  	kfree_skb(skb);  	if (IS_ERR(segs))  		return PTR_ERR(segs); +	if (segs == NULL) +		return -EINVAL;  	do {  		struct sk_buff *nskb = segs->next; diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 4c4e457e7888..88bf289abdc9 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1962,7 +1962,7 @@ static int xdst_queue_output(struct sock *sk, struct sk_buff *skb)  	struct xfrm_policy *pol = xdst->pols[0];  	struct xfrm_policy_queue *pq = &pol->polq; -	if (unlikely(skb_fclone_busy(skb))) { +	if (unlikely(skb_fclone_busy(sk, skb))) {  		kfree_skb(skb);  		return 0;  	} | 
