diff options
Diffstat (limited to 'net/ipv6/ndisc.c')
-rw-r--r-- | net/ipv6/ndisc.c | 92 |
1 files changed, 65 insertions, 27 deletions
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 2a6752dae09d..fbf451c0d77a 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c | |||
@@ -437,38 +437,20 @@ static void pndisc_destructor(struct pneigh_entry *n) | |||
437 | ipv6_dev_mc_dec(dev, &maddr); | 437 | ipv6_dev_mc_dec(dev, &maddr); |
438 | } | 438 | } |
439 | 439 | ||
440 | /* | 440 | struct sk_buff *ndisc_build_skb(struct net_device *dev, |
441 | * Send a Neighbour Advertisement | 441 | const struct in6_addr *daddr, |
442 | */ | 442 | const struct in6_addr *saddr, |
443 | static void __ndisc_send(struct net_device *dev, | 443 | struct icmp6hdr *icmp6h, |
444 | struct neighbour *neigh, | 444 | const struct in6_addr *target, |
445 | const struct in6_addr *daddr, | 445 | int llinfo) |
446 | const struct in6_addr *saddr, | ||
447 | struct icmp6hdr *icmp6h, const struct in6_addr *target, | ||
448 | int llinfo) | ||
449 | { | 446 | { |
450 | struct flowi fl; | ||
451 | struct dst_entry *dst; | ||
452 | struct net *net = dev_net(dev); | 447 | struct net *net = dev_net(dev); |
453 | struct sock *sk = net->ipv6.ndisc_sk; | 448 | struct sock *sk = net->ipv6.ndisc_sk; |
454 | struct sk_buff *skb; | 449 | struct sk_buff *skb; |
455 | struct icmp6hdr *hdr; | 450 | struct icmp6hdr *hdr; |
456 | struct inet6_dev *idev; | ||
457 | int len; | 451 | int len; |
458 | int err; | 452 | int err; |
459 | u8 *opt, type; | 453 | u8 *opt; |
460 | |||
461 | type = icmp6h->icmp6_type; | ||
462 | |||
463 | icmpv6_flow_init(sk, &fl, type, saddr, daddr, dev->ifindex); | ||
464 | |||
465 | dst = icmp6_dst_alloc(dev, neigh, daddr); | ||
466 | if (!dst) | ||
467 | return; | ||
468 | |||
469 | err = xfrm_lookup(&dst, &fl, NULL, 0); | ||
470 | if (err < 0) | ||
471 | return; | ||
472 | 454 | ||
473 | if (!dev->addr_len) | 455 | if (!dev->addr_len) |
474 | llinfo = 0; | 456 | llinfo = 0; |
@@ -485,8 +467,7 @@ static void __ndisc_send(struct net_device *dev, | |||
485 | ND_PRINTK0(KERN_ERR | 467 | ND_PRINTK0(KERN_ERR |
486 | "ICMPv6 ND: %s() failed to allocate an skb.\n", | 468 | "ICMPv6 ND: %s() failed to allocate an skb.\n", |
487 | __func__); | 469 | __func__); |
488 | dst_release(dst); | 470 | return NULL; |
489 | return; | ||
490 | } | 471 | } |
491 | 472 | ||
492 | skb_reserve(skb, LL_RESERVED_SPACE(dev)); | 473 | skb_reserve(skb, LL_RESERVED_SPACE(dev)); |
@@ -513,6 +494,42 @@ static void __ndisc_send(struct net_device *dev, | |||
513 | csum_partial((__u8 *) hdr, | 494 | csum_partial((__u8 *) hdr, |
514 | len, 0)); | 495 | len, 0)); |
515 | 496 | ||
497 | return skb; | ||
498 | } | ||
499 | |||
500 | EXPORT_SYMBOL(ndisc_build_skb); | ||
501 | |||
502 | void ndisc_send_skb(struct sk_buff *skb, | ||
503 | struct net_device *dev, | ||
504 | struct neighbour *neigh, | ||
505 | const struct in6_addr *daddr, | ||
506 | const struct in6_addr *saddr, | ||
507 | struct icmp6hdr *icmp6h) | ||
508 | { | ||
509 | struct flowi fl; | ||
510 | struct dst_entry *dst; | ||
511 | struct net *net = dev_net(dev); | ||
512 | struct sock *sk = net->ipv6.ndisc_sk; | ||
513 | struct inet6_dev *idev; | ||
514 | int err; | ||
515 | u8 type; | ||
516 | |||
517 | type = icmp6h->icmp6_type; | ||
518 | |||
519 | icmpv6_flow_init(sk, &fl, type, saddr, daddr, dev->ifindex); | ||
520 | |||
521 | dst = icmp6_dst_alloc(dev, neigh, daddr); | ||
522 | if (!dst) { | ||
523 | kfree_skb(skb); | ||
524 | return; | ||
525 | } | ||
526 | |||
527 | err = xfrm_lookup(&dst, &fl, NULL, 0); | ||
528 | if (err < 0) { | ||
529 | kfree_skb(skb); | ||
530 | return; | ||
531 | } | ||
532 | |||
516 | skb->dst = dst; | 533 | skb->dst = dst; |
517 | 534 | ||
518 | idev = in6_dev_get(dst->dev); | 535 | idev = in6_dev_get(dst->dev); |
@@ -529,6 +546,27 @@ static void __ndisc_send(struct net_device *dev, | |||
529 | in6_dev_put(idev); | 546 | in6_dev_put(idev); |
530 | } | 547 | } |
531 | 548 | ||
549 | EXPORT_SYMBOL(ndisc_send_skb); | ||
550 | |||
551 | /* | ||
552 | * Send a Neighbour Discover packet | ||
553 | */ | ||
554 | static void __ndisc_send(struct net_device *dev, | ||
555 | struct neighbour *neigh, | ||
556 | const struct in6_addr *daddr, | ||
557 | const struct in6_addr *saddr, | ||
558 | struct icmp6hdr *icmp6h, const struct in6_addr *target, | ||
559 | int llinfo) | ||
560 | { | ||
561 | struct sk_buff *skb; | ||
562 | |||
563 | skb = ndisc_build_skb(dev, daddr, saddr, icmp6h, target, llinfo); | ||
564 | if (!skb) | ||
565 | return; | ||
566 | |||
567 | ndisc_send_skb(skb, dev, neigh, daddr, saddr, icmp6h); | ||
568 | } | ||
569 | |||
532 | static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, | 570 | static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, |
533 | const struct in6_addr *daddr, | 571 | const struct in6_addr *daddr, |
534 | const struct in6_addr *solicited_addr, | 572 | const struct in6_addr *solicited_addr, |