diff options
Diffstat (limited to 'net/ipv6/ndisc.c')
-rw-r--r-- | net/ipv6/ndisc.c | 362 |
1 files changed, 181 insertions, 181 deletions
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 6574175795df..76ef4353d518 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c | |||
@@ -143,16 +143,12 @@ struct neigh_table nd_tbl = { | |||
143 | .gc_thresh3 = 1024, | 143 | .gc_thresh3 = 1024, |
144 | }; | 144 | }; |
145 | 145 | ||
146 | static inline int ndisc_opt_addr_space(struct net_device *dev) | 146 | static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data) |
147 | { | 147 | { |
148 | return NDISC_OPT_SPACE(dev->addr_len + ndisc_addr_option_pad(dev->type)); | 148 | int pad = ndisc_addr_option_pad(skb->dev->type); |
149 | } | 149 | int data_len = skb->dev->addr_len; |
150 | 150 | int space = ndisc_opt_addr_space(skb->dev); | |
151 | static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data, int data_len, | 151 | u8 *opt = skb_put(skb, space); |
152 | unsigned short addr_type) | ||
153 | { | ||
154 | int pad = ndisc_addr_option_pad(addr_type); | ||
155 | int space = NDISC_OPT_SPACE(data_len + pad); | ||
156 | 152 | ||
157 | opt[0] = type; | 153 | opt[0] = type; |
158 | opt[1] = space>>3; | 154 | opt[1] = space>>3; |
@@ -166,7 +162,6 @@ static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data, int data_len, | |||
166 | opt += data_len; | 162 | opt += data_len; |
167 | if ((space -= data_len) > 0) | 163 | if ((space -= data_len) > 0) |
168 | memset(opt, 0, space); | 164 | memset(opt, 0, space); |
169 | return opt + space; | ||
170 | } | 165 | } |
171 | 166 | ||
172 | static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur, | 167 | static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur, |
@@ -370,91 +365,88 @@ static void pndisc_destructor(struct pneigh_entry *n) | |||
370 | ipv6_dev_mc_dec(dev, &maddr); | 365 | ipv6_dev_mc_dec(dev, &maddr); |
371 | } | 366 | } |
372 | 367 | ||
373 | static struct sk_buff *ndisc_build_skb(struct net_device *dev, | 368 | static struct sk_buff *ndisc_alloc_skb(struct net_device *dev, |
374 | const struct in6_addr *daddr, | 369 | int len) |
375 | const struct in6_addr *saddr, | ||
376 | struct icmp6hdr *icmp6h, | ||
377 | const struct in6_addr *target, | ||
378 | int llinfo) | ||
379 | { | 370 | { |
380 | struct net *net = dev_net(dev); | ||
381 | struct sock *sk = net->ipv6.ndisc_sk; | ||
382 | struct sk_buff *skb; | ||
383 | struct icmp6hdr *hdr; | ||
384 | int hlen = LL_RESERVED_SPACE(dev); | 371 | int hlen = LL_RESERVED_SPACE(dev); |
385 | int tlen = dev->needed_tailroom; | 372 | int tlen = dev->needed_tailroom; |
386 | int len; | 373 | struct sock *sk = dev_net(dev)->ipv6.ndisc_sk; |
374 | struct sk_buff *skb; | ||
387 | int err; | 375 | int err; |
388 | u8 *opt; | ||
389 | |||
390 | if (!dev->addr_len) | ||
391 | llinfo = 0; | ||
392 | |||
393 | len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0); | ||
394 | if (llinfo) | ||
395 | len += ndisc_opt_addr_space(dev); | ||
396 | 376 | ||
397 | skb = sock_alloc_send_skb(sk, | 377 | skb = sock_alloc_send_skb(sk, |
398 | (MAX_HEADER + sizeof(struct ipv6hdr) + | 378 | hlen + sizeof(struct ipv6hdr) + len + tlen, |
399 | len + hlen + tlen), | ||
400 | 1, &err); | 379 | 1, &err); |
401 | if (!skb) { | 380 | if (!skb) { |
402 | ND_PRINTK(0, err, "ND: %s failed to allocate an skb, err=%d\n", | 381 | ND_PRINTK(0, err, "ndisc: %s failed to allocate an skb, err=%d\n", |
403 | __func__, err); | 382 | __func__, err); |
404 | return NULL; | 383 | return NULL; |
405 | } | 384 | } |
406 | 385 | ||
407 | skb_reserve(skb, hlen); | 386 | skb->protocol = htons(ETH_P_IPV6); |
408 | ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len); | 387 | skb->dev = dev; |
409 | 388 | ||
410 | skb->transport_header = skb->tail; | 389 | skb_reserve(skb, hlen + sizeof(struct ipv6hdr)); |
411 | skb_put(skb, len); | 390 | skb_reset_transport_header(skb); |
412 | 391 | ||
413 | hdr = (struct icmp6hdr *)skb_transport_header(skb); | 392 | return skb; |
414 | memcpy(hdr, icmp6h, sizeof(*hdr)); | 393 | } |
415 | 394 | ||
416 | opt = skb_transport_header(skb) + sizeof(struct icmp6hdr); | 395 | static void ip6_nd_hdr(struct sk_buff *skb, |
417 | if (target) { | 396 | const struct in6_addr *saddr, |
418 | *(struct in6_addr *)opt = *target; | 397 | const struct in6_addr *daddr, |
419 | opt += sizeof(*target); | 398 | int hop_limit, int len) |
420 | } | 399 | { |
400 | struct ipv6hdr *hdr; | ||
421 | 401 | ||
422 | if (llinfo) | 402 | skb_push(skb, sizeof(*hdr)); |
423 | ndisc_fill_addr_option(opt, llinfo, dev->dev_addr, | 403 | skb_reset_network_header(skb); |
424 | dev->addr_len, dev->type); | 404 | hdr = ipv6_hdr(skb); |
425 | 405 | ||
426 | hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len, | 406 | ip6_flow_hdr(hdr, 0, 0); |
427 | IPPROTO_ICMPV6, | ||
428 | csum_partial(hdr, | ||
429 | len, 0)); | ||
430 | 407 | ||
431 | return skb; | 408 | hdr->payload_len = htons(len); |
409 | hdr->nexthdr = IPPROTO_ICMPV6; | ||
410 | hdr->hop_limit = hop_limit; | ||
411 | |||
412 | hdr->saddr = *saddr; | ||
413 | hdr->daddr = *daddr; | ||
432 | } | 414 | } |
433 | 415 | ||
434 | static void ndisc_send_skb(struct sk_buff *skb, struct net_device *dev, | 416 | static void ndisc_send_skb(struct sk_buff *skb, |
435 | struct neighbour *neigh, | ||
436 | const struct in6_addr *daddr, | 417 | const struct in6_addr *daddr, |
437 | const struct in6_addr *saddr, | 418 | const struct in6_addr *saddr) |
438 | struct icmp6hdr *icmp6h) | ||
439 | { | 419 | { |
440 | struct flowi6 fl6; | 420 | struct dst_entry *dst = skb_dst(skb); |
441 | struct dst_entry *dst; | 421 | struct net *net = dev_net(skb->dev); |
442 | struct net *net = dev_net(dev); | ||
443 | struct sock *sk = net->ipv6.ndisc_sk; | 422 | struct sock *sk = net->ipv6.ndisc_sk; |
444 | struct inet6_dev *idev; | 423 | struct inet6_dev *idev; |
445 | int err; | 424 | int err; |
425 | struct icmp6hdr *icmp6h = icmp6_hdr(skb); | ||
446 | u8 type; | 426 | u8 type; |
447 | 427 | ||
448 | type = icmp6h->icmp6_type; | 428 | type = icmp6h->icmp6_type; |
449 | 429 | ||
450 | icmpv6_flow_init(sk, &fl6, type, saddr, daddr, dev->ifindex); | 430 | if (!dst) { |
451 | dst = icmp6_dst_alloc(dev, neigh, &fl6); | 431 | struct sock *sk = net->ipv6.ndisc_sk; |
452 | if (IS_ERR(dst)) { | 432 | struct flowi6 fl6; |
453 | kfree_skb(skb); | 433 | |
454 | return; | 434 | icmpv6_flow_init(sk, &fl6, type, saddr, daddr, skb->dev->ifindex); |
435 | dst = icmp6_dst_alloc(skb->dev, &fl6); | ||
436 | if (IS_ERR(dst)) { | ||
437 | kfree_skb(skb); | ||
438 | return; | ||
439 | } | ||
440 | |||
441 | skb_dst_set(skb, dst); | ||
455 | } | 442 | } |
456 | 443 | ||
457 | skb_dst_set(skb, dst); | 444 | icmp6h->icmp6_cksum = csum_ipv6_magic(saddr, daddr, skb->len, |
445 | IPPROTO_ICMPV6, | ||
446 | csum_partial(icmp6h, | ||
447 | skb->len, 0)); | ||
448 | |||
449 | ip6_nd_hdr(skb, saddr, daddr, inet6_sk(sk)->hop_limit, skb->len); | ||
458 | 450 | ||
459 | rcu_read_lock(); | 451 | rcu_read_lock(); |
460 | idev = __in6_dev_get(dst->dev); | 452 | idev = __in6_dev_get(dst->dev); |
@@ -470,36 +462,17 @@ static void ndisc_send_skb(struct sk_buff *skb, struct net_device *dev, | |||
470 | rcu_read_unlock(); | 462 | rcu_read_unlock(); |
471 | } | 463 | } |
472 | 464 | ||
473 | /* | ||
474 | * Send a Neighbour Discover packet | ||
475 | */ | ||
476 | static void __ndisc_send(struct net_device *dev, | ||
477 | struct neighbour *neigh, | ||
478 | const struct in6_addr *daddr, | ||
479 | const struct in6_addr *saddr, | ||
480 | struct icmp6hdr *icmp6h, const struct in6_addr *target, | ||
481 | int llinfo) | ||
482 | { | ||
483 | struct sk_buff *skb; | ||
484 | |||
485 | skb = ndisc_build_skb(dev, daddr, saddr, icmp6h, target, llinfo); | ||
486 | if (!skb) | ||
487 | return; | ||
488 | |||
489 | ndisc_send_skb(skb, dev, neigh, daddr, saddr, icmp6h); | ||
490 | } | ||
491 | |||
492 | static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, | 465 | static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, |
493 | const struct in6_addr *daddr, | 466 | const struct in6_addr *daddr, |
494 | const struct in6_addr *solicited_addr, | 467 | const struct in6_addr *solicited_addr, |
495 | int router, int solicited, int override, int inc_opt) | 468 | bool router, bool solicited, bool override, bool inc_opt) |
496 | { | 469 | { |
470 | struct sk_buff *skb; | ||
497 | struct in6_addr tmpaddr; | 471 | struct in6_addr tmpaddr; |
498 | struct inet6_ifaddr *ifp; | 472 | struct inet6_ifaddr *ifp; |
499 | const struct in6_addr *src_addr; | 473 | const struct in6_addr *src_addr; |
500 | struct icmp6hdr icmp6h = { | 474 | struct nd_msg *msg; |
501 | .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT, | 475 | int optlen = 0; |
502 | }; | ||
503 | 476 | ||
504 | /* for anycast or proxy, solicited_addr != src_addr */ | 477 | /* for anycast or proxy, solicited_addr != src_addr */ |
505 | ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1); | 478 | ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1); |
@@ -517,13 +490,32 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, | |||
517 | src_addr = &tmpaddr; | 490 | src_addr = &tmpaddr; |
518 | } | 491 | } |
519 | 492 | ||
520 | icmp6h.icmp6_router = router; | 493 | if (!dev->addr_len) |
521 | icmp6h.icmp6_solicited = solicited; | 494 | inc_opt = 0; |
522 | icmp6h.icmp6_override = override; | 495 | if (inc_opt) |
496 | optlen += ndisc_opt_addr_space(dev); | ||
497 | |||
498 | skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen); | ||
499 | if (!skb) | ||
500 | return; | ||
523 | 501 | ||
524 | __ndisc_send(dev, neigh, daddr, src_addr, | 502 | msg = (struct nd_msg *)skb_put(skb, sizeof(*msg)); |
525 | &icmp6h, solicited_addr, | 503 | *msg = (struct nd_msg) { |
526 | inc_opt ? ND_OPT_TARGET_LL_ADDR : 0); | 504 | .icmph = { |
505 | .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT, | ||
506 | .icmp6_router = router, | ||
507 | .icmp6_solicited = solicited, | ||
508 | .icmp6_override = override, | ||
509 | }, | ||
510 | .target = *solicited_addr, | ||
511 | }; | ||
512 | |||
513 | if (inc_opt) | ||
514 | ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR, | ||
515 | dev->dev_addr); | ||
516 | |||
517 | |||
518 | ndisc_send_skb(skb, daddr, src_addr); | ||
527 | } | 519 | } |
528 | 520 | ||
529 | static void ndisc_send_unsol_na(struct net_device *dev) | 521 | static void ndisc_send_unsol_na(struct net_device *dev) |
@@ -551,10 +543,11 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, | |||
551 | const struct in6_addr *solicit, | 543 | const struct in6_addr *solicit, |
552 | const struct in6_addr *daddr, const struct in6_addr *saddr) | 544 | const struct in6_addr *daddr, const struct in6_addr *saddr) |
553 | { | 545 | { |
546 | struct sk_buff *skb; | ||
554 | struct in6_addr addr_buf; | 547 | struct in6_addr addr_buf; |
555 | struct icmp6hdr icmp6h = { | 548 | int inc_opt = dev->addr_len; |
556 | .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION, | 549 | int optlen = 0; |
557 | }; | 550 | struct nd_msg *msg; |
558 | 551 | ||
559 | if (saddr == NULL) { | 552 | if (saddr == NULL) { |
560 | if (ipv6_get_lladdr(dev, &addr_buf, | 553 | if (ipv6_get_lladdr(dev, &addr_buf, |
@@ -563,18 +556,37 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, | |||
563 | saddr = &addr_buf; | 556 | saddr = &addr_buf; |
564 | } | 557 | } |
565 | 558 | ||
566 | __ndisc_send(dev, neigh, daddr, saddr, | 559 | if (ipv6_addr_any(saddr)) |
567 | &icmp6h, solicit, | 560 | inc_opt = 0; |
568 | !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0); | 561 | if (inc_opt) |
562 | optlen += ndisc_opt_addr_space(dev); | ||
563 | |||
564 | skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen); | ||
565 | if (!skb) | ||
566 | return; | ||
567 | |||
568 | msg = (struct nd_msg *)skb_put(skb, sizeof(*msg)); | ||
569 | *msg = (struct nd_msg) { | ||
570 | .icmph = { | ||
571 | .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION, | ||
572 | }, | ||
573 | .target = *solicit, | ||
574 | }; | ||
575 | |||
576 | if (inc_opt) | ||
577 | ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR, | ||
578 | dev->dev_addr); | ||
579 | |||
580 | ndisc_send_skb(skb, daddr, saddr); | ||
569 | } | 581 | } |
570 | 582 | ||
571 | void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, | 583 | void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, |
572 | const struct in6_addr *daddr) | 584 | const struct in6_addr *daddr) |
573 | { | 585 | { |
574 | struct icmp6hdr icmp6h = { | 586 | struct sk_buff *skb; |
575 | .icmp6_type = NDISC_ROUTER_SOLICITATION, | 587 | struct rs_msg *msg; |
576 | }; | ||
577 | int send_sllao = dev->addr_len; | 588 | int send_sllao = dev->addr_len; |
589 | int optlen = 0; | ||
578 | 590 | ||
579 | #ifdef CONFIG_IPV6_OPTIMISTIC_DAD | 591 | #ifdef CONFIG_IPV6_OPTIMISTIC_DAD |
580 | /* | 592 | /* |
@@ -598,9 +610,27 @@ void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, | |||
598 | } | 610 | } |
599 | } | 611 | } |
600 | #endif | 612 | #endif |
601 | __ndisc_send(dev, NULL, daddr, saddr, | 613 | if (!dev->addr_len) |
602 | &icmp6h, NULL, | 614 | send_sllao = 0; |
603 | send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0); | 615 | if (send_sllao) |
616 | optlen += ndisc_opt_addr_space(dev); | ||
617 | |||
618 | skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen); | ||
619 | if (!skb) | ||
620 | return; | ||
621 | |||
622 | msg = (struct rs_msg *)skb_put(skb, sizeof(*msg)); | ||
623 | *msg = (struct rs_msg) { | ||
624 | .icmph = { | ||
625 | .icmp6_type = NDISC_ROUTER_SOLICITATION, | ||
626 | }, | ||
627 | }; | ||
628 | |||
629 | if (send_sllao) | ||
630 | ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR, | ||
631 | dev->dev_addr); | ||
632 | |||
633 | ndisc_send_skb(skb, daddr, saddr); | ||
604 | } | 634 | } |
605 | 635 | ||
606 | 636 | ||
@@ -676,6 +706,11 @@ static void ndisc_recv_ns(struct sk_buff *skb) | |||
676 | bool inc; | 706 | bool inc; |
677 | int is_router = -1; | 707 | int is_router = -1; |
678 | 708 | ||
709 | if (skb->len < sizeof(struct nd_msg)) { | ||
710 | ND_PRINTK(2, warn, "NS: packet too short\n"); | ||
711 | return; | ||
712 | } | ||
713 | |||
679 | if (ipv6_addr_is_multicast(&msg->target)) { | 714 | if (ipv6_addr_is_multicast(&msg->target)) { |
680 | ND_PRINTK(2, warn, "NS: multicast target address\n"); | 715 | ND_PRINTK(2, warn, "NS: multicast target address\n"); |
681 | return; | 716 | return; |
@@ -685,11 +720,7 @@ static void ndisc_recv_ns(struct sk_buff *skb) | |||
685 | * RFC2461 7.1.1: | 720 | * RFC2461 7.1.1: |
686 | * DAD has to be destined for solicited node multicast address. | 721 | * DAD has to be destined for solicited node multicast address. |
687 | */ | 722 | */ |
688 | if (dad && | 723 | if (dad && !ipv6_addr_is_solict_mult(daddr)) { |
689 | !(daddr->s6_addr32[0] == htonl(0xff020000) && | ||
690 | daddr->s6_addr32[1] == htonl(0x00000000) && | ||
691 | daddr->s6_addr32[2] == htonl(0x00000001) && | ||
692 | daddr->s6_addr [12] == 0xff )) { | ||
693 | ND_PRINTK(2, warn, "NS: bad DAD packet (wrong destination)\n"); | 724 | ND_PRINTK(2, warn, "NS: bad DAD packet (wrong destination)\n"); |
694 | return; | 725 | return; |
695 | } | 726 | } |
@@ -780,11 +811,11 @@ static void ndisc_recv_ns(struct sk_buff *skb) | |||
780 | } | 811 | } |
781 | 812 | ||
782 | if (is_router < 0) | 813 | if (is_router < 0) |
783 | is_router = !!idev->cnf.forwarding; | 814 | is_router = idev->cnf.forwarding; |
784 | 815 | ||
785 | if (dad) { | 816 | if (dad) { |
786 | ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &msg->target, | 817 | ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &msg->target, |
787 | is_router, 0, (ifp != NULL), 1); | 818 | !!is_router, false, (ifp != NULL), true); |
788 | goto out; | 819 | goto out; |
789 | } | 820 | } |
790 | 821 | ||
@@ -805,8 +836,8 @@ static void ndisc_recv_ns(struct sk_buff *skb) | |||
805 | NEIGH_UPDATE_F_OVERRIDE); | 836 | NEIGH_UPDATE_F_OVERRIDE); |
806 | if (neigh || !dev->header_ops) { | 837 | if (neigh || !dev->header_ops) { |
807 | ndisc_send_na(dev, neigh, saddr, &msg->target, | 838 | ndisc_send_na(dev, neigh, saddr, &msg->target, |
808 | is_router, | 839 | !!is_router, |
809 | 1, (ifp != NULL && inc), inc); | 840 | true, (ifp != NULL && inc), inc); |
810 | if (neigh) | 841 | if (neigh) |
811 | neigh_release(neigh); | 842 | neigh_release(neigh); |
812 | } | 843 | } |
@@ -1350,25 +1381,34 @@ static void ndisc_redirect_rcv(struct sk_buff *skb) | |||
1350 | icmpv6_notify(skb, NDISC_REDIRECT, 0, 0); | 1381 | icmpv6_notify(skb, NDISC_REDIRECT, 0, 0); |
1351 | } | 1382 | } |
1352 | 1383 | ||
1384 | static void ndisc_fill_redirect_hdr_option(struct sk_buff *skb, | ||
1385 | struct sk_buff *orig_skb, | ||
1386 | int rd_len) | ||
1387 | { | ||
1388 | u8 *opt = skb_put(skb, rd_len); | ||
1389 | |||
1390 | memset(opt, 0, 8); | ||
1391 | *(opt++) = ND_OPT_REDIRECT_HDR; | ||
1392 | *(opt++) = (rd_len >> 3); | ||
1393 | opt += 6; | ||
1394 | |||
1395 | memcpy(opt, ipv6_hdr(orig_skb), rd_len - 8); | ||
1396 | } | ||
1397 | |||
1353 | void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) | 1398 | void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) |
1354 | { | 1399 | { |
1355 | struct net_device *dev = skb->dev; | 1400 | struct net_device *dev = skb->dev; |
1356 | struct net *net = dev_net(dev); | 1401 | struct net *net = dev_net(dev); |
1357 | struct sock *sk = net->ipv6.ndisc_sk; | 1402 | struct sock *sk = net->ipv6.ndisc_sk; |
1358 | int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr); | 1403 | int optlen = 0; |
1359 | struct inet_peer *peer; | 1404 | struct inet_peer *peer; |
1360 | struct sk_buff *buff; | 1405 | struct sk_buff *buff; |
1361 | struct icmp6hdr *icmph; | 1406 | struct rd_msg *msg; |
1362 | struct in6_addr saddr_buf; | 1407 | struct in6_addr saddr_buf; |
1363 | struct in6_addr *addrp; | ||
1364 | struct rt6_info *rt; | 1408 | struct rt6_info *rt; |
1365 | struct dst_entry *dst; | 1409 | struct dst_entry *dst; |
1366 | struct inet6_dev *idev; | ||
1367 | struct flowi6 fl6; | 1410 | struct flowi6 fl6; |
1368 | u8 *opt; | ||
1369 | int hlen, tlen; | ||
1370 | int rd_len; | 1411 | int rd_len; |
1371 | int err; | ||
1372 | u8 ha_buf[MAX_ADDR_LEN], *ha = NULL; | 1412 | u8 ha_buf[MAX_ADDR_LEN], *ha = NULL; |
1373 | bool ret; | 1413 | bool ret; |
1374 | 1414 | ||
@@ -1424,7 +1464,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) | |||
1424 | memcpy(ha_buf, neigh->ha, dev->addr_len); | 1464 | memcpy(ha_buf, neigh->ha, dev->addr_len); |
1425 | read_unlock_bh(&neigh->lock); | 1465 | read_unlock_bh(&neigh->lock); |
1426 | ha = ha_buf; | 1466 | ha = ha_buf; |
1427 | len += ndisc_opt_addr_space(dev); | 1467 | optlen += ndisc_opt_addr_space(dev); |
1428 | } else | 1468 | } else |
1429 | read_unlock_bh(&neigh->lock); | 1469 | read_unlock_bh(&neigh->lock); |
1430 | 1470 | ||
@@ -1432,80 +1472,40 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) | |||
1432 | } | 1472 | } |
1433 | 1473 | ||
1434 | rd_len = min_t(unsigned int, | 1474 | rd_len = min_t(unsigned int, |
1435 | IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, skb->len + 8); | 1475 | IPV6_MIN_MTU - sizeof(struct ipv6hdr) - sizeof(*msg) - optlen, |
1476 | skb->len + 8); | ||
1436 | rd_len &= ~0x7; | 1477 | rd_len &= ~0x7; |
1437 | len += rd_len; | 1478 | optlen += rd_len; |
1438 | |||
1439 | hlen = LL_RESERVED_SPACE(dev); | ||
1440 | tlen = dev->needed_tailroom; | ||
1441 | buff = sock_alloc_send_skb(sk, | ||
1442 | (MAX_HEADER + sizeof(struct ipv6hdr) + | ||
1443 | len + hlen + tlen), | ||
1444 | 1, &err); | ||
1445 | if (buff == NULL) { | ||
1446 | ND_PRINTK(0, err, | ||
1447 | "Redirect: %s failed to allocate an skb, err=%d\n", | ||
1448 | __func__, err); | ||
1449 | goto release; | ||
1450 | } | ||
1451 | |||
1452 | skb_reserve(buff, hlen); | ||
1453 | ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr, | ||
1454 | IPPROTO_ICMPV6, len); | ||
1455 | |||
1456 | skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data); | ||
1457 | skb_put(buff, len); | ||
1458 | icmph = icmp6_hdr(buff); | ||
1459 | |||
1460 | memset(icmph, 0, sizeof(struct icmp6hdr)); | ||
1461 | icmph->icmp6_type = NDISC_REDIRECT; | ||
1462 | 1479 | ||
1463 | /* | 1480 | buff = ndisc_alloc_skb(dev, sizeof(*msg) + optlen); |
1464 | * copy target and destination addresses | 1481 | if (!buff) |
1465 | */ | 1482 | goto release; |
1466 | |||
1467 | addrp = (struct in6_addr *)(icmph + 1); | ||
1468 | *addrp = *target; | ||
1469 | addrp++; | ||
1470 | *addrp = ipv6_hdr(skb)->daddr; | ||
1471 | 1483 | ||
1472 | opt = (u8*) (addrp + 1); | 1484 | msg = (struct rd_msg *)skb_put(buff, sizeof(*msg)); |
1485 | *msg = (struct rd_msg) { | ||
1486 | .icmph = { | ||
1487 | .icmp6_type = NDISC_REDIRECT, | ||
1488 | }, | ||
1489 | .target = *target, | ||
1490 | .dest = ipv6_hdr(skb)->daddr, | ||
1491 | }; | ||
1473 | 1492 | ||
1474 | /* | 1493 | /* |
1475 | * include target_address option | 1494 | * include target_address option |
1476 | */ | 1495 | */ |
1477 | 1496 | ||
1478 | if (ha) | 1497 | if (ha) |
1479 | opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha, | 1498 | ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR, ha); |
1480 | dev->addr_len, dev->type); | ||
1481 | 1499 | ||
1482 | /* | 1500 | /* |
1483 | * build redirect option and copy skb over to the new packet. | 1501 | * build redirect option and copy skb over to the new packet. |
1484 | */ | 1502 | */ |
1485 | 1503 | ||
1486 | memset(opt, 0, 8); | 1504 | if (rd_len) |
1487 | *(opt++) = ND_OPT_REDIRECT_HDR; | 1505 | ndisc_fill_redirect_hdr_option(buff, skb, rd_len); |
1488 | *(opt++) = (rd_len >> 3); | ||
1489 | opt += 6; | ||
1490 | |||
1491 | memcpy(opt, ipv6_hdr(skb), rd_len - 8); | ||
1492 | |||
1493 | icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr, | ||
1494 | len, IPPROTO_ICMPV6, | ||
1495 | csum_partial(icmph, len, 0)); | ||
1496 | 1506 | ||
1497 | skb_dst_set(buff, dst); | 1507 | skb_dst_set(buff, dst); |
1498 | rcu_read_lock(); | 1508 | ndisc_send_skb(buff, &ipv6_hdr(skb)->saddr, &saddr_buf); |
1499 | idev = __in6_dev_get(dst->dev); | ||
1500 | IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len); | ||
1501 | err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev, | ||
1502 | dst_output); | ||
1503 | if (!err) { | ||
1504 | ICMP6MSGOUT_INC_STATS(net, idev, NDISC_REDIRECT); | ||
1505 | ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); | ||
1506 | } | ||
1507 | |||
1508 | rcu_read_unlock(); | ||
1509 | return; | 1509 | return; |
1510 | 1510 | ||
1511 | release: | 1511 | release: |
@@ -1522,7 +1522,7 @@ int ndisc_rcv(struct sk_buff *skb) | |||
1522 | { | 1522 | { |
1523 | struct nd_msg *msg; | 1523 | struct nd_msg *msg; |
1524 | 1524 | ||
1525 | if (!pskb_may_pull(skb, skb->len)) | 1525 | if (skb_linearize(skb)) |
1526 | return 0; | 1526 | return 0; |
1527 | 1527 | ||
1528 | msg = (struct nd_msg *)skb_transport_header(skb); | 1528 | msg = (struct nd_msg *)skb_transport_header(skb); |