aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/ipv6/ndisc.c283
1 files changed, 84 insertions, 199 deletions
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 4ee1216f8018..d8b36451bada 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -427,38 +427,23 @@ static inline void ndisc_flow_init(struct flowi *fl, u8 type,
427 security_sk_classify_flow(ndisc_socket->sk, fl); 427 security_sk_classify_flow(ndisc_socket->sk, fl);
428} 428}
429 429
430static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, 430static void __ndisc_send(struct net_device *dev,
431 struct in6_addr *daddr, struct in6_addr *solicited_addr, 431 struct neighbour *neigh,
432 int router, int solicited, int override, int inc_opt) 432 struct in6_addr *daddr, struct in6_addr *saddr,
433 struct icmp6hdr *icmp6h, struct in6_addr *target,
434 int llinfo, int icmp6_mib_outnd)
433{ 435{
434 struct in6_addr tmpaddr;
435 struct inet6_ifaddr *ifp;
436 struct inet6_dev *idev;
437 struct flowi fl; 436 struct flowi fl;
438 struct dst_entry* dst; 437 struct dst_entry *dst;
439 struct sock *sk = ndisc_socket->sk; 438 struct sock *sk = ndisc_socket->sk;
440 struct in6_addr *src_addr;
441 struct nd_msg *msg;
442 int len;
443 struct sk_buff *skb; 439 struct sk_buff *skb;
440 struct icmp6hdr *hdr;
441 struct inet6_dev *idev;
442 int len;
444 int err; 443 int err;
444 u8 *opt;
445 445
446 len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr); 446 ndisc_flow_init(&fl, icmp6h->icmp6_type, saddr, daddr,
447
448 /* for anycast or proxy, solicited_addr != src_addr */
449 ifp = ipv6_get_ifaddr(solicited_addr, dev, 1);
450 if (ifp) {
451 src_addr = solicited_addr;
452 if (ifp->flags & IFA_F_OPTIMISTIC)
453 override = 0;
454 in6_ifa_put(ifp);
455 } else {
456 if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr))
457 return;
458 src_addr = &tmpaddr;
459 }
460
461 ndisc_flow_init(&fl, NDISC_NEIGHBOUR_ADVERTISEMENT, src_addr, daddr,
462 dev->ifindex); 447 dev->ifindex);
463 448
464 dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output); 449 dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output);
@@ -469,61 +454,57 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
469 if (err < 0) 454 if (err < 0)
470 return; 455 return;
471 456
472 if (inc_opt) { 457 if (!dev->addr_len)
473 if (dev->addr_len) 458 llinfo = 0;
474 len += ndisc_opt_addr_space(dev); 459
475 else 460 len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0);
476 inc_opt = 0; 461 if (llinfo)
477 } 462 len += ndisc_opt_addr_space(dev);
478 463
479 skb = sock_alloc_send_skb(sk, 464 skb = sock_alloc_send_skb(sk,
480 (MAX_HEADER + sizeof(struct ipv6hdr) + 465 (MAX_HEADER + sizeof(struct ipv6hdr) +
481 len + LL_RESERVED_SPACE(dev)), 466 len + LL_RESERVED_SPACE(dev)),
482 1, &err); 467 1, &err);
483 468 if (!skb) {
484 if (skb == NULL) {
485 ND_PRINTK0(KERN_ERR 469 ND_PRINTK0(KERN_ERR
486 "ICMPv6 NA: %s() failed to allocate an skb.\n", 470 "ICMPv6 ND: %s() failed to allocate an skb.\n",
487 __FUNCTION__); 471 __FUNCTION__);
488 dst_release(dst); 472 dst_release(dst);
489 return; 473 return;
490 } 474 }
491 475
492 skb_reserve(skb, LL_RESERVED_SPACE(dev)); 476 skb_reserve(skb, LL_RESERVED_SPACE(dev));
493 ip6_nd_hdr(sk, skb, dev, src_addr, daddr, IPPROTO_ICMPV6, len); 477 ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
494 478
495 skb->transport_header = skb->tail; 479 skb->transport_header = skb->tail;
496 skb_put(skb, len); 480 skb_put(skb, len);
497 msg = (struct nd_msg *)skb_transport_header(skb);
498 481
499 msg->icmph.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT; 482 hdr = (struct icmp6hdr *)skb_transport_header(skb);
500 msg->icmph.icmp6_code = 0; 483 memcpy(hdr, icmp6h, sizeof(*hdr));
501 msg->icmph.icmp6_cksum = 0;
502 484
503 msg->icmph.icmp6_unused = 0; 485 opt = skb_transport_header(skb) + sizeof(struct icmp6hdr);
504 msg->icmph.icmp6_router = router; 486 if (target) {
505 msg->icmph.icmp6_solicited = solicited; 487 ipv6_addr_copy((struct in6_addr *)opt, target);
506 msg->icmph.icmp6_override = override; 488 opt += sizeof(*target);
507 489 }
508 /* Set the target address. */
509 ipv6_addr_copy(&msg->target, solicited_addr);
510 490
511 if (inc_opt) 491 if (llinfo)
512 ndisc_fill_addr_option(msg->opt, ND_OPT_TARGET_LL_ADDR, dev->dev_addr, 492 ndisc_fill_addr_option(opt, llinfo, dev->dev_addr,
513 dev->addr_len, dev->type); 493 dev->addr_len, dev->type);
514 494
515 /* checksum */ 495 hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len,
516 msg->icmph.icmp6_cksum = csum_ipv6_magic(src_addr, daddr, len, 496 IPPROTO_ICMPV6,
517 IPPROTO_ICMPV6, 497 csum_partial((__u8 *) hdr,
518 csum_partial((__u8 *) msg, 498 len, 0));
519 len, 0));
520 499
521 skb->dst = dst; 500 skb->dst = dst;
501
522 idev = in6_dev_get(dst->dev); 502 idev = in6_dev_get(dst->dev);
523 IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS); 503 IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
504
524 err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output); 505 err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output);
525 if (!err) { 506 if (!err) {
526 ICMP6_INC_STATS(idev, ICMP6_MIB_OUTNEIGHBORADVERTISEMENTS); 507 ICMP6_INC_STATS(idev, icmp6_mib_outnd);
527 ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS); 508 ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS);
528 } 509 }
529 510
@@ -531,20 +512,48 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
531 in6_dev_put(idev); 512 in6_dev_put(idev);
532} 513}
533 514
515static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
516 struct in6_addr *daddr, struct in6_addr *solicited_addr,
517 int router, int solicited, int override, int inc_opt)
518{
519 struct in6_addr tmpaddr;
520 struct inet6_ifaddr *ifp;
521 struct in6_addr *src_addr;
522 struct icmp6hdr icmp6h = {
523 .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
524 };
525
526 /* for anycast or proxy, solicited_addr != src_addr */
527 ifp = ipv6_get_ifaddr(solicited_addr, dev, 1);
528 if (ifp) {
529 src_addr = solicited_addr;
530 if (ifp->flags & IFA_F_OPTIMISTIC)
531 override = 0;
532 in6_ifa_put(ifp);
533 } else {
534 if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr))
535 return;
536 src_addr = &tmpaddr;
537 }
538
539 icmp6h.icmp6_router = router;
540 icmp6h.icmp6_solicited = solicited;
541 icmp6h.icmp6_override = override;
542
543 __ndisc_send(dev, neigh, daddr, src_addr,
544 &icmp6h, solicited_addr,
545 inc_opt ? ND_OPT_TARGET_LL_ADDR : 0,
546 ICMP6_MIB_OUTNEIGHBORADVERTISEMENTS);
547}
548
534void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, 549void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
535 struct in6_addr *solicit, 550 struct in6_addr *solicit,
536 struct in6_addr *daddr, struct in6_addr *saddr) 551 struct in6_addr *daddr, struct in6_addr *saddr)
537{ 552{
538 struct flowi fl;
539 struct dst_entry* dst;
540 struct inet6_dev *idev;
541 struct sock *sk = ndisc_socket->sk;
542 struct sk_buff *skb;
543 struct nd_msg *msg;
544 struct in6_addr addr_buf; 553 struct in6_addr addr_buf;
545 int len; 554 struct icmp6hdr icmp6h = {
546 int err; 555 .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
547 int send_llinfo; 556 };
548 557
549 if (saddr == NULL) { 558 if (saddr == NULL) {
550 if (ipv6_get_lladdr(dev, &addr_buf, 559 if (ipv6_get_lladdr(dev, &addr_buf,
@@ -553,86 +562,19 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
553 saddr = &addr_buf; 562 saddr = &addr_buf;
554 } 563 }
555 564
556 ndisc_flow_init(&fl, NDISC_NEIGHBOUR_SOLICITATION, saddr, daddr, 565 __ndisc_send(dev, neigh, daddr, saddr,
557 dev->ifindex); 566 &icmp6h, solicit,
558 567 !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0,
559 dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output); 568 ICMP6_MIB_OUTNEIGHBORSOLICITS);
560 if (!dst)
561 return;
562
563 err = xfrm_lookup(&dst, &fl, NULL, 0);
564 if (err < 0)
565 return;
566
567 len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
568 send_llinfo = dev->addr_len && !ipv6_addr_any(saddr);
569 if (send_llinfo)
570 len += ndisc_opt_addr_space(dev);
571
572 skb = sock_alloc_send_skb(sk,
573 (MAX_HEADER + sizeof(struct ipv6hdr) +
574 len + LL_RESERVED_SPACE(dev)),
575 1, &err);
576 if (skb == NULL) {
577 ND_PRINTK0(KERN_ERR
578 "ICMPv6 NA: %s() failed to allocate an skb.\n",
579 __FUNCTION__);
580 dst_release(dst);
581 return;
582 }
583
584 skb_reserve(skb, LL_RESERVED_SPACE(dev));
585 ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
586
587 skb->transport_header = skb->tail;
588 skb_put(skb, len);
589 msg = (struct nd_msg *)skb_transport_header(skb);
590 msg->icmph.icmp6_type = NDISC_NEIGHBOUR_SOLICITATION;
591 msg->icmph.icmp6_code = 0;
592 msg->icmph.icmp6_cksum = 0;
593 msg->icmph.icmp6_unused = 0;
594
595 /* Set the target address. */
596 ipv6_addr_copy(&msg->target, solicit);
597
598 if (send_llinfo)
599 ndisc_fill_addr_option(msg->opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr,
600 dev->addr_len, dev->type);
601
602 /* checksum */
603 msg->icmph.icmp6_cksum = csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
604 daddr, len,
605 IPPROTO_ICMPV6,
606 csum_partial((__u8 *) msg,
607 len, 0));
608 /* send it! */
609 skb->dst = dst;
610 idev = in6_dev_get(dst->dev);
611 IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
612 err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output);
613 if (!err) {
614 ICMP6_INC_STATS(idev, ICMP6_MIB_OUTNEIGHBORSOLICITS);
615 ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS);
616 }
617
618 if (likely(idev != NULL))
619 in6_dev_put(idev);
620} 569}
621 570
622void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr, 571void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
623 struct in6_addr *daddr) 572 struct in6_addr *daddr)
624{ 573{
625 struct flowi fl; 574 struct icmp6hdr icmp6h = {
626 struct dst_entry* dst; 575 .icmp6_type = NDISC_ROUTER_SOLICITATION,
627 struct inet6_dev *idev; 576 };
628 struct sock *sk = ndisc_socket->sk;
629 struct sk_buff *skb;
630 struct icmp6hdr *hdr;
631 __u8 * opt;
632 int send_sllao = dev->addr_len; 577 int send_sllao = dev->addr_len;
633 int len;
634 int err;
635
636 578
637#ifdef CONFIG_IPV6_OPTIMISTIC_DAD 579#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
638 /* 580 /*
@@ -655,67 +597,10 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
655 } 597 }
656 } 598 }
657#endif 599#endif
658 ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr, 600 __ndisc_send(dev, NULL, daddr, saddr,
659 dev->ifindex); 601 &icmp6h, NULL,
660 602 send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0,
661 dst = ndisc_dst_alloc(dev, NULL, daddr, ip6_output); 603 ICMP6_MIB_OUTROUTERSOLICITS);
662 if (!dst)
663 return;
664
665 err = xfrm_lookup(&dst, &fl, NULL, 0);
666 if (err < 0)
667 return;
668
669 len = sizeof(struct icmp6hdr);
670 if (send_sllao)
671 len += ndisc_opt_addr_space(dev);
672
673 skb = sock_alloc_send_skb(sk,
674 (MAX_HEADER + sizeof(struct ipv6hdr) +
675 len + LL_RESERVED_SPACE(dev)),
676 1, &err);
677 if (skb == NULL) {
678 ND_PRINTK0(KERN_ERR
679 "ICMPv6 RS: %s() failed to allocate an skb.\n",
680 __FUNCTION__);
681 dst_release(dst);
682 return;
683 }
684
685 skb_reserve(skb, LL_RESERVED_SPACE(dev));
686 ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
687
688 skb->transport_header = skb->tail;
689 skb_put(skb, len);
690 hdr = icmp6_hdr(skb);
691 hdr->icmp6_type = NDISC_ROUTER_SOLICITATION;
692 hdr->icmp6_code = 0;
693 hdr->icmp6_cksum = 0;
694 hdr->icmp6_unused = 0;
695
696 opt = (u8*) (hdr + 1);
697
698 if (send_sllao)
699 ndisc_fill_addr_option(opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr,
700 dev->addr_len, dev->type);
701
702 /* checksum */
703 hdr->icmp6_cksum = csum_ipv6_magic(&ipv6_hdr(skb)->saddr, daddr, len,
704 IPPROTO_ICMPV6,
705 csum_partial((__u8 *) hdr, len, 0));
706
707 /* send it! */
708 skb->dst = dst;
709 idev = in6_dev_get(dst->dev);
710 IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
711 err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output);
712 if (!err) {
713 ICMP6_INC_STATS(idev, ICMP6_MIB_OUTROUTERSOLICITS);
714 ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS);
715 }
716
717 if (likely(idev != NULL))
718 in6_dev_put(idev);
719} 604}
720 605
721 606