diff options
Diffstat (limited to 'net/ipv6/ipv6_sockglue.c')
| -rw-r--r-- | net/ipv6/ipv6_sockglue.c | 44 | 
1 files changed, 31 insertions, 13 deletions
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 56d55fecf8e..86e28a75267 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -67,7 +67,7 @@ int ip6_ra_control(struct sock *sk, int sel, void (*destructor)(struct sock *))  	/* RA packet may be delivered ONLY to IPPROTO_RAW socket */  	if (sk->sk_type != SOCK_RAW || inet_sk(sk)->num != IPPROTO_RAW) -		return -EINVAL; +		return -ENOPROTOOPT;  	new_ra = (sel>=0) ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL; @@ -161,9 +161,17 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,  			struct ipv6_txoptions *opt;  			struct sk_buff *pktopt; -			if (sk->sk_protocol != IPPROTO_UDP && -			    sk->sk_protocol != IPPROTO_UDPLITE && -			    sk->sk_protocol != IPPROTO_TCP) +			if (sk->sk_type == SOCK_RAW) +				break; + +			if (sk->sk_protocol == IPPROTO_UDP || +			    sk->sk_protocol == IPPROTO_UDPLITE) { +				struct udp_sock *up = udp_sk(sk); +				if (up->pending == AF_INET6) { +					retv = -EBUSY; +					break; +				} +			} else if (sk->sk_protocol != IPPROTO_TCP)  				break;  			if (sk->sk_state != TCP_ESTABLISHED) { @@ -337,18 +345,21 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,  	case IPV6_DSTOPTS:  	{  		struct ipv6_txoptions *opt; + +		/* remove any sticky options header with a zero option +		 * length, per RFC3542. +		 */  		if (optlen == 0)  			optval = NULL; +		else if (optlen < sizeof(struct ipv6_opt_hdr) || +			 optlen & 0x7 || optlen > 8 * 255) +			goto e_inval;  		/* hop-by-hop / destination options are privileged option */  		retv = -EPERM;  		if (optname != IPV6_RTHDR && !capable(CAP_NET_RAW))  			break; -		if (optlen < sizeof(struct ipv6_opt_hdr) || -		    optlen & 0x7 || optlen > 8 * 255) -			goto e_inval; -  		opt = ipv6_renew_options(sk, np->opt, optname,  					 (struct ipv6_opt_hdr __user *)optval,  					 optlen); @@ -416,7 +427,7 @@ sticky_done:  		msg.msg_controllen = optlen;  		msg.msg_control = (void*)(opt+1); -		retv = datagram_send_ctl(&msg, &fl, opt, &junk, &junk); +		retv = datagram_send_ctl(net, &msg, &fl, opt, &junk, &junk);  		if (retv)  			goto done;  update: @@ -438,7 +449,7 @@ done:  	case IPV6_MULTICAST_HOPS:  		if (sk->sk_type == SOCK_STREAM) -			goto e_inval; +			break;  		if (optlen < sizeof(int))  			goto e_inval;  		if (val > 255 || val < -1) @@ -450,13 +461,15 @@ done:  	case IPV6_MULTICAST_LOOP:  		if (optlen < sizeof(int))  			goto e_inval; +		if (val != valbool) +			goto e_inval;  		np->mc_loop = valbool;  		retv = 0;  		break;  	case IPV6_MULTICAST_IF:  		if (sk->sk_type == SOCK_STREAM) -			goto e_inval; +			break;  		if (optlen < sizeof(int))  			goto e_inval; @@ -832,7 +845,7 @@ static int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_txoptions *opt,  	len = min_t(unsigned int, len, ipv6_optlen(hdr));  	if (copy_to_user(optval, hdr, len))  		return -EFAULT; -	return ipv6_optlen(hdr); +	return len;  }  static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, @@ -852,7 +865,7 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,  		if (sk->sk_protocol != IPPROTO_UDP &&  		    sk->sk_protocol != IPPROTO_UDPLITE &&  		    sk->sk_protocol != IPPROTO_TCP) -			return -EINVAL; +			return -ENOPROTOOPT;  		if (sk->sk_state != TCP_ESTABLISHED)  			return -ENOTCONN;  		val = sk->sk_family; @@ -866,6 +879,8 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,  			return -EINVAL;  		if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0)))  			return -EFAULT; +		if (gsf.gf_group.ss_family != AF_INET6) +			return -EADDRNOTAVAIL;  		lock_sock(sk);  		err = ip6_mc_msfget(sk, &gsf,  			(struct group_filter __user *)optval, optlen); @@ -975,6 +990,9 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,  		len = ipv6_getsockopt_sticky(sk, np->opt,  					     optname, optval, len);  		release_sock(sk); +		/* check if ipv6_getsockopt_sticky() returns err code */ +		if (len < 0) +			return len;  		return put_user(len, optlen);  	}  |