diff options
Diffstat (limited to 'net/ipv6')
| -rw-r--r-- | net/ipv6/addrconf.c | 124 | ||||
| -rw-r--r-- | net/ipv6/af_inet6.c | 1 | ||||
| -rw-r--r-- | net/ipv6/datagram.c | 4 | ||||
| -rw-r--r-- | net/ipv6/exthdrs.c | 7 | ||||
| -rw-r--r-- | net/ipv6/icmp.c | 28 | ||||
| -rw-r--r-- | net/ipv6/inet6_connection_sock.c | 9 | ||||
| -rw-r--r-- | net/ipv6/ip6_fib.c | 4 | ||||
| -rw-r--r-- | net/ipv6/ip6_output.c | 37 | ||||
| -rw-r--r-- | net/ipv6/ip6_tunnel.c | 54 | ||||
| -rw-r--r-- | net/ipv6/ipv6_sockglue.c | 2 | ||||
| -rw-r--r-- | net/ipv6/ndisc.c | 42 | ||||
| -rw-r--r-- | net/ipv6/netfilter.c | 11 | ||||
| -rw-r--r-- | net/ipv6/netfilter/nf_conntrack_reasm.c | 11 | ||||
| -rw-r--r-- | net/ipv6/raw.c | 11 | ||||
| -rw-r--r-- | net/ipv6/reassembly.c | 4 | ||||
| -rw-r--r-- | net/ipv6/route.c | 3 | ||||
| -rw-r--r-- | net/ipv6/sit.c | 12 | ||||
| -rw-r--r-- | net/ipv6/syncookies.c | 6 | ||||
| -rw-r--r-- | net/ipv6/tcp_ipv6.c | 88 | ||||
| -rw-r--r-- | net/ipv6/udp.c | 16 | ||||
| -rw-r--r-- | net/ipv6/udplite.c | 13 | ||||
| -rw-r--r-- | net/ipv6/xfrm6_output.c | 56 | 
22 files changed, 344 insertions, 199 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 12368c58606..d0611a5de45 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -428,7 +428,7 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)  	ndev->tstamp = jiffies;  	addrconf_sysctl_register(ndev);  	/* protected by rtnl_lock */ -	rcu_assign_pointer(dev->ip6_ptr, ndev); +	RCU_INIT_POINTER(dev->ip6_ptr, ndev);  	/* Join all-node multicast group */  	ipv6_dev_mc_inc(dev, &in6addr_linklocal_allnodes); @@ -824,12 +824,13 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i  {  	struct inet6_dev *idev = ifp->idev;  	struct in6_addr addr, *tmpaddr; -	unsigned long tmp_prefered_lft, tmp_valid_lft, tmp_cstamp, tmp_tstamp, age; +	unsigned long tmp_prefered_lft, tmp_valid_lft, tmp_tstamp, age;  	unsigned long regen_advance;  	int tmp_plen;  	int ret = 0;  	int max_addresses;  	u32 addr_flags; +	unsigned long now = jiffies;  	write_lock(&idev->lock);  	if (ift) { @@ -874,7 +875,7 @@ retry:  		goto out;  	}  	memcpy(&addr.s6_addr[8], idev->rndid, 8); -	age = (jiffies - ifp->tstamp) / HZ; +	age = (now - ifp->tstamp) / HZ;  	tmp_valid_lft = min_t(__u32,  			      ifp->valid_lft,  			      idev->cnf.temp_valid_lft + age); @@ -884,7 +885,6 @@ retry:  				 idev->cnf.max_desync_factor);  	tmp_plen = ifp->prefix_len;  	max_addresses = idev->cnf.max_addresses; -	tmp_cstamp = ifp->cstamp;  	tmp_tstamp = ifp->tstamp;  	spin_unlock_bh(&ifp->lock); @@ -929,7 +929,7 @@ retry:  	ift->ifpub = ifp;  	ift->valid_lft = tmp_valid_lft;  	ift->prefered_lft = tmp_prefered_lft; -	ift->cstamp = tmp_cstamp; +	ift->cstamp = now;  	ift->tstamp = tmp_tstamp;  	spin_unlock_bh(&ift->lock); @@ -1713,6 +1713,40 @@ addrconf_prefix_route(struct in6_addr *pfx, int plen, struct net_device *dev,  	ip6_route_add(&cfg);  } + +static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx, +						  int plen, +						  const struct net_device *dev, +						  u32 flags, u32 noflags) +{ +	struct fib6_node *fn; +	struct rt6_info *rt = NULL; +	struct fib6_table *table; + +	table = fib6_get_table(dev_net(dev), RT6_TABLE_PREFIX); +	if (table == NULL) +		return NULL; + +	write_lock_bh(&table->tb6_lock); +	fn = fib6_locate(&table->tb6_root, pfx, plen, NULL, 0); +	if (!fn) +		goto out; +	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { +		if (rt->rt6i_dev->ifindex != dev->ifindex) +			continue; +		if ((rt->rt6i_flags & flags) != flags) +			continue; +		if ((noflags != 0) && ((rt->rt6i_flags & flags) != 0)) +			continue; +		dst_hold(&rt->dst); +		break; +	} +out: +	write_unlock_bh(&table->tb6_lock); +	return rt; +} + +  /* Create "default" multicast route to the interface */  static void addrconf_add_mroute(struct net_device *dev) @@ -1842,10 +1876,13 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len)  		if (addrconf_finite_timeout(rt_expires))  			rt_expires *= HZ; -		rt = rt6_lookup(net, &pinfo->prefix, NULL, -				dev->ifindex, 1); +		rt = addrconf_get_prefix_route(&pinfo->prefix, +					       pinfo->prefix_len, +					       dev, +					       RTF_ADDRCONF | RTF_PREFIX_RT, +					       RTF_GATEWAY | RTF_DEFAULT); -		if (rt && addrconf_is_prefix_route(rt)) { +		if (rt) {  			/* Autoconf prefix route */  			if (valid_lft == 0) {  				ip6_del_rt(rt); @@ -1999,25 +2036,50 @@ ok:  #ifdef CONFIG_IPV6_PRIVACY  			read_lock_bh(&in6_dev->lock);  			/* update all temporary addresses in the list */ -			list_for_each_entry(ift, &in6_dev->tempaddr_list, tmp_list) { -				/* -				 * When adjusting the lifetimes of an existing -				 * temporary address, only lower the lifetimes. -				 * Implementations must not increase the -				 * lifetimes of an existing temporary address -				 * when processing a Prefix Information Option. -				 */ +			list_for_each_entry(ift, &in6_dev->tempaddr_list, +					    tmp_list) { +				int age, max_valid, max_prefered; +  				if (ifp != ift->ifpub)  					continue; +				/* +				 * RFC 4941 section 3.3: +				 * If a received option will extend the lifetime +				 * of a public address, the lifetimes of +				 * temporary addresses should be extended, +				 * subject to the overall constraint that no +				 * temporary addresses should ever remain +				 * "valid" or "preferred" for a time longer than +				 * (TEMP_VALID_LIFETIME) or +				 * (TEMP_PREFERRED_LIFETIME - DESYNC_FACTOR), +				 * respectively. +				 */ +				age = (now - ift->cstamp) / HZ; +				max_valid = in6_dev->cnf.temp_valid_lft - age; +				if (max_valid < 0) +					max_valid = 0; + +				max_prefered = in6_dev->cnf.temp_prefered_lft - +					       in6_dev->cnf.max_desync_factor - +					       age; +				if (max_prefered < 0) +					max_prefered = 0; + +				if (valid_lft > max_valid) +					valid_lft = max_valid; + +				if (prefered_lft > max_prefered) +					prefered_lft = max_prefered; +  				spin_lock(&ift->lock);  				flags = ift->flags; -				if (ift->valid_lft > valid_lft && -				    ift->valid_lft - valid_lft > (jiffies - ift->tstamp) / HZ) -					ift->valid_lft = valid_lft + (jiffies - ift->tstamp) / HZ; -				if (ift->prefered_lft > prefered_lft && -				    ift->prefered_lft - prefered_lft > (jiffies - ift->tstamp) / HZ) -					ift->prefered_lft = prefered_lft + (jiffies - ift->tstamp) / HZ; +				ift->valid_lft = valid_lft; +				ift->prefered_lft = prefered_lft; +				ift->tstamp = now; +				if (prefered_lft > 0) +					ift->flags &= ~IFA_F_DEPRECATED; +  				spin_unlock(&ift->lock);  				if (!(flags&IFA_F_TENTATIVE))  					ipv6_ifa_notify(0, ift); @@ -2025,9 +2087,11 @@ ok:  			if ((create || list_empty(&in6_dev->tempaddr_list)) && in6_dev->cnf.use_tempaddr > 0) {  				/* -				 * When a new public address is created as described in [ADDRCONF], -				 * also create a new temporary address. Also create a temporary -				 * address if it's enabled but no temporary address currently exists. +				 * When a new public address is created as +				 * described in [ADDRCONF], also create a new +				 * temporary address. Also create a temporary +				 * address if it's enabled but no temporary +				 * address currently exists.  				 */  				read_unlock_bh(&in6_dev->lock);  				ipv6_create_tempaddr(ifp, NULL); @@ -2706,7 +2770,7 @@ static int addrconf_ifdown(struct net_device *dev, int how)  		idev->dead = 1;  		/* protected by rtnl_lock */ -		rcu_assign_pointer(dev->ip6_ptr, NULL); +		RCU_INIT_POINTER(dev->ip6_ptr, NULL);  		/* Step 1.5: remove snmp6 entry */  		snmp6_unregister_dev(idev); @@ -2969,12 +3033,12 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)  	ipv6_ifa_notify(RTM_NEWADDR, ifp); -	/* If added prefix is link local and forwarding is off, -	   start sending router solicitations. +	/* If added prefix is link local and we are prepared to process +	   router advertisements, start sending router solicitations.  	 */ -	if ((ifp->idev->cnf.forwarding == 0 || -	     ifp->idev->cnf.forwarding == 2) && +	if (((ifp->idev->cnf.accept_ra == 1 && !ifp->idev->cnf.forwarding) || +	     ifp->idev->cnf.accept_ra == 2) &&  	    ifp->idev->cnf.rtr_solicits > 0 &&  	    (dev->flags&IFF_LOOPBACK) == 0 &&  	    (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) { diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 3b5669a2582..d27c797f9f0 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -875,6 +875,7 @@ static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,  		skb_reset_transport_header(skb);  		__skb_push(skb, skb_gro_offset(skb)); +		ops = rcu_dereference(inet6_protos[proto]);  		if (!ops || !ops->gro_receive)  			goto out_unlock; diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index b46e9f88ce3..e2480691c22 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -297,10 +297,6 @@ void ipv6_local_rxpmtu(struct sock *sk, struct flowi6 *fl6, u32 mtu)  	ipv6_addr_copy(&iph->daddr, &fl6->daddr);  	mtu_info = IP6CBMTU(skb); -	if (!mtu_info) { -		kfree_skb(skb); -		return; -	}  	mtu_info->ip6m_mtu = mtu;  	mtu_info->ip6m_addr.sin6_family = AF_INET6; diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 79a485e8a70..1318de4c3e8 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -273,12 +273,12 @@ static int ipv6_destopt_rcv(struct sk_buff *skb)  #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)  	__u16 dstbuf;  #endif -	struct dst_entry *dst; +	struct dst_entry *dst = skb_dst(skb);  	if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) ||  	    !pskb_may_pull(skb, (skb_transport_offset(skb) +  				 ((skb_transport_header(skb)[1] + 1) << 3)))) { -		IP6_INC_STATS_BH(dev_net(skb_dst(skb)->dev), ip6_dst_idev(skb_dst(skb)), +		IP6_INC_STATS_BH(dev_net(dst->dev), ip6_dst_idev(dst),  				 IPSTATS_MIB_INHDRERRORS);  		kfree_skb(skb);  		return -1; @@ -289,9 +289,7 @@ static int ipv6_destopt_rcv(struct sk_buff *skb)  	dstbuf = opt->dst1;  #endif -	dst = dst_clone(skb_dst(skb));  	if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) { -		dst_release(dst);  		skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3;  		opt = IP6CB(skb);  #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) @@ -304,7 +302,6 @@ static int ipv6_destopt_rcv(struct sk_buff *skb)  	IP6_INC_STATS_BH(dev_net(dst->dev),  			 ip6_dst_idev(dst), IPSTATS_MIB_INHDRERRORS); -	dst_release(dst);  	return -1;  } diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 11900417b1c..90868fb4275 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -490,7 +490,8 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)  		goto out_dst_release;  	} -	idev = in6_dev_get(skb->dev); +	rcu_read_lock(); +	idev = __in6_dev_get(skb->dev);  	err = ip6_append_data(sk, icmpv6_getfrag, &msg,  			      len + sizeof(struct icmp6hdr), @@ -500,19 +501,16 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)  	if (err) {  		ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTERRORS);  		ip6_flush_pending_frames(sk); -		goto out_put; +	} else { +		err = icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr, +						 len + sizeof(struct icmp6hdr));  	} -	err = icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr, len + sizeof(struct icmp6hdr)); - -out_put: -	if (likely(idev != NULL)) -		in6_dev_put(idev); +	rcu_read_unlock();  out_dst_release:  	dst_release(dst);  out:  	icmpv6_xmit_unlock(sk);  } -  EXPORT_SYMBOL(icmpv6_send);  static void icmpv6_echo_reply(struct sk_buff *skb) @@ -569,7 +567,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)  	if (hlimit < 0)  		hlimit = ip6_dst_hoplimit(dst); -	idev = in6_dev_get(skb->dev); +	idev = __in6_dev_get(skb->dev);  	msg.skb = skb;  	msg.offset = 0; @@ -583,13 +581,10 @@ static void icmpv6_echo_reply(struct sk_buff *skb)  	if (err) {  		ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTERRORS);  		ip6_flush_pending_frames(sk); -		goto out_put; +	} else { +		err = icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr, +						 skb->len + sizeof(struct icmp6hdr));  	} -	err = icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr, skb->len + sizeof(struct icmp6hdr)); - -out_put: -	if (likely(idev != NULL)) -		in6_dev_put(idev);  	dst_release(dst);  out:  	icmpv6_xmit_unlock(sk); @@ -840,8 +835,7 @@ static int __net_init icmpv6_sk_init(struct net *net)  		/* Enough space for 2 64K ICMP packets, including  		 * sk_buff struct overhead.  		 */ -		sk->sk_sndbuf = -			(2 * ((64 * 1024) + sizeof(struct sk_buff))); +		sk->sk_sndbuf = 2 * SKB_TRUESIZE(64 * 1024);  	}  	return 0; diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index 8a58e8cf664..fee46d5a2f1 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -211,6 +211,7 @@ int inet6_csk_xmit(struct sk_buff *skb, struct flowi *fl_unused)  	struct flowi6 fl6;  	struct dst_entry *dst;  	struct in6_addr *final_p, final; +	int res;  	memset(&fl6, 0, sizeof(fl6));  	fl6.flowi6_proto = sk->sk_protocol; @@ -241,12 +242,14 @@ int inet6_csk_xmit(struct sk_buff *skb, struct flowi *fl_unused)  		__inet6_csk_dst_store(sk, dst, NULL, NULL);  	} -	skb_dst_set(skb, dst_clone(dst)); +	rcu_read_lock(); +	skb_dst_set_noref(skb, dst);  	/* Restore final destination back after routing done */  	ipv6_addr_copy(&fl6.daddr, &np->daddr); -	return ip6_xmit(sk, skb, &fl6, np->opt); +	res = ip6_xmit(sk, skb, &fl6, np->opt, np->tclass); +	rcu_read_unlock(); +	return res;  } -  EXPORT_SYMBOL_GPL(inet6_csk_xmit); diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 320d91d20ad..93718f3db79 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -28,10 +28,6 @@  #include <linux/list.h>  #include <linux/slab.h> -#ifdef 	CONFIG_PROC_FS -#include <linux/proc_fs.h> -#endif -  #include <net/ipv6.h>  #include <net/ndisc.h>  #include <net/addrconf.h> diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 4c882cf4e8a..84d0bd5cac9 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -180,7 +180,7 @@ int ip6_output(struct sk_buff *skb)   */  int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6, -	     struct ipv6_txoptions *opt) +	     struct ipv6_txoptions *opt, int tclass)  {  	struct net *net = sock_net(sk);  	struct ipv6_pinfo *np = inet6_sk(sk); @@ -190,7 +190,6 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,  	u8  proto = fl6->flowi6_proto;  	int seg_len = skb->len;  	int hlimit = -1; -	int tclass = 0;  	u32 mtu;  	if (opt) { @@ -228,10 +227,8 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,  	/*  	 *	Fill in the IPv6 header  	 */ -	if (np) { -		tclass = np->tclass; +	if (np)  		hlimit = np->hop_limit; -	}  	if (hlimit < 0)  		hlimit = ip6_dst_hoplimit(dst); @@ -1126,7 +1123,7 @@ static inline int ip6_ufo_append_data(struct sock *sk,  			hh_len + fragheaderlen + transhdrlen + 20,  			(flags & MSG_DONTWAIT), &err);  		if (skb == NULL) -			return -ENOMEM; +			return err;  		/* reserve space for Hardware header */  		skb_reserve(skb, hh_len); @@ -1193,6 +1190,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,  	struct sk_buff *skb;  	unsigned int maxfraglen, fragheaderlen;  	int exthdrlen; +	int dst_exthdrlen;  	int hh_len;  	int mtu;  	int copy; @@ -1248,7 +1246,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,  		np->cork.hop_limit = hlimit;  		np->cork.tclass = tclass;  		mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ? -		      rt->dst.dev->mtu : dst_mtu(rt->dst.path); +		      rt->dst.dev->mtu : dst_mtu(&rt->dst);  		if (np->frag_size < mtu) {  			if (np->frag_size)  				mtu = np->frag_size; @@ -1259,16 +1257,17 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,  		cork->length = 0;  		sk->sk_sndmsg_page = NULL;  		sk->sk_sndmsg_off = 0; -		exthdrlen = rt->dst.header_len + (opt ? opt->opt_flen : 0) - -			    rt->rt6i_nfheader_len; +		exthdrlen = (opt ? opt->opt_flen : 0) - rt->rt6i_nfheader_len;  		length += exthdrlen;  		transhdrlen += exthdrlen; +		dst_exthdrlen = rt->dst.header_len;  	} else {  		rt = (struct rt6_info *)cork->dst;  		fl6 = &inet->cork.fl.u.ip6;  		opt = np->cork.opt;  		transhdrlen = 0;  		exthdrlen = 0; +		dst_exthdrlen = 0;  		mtu = cork->fragsize;  	} @@ -1368,6 +1367,8 @@ alloc_new_skb:  			else  				alloclen = datalen + fragheaderlen; +			alloclen += dst_exthdrlen; +  			/*  			 * The last fragment gets additional space at tail.  			 * Note: we overallocate on fragments with MSG_MODE @@ -1419,9 +1420,9 @@ alloc_new_skb:  			/*  			 *	Find where to start putting bytes  			 */ -			data = skb_put(skb, fraglen); -			skb_set_network_header(skb, exthdrlen); -			data += fragheaderlen; +			data = skb_put(skb, fraglen + dst_exthdrlen); +			skb_set_network_header(skb, exthdrlen + dst_exthdrlen); +			data += fragheaderlen + dst_exthdrlen;  			skb->transport_header = (skb->network_header +  						 fragheaderlen);  			if (fraggap) { @@ -1434,6 +1435,7 @@ alloc_new_skb:  				pskb_trim_unique(skb_prev, maxfraglen);  			}  			copy = datalen - transhdrlen - fraggap; +  			if (copy < 0) {  				err = -EINVAL;  				kfree_skb(skb); @@ -1448,6 +1450,7 @@ alloc_new_skb:  			length -= datalen - fraggap;  			transhdrlen = 0;  			exthdrlen = 0; +			dst_exthdrlen = 0;  			csummode = CHECKSUM_NONE;  			/* @@ -1480,13 +1483,13 @@ alloc_new_skb:  			if (page && (left = PAGE_SIZE - off) > 0) {  				if (copy >= left)  					copy = left; -				if (page != frag->page) { +				if (page != skb_frag_page(frag)) {  					if (i == MAX_SKB_FRAGS) {  						err = -EMSGSIZE;  						goto error;  					} -					get_page(page);  					skb_fill_page_desc(skb, i, page, sk->sk_sndmsg_off, 0); +					skb_frag_ref(skb, i);  					frag = &skb_shinfo(skb)->frags[i];  				}  			} else if(i < MAX_SKB_FRAGS) { @@ -1506,12 +1509,14 @@ alloc_new_skb:  				err = -EMSGSIZE;  				goto error;  			} -			if (getfrag(from, page_address(frag->page)+frag->page_offset+frag->size, offset, copy, skb->len, skb) < 0) { +			if (getfrag(from, +				    skb_frag_address(frag) + skb_frag_size(frag), +				    offset, copy, skb->len, skb) < 0) {  				err = -EFAULT;  				goto error;  			}  			sk->sk_sndmsg_off += copy; -			frag->size += copy; +			skb_frag_size_add(frag, copy);  			skb->len += copy;  			skb->data_len += copy;  			skb->truesize += copy; diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 0bc98886c38..bdc15c9003d 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -218,8 +218,8 @@ ip6_tnl_link(struct ip6_tnl_net *ip6n, struct ip6_tnl *t)  {  	struct ip6_tnl __rcu **tp = ip6_tnl_bucket(ip6n, &t->parms); -	rcu_assign_pointer(t->next , rtnl_dereference(*tp)); -	rcu_assign_pointer(*tp, t); +	RCU_INIT_POINTER(t->next , rtnl_dereference(*tp)); +	RCU_INIT_POINTER(*tp, t);  }  /** @@ -237,7 +237,7 @@ ip6_tnl_unlink(struct ip6_tnl_net *ip6n, struct ip6_tnl *t)  	     (iter = rtnl_dereference(*tp)) != NULL;  	     tp = &iter->next) {  		if (t == iter) { -			rcu_assign_pointer(*tp, t->next); +			RCU_INIT_POINTER(*tp, t->next);  			break;  		}  	} @@ -350,7 +350,7 @@ ip6_tnl_dev_uninit(struct net_device *dev)  	struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);  	if (dev == ip6n->fb_tnl_dev) -		rcu_assign_pointer(ip6n->tnls_wc[0], NULL); +		RCU_INIT_POINTER(ip6n->tnls_wc[0], NULL);  	else  		ip6_tnl_unlink(ip6n, t);  	ip6_tnl_dst_reset(t); @@ -889,7 +889,7 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,  	struct net_device_stats *stats = &t->dev->stats;  	struct ipv6hdr *ipv6h = ipv6_hdr(skb);  	struct ipv6_tel_txoption opt; -	struct dst_entry *dst; +	struct dst_entry *dst = NULL, *ndst = NULL;  	struct net_device *tdev;  	int mtu;  	unsigned int max_headroom = sizeof(struct ipv6hdr); @@ -897,19 +897,20 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,  	int err = -1;  	int pkt_len; -	if ((dst = ip6_tnl_dst_check(t)) != NULL) -		dst_hold(dst); -	else { -		dst = ip6_route_output(net, NULL, fl6); +	if (!fl6->flowi6_mark) +		dst = ip6_tnl_dst_check(t); +	if (!dst) { +		ndst = ip6_route_output(net, NULL, fl6); -		if (dst->error) +		if (ndst->error)  			goto tx_err_link_failure; -		dst = xfrm_lookup(net, dst, flowi6_to_flowi(fl6), NULL, 0); -		if (IS_ERR(dst)) { -			err = PTR_ERR(dst); -			dst = NULL; +		ndst = xfrm_lookup(net, ndst, flowi6_to_flowi(fl6), NULL, 0); +		if (IS_ERR(ndst)) { +			err = PTR_ERR(ndst); +			ndst = NULL;  			goto tx_err_link_failure;  		} +		dst = ndst;  	}  	tdev = dst->dev; @@ -955,8 +956,12 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,  		skb = new_skb;  	}  	skb_dst_drop(skb); -	skb_dst_set(skb, dst_clone(dst)); - +	if (fl6->flowi6_mark) { +		skb_dst_set(skb, dst); +		ndst = NULL; +	} else { +		skb_dst_set_noref(skb, dst); +	}  	skb->transport_header = skb->network_header;  	proto = fl6->flowi6_proto; @@ -987,13 +992,14 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,  		stats->tx_errors++;  		stats->tx_aborted_errors++;  	} -	ip6_tnl_dst_store(t, dst); +	if (ndst) +		ip6_tnl_dst_store(t, ndst);  	return 0;  tx_err_link_failure:  	stats->tx_carrier_errors++;  	dst_link_failure(skb);  tx_err_dst_release: -	dst_release(dst); +	dst_release(ndst);  	return err;  } @@ -1020,9 +1026,11 @@ ip4ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)  	dsfield = ipv4_get_dsfield(iph); -	if ((t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)) +	if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)  		fl6.flowlabel |= htonl((__u32)iph->tos << IPV6_TCLASS_SHIFT)  					  & IPV6_TCLASS_MASK; +	if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK) +		fl6.flowi6_mark = skb->mark;  	err = ip6_tnl_xmit2(skb, dev, dsfield, &fl6, encap_limit, &mtu);  	if (err != 0) { @@ -1069,10 +1077,12 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)  	fl6.flowi6_proto = IPPROTO_IPV6;  	dsfield = ipv6_get_dsfield(ipv6h); -	if ((t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)) +	if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)  		fl6.flowlabel |= (*(__be32 *) ipv6h & IPV6_TCLASS_MASK); -	if ((t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)) +	if (t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)  		fl6.flowlabel |= (*(__be32 *) ipv6h & IPV6_FLOWLABEL_MASK); +	if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK) +		fl6.flowi6_mark = skb->mark;  	err = ip6_tnl_xmit2(skb, dev, dsfield, &fl6, encap_limit, &mtu);  	if (err != 0) { @@ -1439,7 +1449,7 @@ static int __net_init ip6_fb_tnl_dev_init(struct net_device *dev)  	t->parms.proto = IPPROTO_IPV6;  	dev_hold(dev); -	rcu_assign_pointer(ip6n->tnls_wc[0], t); +	RCU_INIT_POINTER(ip6n->tnls_wc[0], t);  	return 0;  } diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 2fbda5fc4cc..c99e3ee9781 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -343,7 +343,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,  		break;  	case IPV6_TRANSPARENT: -		if (!capable(CAP_NET_ADMIN)) { +		if (valbool && !capable(CAP_NET_ADMIN) && !capable(CAP_NET_RAW)) {  			retv = -EPERM;  			break;  		} diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 9da6e02eaae..44e5b7f2a6c 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -370,17 +370,14 @@ static int ndisc_constructor(struct neighbour *neigh)  	struct neigh_parms *parms;  	int is_multicast = ipv6_addr_is_multicast(addr); -	rcu_read_lock();  	in6_dev = in6_dev_get(dev);  	if (in6_dev == NULL) { -		rcu_read_unlock();  		return -EINVAL;  	}  	parms = in6_dev->nd_parms;  	__neigh_parms_put(neigh->parms);  	neigh->parms = neigh_parms_clone(parms); -	rcu_read_unlock();  	neigh->type = is_multicast ? RTN_MULTICAST : RTN_UNICAST;  	if (!dev->header_ops) { @@ -533,7 +530,8 @@ void ndisc_send_skb(struct sk_buff *skb,  	skb_dst_set(skb, dst); -	idev = in6_dev_get(dst->dev); +	rcu_read_lock(); +	idev = __in6_dev_get(dst->dev);  	IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);  	err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev, @@ -543,8 +541,7 @@ void ndisc_send_skb(struct sk_buff *skb,  		ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);  	} -	if (likely(idev != NULL)) -		in6_dev_put(idev); +	rcu_read_unlock();  }  EXPORT_SYMBOL(ndisc_send_skb); @@ -1039,7 +1036,7 @@ static void ndisc_recv_rs(struct sk_buff *skb)  	if (skb->len < sizeof(*rs_msg))  		return; -	idev = in6_dev_get(skb->dev); +	idev = __in6_dev_get(skb->dev);  	if (!idev) {  		if (net_ratelimit())  			ND_PRINTK1("ICMP6 RS: can't find in6 device\n"); @@ -1080,7 +1077,7 @@ static void ndisc_recv_rs(struct sk_buff *skb)  		neigh_release(neigh);  	}  out: -	in6_dev_put(idev); +	return;  }  static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt) @@ -1179,7 +1176,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)  	 *	set the RA_RECV flag in the interface  	 */ -	in6_dev = in6_dev_get(skb->dev); +	in6_dev = __in6_dev_get(skb->dev);  	if (in6_dev == NULL) {  		ND_PRINTK0(KERN_ERR  			   "ICMPv6 RA: can't find inet6 device for %s.\n", @@ -1188,7 +1185,6 @@ static void ndisc_router_discovery(struct sk_buff *skb)  	}  	if (!ndisc_parse_options(opt, optlen, &ndopts)) { -		in6_dev_put(in6_dev);  		ND_PRINTK2(KERN_WARNING  			   "ICMP6 RA: invalid ND options\n");  		return; @@ -1225,6 +1221,9 @@ static void ndisc_router_discovery(struct sk_buff *skb)  	if (!in6_dev->cnf.accept_ra_defrtr)  		goto skip_defrtr; +	if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0)) +		goto skip_defrtr; +  	lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);  #ifdef CONFIG_IPV6_ROUTER_PREF @@ -1255,7 +1254,6 @@ static void ndisc_router_discovery(struct sk_buff *skb)  			ND_PRINTK0(KERN_ERR  				   "ICMPv6 RA: %s() failed to add default route.\n",  				   __func__); -			in6_dev_put(in6_dev);  			return;  		} @@ -1265,7 +1263,6 @@ static void ndisc_router_discovery(struct sk_buff *skb)  				   "ICMPv6 RA: %s() got default router without neighbour.\n",  				   __func__);  			dst_release(&rt->dst); -			in6_dev_put(in6_dev);  			return;  		}  		neigh->flags |= NTF_ROUTER; @@ -1349,6 +1346,9 @@ skip_linkparms:  		goto out;  #ifdef CONFIG_IPV6_ROUTE_INFO +	if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0)) +		goto skip_routeinfo; +  	if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {  		struct nd_opt_hdr *p;  		for (p = ndopts.nd_opts_ri; @@ -1366,6 +1366,8 @@ skip_linkparms:  				      &ipv6_hdr(skb)->saddr);  		}  	} + +skip_routeinfo:  #endif  #ifdef CONFIG_IPV6_NDISC_NODETYPE @@ -1422,7 +1424,6 @@ out:  		dst_release(&rt->dst);  	else if (neigh)  		neigh_release(neigh); -	in6_dev_put(in6_dev);  }  static void ndisc_redirect_rcv(struct sk_buff *skb) @@ -1481,13 +1482,11 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)  		return;  	} -	in6_dev = in6_dev_get(skb->dev); +	in6_dev = __in6_dev_get(skb->dev);  	if (!in6_dev)  		return; -	if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) { -		in6_dev_put(in6_dev); +	if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)  		return; -	}  	/* RFC2461 8.1:  	 *	The IP source address of the Redirect MUST be the same as the current @@ -1497,7 +1496,6 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)  	if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) {  		ND_PRINTK2(KERN_WARNING  			   "ICMPv6 Redirect: invalid ND options\n"); -		in6_dev_put(in6_dev);  		return;  	}  	if (ndopts.nd_opts_tgt_lladdr) { @@ -1506,7 +1504,6 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)  		if (!lladdr) {  			ND_PRINTK2(KERN_WARNING  				   "ICMPv6 Redirect: invalid link-layer address length\n"); -			in6_dev_put(in6_dev);  			return;  		}  	} @@ -1518,7 +1515,6 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)  			     on_link);  		neigh_release(neigh);  	} -	in6_dev_put(in6_dev);  }  void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, @@ -1651,7 +1647,8 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,  					     csum_partial(icmph, len, 0));  	skb_dst_set(buff, dst); -	idev = in6_dev_get(dst->dev); +	rcu_read_lock(); +	idev = __in6_dev_get(dst->dev);  	IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);  	err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev,  		      dst_output); @@ -1660,8 +1657,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,  		ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);  	} -	if (likely(idev != NULL)) -		in6_dev_put(idev); +	rcu_read_unlock();  	return;  release: diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c index 30fcee46544..8992cf6651d 100644 --- a/net/ipv6/netfilter.c +++ b/net/ipv6/netfilter.c @@ -100,9 +100,16 @@ static int nf_ip6_route(struct net *net, struct dst_entry **dst,  		.pinet6 = (struct ipv6_pinfo *) &fake_pinfo,  	};  	const void *sk = strict ? &fake_sk : NULL; +	struct dst_entry *result; +	int err; -	*dst = ip6_route_output(net, sk, &fl->u.ip6); -	return (*dst)->error; +	result = ip6_route_output(net, sk, &fl->u.ip6); +	err = result->error; +	if (err) +		dst_release(result); +	else +		*dst = result; +	return err;  }  __sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook, diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 08572726381..38f00b0298d 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -182,7 +182,6 @@ fq_find(__be32 id, u32 user, struct in6_addr *src, struct in6_addr *dst)  	return container_of(q, struct nf_ct_frag6_queue, q);  oom: -	pr_debug("Can't alloc new queue\n");  	return NULL;  } @@ -370,16 +369,16 @@ nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev)  		struct sk_buff *clone;  		int i, plen = 0; -		if ((clone = alloc_skb(0, GFP_ATOMIC)) == NULL) { -			pr_debug("Can't alloc skb\n"); +		clone = alloc_skb(0, GFP_ATOMIC); +		if (clone == NULL)  			goto out_oom; -		} +  		clone->next = head->next;  		head->next = clone;  		skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list;  		skb_frag_list_init(head); -		for (i=0; i<skb_shinfo(head)->nr_frags; i++) -			plen += skb_shinfo(head)->frags[i].size; +		for (i = 0; i < skb_shinfo(head)->nr_frags; i++) +			plen += skb_frag_size(&skb_shinfo(head)->frags[i]);  		clone->len = clone->data_len = head->data_len - plen;  		head->data_len -= clone->len;  		head->len -= clone->len; diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 343852e5c70..6f7824e1cea 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -130,14 +130,14 @@ static mh_filter_t __rcu *mh_filter __read_mostly;  int rawv6_mh_filter_register(mh_filter_t filter)  { -	rcu_assign_pointer(mh_filter, filter); +	RCU_INIT_POINTER(mh_filter, filter);  	return 0;  }  EXPORT_SYMBOL(rawv6_mh_filter_register);  int rawv6_mh_filter_unregister(mh_filter_t filter)  { -	rcu_assign_pointer(mh_filter, NULL); +	RCU_INIT_POINTER(mh_filter, NULL);  	synchronize_rcu();  	return 0;  } @@ -372,9 +372,9 @@ void raw6_icmp_error(struct sk_buff *skb, int nexthdr,  	read_unlock(&raw_v6_hashinfo.lock);  } -static inline int rawv6_rcv_skb(struct sock * sk, struct sk_buff * skb) +static inline int rawv6_rcv_skb(struct sock *sk, struct sk_buff *skb)  { -	if ((raw6_sk(sk)->checksum || rcu_dereference_raw(sk->sk_filter)) && +	if ((raw6_sk(sk)->checksum || rcu_access_pointer(sk->sk_filter)) &&  	    skb_checksum_complete(skb)) {  		atomic_inc(&sk->sk_drops);  		kfree_skb(skb); @@ -542,8 +542,7 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6,  		goto out;  	offset = rp->offset; -	total_len = inet_sk(sk)->cork.base.length - (skb_network_header(skb) - -						     skb->data); +	total_len = inet_sk(sk)->cork.base.length;  	if (offset >= total_len - 1) {  		err = -EINVAL;  		ip6_flush_pending_frames(sk); diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 7b954e2539d..cc22099ac8b 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -464,8 +464,8 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,  		head->next = clone;  		skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list;  		skb_frag_list_init(head); -		for (i=0; i<skb_shinfo(head)->nr_frags; i++) -			plen += skb_shinfo(head)->frags[i].size; +		for (i = 0; i < skb_shinfo(head)->nr_frags; i++) +			plen += skb_frag_size(&skb_shinfo(head)->frags[i]);  		clone->len = clone->data_len = head->data_len - plen;  		head->data_len -= clone->len;  		head->len -= clone->len; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index fb545edef6e..57b82dc1ae9 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1086,11 +1086,10 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev,  	rt->dst.output  = ip6_output;  	dst_set_neighbour(&rt->dst, neigh);  	atomic_set(&rt->dst.__refcnt, 1); -	dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255); -  	ipv6_addr_copy(&rt->rt6i_dst.addr, addr);  	rt->rt6i_dst.plen = 128;  	rt->rt6i_idev     = idev; +	dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255);  	spin_lock_bh(&icmp6_dst_lock);  	rt->dst.next = icmp6_dst_gc_list; diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 00b15ac7a70..a7a18602a04 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -182,7 +182,7 @@ static void ipip6_tunnel_unlink(struct sit_net *sitn, struct ip_tunnel *t)  	     (iter = rtnl_dereference(*tp)) != NULL;  	     tp = &iter->next) {  		if (t == iter) { -			rcu_assign_pointer(*tp, t->next); +			RCU_INIT_POINTER(*tp, t->next);  			break;  		}  	} @@ -192,8 +192,8 @@ static void ipip6_tunnel_link(struct sit_net *sitn, struct ip_tunnel *t)  {  	struct ip_tunnel __rcu **tp = ipip6_bucket(sitn, t); -	rcu_assign_pointer(t->next, rtnl_dereference(*tp)); -	rcu_assign_pointer(*tp, t); +	RCU_INIT_POINTER(t->next, rtnl_dereference(*tp)); +	RCU_INIT_POINTER(*tp, t);  }  static void ipip6_tunnel_clone_6rd(struct net_device *dev, struct sit_net *sitn) @@ -391,7 +391,7 @@ ipip6_tunnel_add_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a, int chg)  	p->addr = a->addr;  	p->flags = a->flags;  	t->prl_count++; -	rcu_assign_pointer(t->prl, p); +	RCU_INIT_POINTER(t->prl, p);  out:  	return err;  } @@ -474,7 +474,7 @@ static void ipip6_tunnel_uninit(struct net_device *dev)  	struct sit_net *sitn = net_generic(net, sit_net_id);  	if (dev == sitn->fb_tunnel_dev) { -		rcu_assign_pointer(sitn->tunnels_wc[0], NULL); +		RCU_INIT_POINTER(sitn->tunnels_wc[0], NULL);  	} else {  		ipip6_tunnel_unlink(sitn, netdev_priv(dev));  		ipip6_tunnel_del_prl(netdev_priv(dev), NULL); @@ -1176,7 +1176,7 @@ static int __net_init ipip6_fb_tunnel_init(struct net_device *dev)  	if (!dev->tstats)  		return -ENOMEM;  	dev_hold(dev); -	rcu_assign_pointer(sitn->tunnels_wc[0], tunnel); +	RCU_INIT_POINTER(sitn->tunnels_wc[0], tunnel);  	return 0;  } diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index ac838965ff3..5a0d6648bbb 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -115,7 +115,7 @@ static __u32 check_tcp_syn_cookie(__u32 cookie, const struct in6_addr *saddr,  		& COOKIEMASK;  } -__u32 cookie_v6_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp) +__u32 cookie_v6_init_sequence(struct sock *sk, const struct sk_buff *skb, __u16 *mssp)  {  	const struct ipv6hdr *iph = ipv6_hdr(skb);  	const struct tcphdr *th = tcp_hdr(skb); @@ -137,7 +137,7 @@ __u32 cookie_v6_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp)  				     jiffies / (HZ * 60), mssind);  } -static inline int cookie_check(struct sk_buff *skb, __u32 cookie) +static inline int cookie_check(const struct sk_buff *skb, __u32 cookie)  {  	const struct ipv6hdr *iph = ipv6_hdr(skb);  	const struct tcphdr *th = tcp_hdr(skb); @@ -152,7 +152,7 @@ static inline int cookie_check(struct sk_buff *skb, __u32 cookie)  struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)  {  	struct tcp_options_received tcp_opt; -	u8 *hash_location; +	const u8 *hash_location;  	struct inet_request_sock *ireq;  	struct inet6_request_sock *ireq6;  	struct tcp_request_sock *treq; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 3c9fa618b69..36131d122a6 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -114,7 +114,7 @@ static __inline__ __sum16 tcp_v6_check(int len,  	return csum_ipv6_magic(saddr, daddr, len, IPPROTO_TCP, base);  } -static __u32 tcp_v6_init_sequence(struct sk_buff *skb) +static __u32 tcp_v6_init_sequence(const struct sk_buff *skb)  {  	return secure_tcpv6_sequence_number(ipv6_hdr(skb)->daddr.s6_addr32,  					    ipv6_hdr(skb)->saddr.s6_addr32, @@ -513,7 +513,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req,  		__tcp_v6_send_check(skb, &treq->loc_addr, &treq->rmt_addr);  		ipv6_addr_copy(&fl6.daddr, &treq->rmt_addr); -		err = ip6_xmit(sk, skb, &fl6, opt); +		err = ip6_xmit(sk, skb, &fl6, opt, np->tclass);  		err = net_xmit_eval(err);  	} @@ -591,7 +591,8 @@ static int tcp_v6_md5_do_add(struct sock *sk, const struct in6_addr *peer,  			}  			sk_nocaps_add(sk, NETIF_F_GSO_MASK);  		} -		if (tcp_alloc_md5sig_pool(sk) == NULL) { +		if (tp->md5sig_info->entries6 == 0 && +			tcp_alloc_md5sig_pool(sk) == NULL) {  			kfree(newkey);  			return -ENOMEM;  		} @@ -600,8 +601,9 @@ static int tcp_v6_md5_do_add(struct sock *sk, const struct in6_addr *peer,  				       (tp->md5sig_info->entries6 + 1)), GFP_ATOMIC);  			if (!keys) { -				tcp_free_md5sig_pool();  				kfree(newkey); +				if (tp->md5sig_info->entries6 == 0) +					tcp_free_md5sig_pool();  				return -ENOMEM;  			} @@ -647,6 +649,7 @@ static int tcp_v6_md5_do_del(struct sock *sk, const struct in6_addr *peer)  				kfree(tp->md5sig_info->keys6);  				tp->md5sig_info->keys6 = NULL;  				tp->md5sig_info->alloced6 = 0; +				tcp_free_md5sig_pool();  			} else {  				/* shrink the database */  				if (tp->md5sig_info->entries6 != i) @@ -655,7 +658,6 @@ static int tcp_v6_md5_do_del(struct sock *sk, const struct in6_addr *peer)  						(tp->md5sig_info->entries6 - i)  						* sizeof (tp->md5sig_info->keys6[0]));  			} -			tcp_free_md5sig_pool();  			return 0;  		}  	} @@ -759,7 +761,7 @@ static int tcp_v6_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp,  static int tcp_v6_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key,  			       const struct in6_addr *daddr, struct in6_addr *saddr, -			       struct tcphdr *th) +			       const struct tcphdr *th)  {  	struct tcp_md5sig_pool *hp;  	struct hash_desc *desc; @@ -791,13 +793,14 @@ clear_hash_noput:  }  static int tcp_v6_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key, -			       struct sock *sk, struct request_sock *req, -			       struct sk_buff *skb) +			       const struct sock *sk, +			       const struct request_sock *req, +			       const struct sk_buff *skb)  {  	const struct in6_addr *saddr, *daddr;  	struct tcp_md5sig_pool *hp;  	struct hash_desc *desc; -	struct tcphdr *th = tcp_hdr(skb); +	const struct tcphdr *th = tcp_hdr(skb);  	if (sk) {  		saddr = &inet6_sk(sk)->saddr; @@ -840,12 +843,12 @@ clear_hash_noput:  	return 1;  } -static int tcp_v6_inbound_md5_hash (struct sock *sk, struct sk_buff *skb) +static int tcp_v6_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb)  { -	__u8 *hash_location = NULL; +	const __u8 *hash_location = NULL;  	struct tcp_md5sig_key *hash_expected;  	const struct ipv6hdr *ip6h = ipv6_hdr(skb); -	struct tcphdr *th = tcp_hdr(skb); +	const struct tcphdr *th = tcp_hdr(skb);  	int genhash;  	u8 newhash[16]; @@ -976,9 +979,10 @@ static int tcp6_gro_complete(struct sk_buff *skb)  }  static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, -				 u32 ts, struct tcp_md5sig_key *key, int rst) +				 u32 ts, struct tcp_md5sig_key *key, int rst, u8 tclass)  { -	struct tcphdr *th = tcp_hdr(skb), *t1; +	const struct tcphdr *th = tcp_hdr(skb); +	struct tcphdr *t1;  	struct sk_buff *buff;  	struct flowi6 fl6;  	struct net *net = dev_net(skb_dst(skb)->dev); @@ -1056,7 +1060,7 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win,  	dst = ip6_dst_lookup_flow(ctl_sk, &fl6, NULL, false);  	if (!IS_ERR(dst)) {  		skb_dst_set(buff, dst); -		ip6_xmit(ctl_sk, buff, &fl6, NULL); +		ip6_xmit(ctl_sk, buff, &fl6, NULL, tclass);  		TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS);  		if (rst)  			TCP_INC_STATS_BH(net, TCP_MIB_OUTRSTS); @@ -1068,7 +1072,7 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win,  static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb)  { -	struct tcphdr *th = tcp_hdr(skb); +	const struct tcphdr *th = tcp_hdr(skb);  	u32 seq = 0, ack_seq = 0;  	struct tcp_md5sig_key *key = NULL; @@ -1089,13 +1093,13 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb)  		ack_seq = ntohl(th->seq) + th->syn + th->fin + skb->len -  			  (th->doff << 2); -	tcp_v6_send_response(skb, seq, ack_seq, 0, 0, key, 1); +	tcp_v6_send_response(skb, seq, ack_seq, 0, 0, key, 1, 0);  }  static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts, -			    struct tcp_md5sig_key *key) +			    struct tcp_md5sig_key *key, u8 tclass)  { -	tcp_v6_send_response(skb, seq, ack, win, ts, key, 0); +	tcp_v6_send_response(skb, seq, ack, win, ts, key, 0, tclass);  }  static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb) @@ -1105,7 +1109,8 @@ static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb)  	tcp_v6_send_ack(skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,  			tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale, -			tcptw->tw_ts_recent, tcp_twsk_md5_key(tcptw)); +			tcptw->tw_ts_recent, tcp_twsk_md5_key(tcptw), +			tw->tw_tclass);  	inet_twsk_put(tw);  } @@ -1114,7 +1119,7 @@ static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,  				  struct request_sock *req)  {  	tcp_v6_send_ack(skb, tcp_rsk(req)->snt_isn + 1, tcp_rsk(req)->rcv_isn + 1, req->rcv_wnd, req->ts_recent, -			tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr)); +			tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr), 0);  } @@ -1158,7 +1163,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)  {  	struct tcp_extend_values tmp_ext;  	struct tcp_options_received tmp_opt; -	u8 *hash_location; +	const u8 *hash_location;  	struct request_sock *req;  	struct inet6_request_sock *treq;  	struct ipv6_pinfo *np = inet6_sk(sk); @@ -1383,6 +1388,8 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,  		newtp->af_specific = &tcp_sock_ipv6_mapped_specific;  #endif +		newnp->ipv6_ac_list = NULL; +		newnp->ipv6_fl_list = NULL;  		newnp->pktoptions  = NULL;  		newnp->opt	   = NULL;  		newnp->mcast_oif   = inet6_iif(skb); @@ -1447,6 +1454,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,  	   First: no IPv4 options.  	 */  	newinet->inet_opt = NULL; +	newnp->ipv6_ac_list = NULL;  	newnp->ipv6_fl_list = NULL;  	/* Clone RX bits */ @@ -1603,7 +1611,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)  		opt_skb = skb_clone(skb, GFP_ATOMIC);  	if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */ -		sock_rps_save_rxhash(sk, skb->rxhash); +		sock_rps_save_rxhash(sk, skb);  		if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len))  			goto reset;  		if (opt_skb) @@ -1625,7 +1633,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)  		 * the new socket..  		 */  		if(nsk != sk) { -			sock_rps_save_rxhash(nsk, skb->rxhash); +			sock_rps_save_rxhash(nsk, skb);  			if (tcp_child_process(sk, nsk, skb))  				goto reset;  			if (opt_skb) @@ -1633,7 +1641,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)  			return 0;  		}  	} else -		sock_rps_save_rxhash(sk, skb->rxhash); +		sock_rps_save_rxhash(sk, skb);  	if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len))  		goto reset; @@ -1683,7 +1691,7 @@ ipv6_pktoptions:  static int tcp_v6_rcv(struct sk_buff *skb)  { -	struct tcphdr *th; +	const struct tcphdr *th;  	const struct ipv6hdr *hdr;  	struct sock *sk;  	int ret; @@ -1717,7 +1725,7 @@ static int tcp_v6_rcv(struct sk_buff *skb)  				    skb->len - th->doff*4);  	TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);  	TCP_SKB_CB(skb)->when = 0; -	TCP_SKB_CB(skb)->flags = ipv6_get_dsfield(hdr); +	TCP_SKB_CB(skb)->ip_dsfield = ipv6_get_dsfield(hdr);  	TCP_SKB_CB(skb)->sacked = 0;  	sk = __inet6_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest); @@ -1851,8 +1859,8 @@ static struct inet_peer *tcp_v6_get_peer(struct sock *sk, bool *release_it)  static void *tcp_v6_tw_get_peer(struct sock *sk)  { -	struct inet6_timewait_sock *tw6 = inet6_twsk(sk); -	struct inet_timewait_sock *tw = inet_twsk(sk); +	const struct inet6_timewait_sock *tw6 = inet6_twsk(sk); +	const struct inet_timewait_sock *tw = inet_twsk(sk);  	if (tw->tw_family == AF_INET)  		return tcp_v4_tw_get_peer(sk); @@ -2007,7 +2015,7 @@ static void tcp_v6_destroy_sock(struct sock *sk)  #ifdef CONFIG_PROC_FS  /* Proc filesystem TCPv6 sock list dumping. */  static void get_openreq6(struct seq_file *seq, -			 struct sock *sk, struct request_sock *req, int i, int uid) +			 const struct sock *sk, struct request_sock *req, int i, int uid)  {  	int ttd = req->expires - jiffies;  	const struct in6_addr *src = &inet6_rsk(req)->loc_addr; @@ -2043,10 +2051,10 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)  	__u16 destp, srcp;  	int timer_active;  	unsigned long timer_expires; -	struct inet_sock *inet = inet_sk(sp); -	struct tcp_sock *tp = tcp_sk(sp); +	const struct inet_sock *inet = inet_sk(sp); +	const struct tcp_sock *tp = tcp_sk(sp);  	const struct inet_connection_sock *icsk = inet_csk(sp); -	struct ipv6_pinfo *np = inet6_sk(sp); +	const struct ipv6_pinfo *np = inet6_sk(sp);  	dest  = &np->daddr;  	src   = &np->rcv_saddr; @@ -2098,7 +2106,7 @@ static void get_timewait6_sock(struct seq_file *seq,  {  	const struct in6_addr *dest, *src;  	__u16 destp, srcp; -	struct inet6_timewait_sock *tw6 = inet6_twsk((struct sock *)tw); +	const struct inet6_timewait_sock *tw6 = inet6_twsk((struct sock *)tw);  	int ttd = tw->tw_ttd - jiffies;  	if (ttd < 0) @@ -2153,12 +2161,18 @@ out:  	return 0;  } +static const struct file_operations tcp6_afinfo_seq_fops = { +	.owner   = THIS_MODULE, +	.open    = tcp_seq_open, +	.read    = seq_read, +	.llseek  = seq_lseek, +	.release = seq_release_net +}; +  static struct tcp_seq_afinfo tcp6_seq_afinfo = {  	.name		= "tcp6",  	.family		= AF_INET6, -	.seq_fops	= { -		.owner		= THIS_MODULE, -	}, +	.seq_fops	= &tcp6_afinfo_seq_fops,  	.seq_ops	= {  		.show		= tcp6_seq_show,  	}, diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index bb95e8e1c6f..846f4757eb8 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -509,7 +509,7 @@ int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)  	int is_udplite = IS_UDPLITE(sk);  	if (!ipv6_addr_any(&inet6_sk(sk)->daddr)) -		sock_rps_save_rxhash(sk, skb->rxhash); +		sock_rps_save_rxhash(sk, skb);  	if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))  		goto drop; @@ -533,7 +533,7 @@ int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)  		}  	} -	if (rcu_dereference_raw(sk->sk_filter)) { +	if (rcu_access_pointer(sk->sk_filter)) {  		if (udp_lib_checksum_complete(skb))  			goto drop;  	} @@ -1424,13 +1424,19 @@ int udp6_seq_show(struct seq_file *seq, void *v)  	return 0;  } +static const struct file_operations udp6_afinfo_seq_fops = { +	.owner    = THIS_MODULE, +	.open     = udp_seq_open, +	.read     = seq_read, +	.llseek   = seq_lseek, +	.release  = seq_release_net +}; +  static struct udp_seq_afinfo udp6_seq_afinfo = {  	.name		= "udp6",  	.family		= AF_INET6,  	.udp_table	= &udp_table, -	.seq_fops	= { -		.owner	=	THIS_MODULE, -	}, +	.seq_fops	= &udp6_afinfo_seq_fops,  	.seq_ops	= {  		.show		= udp6_seq_show,  	}, diff --git a/net/ipv6/udplite.c b/net/ipv6/udplite.c index 986c4de5292..8889aa22ed4 100644 --- a/net/ipv6/udplite.c +++ b/net/ipv6/udplite.c @@ -93,13 +93,20 @@ void udplitev6_exit(void)  }  #ifdef CONFIG_PROC_FS + +static const struct file_operations udplite6_afinfo_seq_fops = { +	.owner    = THIS_MODULE, +	.open     = udp_seq_open, +	.read     = seq_read, +	.llseek   = seq_lseek, +	.release  = seq_release_net +}; +  static struct udp_seq_afinfo udplite6_seq_afinfo = {  	.name		= "udplite6",  	.family		= AF_INET6,  	.udp_table	= &udplite_table, -	.seq_fops	= { -		.owner	=	THIS_MODULE, -	}, +	.seq_fops	= &udplite6_afinfo_seq_fops,  	.seq_ops	= {  		.show		= udp6_seq_show,  	}, diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c index 49a91c5f562..faae41737fc 100644 --- a/net/ipv6/xfrm6_output.c +++ b/net/ipv6/xfrm6_output.c @@ -28,6 +28,43 @@ int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb,  EXPORT_SYMBOL(xfrm6_find_1stfragopt); +static int xfrm6_local_dontfrag(struct sk_buff *skb) +{ +	int proto; +	struct sock *sk = skb->sk; + +	if (sk) { +		proto = sk->sk_protocol; + +		if (proto == IPPROTO_UDP || proto == IPPROTO_RAW) +			return inet6_sk(sk)->dontfrag; +	} + +	return 0; +} + +static void xfrm6_local_rxpmtu(struct sk_buff *skb, u32 mtu) +{ +	struct flowi6 fl6; +	struct sock *sk = skb->sk; + +	fl6.flowi6_oif = sk->sk_bound_dev_if; +	ipv6_addr_copy(&fl6.daddr, &ipv6_hdr(skb)->daddr); + +	ipv6_local_rxpmtu(sk, &fl6, mtu); +} + +static void xfrm6_local_error(struct sk_buff *skb, u32 mtu) +{ +	struct flowi6 fl6; +	struct sock *sk = skb->sk; + +	fl6.fl6_dport = inet_sk(sk)->inet_dport; +	ipv6_addr_copy(&fl6.daddr, &ipv6_hdr(skb)->daddr); + +	ipv6_local_error(sk, EMSGSIZE, &fl6, mtu); +} +  static int xfrm6_tunnel_check_size(struct sk_buff *skb)  {  	int mtu, ret = 0; @@ -39,7 +76,13 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb)  	if (!skb->local_df && skb->len > mtu) {  		skb->dev = dst->dev; -		icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); + +		if (xfrm6_local_dontfrag(skb)) +			xfrm6_local_rxpmtu(skb, mtu); +		else if (skb->sk) +			xfrm6_local_error(skb, mtu); +		else +			icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);  		ret = -EMSGSIZE;  	} @@ -93,9 +136,18 @@ static int __xfrm6_output(struct sk_buff *skb)  {  	struct dst_entry *dst = skb_dst(skb);  	struct xfrm_state *x = dst->xfrm; +	int mtu = ip6_skb_dst_mtu(skb); + +	if (skb->len > mtu && xfrm6_local_dontfrag(skb)) { +		xfrm6_local_rxpmtu(skb, mtu); +		return -EMSGSIZE; +	} else if (!skb->local_df && skb->len > mtu && skb->sk) { +		xfrm6_local_error(skb, mtu); +		return -EMSGSIZE; +	}  	if ((x && x->props.mode == XFRM_MODE_TUNNEL) && -	    ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) || +	    ((skb->len > mtu && !skb_is_gso(skb)) ||  		dst_allfrag(skb_dst(skb)))) {  			return ip6_fragment(skb, x->outer_mode->afinfo->output_finish);  	}  |