diff options
Diffstat (limited to 'net/ipv6')
| -rw-r--r-- | net/ipv6/addrconf.c | 77 | ||||
| -rw-r--r-- | net/ipv6/addrconf_core.c | 19 | ||||
| -rw-r--r-- | net/ipv6/ip6_input.c | 15 | ||||
| -rw-r--r-- | net/ipv6/netfilter/ip6t_NPT.c | 4 | ||||
| -rw-r--r-- | net/ipv6/netfilter/ip6t_rpfilter.c | 8 | ||||
| -rw-r--r-- | net/ipv6/netfilter/nf_conntrack_reasm.c | 12 | ||||
| -rw-r--r-- | net/ipv6/reassembly.c | 20 | ||||
| -rw-r--r-- | net/ipv6/tcp_ipv6.c | 8 | ||||
| -rw-r--r-- | net/ipv6/udp.c | 8 | 
9 files changed, 121 insertions, 50 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index f2c7e615f90..dae802c0af7 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -168,8 +168,6 @@ static void inet6_prefix_notify(int event, struct inet6_dev *idev,  static bool ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr,  			       struct net_device *dev); -static ATOMIC_NOTIFIER_HEAD(inet6addr_chain); -  static struct ipv6_devconf ipv6_devconf __read_mostly = {  	.forwarding		= 0,  	.hop_limit		= IPV6_DEFAULT_HOPLIMIT, @@ -837,7 +835,7 @@ out2:  	rcu_read_unlock_bh();  	if (likely(err == 0)) -		atomic_notifier_call_chain(&inet6addr_chain, NETDEV_UP, ifa); +		inet6addr_notifier_call_chain(NETDEV_UP, ifa);  	else {  		kfree(ifa);  		ifa = ERR_PTR(err); @@ -927,7 +925,7 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)  	ipv6_ifa_notify(RTM_DELADDR, ifp); -	atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifp); +	inet6addr_notifier_call_chain(NETDEV_DOWN, ifp);  	/*  	 * Purge or update corresponding prefix @@ -2529,6 +2527,9 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)  static void init_loopback(struct net_device *dev)  {  	struct inet6_dev  *idev; +	struct net_device *sp_dev; +	struct inet6_ifaddr *sp_ifa; +	struct rt6_info *sp_rt;  	/* ::1 */ @@ -2540,6 +2541,30 @@ static void init_loopback(struct net_device *dev)  	}  	add_addr(idev, &in6addr_loopback, 128, IFA_HOST); + +	/* Add routes to other interface's IPv6 addresses */ +	for_each_netdev(dev_net(dev), sp_dev) { +		if (!strcmp(sp_dev->name, dev->name)) +			continue; + +		idev = __in6_dev_get(sp_dev); +		if (!idev) +			continue; + +		read_lock_bh(&idev->lock); +		list_for_each_entry(sp_ifa, &idev->addr_list, if_list) { + +			if (sp_ifa->flags & (IFA_F_DADFAILED | IFA_F_TENTATIVE)) +				continue; + +			sp_rt = addrconf_dst_alloc(idev, &sp_ifa->addr, 0); + +			/* Failure cases are ignored */ +			if (!IS_ERR(sp_rt)) +				ip6_ins_rt(sp_rt); +		} +		read_unlock_bh(&idev->lock); +	}  }  static void addrconf_add_linklocal(struct inet6_dev *idev, const struct in6_addr *addr) @@ -2961,7 +2986,7 @@ static int addrconf_ifdown(struct net_device *dev, int how)  		if (state != INET6_IFADDR_STATE_DEAD) {  			__ipv6_ifa_notify(RTM_DELADDR, ifa); -			atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa); +			inet6addr_notifier_call_chain(NETDEV_DOWN, ifa);  		}  		in6_ifa_put(ifa); @@ -4784,26 +4809,20 @@ static void addrconf_sysctl_unregister(struct inet6_dev *idev)  static int __net_init addrconf_init_net(struct net *net)  { -	int err; +	int err = -ENOMEM;  	struct ipv6_devconf *all, *dflt; -	err = -ENOMEM; -	all = &ipv6_devconf; -	dflt = &ipv6_devconf_dflt; +	all = kmemdup(&ipv6_devconf, sizeof(ipv6_devconf), GFP_KERNEL); +	if (all == NULL) +		goto err_alloc_all; -	if (!net_eq(net, &init_net)) { -		all = kmemdup(all, sizeof(ipv6_devconf), GFP_KERNEL); -		if (all == NULL) -			goto err_alloc_all; +	dflt = kmemdup(&ipv6_devconf_dflt, sizeof(ipv6_devconf_dflt), GFP_KERNEL); +	if (dflt == NULL) +		goto err_alloc_dflt; -		dflt = kmemdup(dflt, sizeof(ipv6_devconf_dflt), GFP_KERNEL); -		if (dflt == NULL) -			goto err_alloc_dflt; -	} else { -		/* these will be inherited by all namespaces */ -		dflt->autoconf = ipv6_defaults.autoconf; -		dflt->disable_ipv6 = ipv6_defaults.disable_ipv6; -	} +	/* these will be inherited by all namespaces */ +	dflt->autoconf = ipv6_defaults.autoconf; +	dflt->disable_ipv6 = ipv6_defaults.disable_ipv6;  	net->ipv6.devconf_all = all;  	net->ipv6.devconf_dflt = dflt; @@ -4848,22 +4867,6 @@ static struct pernet_operations addrconf_ops = {  	.exit = addrconf_exit_net,  }; -/* - *      Device notifier - */ - -int register_inet6addr_notifier(struct notifier_block *nb) -{ -	return atomic_notifier_chain_register(&inet6addr_chain, nb); -} -EXPORT_SYMBOL(register_inet6addr_notifier); - -int unregister_inet6addr_notifier(struct notifier_block *nb) -{ -	return atomic_notifier_chain_unregister(&inet6addr_chain, nb); -} -EXPORT_SYMBOL(unregister_inet6addr_notifier); -  static struct rtnl_af_ops inet6_ops = {  	.family		  = AF_INET6,  	.fill_link_af	  = inet6_fill_link_af, diff --git a/net/ipv6/addrconf_core.c b/net/ipv6/addrconf_core.c index d051e5f4bf3..72104562c86 100644 --- a/net/ipv6/addrconf_core.c +++ b/net/ipv6/addrconf_core.c @@ -78,3 +78,22 @@ int __ipv6_addr_type(const struct in6_addr *addr)  }  EXPORT_SYMBOL(__ipv6_addr_type); +static ATOMIC_NOTIFIER_HEAD(inet6addr_chain); + +int register_inet6addr_notifier(struct notifier_block *nb) +{ +	return atomic_notifier_chain_register(&inet6addr_chain, nb); +} +EXPORT_SYMBOL(register_inet6addr_notifier); + +int unregister_inet6addr_notifier(struct notifier_block *nb) +{ +	return atomic_notifier_chain_unregister(&inet6addr_chain, nb); +} +EXPORT_SYMBOL(unregister_inet6addr_notifier); + +int inet6addr_notifier_call_chain(unsigned long val, void *v) +{ +	return atomic_notifier_call_chain(&inet6addr_chain, val, v); +} +EXPORT_SYMBOL(inet6addr_notifier_call_chain); diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index b1876e52091..2bab2aa5974 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -118,6 +118,18 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt  	    ipv6_addr_loopback(&hdr->daddr))  		goto err; +	/* RFC4291 Errata ID: 3480 +	 * Interface-Local scope spans only a single interface on a +	 * node and is useful only for loopback transmission of +	 * multicast.  Packets with interface-local scope received +	 * from another node must be discarded. +	 */ +	if (!(skb->pkt_type == PACKET_LOOPBACK || +	      dev->flags & IFF_LOOPBACK) && +	    ipv6_addr_is_multicast(&hdr->daddr) && +	    IPV6_ADDR_MC_SCOPE(&hdr->daddr) == 1) +		goto err; +  	/* RFC4291 2.7  	 * Nodes must not originate a packet to a multicast address whose scope  	 * field contains the reserved value 0; if such a packet is received, it @@ -281,7 +293,8 @@ int ip6_mc_input(struct sk_buff *skb)  	 *      IPv6 multicast router mode is now supported ;)  	 */  	if (dev_net(skb->dev)->ipv6.devconf_all->mc_forwarding && -	    !(ipv6_addr_type(&hdr->daddr) & IPV6_ADDR_LINKLOCAL) && +	    !(ipv6_addr_type(&hdr->daddr) & +	      (IPV6_ADDR_LOOPBACK|IPV6_ADDR_LINKLOCAL)) &&  	    likely(!(IP6CB(skb)->flags & IP6SKB_FORWARDED))) {  		/*  		 * Okay, we try to forward - split and duplicate diff --git a/net/ipv6/netfilter/ip6t_NPT.c b/net/ipv6/netfilter/ip6t_NPT.c index 83acc1405a1..cb631143721 100644 --- a/net/ipv6/netfilter/ip6t_NPT.c +++ b/net/ipv6/netfilter/ip6t_NPT.c @@ -57,7 +57,7 @@ static bool ip6t_npt_map_pfx(const struct ip6t_npt_tginfo *npt,  		if (pfx_len - i >= 32)  			mask = 0;  		else -			mask = htonl(~((1 << (pfx_len - i)) - 1)); +			mask = htonl((1 << (i - pfx_len + 32)) - 1);  		idx = i / 32;  		addr->s6_addr32[idx] &= mask; @@ -114,6 +114,7 @@ ip6t_dnpt_tg(struct sk_buff *skb, const struct xt_action_param *par)  static struct xt_target ip6t_npt_target_reg[] __read_mostly = {  	{  		.name		= "SNPT", +		.table		= "mangle",  		.target		= ip6t_snpt_tg,  		.targetsize	= sizeof(struct ip6t_npt_tginfo),  		.checkentry	= ip6t_npt_checkentry, @@ -124,6 +125,7 @@ static struct xt_target ip6t_npt_target_reg[] __read_mostly = {  	},  	{  		.name		= "DNPT", +		.table		= "mangle",  		.target		= ip6t_dnpt_tg,  		.targetsize	= sizeof(struct ip6t_npt_tginfo),  		.checkentry	= ip6t_npt_checkentry, diff --git a/net/ipv6/netfilter/ip6t_rpfilter.c b/net/ipv6/netfilter/ip6t_rpfilter.c index 5060d54199a..e0983f3648a 100644 --- a/net/ipv6/netfilter/ip6t_rpfilter.c +++ b/net/ipv6/netfilter/ip6t_rpfilter.c @@ -71,6 +71,12 @@ static bool rpfilter_lookup_reverse6(const struct sk_buff *skb,  	return ret;  } +static bool rpfilter_is_local(const struct sk_buff *skb) +{ +	const struct rt6_info *rt = (const void *) skb_dst(skb); +	return rt && (rt->rt6i_flags & RTF_LOCAL); +} +  static bool rpfilter_mt(const struct sk_buff *skb, struct xt_action_param *par)  {  	const struct xt_rpfilter_info *info = par->matchinfo; @@ -78,7 +84,7 @@ static bool rpfilter_mt(const struct sk_buff *skb, struct xt_action_param *par)  	struct ipv6hdr *iph;  	bool invert = info->flags & XT_RPFILTER_INVERT; -	if (par->in->flags & IFF_LOOPBACK) +	if (rpfilter_is_local(skb))  		return true ^ invert;  	iph = ipv6_hdr(skb); diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 54087e96d7b..6700069949d 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -14,6 +14,8 @@   * 2 of the License, or (at your option) any later version.   */ +#define pr_fmt(fmt) "IPv6-nf: " fmt +  #include <linux/errno.h>  #include <linux/types.h>  #include <linux/string.h> @@ -180,13 +182,11 @@ static inline struct frag_queue *fq_find(struct net *net, __be32 id,  	q = inet_frag_find(&net->nf_frag.frags, &nf_frags, &arg, hash);  	local_bh_enable(); -	if (q == NULL) -		goto oom; - +	if (IS_ERR_OR_NULL(q)) { +		inet_frag_maybe_warn_overflow(q, pr_fmt()); +		return NULL; +	}  	return container_of(q, struct frag_queue, q); - -oom: -	return NULL;  } diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 3c6a77290c6..0ba10e53a62 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -26,6 +26,9 @@   *	YOSHIFUJI,H. @USAGI	Always remove fragment header to   *				calculate ICV correctly.   */ + +#define pr_fmt(fmt) "IPv6: " fmt +  #include <linux/errno.h>  #include <linux/types.h>  #include <linux/string.h> @@ -185,9 +188,10 @@ fq_find(struct net *net, __be32 id, const struct in6_addr *src, const struct in6  	hash = inet6_hash_frag(id, src, dst, ip6_frags.rnd);  	q = inet_frag_find(&net->ipv6.frags, &ip6_frags, &arg, hash); -	if (q == NULL) +	if (IS_ERR_OR_NULL(q)) { +		inet_frag_maybe_warn_overflow(q, pr_fmt());  		return NULL; - +	}  	return container_of(q, struct frag_queue, q);  } @@ -326,9 +330,17 @@ found:  	}  	if (fq->q.last_in == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) && -	    fq->q.meat == fq->q.len) -		return ip6_frag_reasm(fq, prev, dev); +	    fq->q.meat == fq->q.len) { +		int res; +		unsigned long orefdst = skb->_skb_refdst; + +		skb->_skb_refdst = 0UL; +		res = ip6_frag_reasm(fq, prev, dev); +		skb->_skb_refdst = orefdst; +		return res; +	} +	skb_dst_drop(skb);  	inet_frag_lru_move(&fq->q);  	return -1; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 9b6460055df..46a5be85be8 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -386,9 +386,17 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,  		if (dst)  			dst->ops->redirect(dst, sk, skb); +		goto out;  	}  	if (type == ICMPV6_PKT_TOOBIG) { +		/* We are not interested in TCP_LISTEN and open_requests +		 * (SYN-ACKs send out by Linux are always <576bytes so +		 * they should go through unfragmented). +		 */ +		if (sk->sk_state == TCP_LISTEN) +			goto out; +  		tp->mtu_info = ntohl(info);  		if (!sock_owned_by_user(sk))  			tcp_v6_mtu_reduced(sk); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 599e1ba6d1c..d8e5e852fc7 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1285,10 +1285,18 @@ do_confirm:  void udpv6_destroy_sock(struct sock *sk)  { +	struct udp_sock *up = udp_sk(sk);  	lock_sock(sk);  	udp_v6_flush_pending_frames(sk);  	release_sock(sk); +	if (static_key_false(&udpv6_encap_needed) && up->encap_type) { +		void (*encap_destroy)(struct sock *sk); +		encap_destroy = ACCESS_ONCE(up->encap_destroy); +		if (encap_destroy) +			encap_destroy(sk); +	} +  	inet6_destroy_sock(sk);  }  |