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 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; |
422 | update: | 433 | update: |
@@ -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 | ||
838 | static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, | 851 | 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, | |||
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 | ||