diff options
Diffstat (limited to 'net/ipv6/udp.c')
| -rw-r--r-- | net/ipv6/udp.c | 194 | 
1 files changed, 121 insertions, 73 deletions
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 72b7eaaf3ca..7092ff78fd8 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -53,22 +53,42 @@  #include <trace/events/skb.h>  #include "udp_impl.h" +static unsigned int udp6_ehashfn(struct net *net, +				  const struct in6_addr *laddr, +				  const u16 lport, +				  const struct in6_addr *faddr, +				  const __be16 fport) +{ +	static u32 udp6_ehash_secret __read_mostly; +	static u32 udp_ipv6_hash_secret __read_mostly; + +	u32 lhash, fhash; + +	net_get_random_once(&udp6_ehash_secret, +			    sizeof(udp6_ehash_secret)); +	net_get_random_once(&udp_ipv6_hash_secret, +			    sizeof(udp_ipv6_hash_secret)); + +	lhash = (__force u32)laddr->s6_addr32[3]; +	fhash = __ipv6_addr_jhash(faddr, udp_ipv6_hash_secret); + +	return __inet6_ehashfn(lhash, lport, fhash, fport, +			       udp_ipv6_hash_secret + net_hash_mix(net)); +} +  int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2)  { -	const struct in6_addr *sk_rcv_saddr6 = &inet6_sk(sk)->rcv_saddr;  	const struct in6_addr *sk2_rcv_saddr6 = inet6_rcv_saddr(sk2); -	__be32 sk1_rcv_saddr = sk_rcv_saddr(sk); -	__be32 sk2_rcv_saddr = sk_rcv_saddr(sk2);  	int sk_ipv6only = ipv6_only_sock(sk);  	int sk2_ipv6only = inet_v6_ipv6only(sk2); -	int addr_type = ipv6_addr_type(sk_rcv_saddr6); +	int addr_type = ipv6_addr_type(&sk->sk_v6_rcv_saddr);  	int addr_type2 = sk2_rcv_saddr6 ? ipv6_addr_type(sk2_rcv_saddr6) : IPV6_ADDR_MAPPED;  	/* if both are mapped, treat as IPv4 */  	if (addr_type == IPV6_ADDR_MAPPED && addr_type2 == IPV6_ADDR_MAPPED)  		return (!sk2_ipv6only && -			(!sk1_rcv_saddr || !sk2_rcv_saddr || -			  sk1_rcv_saddr == sk2_rcv_saddr)); +			(!sk->sk_rcv_saddr || !sk2->sk_rcv_saddr || +			  sk->sk_rcv_saddr == sk2->sk_rcv_saddr));  	if (addr_type2 == IPV6_ADDR_ANY &&  	    !(sk2_ipv6only && addr_type == IPV6_ADDR_MAPPED)) @@ -79,7 +99,7 @@ int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2)  		return 1;  	if (sk2_rcv_saddr6 && -	    ipv6_addr_equal(sk_rcv_saddr6, sk2_rcv_saddr6)) +	    ipv6_addr_equal(&sk->sk_v6_rcv_saddr, sk2_rcv_saddr6))  		return 1;  	return 0; @@ -107,7 +127,7 @@ int udp_v6_get_port(struct sock *sk, unsigned short snum)  	unsigned int hash2_nulladdr =  		udp6_portaddr_hash(sock_net(sk), &in6addr_any, snum);  	unsigned int hash2_partial = -		udp6_portaddr_hash(sock_net(sk), &inet6_sk(sk)->rcv_saddr, 0); +		udp6_portaddr_hash(sock_net(sk), &sk->sk_v6_rcv_saddr, 0);  	/* precompute partial secondary hash */  	udp_sk(sk)->udp_portaddr_hash = hash2_partial; @@ -117,7 +137,7 @@ int udp_v6_get_port(struct sock *sk, unsigned short snum)  static void udp_v6_rehash(struct sock *sk)  {  	u16 new_hash = udp6_portaddr_hash(sock_net(sk), -					  &inet6_sk(sk)->rcv_saddr, +					  &sk->sk_v6_rcv_saddr,  					  inet_sk(sk)->inet_num);  	udp_lib_rehash(sk, new_hash); @@ -133,7 +153,6 @@ static inline int compute_score(struct sock *sk, struct net *net,  	if (net_eq(sock_net(sk), net) && udp_sk(sk)->udp_port_hash == hnum &&  			sk->sk_family == PF_INET6) { -		struct ipv6_pinfo *np = inet6_sk(sk);  		struct inet_sock *inet = inet_sk(sk);  		score = 0; @@ -142,13 +161,13 @@ static inline int compute_score(struct sock *sk, struct net *net,  				return -1;  			score++;  		} -		if (!ipv6_addr_any(&np->rcv_saddr)) { -			if (!ipv6_addr_equal(&np->rcv_saddr, daddr)) +		if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) { +			if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr))  				return -1;  			score++;  		} -		if (!ipv6_addr_any(&np->daddr)) { -			if (!ipv6_addr_equal(&np->daddr, saddr)) +		if (!ipv6_addr_any(&sk->sk_v6_daddr)) { +			if (!ipv6_addr_equal(&sk->sk_v6_daddr, saddr))  				return -1;  			score++;  		} @@ -171,10 +190,9 @@ static inline int compute_score2(struct sock *sk, struct net *net,  	if (net_eq(sock_net(sk), net) && udp_sk(sk)->udp_port_hash == hnum &&  			sk->sk_family == PF_INET6) { -		struct ipv6_pinfo *np = inet6_sk(sk);  		struct inet_sock *inet = inet_sk(sk); -		if (!ipv6_addr_equal(&np->rcv_saddr, daddr)) +		if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr))  			return -1;  		score = 0;  		if (inet->inet_dport) { @@ -182,8 +200,8 @@ static inline int compute_score2(struct sock *sk, struct net *net,  				return -1;  			score++;  		} -		if (!ipv6_addr_any(&np->daddr)) { -			if (!ipv6_addr_equal(&np->daddr, saddr)) +		if (!ipv6_addr_any(&sk->sk_v6_daddr)) { +			if (!ipv6_addr_equal(&sk->sk_v6_daddr, saddr))  				return -1;  			score++;  		} @@ -219,8 +237,8 @@ begin:  			badness = score;  			reuseport = sk->sk_reuseport;  			if (reuseport) { -				hash = inet6_ehashfn(net, daddr, hnum, -						     saddr, sport); +				hash = udp6_ehashfn(net, daddr, hnum, +						    saddr, sport);  				matches = 1;  			} else if (score == SCORE2_MAX)  				goto exact_match; @@ -300,8 +318,8 @@ begin:  			badness = score;  			reuseport = sk->sk_reuseport;  			if (reuseport) { -				hash = inet6_ehashfn(net, daddr, hnum, -						     saddr, sport); +				hash = udp6_ehashfn(net, daddr, hnum, +						    saddr, sport);  				matches = 1;  			}  		} else if (score == badness && reuseport) { @@ -374,14 +392,11 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk,  	int is_udp4;  	bool slow; -	if (addr_len) -		*addr_len = sizeof(struct sockaddr_in6); -  	if (flags & MSG_ERRQUEUE) -		return ipv6_recv_error(sk, msg, len); +		return ipv6_recv_error(sk, msg, len, addr_len);  	if (np->rxpmtu && np->rxopt.bits.rxpmtu) -		return ipv6_recv_rxpmtu(sk, msg, len); +		return ipv6_recv_rxpmtu(sk, msg, len, addr_len);  try_again:  	skb = __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0), @@ -445,9 +460,7 @@ try_again:  	/* Copy the address. */  	if (msg->msg_name) { -		struct sockaddr_in6 *sin6; - -		sin6 = (struct sockaddr_in6 *) msg->msg_name; +		DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name);  		sin6->sin6_family = AF_INET6;  		sin6->sin6_port = udp_hdr(skb)->source;  		sin6->sin6_flowinfo = 0; @@ -462,14 +475,18 @@ try_again:  				ipv6_iface_scope_id(&sin6->sin6_addr,  						    IP6CB(skb)->iif);  		} - +		*addr_len = sizeof(*sin6);  	} + +	if (np->rxopt.all) +		ip6_datagram_recv_common_ctl(sk, msg, skb); +  	if (is_udp4) {  		if (inet->cmsg_flags)  			ip_cmsg_recv(msg, skb);  	} else {  		if (np->rxopt.all) -			ip6_datagram_recv_ctl(sk, msg, skb); +			ip6_datagram_recv_specific_ctl(sk, msg, skb);  	}  	err = copied; @@ -523,8 +540,11 @@ void __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt,  	if (sk == NULL)  		return; -	if (type == ICMPV6_PKT_TOOBIG) +	if (type == ICMPV6_PKT_TOOBIG) { +		if (!ip6_sk_accept_pmtu(sk)) +			goto out;  		ip6_sk_update_pmtu(skb, sk, info); +	}  	if (type == NDISC_REDIRECT) {  		ip6_sk_redirect(skb, sk);  		goto out; @@ -551,8 +571,10 @@ static int __udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)  {  	int rc; -	if (!ipv6_addr_any(&inet6_sk(sk)->daddr)) +	if (!ipv6_addr_any(&sk->sk_v6_daddr)) {  		sock_rps_save_rxhash(sk, skb); +		sk_mark_napi_id(sk, skb); +	}  	rc = sock_queue_rcv_skb(sk, skb);  	if (rc < 0) { @@ -612,6 +634,10 @@ int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)  		if (skb->len > sizeof(struct udphdr) && encap_rcv != NULL) {  			int ret; +			/* Verify checksum before giving to encap */ +			if (udp_lib_checksum_complete(skb)) +				goto csum_error; +  			ret = encap_rcv(sk, skb);  			if (ret <= 0) {  				UDP_INC_STATS_BH(sock_net(sk), @@ -648,8 +674,11 @@ int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)  			goto csum_error;  	} -	if (sk_rcvqueues_full(sk, skb, sk->sk_rcvbuf)) +	if (sk_rcvqueues_full(sk, skb, sk->sk_rcvbuf)) { +		UDP6_INC_STATS_BH(sock_net(sk), +				  UDP_MIB_RCVBUFERRORS, is_udplite);  		goto drop; +	}  	skb_dst_drop(skb); @@ -664,6 +693,7 @@ int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)  	bh_unlock_sock(sk);  	return rc; +  csum_error:  	UDP6_INC_STATS_BH(sock_net(sk), UDP_MIB_CSUMERRORS, is_udplite);  drop: @@ -679,36 +709,34 @@ static struct sock *udp_v6_mcast_next(struct net *net, struct sock *sk,  				      int dif)  {  	struct hlist_nulls_node *node; -	struct sock *s = sk;  	unsigned short num = ntohs(loc_port); -	sk_nulls_for_each_from(s, node) { -		struct inet_sock *inet = inet_sk(s); +	sk_nulls_for_each_from(sk, node) { +		struct inet_sock *inet = inet_sk(sk); -		if (!net_eq(sock_net(s), net)) +		if (!net_eq(sock_net(sk), net))  			continue; -		if (udp_sk(s)->udp_port_hash == num && -		    s->sk_family == PF_INET6) { -			struct ipv6_pinfo *np = inet6_sk(s); +		if (udp_sk(sk)->udp_port_hash == num && +		    sk->sk_family == PF_INET6) {  			if (inet->inet_dport) {  				if (inet->inet_dport != rmt_port)  					continue;  			} -			if (!ipv6_addr_any(&np->daddr) && -			    !ipv6_addr_equal(&np->daddr, rmt_addr)) +			if (!ipv6_addr_any(&sk->sk_v6_daddr) && +			    !ipv6_addr_equal(&sk->sk_v6_daddr, rmt_addr))  				continue; -			if (s->sk_bound_dev_if && s->sk_bound_dev_if != dif) +			if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif)  				continue; -			if (!ipv6_addr_any(&np->rcv_saddr)) { -				if (!ipv6_addr_equal(&np->rcv_saddr, loc_addr)) +			if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) { +				if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, loc_addr))  					continue;  			} -			if (!inet6_mc_check(s, loc_addr, rmt_addr)) +			if (!inet6_mc_check(sk, loc_addr, rmt_addr))  				continue; -			return s; +			return sk;  		}  	}  	return NULL; @@ -739,6 +767,17 @@ static void flush_stack(struct sock **stack, unsigned int count,  	if (unlikely(skb1))  		kfree_skb(skb1);  } + +static void udp6_csum_zero_error(struct sk_buff *skb) +{ +	/* RFC 2460 section 8.1 says that we SHOULD log +	 * this error. Well, it is reasonable. +	 */ +	LIMIT_NETDEBUG(KERN_INFO "IPv6: udp checksum is 0 for [%pI6c]:%u->[%pI6c]:%u\n", +		       &ipv6_hdr(skb)->saddr, ntohs(udp_hdr(skb)->source), +		       &ipv6_hdr(skb)->daddr, ntohs(udp_hdr(skb)->dest)); +} +  /*   * Note: called only from the BH handler context,   * so we don't need to lock the hashes. @@ -758,7 +797,12 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb,  	dif = inet6_iif(skb);  	sk = udp_v6_mcast_next(net, sk, uh->dest, daddr, uh->source, saddr, dif);  	while (sk) { -		stack[count++] = sk; +		/* If zero checksum and no_check is not on for +		 * the socket then skip it. +		 */ +		if (uh->check || udp_sk(sk)->no_check6_rx) +			stack[count++] = sk; +  		sk = udp_v6_mcast_next(net, sk_nulls_next(sk), uh->dest, daddr,  				       uh->source, saddr, dif);  		if (unlikely(count == ARRAY_SIZE(stack))) { @@ -846,7 +890,12 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,  	if (sk != NULL) {  		int ret; -		sk_mark_napi_id(sk, skb); +		if (!uh->check && !udp_sk(sk)->no_check6_rx) { +			sock_put(sk); +			udp6_csum_zero_error(skb); +			goto csum_error; +		} +  		ret = udpv6_queue_rcv_skb(sk, skb);  		sock_put(sk); @@ -859,6 +908,11 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,  		return 0;  	} +	if (!uh->check) { +		udp6_csum_zero_error(skb); +		goto csum_error; +	} +  	if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))  		goto discard; @@ -986,7 +1040,10 @@ static int udp_v6_push_pending_frames(struct sock *sk)  	if (is_udplite)  		csum = udplite_csum_outgoing(sk, skb); -	else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */ +	else if (up->no_check6_tx) {   /* UDP csum disabled */ +		skb->ip_summed = CHECKSUM_NONE; +		goto send; +	} else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */  		udp6_hwcsum_outgoing(sk, skb, &fl6->saddr, &fl6->daddr,  				     up->len);  		goto send; @@ -1023,7 +1080,7 @@ int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,  	struct udp_sock *up = udp_sk(sk);  	struct inet_sock *inet = inet_sk(sk);  	struct ipv6_pinfo *np = inet6_sk(sk); -	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) msg->msg_name; +	DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name);  	struct in6_addr *daddr, *final_p, final;  	struct ipv6_txoptions *opt = NULL;  	struct ip6_flowlabel *flowlabel = NULL; @@ -1064,7 +1121,7 @@ int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,  	} else if (!up->pending) {  		if (sk->sk_state != TCP_ESTABLISHED)  			return -EDESTADDRREQ; -		daddr = &np->daddr; +		daddr = &sk->sk_v6_daddr;  	} else  		daddr = NULL; @@ -1125,7 +1182,6 @@ do_udp_sendmsg:  				flowlabel = fl6_sock_lookup(sk, fl6.flowlabel);  				if (flowlabel == NULL)  					return -EINVAL; -				daddr = &flowlabel->dst;  			}  		} @@ -1134,8 +1190,8 @@ do_udp_sendmsg:  		 * sk->sk_dst_cache.  		 */  		if (sk->sk_state == TCP_ESTABLISHED && -		    ipv6_addr_equal(daddr, &np->daddr)) -			daddr = &np->daddr; +		    ipv6_addr_equal(daddr, &sk->sk_v6_daddr)) +			daddr = &sk->sk_v6_daddr;  		if (addr_len >= sizeof(struct sockaddr_in6) &&  		    sin6->sin6_scope_id && @@ -1146,7 +1202,7 @@ do_udp_sendmsg:  			return -EDESTADDRREQ;  		fl6.fl6_dport = inet->inet_dport; -		daddr = &np->daddr; +		daddr = &sk->sk_v6_daddr;  		fl6.flowlabel = np->flow_label;  		connected = 1;  	} @@ -1206,28 +1262,19 @@ do_udp_sendmsg:  	security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); -	dst = ip6_sk_dst_lookup_flow(sk, &fl6, final_p, true); +	dst = ip6_sk_dst_lookup_flow(sk, &fl6, final_p);  	if (IS_ERR(dst)) {  		err = PTR_ERR(dst);  		dst = NULL;  		goto out;  	} -	if (hlimit < 0) { -		if (ipv6_addr_is_multicast(&fl6.daddr)) -			hlimit = np->mcast_hops; -		else -			hlimit = np->hop_limit; -		if (hlimit < 0) -			hlimit = ip6_dst_hoplimit(dst); -	} +	if (hlimit < 0) +		hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);  	if (tclass < 0)  		tclass = np->tclass; -	if (dontfrag < 0) -		dontfrag = np->dontfrag; -  	if (msg->msg_flags&MSG_CONFIRM)  		goto do_confirm;  back_from_confirm: @@ -1246,6 +1293,8 @@ back_from_confirm:  	up->pending = AF_INET6;  do_append_data: +	if (dontfrag < 0) +		dontfrag = np->dontfrag;  	up->len += ulen;  	getfrag  =  is_udplite ?  udplite_getfrag : ip_generic_getfrag;  	err = ip6_append_data(sk, getfrag, msg->msg_iov, ulen, @@ -1262,8 +1311,8 @@ do_append_data:  	if (dst) {  		if (connected) {  			ip6_dst_store(sk, dst, -				      ipv6_addr_equal(&fl6.daddr, &np->daddr) ? -				      &np->daddr : NULL, +				      ipv6_addr_equal(&fl6.daddr, &sk->sk_v6_daddr) ? +				      &sk->sk_v6_daddr : NULL,  #ifdef CONFIG_IPV6_SUBTREES  				      ipv6_addr_equal(&fl6.saddr, &np->saddr) ?  				      &np->saddr : @@ -1461,7 +1510,6 @@ static struct inet_protosw udpv6_protosw = {  	.protocol =  IPPROTO_UDP,  	.prot =      &udpv6_prot,  	.ops =       &inet6_dgram_ops, -	.no_check =  UDP_CSUM_DEFAULT,  	.flags =     INET_PROTOSW_PERMANENT,  };  | 
