diff options
Diffstat (limited to 'net/ipv4/ip_sockglue.c')
-rw-r--r-- | net/ipv4/ip_sockglue.c | 1169 |
1 files changed, 587 insertions, 582 deletions
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 23048d9f3584..4d544573f48a 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c | |||
@@ -59,7 +59,7 @@ static void ip_cmsg_recv_pktinfo(struct msghdr *msg, struct sk_buff *skb) | |||
59 | struct in_pktinfo info; | 59 | struct in_pktinfo info; |
60 | struct rtable *rt = (struct rtable *)skb->dst; | 60 | struct rtable *rt = (struct rtable *)skb->dst; |
61 | 61 | ||
62 | info.ipi_addr.s_addr = skb->nh.iph->daddr; | 62 | info.ipi_addr.s_addr = ip_hdr(skb)->daddr; |
63 | if (rt) { | 63 | if (rt) { |
64 | info.ipi_ifindex = rt->rt_iif; | 64 | info.ipi_ifindex = rt->rt_iif; |
65 | info.ipi_spec_dst.s_addr = rt->rt_spec_dst; | 65 | info.ipi_spec_dst.s_addr = rt->rt_spec_dst; |
@@ -73,13 +73,13 @@ static void ip_cmsg_recv_pktinfo(struct msghdr *msg, struct sk_buff *skb) | |||
73 | 73 | ||
74 | static void ip_cmsg_recv_ttl(struct msghdr *msg, struct sk_buff *skb) | 74 | static void ip_cmsg_recv_ttl(struct msghdr *msg, struct sk_buff *skb) |
75 | { | 75 | { |
76 | int ttl = skb->nh.iph->ttl; | 76 | int ttl = ip_hdr(skb)->ttl; |
77 | put_cmsg(msg, SOL_IP, IP_TTL, sizeof(int), &ttl); | 77 | put_cmsg(msg, SOL_IP, IP_TTL, sizeof(int), &ttl); |
78 | } | 78 | } |
79 | 79 | ||
80 | static void ip_cmsg_recv_tos(struct msghdr *msg, struct sk_buff *skb) | 80 | static void ip_cmsg_recv_tos(struct msghdr *msg, struct sk_buff *skb) |
81 | { | 81 | { |
82 | put_cmsg(msg, SOL_IP, IP_TOS, 1, &skb->nh.iph->tos); | 82 | put_cmsg(msg, SOL_IP, IP_TOS, 1, &ip_hdr(skb)->tos); |
83 | } | 83 | } |
84 | 84 | ||
85 | static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb) | 85 | static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb) |
@@ -87,7 +87,8 @@ static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb) | |||
87 | if (IPCB(skb)->opt.optlen == 0) | 87 | if (IPCB(skb)->opt.optlen == 0) |
88 | return; | 88 | return; |
89 | 89 | ||
90 | put_cmsg(msg, SOL_IP, IP_RECVOPTS, IPCB(skb)->opt.optlen, skb->nh.iph+1); | 90 | put_cmsg(msg, SOL_IP, IP_RECVOPTS, IPCB(skb)->opt.optlen, |
91 | ip_hdr(skb) + 1); | ||
91 | } | 92 | } |
92 | 93 | ||
93 | 94 | ||
@@ -268,18 +269,21 @@ void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err, | |||
268 | serr = SKB_EXT_ERR(skb); | 269 | serr = SKB_EXT_ERR(skb); |
269 | serr->ee.ee_errno = err; | 270 | serr->ee.ee_errno = err; |
270 | serr->ee.ee_origin = SO_EE_ORIGIN_ICMP; | 271 | serr->ee.ee_origin = SO_EE_ORIGIN_ICMP; |
271 | serr->ee.ee_type = skb->h.icmph->type; | 272 | serr->ee.ee_type = icmp_hdr(skb)->type; |
272 | serr->ee.ee_code = skb->h.icmph->code; | 273 | serr->ee.ee_code = icmp_hdr(skb)->code; |
273 | serr->ee.ee_pad = 0; | 274 | serr->ee.ee_pad = 0; |
274 | serr->ee.ee_info = info; | 275 | serr->ee.ee_info = info; |
275 | serr->ee.ee_data = 0; | 276 | serr->ee.ee_data = 0; |
276 | serr->addr_offset = (u8*)&(((struct iphdr*)(skb->h.icmph+1))->daddr) - skb->nh.raw; | 277 | serr->addr_offset = (u8 *)&(((struct iphdr *)(icmp_hdr(skb) + 1))->daddr) - |
278 | skb_network_header(skb); | ||
277 | serr->port = port; | 279 | serr->port = port; |
278 | 280 | ||
279 | skb->h.raw = payload; | 281 | if (skb_pull(skb, payload - skb->data) != NULL) { |
280 | if (!skb_pull(skb, payload - skb->data) || | 282 | skb_reset_transport_header(skb); |
281 | sock_queue_err_skb(sk, skb)) | 283 | if (sock_queue_err_skb(sk, skb) == 0) |
282 | kfree_skb(skb); | 284 | return; |
285 | } | ||
286 | kfree_skb(skb); | ||
283 | } | 287 | } |
284 | 288 | ||
285 | void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 info) | 289 | void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 info) |
@@ -296,8 +300,9 @@ void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 inf | |||
296 | if (!skb) | 300 | if (!skb) |
297 | return; | 301 | return; |
298 | 302 | ||
299 | iph = (struct iphdr*)skb_put(skb, sizeof(struct iphdr)); | 303 | skb_put(skb, sizeof(struct iphdr)); |
300 | skb->nh.iph = iph; | 304 | skb_reset_network_header(skb); |
305 | iph = ip_hdr(skb); | ||
301 | iph->daddr = daddr; | 306 | iph->daddr = daddr; |
302 | 307 | ||
303 | serr = SKB_EXT_ERR(skb); | 308 | serr = SKB_EXT_ERR(skb); |
@@ -308,11 +313,11 @@ void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 inf | |||
308 | serr->ee.ee_pad = 0; | 313 | serr->ee.ee_pad = 0; |
309 | serr->ee.ee_info = info; | 314 | serr->ee.ee_info = info; |
310 | serr->ee.ee_data = 0; | 315 | serr->ee.ee_data = 0; |
311 | serr->addr_offset = (u8*)&iph->daddr - skb->nh.raw; | 316 | serr->addr_offset = (u8 *)&iph->daddr - skb_network_header(skb); |
312 | serr->port = port; | 317 | serr->port = port; |
313 | 318 | ||
314 | skb->h.raw = skb->tail; | 319 | __skb_pull(skb, skb_tail_pointer(skb) - skb->data); |
315 | __skb_pull(skb, skb->tail - skb->data); | 320 | skb_reset_transport_header(skb); |
316 | 321 | ||
317 | if (sock_queue_err_skb(sk, skb)) | 322 | if (sock_queue_err_skb(sk, skb)) |
318 | kfree_skb(skb); | 323 | kfree_skb(skb); |
@@ -354,7 +359,8 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len) | |||
354 | sin = (struct sockaddr_in *)msg->msg_name; | 359 | sin = (struct sockaddr_in *)msg->msg_name; |
355 | if (sin) { | 360 | if (sin) { |
356 | sin->sin_family = AF_INET; | 361 | sin->sin_family = AF_INET; |
357 | sin->sin_addr.s_addr = *(__be32*)(skb->nh.raw + serr->addr_offset); | 362 | sin->sin_addr.s_addr = *(__be32 *)(skb_network_header(skb) + |
363 | serr->addr_offset); | ||
358 | sin->sin_port = serr->port; | 364 | sin->sin_port = serr->port; |
359 | memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); | 365 | memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); |
360 | } | 366 | } |
@@ -366,7 +372,7 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len) | |||
366 | struct inet_sock *inet = inet_sk(sk); | 372 | struct inet_sock *inet = inet_sk(sk); |
367 | 373 | ||
368 | sin->sin_family = AF_INET; | 374 | sin->sin_family = AF_INET; |
369 | sin->sin_addr.s_addr = skb->nh.iph->saddr; | 375 | sin->sin_addr.s_addr = ip_hdr(skb)->saddr; |
370 | sin->sin_port = 0; | 376 | sin->sin_port = 0; |
371 | memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); | 377 | memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); |
372 | if (inet->cmsg_flags) | 378 | if (inet->cmsg_flags) |
@@ -403,20 +409,20 @@ out: | |||
403 | */ | 409 | */ |
404 | 410 | ||
405 | static int do_ip_setsockopt(struct sock *sk, int level, | 411 | static int do_ip_setsockopt(struct sock *sk, int level, |
406 | int optname, char __user *optval, int optlen) | 412 | int optname, char __user *optval, int optlen) |
407 | { | 413 | { |
408 | struct inet_sock *inet = inet_sk(sk); | 414 | struct inet_sock *inet = inet_sk(sk); |
409 | int val=0,err; | 415 | int val=0,err; |
410 | 416 | ||
411 | if (((1<<optname) & ((1<<IP_PKTINFO) | (1<<IP_RECVTTL) | | 417 | if (((1<<optname) & ((1<<IP_PKTINFO) | (1<<IP_RECVTTL) | |
412 | (1<<IP_RECVOPTS) | (1<<IP_RECVTOS) | | 418 | (1<<IP_RECVOPTS) | (1<<IP_RECVTOS) | |
413 | (1<<IP_RETOPTS) | (1<<IP_TOS) | | 419 | (1<<IP_RETOPTS) | (1<<IP_TOS) | |
414 | (1<<IP_TTL) | (1<<IP_HDRINCL) | | 420 | (1<<IP_TTL) | (1<<IP_HDRINCL) | |
415 | (1<<IP_MTU_DISCOVER) | (1<<IP_RECVERR) | | 421 | (1<<IP_MTU_DISCOVER) | (1<<IP_RECVERR) | |
416 | (1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND) | | 422 | (1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND) | |
417 | (1<<IP_PASSSEC))) || | 423 | (1<<IP_PASSSEC))) || |
418 | optname == IP_MULTICAST_TTL || | 424 | optname == IP_MULTICAST_TTL || |
419 | optname == IP_MULTICAST_LOOP) { | 425 | optname == IP_MULTICAST_LOOP) { |
420 | if (optlen >= sizeof(int)) { | 426 | if (optlen >= sizeof(int)) { |
421 | if (get_user(val, (int __user *) optval)) | 427 | if (get_user(val, (int __user *) optval)) |
422 | return -EFAULT; | 428 | return -EFAULT; |
@@ -440,444 +446,444 @@ static int do_ip_setsockopt(struct sock *sk, int level, | |||
440 | lock_sock(sk); | 446 | lock_sock(sk); |
441 | 447 | ||
442 | switch (optname) { | 448 | switch (optname) { |
443 | case IP_OPTIONS: | 449 | case IP_OPTIONS: |
444 | { | 450 | { |
445 | struct ip_options * opt = NULL; | 451 | struct ip_options * opt = NULL; |
446 | if (optlen > 40 || optlen < 0) | 452 | if (optlen > 40 || optlen < 0) |
447 | goto e_inval; | 453 | goto e_inval; |
448 | err = ip_options_get_from_user(&opt, optval, optlen); | 454 | err = ip_options_get_from_user(&opt, optval, optlen); |
449 | if (err) | 455 | if (err) |
450 | break; | 456 | break; |
451 | if (inet->is_icsk) { | 457 | if (inet->is_icsk) { |
452 | struct inet_connection_sock *icsk = inet_csk(sk); | 458 | struct inet_connection_sock *icsk = inet_csk(sk); |
453 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | 459 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
454 | if (sk->sk_family == PF_INET || | 460 | if (sk->sk_family == PF_INET || |
455 | (!((1 << sk->sk_state) & | 461 | (!((1 << sk->sk_state) & |
456 | (TCPF_LISTEN | TCPF_CLOSE)) && | 462 | (TCPF_LISTEN | TCPF_CLOSE)) && |
457 | inet->daddr != LOOPBACK4_IPV6)) { | 463 | inet->daddr != LOOPBACK4_IPV6)) { |
458 | #endif | 464 | #endif |
459 | if (inet->opt) | 465 | if (inet->opt) |
460 | icsk->icsk_ext_hdr_len -= inet->opt->optlen; | 466 | icsk->icsk_ext_hdr_len -= inet->opt->optlen; |
461 | if (opt) | 467 | if (opt) |
462 | icsk->icsk_ext_hdr_len += opt->optlen; | 468 | icsk->icsk_ext_hdr_len += opt->optlen; |
463 | icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie); | 469 | icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie); |
464 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | 470 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
465 | } | ||
466 | #endif | ||
467 | } | 471 | } |
468 | opt = xchg(&inet->opt, opt); | 472 | #endif |
469 | kfree(opt); | ||
470 | break; | ||
471 | } | 473 | } |
472 | case IP_PKTINFO: | 474 | opt = xchg(&inet->opt, opt); |
473 | if (val) | 475 | kfree(opt); |
474 | inet->cmsg_flags |= IP_CMSG_PKTINFO; | 476 | break; |
475 | else | 477 | } |
476 | inet->cmsg_flags &= ~IP_CMSG_PKTINFO; | 478 | case IP_PKTINFO: |
477 | break; | 479 | if (val) |
478 | case IP_RECVTTL: | 480 | inet->cmsg_flags |= IP_CMSG_PKTINFO; |
479 | if (val) | 481 | else |
480 | inet->cmsg_flags |= IP_CMSG_TTL; | 482 | inet->cmsg_flags &= ~IP_CMSG_PKTINFO; |
481 | else | 483 | break; |
482 | inet->cmsg_flags &= ~IP_CMSG_TTL; | 484 | case IP_RECVTTL: |
483 | break; | 485 | if (val) |
484 | case IP_RECVTOS: | 486 | inet->cmsg_flags |= IP_CMSG_TTL; |
485 | if (val) | 487 | else |
486 | inet->cmsg_flags |= IP_CMSG_TOS; | 488 | inet->cmsg_flags &= ~IP_CMSG_TTL; |
487 | else | 489 | break; |
488 | inet->cmsg_flags &= ~IP_CMSG_TOS; | 490 | case IP_RECVTOS: |
489 | break; | 491 | if (val) |
490 | case IP_RECVOPTS: | 492 | inet->cmsg_flags |= IP_CMSG_TOS; |
491 | if (val) | 493 | else |
492 | inet->cmsg_flags |= IP_CMSG_RECVOPTS; | 494 | inet->cmsg_flags &= ~IP_CMSG_TOS; |
493 | else | 495 | break; |
494 | inet->cmsg_flags &= ~IP_CMSG_RECVOPTS; | 496 | case IP_RECVOPTS: |
495 | break; | 497 | if (val) |
496 | case IP_RETOPTS: | 498 | inet->cmsg_flags |= IP_CMSG_RECVOPTS; |
497 | if (val) | 499 | else |
498 | inet->cmsg_flags |= IP_CMSG_RETOPTS; | 500 | inet->cmsg_flags &= ~IP_CMSG_RECVOPTS; |
499 | else | 501 | break; |
500 | inet->cmsg_flags &= ~IP_CMSG_RETOPTS; | 502 | case IP_RETOPTS: |
503 | if (val) | ||
504 | inet->cmsg_flags |= IP_CMSG_RETOPTS; | ||
505 | else | ||
506 | inet->cmsg_flags &= ~IP_CMSG_RETOPTS; | ||
507 | break; | ||
508 | case IP_PASSSEC: | ||
509 | if (val) | ||
510 | inet->cmsg_flags |= IP_CMSG_PASSSEC; | ||
511 | else | ||
512 | inet->cmsg_flags &= ~IP_CMSG_PASSSEC; | ||
513 | break; | ||
514 | case IP_TOS: /* This sets both TOS and Precedence */ | ||
515 | if (sk->sk_type == SOCK_STREAM) { | ||
516 | val &= ~3; | ||
517 | val |= inet->tos & 3; | ||
518 | } | ||
519 | if (IPTOS_PREC(val) >= IPTOS_PREC_CRITIC_ECP && | ||
520 | !capable(CAP_NET_ADMIN)) { | ||
521 | err = -EPERM; | ||
501 | break; | 522 | break; |
502 | case IP_PASSSEC: | 523 | } |
503 | if (val) | 524 | if (inet->tos != val) { |
504 | inet->cmsg_flags |= IP_CMSG_PASSSEC; | 525 | inet->tos = val; |
505 | else | 526 | sk->sk_priority = rt_tos2priority(val); |
506 | inet->cmsg_flags &= ~IP_CMSG_PASSSEC; | 527 | sk_dst_reset(sk); |
528 | } | ||
529 | break; | ||
530 | case IP_TTL: | ||
531 | if (optlen<1) | ||
532 | goto e_inval; | ||
533 | if (val != -1 && (val < 1 || val>255)) | ||
534 | goto e_inval; | ||
535 | inet->uc_ttl = val; | ||
536 | break; | ||
537 | case IP_HDRINCL: | ||
538 | if (sk->sk_type != SOCK_RAW) { | ||
539 | err = -ENOPROTOOPT; | ||
507 | break; | 540 | break; |
508 | case IP_TOS: /* This sets both TOS and Precedence */ | 541 | } |
509 | if (sk->sk_type == SOCK_STREAM) { | 542 | inet->hdrincl = val ? 1 : 0; |
510 | val &= ~3; | 543 | break; |
511 | val |= inet->tos & 3; | 544 | case IP_MTU_DISCOVER: |
512 | } | 545 | if (val<0 || val>3) |
513 | if (IPTOS_PREC(val) >= IPTOS_PREC_CRITIC_ECP && | 546 | goto e_inval; |
514 | !capable(CAP_NET_ADMIN)) { | 547 | inet->pmtudisc = val; |
515 | err = -EPERM; | 548 | break; |
549 | case IP_RECVERR: | ||
550 | inet->recverr = !!val; | ||
551 | if (!val) | ||
552 | skb_queue_purge(&sk->sk_error_queue); | ||
553 | break; | ||
554 | case IP_MULTICAST_TTL: | ||
555 | if (sk->sk_type == SOCK_STREAM) | ||
556 | goto e_inval; | ||
557 | if (optlen<1) | ||
558 | goto e_inval; | ||
559 | if (val==-1) | ||
560 | val = 1; | ||
561 | if (val < 0 || val > 255) | ||
562 | goto e_inval; | ||
563 | inet->mc_ttl = val; | ||
564 | break; | ||
565 | case IP_MULTICAST_LOOP: | ||
566 | if (optlen<1) | ||
567 | goto e_inval; | ||
568 | inet->mc_loop = !!val; | ||
569 | break; | ||
570 | case IP_MULTICAST_IF: | ||
571 | { | ||
572 | struct ip_mreqn mreq; | ||
573 | struct net_device *dev = NULL; | ||
574 | |||
575 | if (sk->sk_type == SOCK_STREAM) | ||
576 | goto e_inval; | ||
577 | /* | ||
578 | * Check the arguments are allowable | ||
579 | */ | ||
580 | |||
581 | err = -EFAULT; | ||
582 | if (optlen >= sizeof(struct ip_mreqn)) { | ||
583 | if (copy_from_user(&mreq,optval,sizeof(mreq))) | ||
516 | break; | 584 | break; |
517 | } | 585 | } else { |
518 | if (inet->tos != val) { | 586 | memset(&mreq, 0, sizeof(mreq)); |
519 | inet->tos = val; | 587 | if (optlen >= sizeof(struct in_addr) && |
520 | sk->sk_priority = rt_tos2priority(val); | 588 | copy_from_user(&mreq.imr_address,optval,sizeof(struct in_addr))) |
521 | sk_dst_reset(sk); | 589 | break; |
522 | } | 590 | } |
523 | break; | 591 | |
524 | case IP_TTL: | 592 | if (!mreq.imr_ifindex) { |
525 | if (optlen<1) | 593 | if (mreq.imr_address.s_addr == INADDR_ANY) { |
526 | goto e_inval; | 594 | inet->mc_index = 0; |
527 | if (val != -1 && (val < 1 || val>255)) | 595 | inet->mc_addr = 0; |
528 | goto e_inval; | 596 | err = 0; |
529 | inet->uc_ttl = val; | ||
530 | break; | ||
531 | case IP_HDRINCL: | ||
532 | if (sk->sk_type != SOCK_RAW) { | ||
533 | err = -ENOPROTOOPT; | ||
534 | break; | 597 | break; |
535 | } | 598 | } |
536 | inet->hdrincl = val ? 1 : 0; | 599 | dev = ip_dev_find(mreq.imr_address.s_addr); |
537 | break; | 600 | if (dev) { |
538 | case IP_MTU_DISCOVER: | 601 | mreq.imr_ifindex = dev->ifindex; |
539 | if (val<0 || val>2) | 602 | dev_put(dev); |
540 | goto e_inval; | 603 | } |
541 | inet->pmtudisc = val; | 604 | } else |
542 | break; | 605 | dev = __dev_get_by_index(mreq.imr_ifindex); |
543 | case IP_RECVERR: | ||
544 | inet->recverr = !!val; | ||
545 | if (!val) | ||
546 | skb_queue_purge(&sk->sk_error_queue); | ||
547 | break; | ||
548 | case IP_MULTICAST_TTL: | ||
549 | if (sk->sk_type == SOCK_STREAM) | ||
550 | goto e_inval; | ||
551 | if (optlen<1) | ||
552 | goto e_inval; | ||
553 | if (val==-1) | ||
554 | val = 1; | ||
555 | if (val < 0 || val > 255) | ||
556 | goto e_inval; | ||
557 | inet->mc_ttl = val; | ||
558 | break; | ||
559 | case IP_MULTICAST_LOOP: | ||
560 | if (optlen<1) | ||
561 | goto e_inval; | ||
562 | inet->mc_loop = !!val; | ||
563 | break; | ||
564 | case IP_MULTICAST_IF: | ||
565 | { | ||
566 | struct ip_mreqn mreq; | ||
567 | struct net_device *dev = NULL; | ||
568 | 606 | ||
569 | if (sk->sk_type == SOCK_STREAM) | ||
570 | goto e_inval; | ||
571 | /* | ||
572 | * Check the arguments are allowable | ||
573 | */ | ||
574 | 607 | ||
575 | err = -EFAULT; | 608 | err = -EADDRNOTAVAIL; |
576 | if (optlen >= sizeof(struct ip_mreqn)) { | 609 | if (!dev) |
577 | if (copy_from_user(&mreq,optval,sizeof(mreq))) | 610 | break; |
578 | break; | ||
579 | } else { | ||
580 | memset(&mreq, 0, sizeof(mreq)); | ||
581 | if (optlen >= sizeof(struct in_addr) && | ||
582 | copy_from_user(&mreq.imr_address,optval,sizeof(struct in_addr))) | ||
583 | break; | ||
584 | } | ||
585 | 611 | ||
586 | if (!mreq.imr_ifindex) { | 612 | err = -EINVAL; |
587 | if (mreq.imr_address.s_addr == INADDR_ANY) { | 613 | if (sk->sk_bound_dev_if && |
588 | inet->mc_index = 0; | 614 | mreq.imr_ifindex != sk->sk_bound_dev_if) |
589 | inet->mc_addr = 0; | 615 | break; |
590 | err = 0; | ||
591 | break; | ||
592 | } | ||
593 | dev = ip_dev_find(mreq.imr_address.s_addr); | ||
594 | if (dev) { | ||
595 | mreq.imr_ifindex = dev->ifindex; | ||
596 | dev_put(dev); | ||
597 | } | ||
598 | } else | ||
599 | dev = __dev_get_by_index(mreq.imr_ifindex); | ||
600 | 616 | ||
617 | inet->mc_index = mreq.imr_ifindex; | ||
618 | inet->mc_addr = mreq.imr_address.s_addr; | ||
619 | err = 0; | ||
620 | break; | ||
621 | } | ||
601 | 622 | ||
602 | err = -EADDRNOTAVAIL; | 623 | case IP_ADD_MEMBERSHIP: |
603 | if (!dev) | 624 | case IP_DROP_MEMBERSHIP: |
604 | break; | 625 | { |
626 | struct ip_mreqn mreq; | ||
605 | 627 | ||
606 | err = -EINVAL; | 628 | if (optlen < sizeof(struct ip_mreq)) |
607 | if (sk->sk_bound_dev_if && | 629 | goto e_inval; |
608 | mreq.imr_ifindex != sk->sk_bound_dev_if) | 630 | err = -EFAULT; |
631 | if (optlen >= sizeof(struct ip_mreqn)) { | ||
632 | if (copy_from_user(&mreq,optval,sizeof(mreq))) | ||
609 | break; | 633 | break; |
634 | } else { | ||
635 | memset(&mreq, 0, sizeof(mreq)); | ||
636 | if (copy_from_user(&mreq,optval,sizeof(struct ip_mreq))) | ||
637 | break; | ||
638 | } | ||
610 | 639 | ||
611 | inet->mc_index = mreq.imr_ifindex; | 640 | if (optname == IP_ADD_MEMBERSHIP) |
612 | inet->mc_addr = mreq.imr_address.s_addr; | 641 | err = ip_mc_join_group(sk, &mreq); |
613 | err = 0; | 642 | else |
643 | err = ip_mc_leave_group(sk, &mreq); | ||
644 | break; | ||
645 | } | ||
646 | case IP_MSFILTER: | ||
647 | { | ||
648 | extern int sysctl_igmp_max_msf; | ||
649 | struct ip_msfilter *msf; | ||
650 | |||
651 | if (optlen < IP_MSFILTER_SIZE(0)) | ||
652 | goto e_inval; | ||
653 | if (optlen > sysctl_optmem_max) { | ||
654 | err = -ENOBUFS; | ||
614 | break; | 655 | break; |
615 | } | 656 | } |
657 | msf = kmalloc(optlen, GFP_KERNEL); | ||
658 | if (msf == 0) { | ||
659 | err = -ENOBUFS; | ||
660 | break; | ||
661 | } | ||
662 | err = -EFAULT; | ||
663 | if (copy_from_user(msf, optval, optlen)) { | ||
664 | kfree(msf); | ||
665 | break; | ||
666 | } | ||
667 | /* numsrc >= (1G-4) overflow in 32 bits */ | ||
668 | if (msf->imsf_numsrc >= 0x3ffffffcU || | ||
669 | msf->imsf_numsrc > sysctl_igmp_max_msf) { | ||
670 | kfree(msf); | ||
671 | err = -ENOBUFS; | ||
672 | break; | ||
673 | } | ||
674 | if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) { | ||
675 | kfree(msf); | ||
676 | err = -EINVAL; | ||
677 | break; | ||
678 | } | ||
679 | err = ip_mc_msfilter(sk, msf, 0); | ||
680 | kfree(msf); | ||
681 | break; | ||
682 | } | ||
683 | case IP_BLOCK_SOURCE: | ||
684 | case IP_UNBLOCK_SOURCE: | ||
685 | case IP_ADD_SOURCE_MEMBERSHIP: | ||
686 | case IP_DROP_SOURCE_MEMBERSHIP: | ||
687 | { | ||
688 | struct ip_mreq_source mreqs; | ||
689 | int omode, add; | ||
616 | 690 | ||
617 | case IP_ADD_MEMBERSHIP: | 691 | if (optlen != sizeof(struct ip_mreq_source)) |
618 | case IP_DROP_MEMBERSHIP: | 692 | goto e_inval; |
619 | { | 693 | if (copy_from_user(&mreqs, optval, sizeof(mreqs))) { |
620 | struct ip_mreqn mreq; | ||
621 | |||
622 | if (optlen < sizeof(struct ip_mreq)) | ||
623 | goto e_inval; | ||
624 | err = -EFAULT; | 694 | err = -EFAULT; |
625 | if (optlen >= sizeof(struct ip_mreqn)) { | ||
626 | if(copy_from_user(&mreq,optval,sizeof(mreq))) | ||
627 | break; | ||
628 | } else { | ||
629 | memset(&mreq, 0, sizeof(mreq)); | ||
630 | if (copy_from_user(&mreq,optval,sizeof(struct ip_mreq))) | ||
631 | break; | ||
632 | } | ||
633 | |||
634 | if (optname == IP_ADD_MEMBERSHIP) | ||
635 | err = ip_mc_join_group(sk, &mreq); | ||
636 | else | ||
637 | err = ip_mc_leave_group(sk, &mreq); | ||
638 | break; | 695 | break; |
639 | } | 696 | } |
640 | case IP_MSFILTER: | 697 | if (optname == IP_BLOCK_SOURCE) { |
641 | { | 698 | omode = MCAST_EXCLUDE; |
642 | extern int sysctl_igmp_max_msf; | 699 | add = 1; |
643 | struct ip_msfilter *msf; | 700 | } else if (optname == IP_UNBLOCK_SOURCE) { |
701 | omode = MCAST_EXCLUDE; | ||
702 | add = 0; | ||
703 | } else if (optname == IP_ADD_SOURCE_MEMBERSHIP) { | ||
704 | struct ip_mreqn mreq; | ||
644 | 705 | ||
645 | if (optlen < IP_MSFILTER_SIZE(0)) | 706 | mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr; |
646 | goto e_inval; | 707 | mreq.imr_address.s_addr = mreqs.imr_interface; |
647 | if (optlen > sysctl_optmem_max) { | 708 | mreq.imr_ifindex = 0; |
648 | err = -ENOBUFS; | 709 | err = ip_mc_join_group(sk, &mreq); |
649 | break; | 710 | if (err && err != -EADDRINUSE) |
650 | } | ||
651 | msf = kmalloc(optlen, GFP_KERNEL); | ||
652 | if (msf == 0) { | ||
653 | err = -ENOBUFS; | ||
654 | break; | 711 | break; |
655 | } | 712 | omode = MCAST_INCLUDE; |
713 | add = 1; | ||
714 | } else /* IP_DROP_SOURCE_MEMBERSHIP */ { | ||
715 | omode = MCAST_INCLUDE; | ||
716 | add = 0; | ||
717 | } | ||
718 | err = ip_mc_source(add, omode, sk, &mreqs, 0); | ||
719 | break; | ||
720 | } | ||
721 | case MCAST_JOIN_GROUP: | ||
722 | case MCAST_LEAVE_GROUP: | ||
723 | { | ||
724 | struct group_req greq; | ||
725 | struct sockaddr_in *psin; | ||
726 | struct ip_mreqn mreq; | ||
727 | |||
728 | if (optlen < sizeof(struct group_req)) | ||
729 | goto e_inval; | ||
730 | err = -EFAULT; | ||
731 | if (copy_from_user(&greq, optval, sizeof(greq))) | ||
732 | break; | ||
733 | psin = (struct sockaddr_in *)&greq.gr_group; | ||
734 | if (psin->sin_family != AF_INET) | ||
735 | goto e_inval; | ||
736 | memset(&mreq, 0, sizeof(mreq)); | ||
737 | mreq.imr_multiaddr = psin->sin_addr; | ||
738 | mreq.imr_ifindex = greq.gr_interface; | ||
739 | |||
740 | if (optname == MCAST_JOIN_GROUP) | ||
741 | err = ip_mc_join_group(sk, &mreq); | ||
742 | else | ||
743 | err = ip_mc_leave_group(sk, &mreq); | ||
744 | break; | ||
745 | } | ||
746 | case MCAST_JOIN_SOURCE_GROUP: | ||
747 | case MCAST_LEAVE_SOURCE_GROUP: | ||
748 | case MCAST_BLOCK_SOURCE: | ||
749 | case MCAST_UNBLOCK_SOURCE: | ||
750 | { | ||
751 | struct group_source_req greqs; | ||
752 | struct ip_mreq_source mreqs; | ||
753 | struct sockaddr_in *psin; | ||
754 | int omode, add; | ||
755 | |||
756 | if (optlen != sizeof(struct group_source_req)) | ||
757 | goto e_inval; | ||
758 | if (copy_from_user(&greqs, optval, sizeof(greqs))) { | ||
656 | err = -EFAULT; | 759 | err = -EFAULT; |
657 | if (copy_from_user(msf, optval, optlen)) { | ||
658 | kfree(msf); | ||
659 | break; | ||
660 | } | ||
661 | /* numsrc >= (1G-4) overflow in 32 bits */ | ||
662 | if (msf->imsf_numsrc >= 0x3ffffffcU || | ||
663 | msf->imsf_numsrc > sysctl_igmp_max_msf) { | ||
664 | kfree(msf); | ||
665 | err = -ENOBUFS; | ||
666 | break; | ||
667 | } | ||
668 | if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) { | ||
669 | kfree(msf); | ||
670 | err = -EINVAL; | ||
671 | break; | ||
672 | } | ||
673 | err = ip_mc_msfilter(sk, msf, 0); | ||
674 | kfree(msf); | ||
675 | break; | 760 | break; |
676 | } | 761 | } |
677 | case IP_BLOCK_SOURCE: | 762 | if (greqs.gsr_group.ss_family != AF_INET || |
678 | case IP_UNBLOCK_SOURCE: | 763 | greqs.gsr_source.ss_family != AF_INET) { |
679 | case IP_ADD_SOURCE_MEMBERSHIP: | 764 | err = -EADDRNOTAVAIL; |
680 | case IP_DROP_SOURCE_MEMBERSHIP: | ||
681 | { | ||
682 | struct ip_mreq_source mreqs; | ||
683 | int omode, add; | ||
684 | |||
685 | if (optlen != sizeof(struct ip_mreq_source)) | ||
686 | goto e_inval; | ||
687 | if (copy_from_user(&mreqs, optval, sizeof(mreqs))) { | ||
688 | err = -EFAULT; | ||
689 | break; | ||
690 | } | ||
691 | if (optname == IP_BLOCK_SOURCE) { | ||
692 | omode = MCAST_EXCLUDE; | ||
693 | add = 1; | ||
694 | } else if (optname == IP_UNBLOCK_SOURCE) { | ||
695 | omode = MCAST_EXCLUDE; | ||
696 | add = 0; | ||
697 | } else if (optname == IP_ADD_SOURCE_MEMBERSHIP) { | ||
698 | struct ip_mreqn mreq; | ||
699 | |||
700 | mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr; | ||
701 | mreq.imr_address.s_addr = mreqs.imr_interface; | ||
702 | mreq.imr_ifindex = 0; | ||
703 | err = ip_mc_join_group(sk, &mreq); | ||
704 | if (err && err != -EADDRINUSE) | ||
705 | break; | ||
706 | omode = MCAST_INCLUDE; | ||
707 | add = 1; | ||
708 | } else /* IP_DROP_SOURCE_MEMBERSHIP */ { | ||
709 | omode = MCAST_INCLUDE; | ||
710 | add = 0; | ||
711 | } | ||
712 | err = ip_mc_source(add, omode, sk, &mreqs, 0); | ||
713 | break; | 765 | break; |
714 | } | 766 | } |
715 | case MCAST_JOIN_GROUP: | 767 | psin = (struct sockaddr_in *)&greqs.gsr_group; |
716 | case MCAST_LEAVE_GROUP: | 768 | mreqs.imr_multiaddr = psin->sin_addr.s_addr; |
717 | { | 769 | psin = (struct sockaddr_in *)&greqs.gsr_source; |
718 | struct group_req greq; | 770 | mreqs.imr_sourceaddr = psin->sin_addr.s_addr; |
719 | struct sockaddr_in *psin; | 771 | mreqs.imr_interface = 0; /* use index for mc_source */ |
772 | |||
773 | if (optname == MCAST_BLOCK_SOURCE) { | ||
774 | omode = MCAST_EXCLUDE; | ||
775 | add = 1; | ||
776 | } else if (optname == MCAST_UNBLOCK_SOURCE) { | ||
777 | omode = MCAST_EXCLUDE; | ||
778 | add = 0; | ||
779 | } else if (optname == MCAST_JOIN_SOURCE_GROUP) { | ||
720 | struct ip_mreqn mreq; | 780 | struct ip_mreqn mreq; |
721 | 781 | ||
722 | if (optlen < sizeof(struct group_req)) | 782 | psin = (struct sockaddr_in *)&greqs.gsr_group; |
723 | goto e_inval; | ||
724 | err = -EFAULT; | ||
725 | if(copy_from_user(&greq, optval, sizeof(greq))) | ||
726 | break; | ||
727 | psin = (struct sockaddr_in *)&greq.gr_group; | ||
728 | if (psin->sin_family != AF_INET) | ||
729 | goto e_inval; | ||
730 | memset(&mreq, 0, sizeof(mreq)); | ||
731 | mreq.imr_multiaddr = psin->sin_addr; | 783 | mreq.imr_multiaddr = psin->sin_addr; |
732 | mreq.imr_ifindex = greq.gr_interface; | 784 | mreq.imr_address.s_addr = 0; |
733 | 785 | mreq.imr_ifindex = greqs.gsr_interface; | |
734 | if (optname == MCAST_JOIN_GROUP) | 786 | err = ip_mc_join_group(sk, &mreq); |
735 | err = ip_mc_join_group(sk, &mreq); | 787 | if (err && err != -EADDRINUSE) |
736 | else | 788 | break; |
737 | err = ip_mc_leave_group(sk, &mreq); | 789 | greqs.gsr_interface = mreq.imr_ifindex; |
790 | omode = MCAST_INCLUDE; | ||
791 | add = 1; | ||
792 | } else /* MCAST_LEAVE_SOURCE_GROUP */ { | ||
793 | omode = MCAST_INCLUDE; | ||
794 | add = 0; | ||
795 | } | ||
796 | err = ip_mc_source(add, omode, sk, &mreqs, | ||
797 | greqs.gsr_interface); | ||
798 | break; | ||
799 | } | ||
800 | case MCAST_MSFILTER: | ||
801 | { | ||
802 | extern int sysctl_igmp_max_msf; | ||
803 | struct sockaddr_in *psin; | ||
804 | struct ip_msfilter *msf = NULL; | ||
805 | struct group_filter *gsf = NULL; | ||
806 | int msize, i, ifindex; | ||
807 | |||
808 | if (optlen < GROUP_FILTER_SIZE(0)) | ||
809 | goto e_inval; | ||
810 | if (optlen > sysctl_optmem_max) { | ||
811 | err = -ENOBUFS; | ||
738 | break; | 812 | break; |
739 | } | 813 | } |
740 | case MCAST_JOIN_SOURCE_GROUP: | 814 | gsf = kmalloc(optlen,GFP_KERNEL); |
741 | case MCAST_LEAVE_SOURCE_GROUP: | 815 | if (gsf == 0) { |
742 | case MCAST_BLOCK_SOURCE: | 816 | err = -ENOBUFS; |
743 | case MCAST_UNBLOCK_SOURCE: | ||
744 | { | ||
745 | struct group_source_req greqs; | ||
746 | struct ip_mreq_source mreqs; | ||
747 | struct sockaddr_in *psin; | ||
748 | int omode, add; | ||
749 | |||
750 | if (optlen != sizeof(struct group_source_req)) | ||
751 | goto e_inval; | ||
752 | if (copy_from_user(&greqs, optval, sizeof(greqs))) { | ||
753 | err = -EFAULT; | ||
754 | break; | ||
755 | } | ||
756 | if (greqs.gsr_group.ss_family != AF_INET || | ||
757 | greqs.gsr_source.ss_family != AF_INET) { | ||
758 | err = -EADDRNOTAVAIL; | ||
759 | break; | ||
760 | } | ||
761 | psin = (struct sockaddr_in *)&greqs.gsr_group; | ||
762 | mreqs.imr_multiaddr = psin->sin_addr.s_addr; | ||
763 | psin = (struct sockaddr_in *)&greqs.gsr_source; | ||
764 | mreqs.imr_sourceaddr = psin->sin_addr.s_addr; | ||
765 | mreqs.imr_interface = 0; /* use index for mc_source */ | ||
766 | |||
767 | if (optname == MCAST_BLOCK_SOURCE) { | ||
768 | omode = MCAST_EXCLUDE; | ||
769 | add = 1; | ||
770 | } else if (optname == MCAST_UNBLOCK_SOURCE) { | ||
771 | omode = MCAST_EXCLUDE; | ||
772 | add = 0; | ||
773 | } else if (optname == MCAST_JOIN_SOURCE_GROUP) { | ||
774 | struct ip_mreqn mreq; | ||
775 | |||
776 | psin = (struct sockaddr_in *)&greqs.gsr_group; | ||
777 | mreq.imr_multiaddr = psin->sin_addr; | ||
778 | mreq.imr_address.s_addr = 0; | ||
779 | mreq.imr_ifindex = greqs.gsr_interface; | ||
780 | err = ip_mc_join_group(sk, &mreq); | ||
781 | if (err && err != -EADDRINUSE) | ||
782 | break; | ||
783 | greqs.gsr_interface = mreq.imr_ifindex; | ||
784 | omode = MCAST_INCLUDE; | ||
785 | add = 1; | ||
786 | } else /* MCAST_LEAVE_SOURCE_GROUP */ { | ||
787 | omode = MCAST_INCLUDE; | ||
788 | add = 0; | ||
789 | } | ||
790 | err = ip_mc_source(add, omode, sk, &mreqs, | ||
791 | greqs.gsr_interface); | ||
792 | break; | 817 | break; |
793 | } | 818 | } |
794 | case MCAST_MSFILTER: | 819 | err = -EFAULT; |
795 | { | 820 | if (copy_from_user(gsf, optval, optlen)) { |
796 | extern int sysctl_igmp_max_msf; | 821 | goto mc_msf_out; |
797 | struct sockaddr_in *psin; | 822 | } |
798 | struct ip_msfilter *msf = NULL; | 823 | /* numsrc >= (4G-140)/128 overflow in 32 bits */ |
799 | struct group_filter *gsf = NULL; | 824 | if (gsf->gf_numsrc >= 0x1ffffff || |
800 | int msize, i, ifindex; | 825 | gsf->gf_numsrc > sysctl_igmp_max_msf) { |
801 | 826 | err = -ENOBUFS; | |
802 | if (optlen < GROUP_FILTER_SIZE(0)) | 827 | goto mc_msf_out; |
803 | goto e_inval; | 828 | } |
804 | if (optlen > sysctl_optmem_max) { | 829 | if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) { |
805 | err = -ENOBUFS; | 830 | err = -EINVAL; |
806 | break; | 831 | goto mc_msf_out; |
807 | } | 832 | } |
808 | gsf = kmalloc(optlen,GFP_KERNEL); | 833 | msize = IP_MSFILTER_SIZE(gsf->gf_numsrc); |
809 | if (gsf == 0) { | 834 | msf = kmalloc(msize,GFP_KERNEL); |
810 | err = -ENOBUFS; | 835 | if (msf == 0) { |
811 | break; | 836 | err = -ENOBUFS; |
812 | } | 837 | goto mc_msf_out; |
813 | err = -EFAULT; | 838 | } |
814 | if (copy_from_user(gsf, optval, optlen)) { | 839 | ifindex = gsf->gf_interface; |
815 | goto mc_msf_out; | 840 | psin = (struct sockaddr_in *)&gsf->gf_group; |
816 | } | 841 | if (psin->sin_family != AF_INET) { |
817 | /* numsrc >= (4G-140)/128 overflow in 32 bits */ | ||
818 | if (gsf->gf_numsrc >= 0x1ffffff || | ||
819 | gsf->gf_numsrc > sysctl_igmp_max_msf) { | ||
820 | err = -ENOBUFS; | ||
821 | goto mc_msf_out; | ||
822 | } | ||
823 | if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) { | ||
824 | err = -EINVAL; | ||
825 | goto mc_msf_out; | ||
826 | } | ||
827 | msize = IP_MSFILTER_SIZE(gsf->gf_numsrc); | ||
828 | msf = kmalloc(msize,GFP_KERNEL); | ||
829 | if (msf == 0) { | ||
830 | err = -ENOBUFS; | ||
831 | goto mc_msf_out; | ||
832 | } | ||
833 | ifindex = gsf->gf_interface; | ||
834 | psin = (struct sockaddr_in *)&gsf->gf_group; | ||
835 | if (psin->sin_family != AF_INET) { | ||
836 | err = -EADDRNOTAVAIL; | ||
837 | goto mc_msf_out; | ||
838 | } | ||
839 | msf->imsf_multiaddr = psin->sin_addr.s_addr; | ||
840 | msf->imsf_interface = 0; | ||
841 | msf->imsf_fmode = gsf->gf_fmode; | ||
842 | msf->imsf_numsrc = gsf->gf_numsrc; | ||
843 | err = -EADDRNOTAVAIL; | 842 | err = -EADDRNOTAVAIL; |
844 | for (i=0; i<gsf->gf_numsrc; ++i) { | 843 | goto mc_msf_out; |
845 | psin = (struct sockaddr_in *)&gsf->gf_slist[i]; | ||
846 | |||
847 | if (psin->sin_family != AF_INET) | ||
848 | goto mc_msf_out; | ||
849 | msf->imsf_slist[i] = psin->sin_addr.s_addr; | ||
850 | } | ||
851 | kfree(gsf); | ||
852 | gsf = NULL; | ||
853 | |||
854 | err = ip_mc_msfilter(sk, msf, ifindex); | ||
855 | mc_msf_out: | ||
856 | kfree(msf); | ||
857 | kfree(gsf); | ||
858 | break; | ||
859 | } | 844 | } |
860 | case IP_ROUTER_ALERT: | 845 | msf->imsf_multiaddr = psin->sin_addr.s_addr; |
861 | err = ip_ra_control(sk, val ? 1 : 0, NULL); | 846 | msf->imsf_interface = 0; |
862 | break; | 847 | msf->imsf_fmode = gsf->gf_fmode; |
863 | 848 | msf->imsf_numsrc = gsf->gf_numsrc; | |
864 | case IP_FREEBIND: | 849 | err = -EADDRNOTAVAIL; |
865 | if (optlen<1) | 850 | for (i=0; i<gsf->gf_numsrc; ++i) { |
866 | goto e_inval; | 851 | psin = (struct sockaddr_in *)&gsf->gf_slist[i]; |
867 | inet->freebind = !!val; | ||
868 | break; | ||
869 | 852 | ||
870 | case IP_IPSEC_POLICY: | 853 | if (psin->sin_family != AF_INET) |
871 | case IP_XFRM_POLICY: | 854 | goto mc_msf_out; |
872 | err = -EPERM; | 855 | msf->imsf_slist[i] = psin->sin_addr.s_addr; |
873 | if (!capable(CAP_NET_ADMIN)) | 856 | } |
874 | break; | 857 | kfree(gsf); |
875 | err = xfrm_user_policy(sk, optname, optval, optlen); | 858 | gsf = NULL; |
859 | |||
860 | err = ip_mc_msfilter(sk, msf, ifindex); | ||
861 | mc_msf_out: | ||
862 | kfree(msf); | ||
863 | kfree(gsf); | ||
864 | break; | ||
865 | } | ||
866 | case IP_ROUTER_ALERT: | ||
867 | err = ip_ra_control(sk, val ? 1 : 0, NULL); | ||
868 | break; | ||
869 | |||
870 | case IP_FREEBIND: | ||
871 | if (optlen<1) | ||
872 | goto e_inval; | ||
873 | inet->freebind = !!val; | ||
874 | break; | ||
875 | |||
876 | case IP_IPSEC_POLICY: | ||
877 | case IP_XFRM_POLICY: | ||
878 | err = -EPERM; | ||
879 | if (!capable(CAP_NET_ADMIN)) | ||
876 | break; | 880 | break; |
881 | err = xfrm_user_policy(sk, optname, optval, optlen); | ||
882 | break; | ||
877 | 883 | ||
878 | default: | 884 | default: |
879 | err = -ENOPROTOOPT; | 885 | err = -ENOPROTOOPT; |
880 | break; | 886 | break; |
881 | } | 887 | } |
882 | release_sock(sk); | 888 | release_sock(sk); |
883 | return err; | 889 | return err; |
@@ -948,214 +954,213 @@ EXPORT_SYMBOL(compat_ip_setsockopt); | |||
948 | */ | 954 | */ |
949 | 955 | ||
950 | static int do_ip_getsockopt(struct sock *sk, int level, int optname, | 956 | static int do_ip_getsockopt(struct sock *sk, int level, int optname, |
951 | char __user *optval, int __user *optlen) | 957 | char __user *optval, int __user *optlen) |
952 | { | 958 | { |
953 | struct inet_sock *inet = inet_sk(sk); | 959 | struct inet_sock *inet = inet_sk(sk); |
954 | int val; | 960 | int val; |
955 | int len; | 961 | int len; |
956 | 962 | ||
957 | if(level!=SOL_IP) | 963 | if (level != SOL_IP) |
958 | return -EOPNOTSUPP; | 964 | return -EOPNOTSUPP; |
959 | 965 | ||
960 | #ifdef CONFIG_IP_MROUTE | 966 | #ifdef CONFIG_IP_MROUTE |
961 | if(optname>=MRT_BASE && optname <=MRT_BASE+10) | 967 | if (optname >= MRT_BASE && optname <= MRT_BASE+10) { |
962 | { | ||
963 | return ip_mroute_getsockopt(sk,optname,optval,optlen); | 968 | return ip_mroute_getsockopt(sk,optname,optval,optlen); |
964 | } | 969 | } |
965 | #endif | 970 | #endif |
966 | 971 | ||
967 | if(get_user(len,optlen)) | 972 | if (get_user(len,optlen)) |
968 | return -EFAULT; | 973 | return -EFAULT; |
969 | if(len < 0) | 974 | if (len < 0) |
970 | return -EINVAL; | 975 | return -EINVAL; |
971 | 976 | ||
972 | lock_sock(sk); | 977 | lock_sock(sk); |
973 | 978 | ||
974 | switch(optname) { | 979 | switch (optname) { |
975 | case IP_OPTIONS: | 980 | case IP_OPTIONS: |
976 | { | 981 | { |
977 | unsigned char optbuf[sizeof(struct ip_options)+40]; | 982 | unsigned char optbuf[sizeof(struct ip_options)+40]; |
978 | struct ip_options * opt = (struct ip_options*)optbuf; | 983 | struct ip_options * opt = (struct ip_options*)optbuf; |
979 | opt->optlen = 0; | 984 | opt->optlen = 0; |
980 | if (inet->opt) | 985 | if (inet->opt) |
981 | memcpy(optbuf, inet->opt, | 986 | memcpy(optbuf, inet->opt, |
982 | sizeof(struct ip_options)+ | 987 | sizeof(struct ip_options)+ |
983 | inet->opt->optlen); | 988 | inet->opt->optlen); |
984 | release_sock(sk); | 989 | release_sock(sk); |
985 | 990 | ||
986 | if (opt->optlen == 0) | 991 | if (opt->optlen == 0) |
987 | return put_user(0, optlen); | 992 | return put_user(0, optlen); |
988 | 993 | ||
989 | ip_options_undo(opt); | 994 | ip_options_undo(opt); |
990 | 995 | ||
991 | len = min_t(unsigned int, len, opt->optlen); | 996 | len = min_t(unsigned int, len, opt->optlen); |
992 | if(put_user(len, optlen)) | 997 | if (put_user(len, optlen)) |
993 | return -EFAULT; | 998 | return -EFAULT; |
994 | if(copy_to_user(optval, opt->__data, len)) | 999 | if (copy_to_user(optval, opt->__data, len)) |
995 | return -EFAULT; | 1000 | return -EFAULT; |
996 | return 0; | 1001 | return 0; |
997 | } | 1002 | } |
998 | case IP_PKTINFO: | 1003 | case IP_PKTINFO: |
999 | val = (inet->cmsg_flags & IP_CMSG_PKTINFO) != 0; | 1004 | val = (inet->cmsg_flags & IP_CMSG_PKTINFO) != 0; |
1000 | break; | 1005 | break; |
1001 | case IP_RECVTTL: | 1006 | case IP_RECVTTL: |
1002 | val = (inet->cmsg_flags & IP_CMSG_TTL) != 0; | 1007 | val = (inet->cmsg_flags & IP_CMSG_TTL) != 0; |
1003 | break; | 1008 | break; |
1004 | case IP_RECVTOS: | 1009 | case IP_RECVTOS: |
1005 | val = (inet->cmsg_flags & IP_CMSG_TOS) != 0; | 1010 | val = (inet->cmsg_flags & IP_CMSG_TOS) != 0; |
1006 | break; | 1011 | break; |
1007 | case IP_RECVOPTS: | 1012 | case IP_RECVOPTS: |
1008 | val = (inet->cmsg_flags & IP_CMSG_RECVOPTS) != 0; | 1013 | val = (inet->cmsg_flags & IP_CMSG_RECVOPTS) != 0; |
1009 | break; | 1014 | break; |
1010 | case IP_RETOPTS: | 1015 | case IP_RETOPTS: |
1011 | val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0; | 1016 | val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0; |
1012 | break; | 1017 | break; |
1013 | case IP_PASSSEC: | 1018 | case IP_PASSSEC: |
1014 | val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0; | 1019 | val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0; |
1015 | break; | 1020 | break; |
1016 | case IP_TOS: | 1021 | case IP_TOS: |
1017 | val = inet->tos; | 1022 | val = inet->tos; |
1018 | break; | 1023 | break; |
1019 | case IP_TTL: | 1024 | case IP_TTL: |
1020 | val = (inet->uc_ttl == -1 ? | 1025 | val = (inet->uc_ttl == -1 ? |
1021 | sysctl_ip_default_ttl : | 1026 | sysctl_ip_default_ttl : |
1022 | inet->uc_ttl); | 1027 | inet->uc_ttl); |
1023 | break; | 1028 | break; |
1024 | case IP_HDRINCL: | 1029 | case IP_HDRINCL: |
1025 | val = inet->hdrincl; | 1030 | val = inet->hdrincl; |
1026 | break; | 1031 | break; |
1027 | case IP_MTU_DISCOVER: | 1032 | case IP_MTU_DISCOVER: |
1028 | val = inet->pmtudisc; | 1033 | val = inet->pmtudisc; |
1029 | break; | 1034 | break; |
1030 | case IP_MTU: | 1035 | case IP_MTU: |
1031 | { | 1036 | { |
1032 | struct dst_entry *dst; | 1037 | struct dst_entry *dst; |
1033 | val = 0; | 1038 | val = 0; |
1034 | dst = sk_dst_get(sk); | 1039 | dst = sk_dst_get(sk); |
1035 | if (dst) { | 1040 | if (dst) { |
1036 | val = dst_mtu(dst); | 1041 | val = dst_mtu(dst); |
1037 | dst_release(dst); | 1042 | dst_release(dst); |
1038 | } | ||
1039 | if (!val) { | ||
1040 | release_sock(sk); | ||
1041 | return -ENOTCONN; | ||
1042 | } | ||
1043 | break; | ||
1044 | } | 1043 | } |
1045 | case IP_RECVERR: | 1044 | if (!val) { |
1046 | val = inet->recverr; | ||
1047 | break; | ||
1048 | case IP_MULTICAST_TTL: | ||
1049 | val = inet->mc_ttl; | ||
1050 | break; | ||
1051 | case IP_MULTICAST_LOOP: | ||
1052 | val = inet->mc_loop; | ||
1053 | break; | ||
1054 | case IP_MULTICAST_IF: | ||
1055 | { | ||
1056 | struct in_addr addr; | ||
1057 | len = min_t(unsigned int, len, sizeof(struct in_addr)); | ||
1058 | addr.s_addr = inet->mc_addr; | ||
1059 | release_sock(sk); | 1045 | release_sock(sk); |
1060 | 1046 | return -ENOTCONN; | |
1061 | if(put_user(len, optlen)) | ||
1062 | return -EFAULT; | ||
1063 | if(copy_to_user(optval, &addr, len)) | ||
1064 | return -EFAULT; | ||
1065 | return 0; | ||
1066 | } | 1047 | } |
1067 | case IP_MSFILTER: | 1048 | break; |
1068 | { | 1049 | } |
1069 | struct ip_msfilter msf; | 1050 | case IP_RECVERR: |
1070 | int err; | 1051 | val = inet->recverr; |
1052 | break; | ||
1053 | case IP_MULTICAST_TTL: | ||
1054 | val = inet->mc_ttl; | ||
1055 | break; | ||
1056 | case IP_MULTICAST_LOOP: | ||
1057 | val = inet->mc_loop; | ||
1058 | break; | ||
1059 | case IP_MULTICAST_IF: | ||
1060 | { | ||
1061 | struct in_addr addr; | ||
1062 | len = min_t(unsigned int, len, sizeof(struct in_addr)); | ||
1063 | addr.s_addr = inet->mc_addr; | ||
1064 | release_sock(sk); | ||
1071 | 1065 | ||
1072 | if (len < IP_MSFILTER_SIZE(0)) { | 1066 | if (put_user(len, optlen)) |
1073 | release_sock(sk); | 1067 | return -EFAULT; |
1074 | return -EINVAL; | 1068 | if (copy_to_user(optval, &addr, len)) |
1075 | } | 1069 | return -EFAULT; |
1076 | if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) { | 1070 | return 0; |
1077 | release_sock(sk); | 1071 | } |
1078 | return -EFAULT; | 1072 | case IP_MSFILTER: |
1079 | } | 1073 | { |
1080 | err = ip_mc_msfget(sk, &msf, | 1074 | struct ip_msfilter msf; |
1081 | (struct ip_msfilter __user *)optval, optlen); | 1075 | int err; |
1076 | |||
1077 | if (len < IP_MSFILTER_SIZE(0)) { | ||
1082 | release_sock(sk); | 1078 | release_sock(sk); |
1083 | return err; | 1079 | return -EINVAL; |
1084 | } | 1080 | } |
1085 | case MCAST_MSFILTER: | 1081 | if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) { |
1086 | { | ||
1087 | struct group_filter gsf; | ||
1088 | int err; | ||
1089 | |||
1090 | if (len < GROUP_FILTER_SIZE(0)) { | ||
1091 | release_sock(sk); | ||
1092 | return -EINVAL; | ||
1093 | } | ||
1094 | if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) { | ||
1095 | release_sock(sk); | ||
1096 | return -EFAULT; | ||
1097 | } | ||
1098 | err = ip_mc_gsfget(sk, &gsf, | ||
1099 | (struct group_filter __user *)optval, optlen); | ||
1100 | release_sock(sk); | 1082 | release_sock(sk); |
1101 | return err; | 1083 | return -EFAULT; |
1102 | } | 1084 | } |
1103 | case IP_PKTOPTIONS: | 1085 | err = ip_mc_msfget(sk, &msf, |
1104 | { | 1086 | (struct ip_msfilter __user *)optval, optlen); |
1105 | struct msghdr msg; | 1087 | release_sock(sk); |
1088 | return err; | ||
1089 | } | ||
1090 | case MCAST_MSFILTER: | ||
1091 | { | ||
1092 | struct group_filter gsf; | ||
1093 | int err; | ||
1106 | 1094 | ||
1095 | if (len < GROUP_FILTER_SIZE(0)) { | ||
1107 | release_sock(sk); | 1096 | release_sock(sk); |
1097 | return -EINVAL; | ||
1098 | } | ||
1099 | if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) { | ||
1100 | release_sock(sk); | ||
1101 | return -EFAULT; | ||
1102 | } | ||
1103 | err = ip_mc_gsfget(sk, &gsf, | ||
1104 | (struct group_filter __user *)optval, optlen); | ||
1105 | release_sock(sk); | ||
1106 | return err; | ||
1107 | } | ||
1108 | case IP_PKTOPTIONS: | ||
1109 | { | ||
1110 | struct msghdr msg; | ||
1111 | |||
1112 | release_sock(sk); | ||
1108 | 1113 | ||
1109 | if (sk->sk_type != SOCK_STREAM) | 1114 | if (sk->sk_type != SOCK_STREAM) |
1110 | return -ENOPROTOOPT; | 1115 | return -ENOPROTOOPT; |
1111 | 1116 | ||
1112 | msg.msg_control = optval; | 1117 | msg.msg_control = optval; |
1113 | msg.msg_controllen = len; | 1118 | msg.msg_controllen = len; |
1114 | msg.msg_flags = 0; | 1119 | msg.msg_flags = 0; |
1115 | 1120 | ||
1116 | if (inet->cmsg_flags & IP_CMSG_PKTINFO) { | 1121 | if (inet->cmsg_flags & IP_CMSG_PKTINFO) { |
1117 | struct in_pktinfo info; | 1122 | struct in_pktinfo info; |
1118 | 1123 | ||
1119 | info.ipi_addr.s_addr = inet->rcv_saddr; | 1124 | info.ipi_addr.s_addr = inet->rcv_saddr; |
1120 | info.ipi_spec_dst.s_addr = inet->rcv_saddr; | 1125 | info.ipi_spec_dst.s_addr = inet->rcv_saddr; |
1121 | info.ipi_ifindex = inet->mc_index; | 1126 | info.ipi_ifindex = inet->mc_index; |
1122 | put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info); | 1127 | put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info); |
1123 | } | ||
1124 | if (inet->cmsg_flags & IP_CMSG_TTL) { | ||
1125 | int hlim = inet->mc_ttl; | ||
1126 | put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim); | ||
1127 | } | ||
1128 | len -= msg.msg_controllen; | ||
1129 | return put_user(len, optlen); | ||
1130 | } | 1128 | } |
1131 | case IP_FREEBIND: | 1129 | if (inet->cmsg_flags & IP_CMSG_TTL) { |
1132 | val = inet->freebind; | 1130 | int hlim = inet->mc_ttl; |
1133 | break; | 1131 | put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim); |
1134 | default: | 1132 | } |
1135 | release_sock(sk); | 1133 | len -= msg.msg_controllen; |
1136 | return -ENOPROTOOPT; | 1134 | return put_user(len, optlen); |
1135 | } | ||
1136 | case IP_FREEBIND: | ||
1137 | val = inet->freebind; | ||
1138 | break; | ||
1139 | default: | ||
1140 | release_sock(sk); | ||
1141 | return -ENOPROTOOPT; | ||
1137 | } | 1142 | } |
1138 | release_sock(sk); | 1143 | release_sock(sk); |
1139 | 1144 | ||
1140 | if (len < sizeof(int) && len > 0 && val>=0 && val<255) { | 1145 | if (len < sizeof(int) && len > 0 && val>=0 && val<255) { |
1141 | unsigned char ucval = (unsigned char)val; | 1146 | unsigned char ucval = (unsigned char)val; |
1142 | len = 1; | 1147 | len = 1; |
1143 | if(put_user(len, optlen)) | 1148 | if (put_user(len, optlen)) |
1144 | return -EFAULT; | 1149 | return -EFAULT; |
1145 | if(copy_to_user(optval,&ucval,1)) | 1150 | if (copy_to_user(optval,&ucval,1)) |
1146 | return -EFAULT; | 1151 | return -EFAULT; |
1147 | } else { | 1152 | } else { |
1148 | len = min_t(unsigned int, sizeof(int), len); | 1153 | len = min_t(unsigned int, sizeof(int), len); |
1149 | if(put_user(len, optlen)) | 1154 | if (put_user(len, optlen)) |
1150 | return -EFAULT; | 1155 | return -EFAULT; |
1151 | if(copy_to_user(optval,&val,len)) | 1156 | if (copy_to_user(optval,&val,len)) |
1152 | return -EFAULT; | 1157 | return -EFAULT; |
1153 | } | 1158 | } |
1154 | return 0; | 1159 | return 0; |
1155 | } | 1160 | } |
1156 | 1161 | ||
1157 | int ip_getsockopt(struct sock *sk, int level, | 1162 | int ip_getsockopt(struct sock *sk, int level, |
1158 | int optname, char __user *optval, int __user *optlen) | 1163 | int optname, char __user *optval, int __user *optlen) |
1159 | { | 1164 | { |
1160 | int err; | 1165 | int err; |
1161 | 1166 | ||
@@ -1169,7 +1174,7 @@ int ip_getsockopt(struct sock *sk, int level, | |||
1169 | ) { | 1174 | ) { |
1170 | int len; | 1175 | int len; |
1171 | 1176 | ||
1172 | if(get_user(len,optlen)) | 1177 | if (get_user(len,optlen)) |
1173 | return -EFAULT; | 1178 | return -EFAULT; |
1174 | 1179 | ||
1175 | lock_sock(sk); | 1180 | lock_sock(sk); |