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 | ||
