aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/ipv6_sockglue.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/ipv6_sockglue.c')
-rw-r--r--net/ipv6/ipv6_sockglue.c44
1 files changed, 31 insertions, 13 deletions
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 56d55fecf8ec..86e28a75267f 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 *))
67 67
68 /* RA packet may be delivered ONLY to IPPROTO_RAW socket */ 68 /* RA packet may be delivered ONLY to IPPROTO_RAW socket */
69 if (sk->sk_type != SOCK_RAW || inet_sk(sk)->num != IPPROTO_RAW) 69 if (sk->sk_type != SOCK_RAW || inet_sk(sk)->num != IPPROTO_RAW)
70 return -EINVAL; 70 return -ENOPROTOOPT;
71 71
72 new_ra = (sel>=0) ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL; 72 new_ra = (sel>=0) ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL;
73 73
@@ -161,9 +161,17 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
161 struct ipv6_txoptions *opt; 161 struct ipv6_txoptions *opt;
162 struct sk_buff *pktopt; 162 struct sk_buff *pktopt;
163 163
164 if (sk->sk_protocol != IPPROTO_UDP && 164 if (sk->sk_type == SOCK_RAW)
165 sk->sk_protocol != IPPROTO_UDPLITE && 165 break;
166 sk->sk_protocol != IPPROTO_TCP) 166
167 if (sk->sk_protocol == IPPROTO_UDP ||
168 sk->sk_protocol == IPPROTO_UDPLITE) {
169 struct udp_sock *up = udp_sk(sk);
170 if (up->pending == AF_INET6) {
171 retv = -EBUSY;
172 break;
173 }
174 } else if (sk->sk_protocol != IPPROTO_TCP)
167 break; 175 break;
168 176
169 if (sk->sk_state != TCP_ESTABLISHED) { 177 if (sk->sk_state != TCP_ESTABLISHED) {
@@ -337,18 +345,21 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
337 case IPV6_DSTOPTS: 345 case IPV6_DSTOPTS:
338 { 346 {
339 struct ipv6_txoptions *opt; 347 struct ipv6_txoptions *opt;
348
349 /* remove any sticky options header with a zero option
350 * length, per RFC3542.
351 */
340 if (optlen == 0) 352 if (optlen == 0)
341 optval = NULL; 353 optval = NULL;
354 else if (optlen < sizeof(struct ipv6_opt_hdr) ||
355 optlen & 0x7 || optlen > 8 * 255)
356 goto e_inval;
342 357
343 /* hop-by-hop / destination options are privileged option */ 358 /* hop-by-hop / destination options are privileged option */
344 retv = -EPERM; 359 retv = -EPERM;
345 if (optname != IPV6_RTHDR && !capable(CAP_NET_RAW)) 360 if (optname != IPV6_RTHDR && !capable(CAP_NET_RAW))
346 break; 361 break;
347 362
348 if (optlen < sizeof(struct ipv6_opt_hdr) ||
349 optlen & 0x7 || optlen > 8 * 255)
350 goto e_inval;
351
352 opt = ipv6_renew_options(sk, np->opt, optname, 363 opt = ipv6_renew_options(sk, np->opt, optname,
353 (struct ipv6_opt_hdr __user *)optval, 364 (struct ipv6_opt_hdr __user *)optval,
354 optlen); 365 optlen);
@@ -416,7 +427,7 @@ sticky_done:
416 msg.msg_controllen = optlen; 427 msg.msg_controllen = optlen;
417 msg.msg_control = (void*)(opt+1); 428 msg.msg_control = (void*)(opt+1);
418 429
419 retv = datagram_send_ctl(&msg, &fl, opt, &junk, &junk); 430 retv = datagram_send_ctl(net, &msg, &fl, opt, &junk, &junk);
420 if (retv) 431 if (retv)
421 goto done; 432 goto done;
422update: 433update:
@@ -438,7 +449,7 @@ done:
438 449
439 case IPV6_MULTICAST_HOPS: 450 case IPV6_MULTICAST_HOPS:
440 if (sk->sk_type == SOCK_STREAM) 451 if (sk->sk_type == SOCK_STREAM)
441 goto e_inval; 452 break;
442 if (optlen < sizeof(int)) 453 if (optlen < sizeof(int))
443 goto e_inval; 454 goto e_inval;
444 if (val > 255 || val < -1) 455 if (val > 255 || val < -1)
@@ -450,13 +461,15 @@ done:
450 case IPV6_MULTICAST_LOOP: 461 case IPV6_MULTICAST_LOOP:
451 if (optlen < sizeof(int)) 462 if (optlen < sizeof(int))
452 goto e_inval; 463 goto e_inval;
464 if (val != valbool)
465 goto e_inval;
453 np->mc_loop = valbool; 466 np->mc_loop = valbool;
454 retv = 0; 467 retv = 0;
455 break; 468 break;
456 469
457 case IPV6_MULTICAST_IF: 470 case IPV6_MULTICAST_IF:
458 if (sk->sk_type == SOCK_STREAM) 471 if (sk->sk_type == SOCK_STREAM)
459 goto e_inval; 472 break;
460 if (optlen < sizeof(int)) 473 if (optlen < sizeof(int))
461 goto e_inval; 474 goto e_inval;
462 475
@@ -832,7 +845,7 @@ static int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_txoptions *opt,
832 len = min_t(unsigned int, len, ipv6_optlen(hdr)); 845 len = min_t(unsigned int, len, ipv6_optlen(hdr));
833 if (copy_to_user(optval, hdr, len)) 846 if (copy_to_user(optval, hdr, len))
834 return -EFAULT; 847 return -EFAULT;
835 return ipv6_optlen(hdr); 848 return len;
836} 849}
837 850
838static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, 851static 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,
852 if (sk->sk_protocol != IPPROTO_UDP && 865 if (sk->sk_protocol != IPPROTO_UDP &&
853 sk->sk_protocol != IPPROTO_UDPLITE && 866 sk->sk_protocol != IPPROTO_UDPLITE &&
854 sk->sk_protocol != IPPROTO_TCP) 867 sk->sk_protocol != IPPROTO_TCP)
855 return -EINVAL; 868 return -ENOPROTOOPT;
856 if (sk->sk_state != TCP_ESTABLISHED) 869 if (sk->sk_state != TCP_ESTABLISHED)
857 return -ENOTCONN; 870 return -ENOTCONN;
858 val = sk->sk_family; 871 val = sk->sk_family;
@@ -866,6 +879,8 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
866 return -EINVAL; 879 return -EINVAL;
867 if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) 880 if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0)))
868 return -EFAULT; 881 return -EFAULT;
882 if (gsf.gf_group.ss_family != AF_INET6)
883 return -EADDRNOTAVAIL;
869 lock_sock(sk); 884 lock_sock(sk);
870 err = ip6_mc_msfget(sk, &gsf, 885 err = ip6_mc_msfget(sk, &gsf,
871 (struct group_filter __user *)optval, optlen); 886 (struct group_filter __user *)optval, optlen);
@@ -975,6 +990,9 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
975 len = ipv6_getsockopt_sticky(sk, np->opt, 990 len = ipv6_getsockopt_sticky(sk, np->opt,
976 optname, optval, len); 991 optname, optval, len);
977 release_sock(sk); 992 release_sock(sk);
993 /* check if ipv6_getsockopt_sticky() returns err code */
994 if (len < 0)
995 return len;
978 return put_user(len, optlen); 996 return put_user(len, optlen);
979 } 997 }
980 998