diff options
Diffstat (limited to 'net/ipv6/ip6_tunnel.c')
-rw-r--r-- | net/ipv6/ip6_tunnel.c | 296 |
1 files changed, 148 insertions, 148 deletions
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index b9f40290d12a..8d918348f5bb 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c | |||
@@ -66,7 +66,7 @@ MODULE_LICENSE("GPL"); | |||
66 | 66 | ||
67 | #define HASH_SIZE 32 | 67 | #define HASH_SIZE 32 |
68 | 68 | ||
69 | #define HASH(addr) (((addr)->s6_addr32[0] ^ (addr)->s6_addr32[1] ^ \ | 69 | #define HASH(addr) ((__force u32)((addr)->s6_addr32[0] ^ (addr)->s6_addr32[1] ^ \ |
70 | (addr)->s6_addr32[2] ^ (addr)->s6_addr32[3]) & \ | 70 | (addr)->s6_addr32[2] ^ (addr)->s6_addr32[3]) & \ |
71 | (HASH_SIZE - 1)) | 71 | (HASH_SIZE - 1)) |
72 | 72 | ||
@@ -215,11 +215,10 @@ ip6ip6_tnl_unlink(struct ip6_tnl *t) | |||
215 | * Create tunnel matching given parameters. | 215 | * Create tunnel matching given parameters. |
216 | * | 216 | * |
217 | * Return: | 217 | * Return: |
218 | * 0 on success | 218 | * created tunnel or NULL |
219 | **/ | 219 | **/ |
220 | 220 | ||
221 | static int | 221 | static struct ip6_tnl *ip6_tnl_create(struct ip6_tnl_parm *p) |
222 | ip6_tnl_create(struct ip6_tnl_parm *p, struct ip6_tnl **pt) | ||
223 | { | 222 | { |
224 | struct net_device *dev; | 223 | struct net_device *dev; |
225 | struct ip6_tnl *t; | 224 | struct ip6_tnl *t; |
@@ -236,11 +235,11 @@ ip6_tnl_create(struct ip6_tnl_parm *p, struct ip6_tnl **pt) | |||
236 | break; | 235 | break; |
237 | } | 236 | } |
238 | if (i == IP6_TNL_MAX) | 237 | if (i == IP6_TNL_MAX) |
239 | return -ENOBUFS; | 238 | goto failed; |
240 | } | 239 | } |
241 | dev = alloc_netdev(sizeof (*t), name, ip6ip6_tnl_dev_setup); | 240 | dev = alloc_netdev(sizeof (*t), name, ip6ip6_tnl_dev_setup); |
242 | if (dev == NULL) | 241 | if (dev == NULL) |
243 | return -ENOMEM; | 242 | goto failed; |
244 | 243 | ||
245 | t = netdev_priv(dev); | 244 | t = netdev_priv(dev); |
246 | dev->init = ip6ip6_tnl_dev_init; | 245 | dev->init = ip6ip6_tnl_dev_init; |
@@ -248,13 +247,13 @@ ip6_tnl_create(struct ip6_tnl_parm *p, struct ip6_tnl **pt) | |||
248 | 247 | ||
249 | if ((err = register_netdevice(dev)) < 0) { | 248 | if ((err = register_netdevice(dev)) < 0) { |
250 | free_netdev(dev); | 249 | free_netdev(dev); |
251 | return err; | 250 | goto failed; |
252 | } | 251 | } |
253 | dev_hold(dev); | 252 | dev_hold(dev); |
254 | |||
255 | ip6ip6_tnl_link(t); | 253 | ip6ip6_tnl_link(t); |
256 | *pt = t; | 254 | return t; |
257 | return 0; | 255 | failed: |
256 | return NULL; | ||
258 | } | 257 | } |
259 | 258 | ||
260 | /** | 259 | /** |
@@ -268,32 +267,23 @@ ip6_tnl_create(struct ip6_tnl_parm *p, struct ip6_tnl **pt) | |||
268 | * tunnel device is created and registered for use. | 267 | * tunnel device is created and registered for use. |
269 | * | 268 | * |
270 | * Return: | 269 | * Return: |
271 | * 0 if tunnel located or created, | 270 | * matching tunnel or NULL |
272 | * -EINVAL if parameters incorrect, | ||
273 | * -ENODEV if no matching tunnel available | ||
274 | **/ | 271 | **/ |
275 | 272 | ||
276 | static int | 273 | static struct ip6_tnl *ip6ip6_tnl_locate(struct ip6_tnl_parm *p, int create) |
277 | ip6ip6_tnl_locate(struct ip6_tnl_parm *p, struct ip6_tnl **pt, int create) | ||
278 | { | 274 | { |
279 | struct in6_addr *remote = &p->raddr; | 275 | struct in6_addr *remote = &p->raddr; |
280 | struct in6_addr *local = &p->laddr; | 276 | struct in6_addr *local = &p->laddr; |
281 | struct ip6_tnl *t; | 277 | struct ip6_tnl *t; |
282 | 278 | ||
283 | if (p->proto != IPPROTO_IPV6) | ||
284 | return -EINVAL; | ||
285 | |||
286 | for (t = *ip6ip6_bucket(p); t; t = t->next) { | 279 | for (t = *ip6ip6_bucket(p); t; t = t->next) { |
287 | if (ipv6_addr_equal(local, &t->parms.laddr) && | 280 | if (ipv6_addr_equal(local, &t->parms.laddr) && |
288 | ipv6_addr_equal(remote, &t->parms.raddr)) { | 281 | ipv6_addr_equal(remote, &t->parms.raddr)) |
289 | *pt = t; | 282 | return t; |
290 | return (create ? -EEXIST : 0); | ||
291 | } | ||
292 | } | 283 | } |
293 | if (!create) | 284 | if (!create) |
294 | return -ENODEV; | 285 | return NULL; |
295 | 286 | return ip6_tnl_create(p); | |
296 | return ip6_tnl_create(p, pt); | ||
297 | } | 287 | } |
298 | 288 | ||
299 | /** | 289 | /** |
@@ -391,7 +381,7 @@ parse_tlv_tnl_enc_lim(struct sk_buff *skb, __u8 * raw) | |||
391 | 381 | ||
392 | static int | 382 | static int |
393 | ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | 383 | ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, |
394 | int type, int code, int offset, __u32 info) | 384 | int type, int code, int offset, __be32 info) |
395 | { | 385 | { |
396 | struct ipv6hdr *ipv6h = (struct ipv6hdr *) skb->data; | 386 | struct ipv6hdr *ipv6h = (struct ipv6hdr *) skb->data; |
397 | struct ip6_tnl *t; | 387 | struct ip6_tnl *t; |
@@ -434,12 +424,9 @@ ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | |||
434 | } | 424 | } |
435 | break; | 425 | break; |
436 | case ICMPV6_PARAMPROB: | 426 | case ICMPV6_PARAMPROB: |
437 | /* ignore if parameter problem not caused by a tunnel | 427 | teli = 0; |
438 | encapsulation limit sub-option */ | 428 | if (code == ICMPV6_HDR_FIELD) |
439 | if (code != ICMPV6_HDR_FIELD) { | 429 | teli = parse_tlv_tnl_enc_lim(skb, skb->data); |
440 | break; | ||
441 | } | ||
442 | teli = parse_tlv_tnl_enc_lim(skb, skb->data); | ||
443 | 430 | ||
444 | if (teli && teli == ntohl(info) - 2) { | 431 | if (teli && teli == ntohl(info) - 2) { |
445 | tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->data[teli]; | 432 | tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->data[teli]; |
@@ -451,6 +438,10 @@ ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | |||
451 | "tunnel!\n", t->parms.name); | 438 | "tunnel!\n", t->parms.name); |
452 | rel_msg = 1; | 439 | rel_msg = 1; |
453 | } | 440 | } |
441 | } else if (net_ratelimit()) { | ||
442 | printk(KERN_WARNING | ||
443 | "%s: Recipient unable to parse tunneled " | ||
444 | "packet!\n ", t->parms.name); | ||
454 | } | 445 | } |
455 | break; | 446 | break; |
456 | case ICMPV6_PKT_TOOBIG: | 447 | case ICMPV6_PKT_TOOBIG: |
@@ -470,6 +461,7 @@ ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | |||
470 | if (rel_msg && pskb_may_pull(skb, offset + sizeof (*ipv6h))) { | 461 | if (rel_msg && pskb_may_pull(skb, offset + sizeof (*ipv6h))) { |
471 | struct rt6_info *rt; | 462 | struct rt6_info *rt; |
472 | struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); | 463 | struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); |
464 | |||
473 | if (!skb2) | 465 | if (!skb2) |
474 | goto out; | 466 | goto out; |
475 | 467 | ||
@@ -504,6 +496,27 @@ static inline void ip6ip6_ecn_decapsulate(struct ipv6hdr *outer_iph, | |||
504 | if (INET_ECN_is_ce(ipv6_get_dsfield(outer_iph))) | 496 | if (INET_ECN_is_ce(ipv6_get_dsfield(outer_iph))) |
505 | IP6_ECN_set_ce(inner_iph); | 497 | IP6_ECN_set_ce(inner_iph); |
506 | } | 498 | } |
499 | static inline int ip6_tnl_rcv_ctl(struct ip6_tnl *t) | ||
500 | { | ||
501 | struct ip6_tnl_parm *p = &t->parms; | ||
502 | int ret = 0; | ||
503 | |||
504 | if (p->flags & IP6_TNL_F_CAP_RCV) { | ||
505 | struct net_device *ldev = NULL; | ||
506 | |||
507 | if (p->link) | ||
508 | ldev = dev_get_by_index(p->link); | ||
509 | |||
510 | if ((ipv6_addr_is_multicast(&p->laddr) || | ||
511 | likely(ipv6_chk_addr(&p->laddr, ldev, 0))) && | ||
512 | likely(!ipv6_chk_addr(&p->raddr, NULL, 0))) | ||
513 | ret = 1; | ||
514 | |||
515 | if (ldev) | ||
516 | dev_put(ldev); | ||
517 | } | ||
518 | return ret; | ||
519 | } | ||
507 | 520 | ||
508 | /** | 521 | /** |
509 | * ip6ip6_rcv - decapsulate IPv6 packet and retransmit it locally | 522 | * ip6ip6_rcv - decapsulate IPv6 packet and retransmit it locally |
@@ -528,7 +541,7 @@ ip6ip6_rcv(struct sk_buff *skb) | |||
528 | goto discard; | 541 | goto discard; |
529 | } | 542 | } |
530 | 543 | ||
531 | if (!(t->parms.flags & IP6_TNL_F_CAP_RCV)) { | 544 | if (!ip6_tnl_rcv_ctl(t)) { |
532 | t->stat.rx_dropped++; | 545 | t->stat.rx_dropped++; |
533 | read_unlock(&ip6ip6_lock); | 546 | read_unlock(&ip6ip6_lock); |
534 | goto discard; | 547 | goto discard; |
@@ -560,31 +573,23 @@ discard: | |||
560 | return 0; | 573 | return 0; |
561 | } | 574 | } |
562 | 575 | ||
563 | static inline struct ipv6_txoptions *create_tel(__u8 encap_limit) | 576 | struct ipv6_tel_txoption { |
564 | { | 577 | struct ipv6_txoptions ops; |
565 | struct ipv6_tlv_tnl_enc_lim *tel; | 578 | __u8 dst_opt[8]; |
566 | struct ipv6_txoptions *opt; | 579 | }; |
567 | __u8 *raw; | ||
568 | |||
569 | int opt_len = sizeof(*opt) + 8; | ||
570 | |||
571 | if (!(opt = kzalloc(opt_len, GFP_ATOMIC))) { | ||
572 | return NULL; | ||
573 | } | ||
574 | opt->tot_len = opt_len; | ||
575 | opt->dst0opt = (struct ipv6_opt_hdr *) (opt + 1); | ||
576 | opt->opt_nflen = 8; | ||
577 | 580 | ||
578 | tel = (struct ipv6_tlv_tnl_enc_lim *) (opt->dst0opt + 1); | 581 | static void init_tel_txopt(struct ipv6_tel_txoption *opt, __u8 encap_limit) |
579 | tel->type = IPV6_TLV_TNL_ENCAP_LIMIT; | 582 | { |
580 | tel->length = 1; | 583 | memset(opt, 0, sizeof(struct ipv6_tel_txoption)); |
581 | tel->encap_limit = encap_limit; | ||
582 | 584 | ||
583 | raw = (__u8 *) opt->dst0opt; | 585 | opt->dst_opt[2] = IPV6_TLV_TNL_ENCAP_LIMIT; |
584 | raw[5] = IPV6_TLV_PADN; | 586 | opt->dst_opt[3] = 1; |
585 | raw[6] = 1; | 587 | opt->dst_opt[4] = encap_limit; |
588 | opt->dst_opt[5] = IPV6_TLV_PADN; | ||
589 | opt->dst_opt[6] = 1; | ||
586 | 590 | ||
587 | return opt; | 591 | opt->ops.dst0opt = (struct ipv6_opt_hdr *) opt->dst_opt; |
592 | opt->ops.opt_nflen = 8; | ||
588 | } | 593 | } |
589 | 594 | ||
590 | /** | 595 | /** |
@@ -607,6 +612,34 @@ ip6ip6_tnl_addr_conflict(struct ip6_tnl *t, struct ipv6hdr *hdr) | |||
607 | return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr); | 612 | return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr); |
608 | } | 613 | } |
609 | 614 | ||
615 | static inline int ip6_tnl_xmit_ctl(struct ip6_tnl *t) | ||
616 | { | ||
617 | struct ip6_tnl_parm *p = &t->parms; | ||
618 | int ret = 0; | ||
619 | |||
620 | if (p->flags & IP6_TNL_F_CAP_XMIT) { | ||
621 | struct net_device *ldev = NULL; | ||
622 | |||
623 | if (p->link) | ||
624 | ldev = dev_get_by_index(p->link); | ||
625 | |||
626 | if (unlikely(!ipv6_chk_addr(&p->laddr, ldev, 0))) | ||
627 | printk(KERN_WARNING | ||
628 | "%s xmit: Local address not yet configured!\n", | ||
629 | p->name); | ||
630 | else if (!ipv6_addr_is_multicast(&p->raddr) && | ||
631 | unlikely(ipv6_chk_addr(&p->raddr, NULL, 0))) | ||
632 | printk(KERN_WARNING | ||
633 | "%s xmit: Routing loop! " | ||
634 | "Remote address found on this node!\n", | ||
635 | p->name); | ||
636 | else | ||
637 | ret = 1; | ||
638 | if (ldev) | ||
639 | dev_put(ldev); | ||
640 | } | ||
641 | return ret; | ||
642 | } | ||
610 | /** | 643 | /** |
611 | * ip6ip6_tnl_xmit - encapsulate packet and send | 644 | * ip6ip6_tnl_xmit - encapsulate packet and send |
612 | * @skb: the outgoing socket buffer | 645 | * @skb: the outgoing socket buffer |
@@ -626,8 +659,8 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) | |||
626 | struct ip6_tnl *t = netdev_priv(dev); | 659 | struct ip6_tnl *t = netdev_priv(dev); |
627 | struct net_device_stats *stats = &t->stat; | 660 | struct net_device_stats *stats = &t->stat; |
628 | struct ipv6hdr *ipv6h = skb->nh.ipv6h; | 661 | struct ipv6hdr *ipv6h = skb->nh.ipv6h; |
629 | struct ipv6_txoptions *opt = NULL; | ||
630 | int encap_limit = -1; | 662 | int encap_limit = -1; |
663 | struct ipv6_tel_txoption opt; | ||
631 | __u16 offset; | 664 | __u16 offset; |
632 | struct flowi fl; | 665 | struct flowi fl; |
633 | struct dst_entry *dst; | 666 | struct dst_entry *dst; |
@@ -644,10 +677,9 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) | |||
644 | goto tx_err; | 677 | goto tx_err; |
645 | } | 678 | } |
646 | if (skb->protocol != htons(ETH_P_IPV6) || | 679 | if (skb->protocol != htons(ETH_P_IPV6) || |
647 | !(t->parms.flags & IP6_TNL_F_CAP_XMIT) || | 680 | !ip6_tnl_xmit_ctl(t) || ip6ip6_tnl_addr_conflict(t, ipv6h)) |
648 | ip6ip6_tnl_addr_conflict(t, ipv6h)) { | ||
649 | goto tx_err; | 681 | goto tx_err; |
650 | } | 682 | |
651 | if ((offset = parse_tlv_tnl_enc_lim(skb, skb->nh.raw)) > 0) { | 683 | if ((offset = parse_tlv_tnl_enc_lim(skb, skb->nh.raw)) > 0) { |
652 | struct ipv6_tlv_tnl_enc_lim *tel; | 684 | struct ipv6_tlv_tnl_enc_lim *tel; |
653 | tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->nh.raw[offset]; | 685 | tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->nh.raw[offset]; |
@@ -657,20 +689,17 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) | |||
657 | goto tx_err; | 689 | goto tx_err; |
658 | } | 690 | } |
659 | encap_limit = tel->encap_limit - 1; | 691 | encap_limit = tel->encap_limit - 1; |
660 | } else if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) { | 692 | } else if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) |
661 | encap_limit = t->parms.encap_limit; | 693 | encap_limit = t->parms.encap_limit; |
662 | } | 694 | |
663 | memcpy(&fl, &t->fl, sizeof (fl)); | 695 | memcpy(&fl, &t->fl, sizeof (fl)); |
664 | proto = fl.proto; | 696 | proto = fl.proto; |
665 | 697 | ||
666 | dsfield = ipv6_get_dsfield(ipv6h); | 698 | dsfield = ipv6_get_dsfield(ipv6h); |
667 | if ((t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)) | 699 | if ((t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)) |
668 | fl.fl6_flowlabel |= (*(__u32 *) ipv6h & IPV6_TCLASS_MASK); | 700 | fl.fl6_flowlabel |= (*(__be32 *) ipv6h & IPV6_TCLASS_MASK); |
669 | if ((t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)) | 701 | if ((t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)) |
670 | fl.fl6_flowlabel |= (*(__u32 *) ipv6h & IPV6_FLOWLABEL_MASK); | 702 | fl.fl6_flowlabel |= (*(__be32 *) ipv6h & IPV6_FLOWLABEL_MASK); |
671 | |||
672 | if (encap_limit >= 0 && (opt = create_tel(encap_limit)) == NULL) | ||
673 | goto tx_err; | ||
674 | 703 | ||
675 | if ((dst = ip6_tnl_dst_check(t)) != NULL) | 704 | if ((dst = ip6_tnl_dst_check(t)) != NULL) |
676 | dst_hold(dst); | 705 | dst_hold(dst); |
@@ -692,7 +721,7 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) | |||
692 | goto tx_err_dst_release; | 721 | goto tx_err_dst_release; |
693 | } | 722 | } |
694 | mtu = dst_mtu(dst) - sizeof (*ipv6h); | 723 | mtu = dst_mtu(dst) - sizeof (*ipv6h); |
695 | if (opt) { | 724 | if (encap_limit >= 0) { |
696 | max_headroom += 8; | 725 | max_headroom += 8; |
697 | mtu -= 8; | 726 | mtu -= 8; |
698 | } | 727 | } |
@@ -730,12 +759,13 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) | |||
730 | 759 | ||
731 | skb->h.raw = skb->nh.raw; | 760 | skb->h.raw = skb->nh.raw; |
732 | 761 | ||
733 | if (opt) | 762 | if (encap_limit >= 0) { |
734 | ipv6_push_nfrag_opts(skb, opt, &proto, NULL); | 763 | init_tel_txopt(&opt, encap_limit); |
735 | 764 | ipv6_push_nfrag_opts(skb, &opt.ops, &proto, NULL); | |
765 | } | ||
736 | skb->nh.raw = skb_push(skb, sizeof(struct ipv6hdr)); | 766 | skb->nh.raw = skb_push(skb, sizeof(struct ipv6hdr)); |
737 | ipv6h = skb->nh.ipv6h; | 767 | ipv6h = skb->nh.ipv6h; |
738 | *(u32*)ipv6h = fl.fl6_flowlabel | htonl(0x60000000); | 768 | *(__be32*)ipv6h = fl.fl6_flowlabel | htonl(0x60000000); |
739 | dsfield = INET_ECN_encapsulate(0, dsfield); | 769 | dsfield = INET_ECN_encapsulate(0, dsfield); |
740 | ipv6_change_dsfield(ipv6h, ~INET_ECN_MASK, dsfield); | 770 | ipv6_change_dsfield(ipv6h, ~INET_ECN_MASK, dsfield); |
741 | ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); | 771 | ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); |
@@ -748,7 +778,7 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) | |||
748 | err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, | 778 | err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, |
749 | skb->dst->dev, dst_output); | 779 | skb->dst->dev, dst_output); |
750 | 780 | ||
751 | if (err == NET_XMIT_SUCCESS || err == NET_XMIT_CN) { | 781 | if (net_xmit_eval(err) == 0) { |
752 | stats->tx_bytes += pkt_len; | 782 | stats->tx_bytes += pkt_len; |
753 | stats->tx_packets++; | 783 | stats->tx_packets++; |
754 | } else { | 784 | } else { |
@@ -756,9 +786,6 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) | |||
756 | stats->tx_aborted_errors++; | 786 | stats->tx_aborted_errors++; |
757 | } | 787 | } |
758 | ip6_tnl_dst_store(t, dst); | 788 | ip6_tnl_dst_store(t, dst); |
759 | |||
760 | kfree(opt); | ||
761 | |||
762 | t->recursion--; | 789 | t->recursion--; |
763 | return 0; | 790 | return 0; |
764 | tx_err_link_failure: | 791 | tx_err_link_failure: |
@@ -766,7 +793,6 @@ tx_err_link_failure: | |||
766 | dst_link_failure(skb); | 793 | dst_link_failure(skb); |
767 | tx_err_dst_release: | 794 | tx_err_dst_release: |
768 | dst_release(dst); | 795 | dst_release(dst); |
769 | kfree(opt); | ||
770 | tx_err: | 796 | tx_err: |
771 | stats->tx_errors++; | 797 | stats->tx_errors++; |
772 | stats->tx_dropped++; | 798 | stats->tx_dropped++; |
@@ -778,39 +804,19 @@ tx_err: | |||
778 | static void ip6_tnl_set_cap(struct ip6_tnl *t) | 804 | static void ip6_tnl_set_cap(struct ip6_tnl *t) |
779 | { | 805 | { |
780 | struct ip6_tnl_parm *p = &t->parms; | 806 | struct ip6_tnl_parm *p = &t->parms; |
781 | struct in6_addr *laddr = &p->laddr; | 807 | int ltype = ipv6_addr_type(&p->laddr); |
782 | struct in6_addr *raddr = &p->raddr; | 808 | int rtype = ipv6_addr_type(&p->raddr); |
783 | int ltype = ipv6_addr_type(laddr); | ||
784 | int rtype = ipv6_addr_type(raddr); | ||
785 | 809 | ||
786 | p->flags &= ~(IP6_TNL_F_CAP_XMIT|IP6_TNL_F_CAP_RCV); | 810 | p->flags &= ~(IP6_TNL_F_CAP_XMIT|IP6_TNL_F_CAP_RCV); |
787 | 811 | ||
788 | if (ltype != IPV6_ADDR_ANY && rtype != IPV6_ADDR_ANY && | 812 | if (ltype & (IPV6_ADDR_UNICAST|IPV6_ADDR_MULTICAST) && |
789 | ((ltype|rtype) & | 813 | rtype & (IPV6_ADDR_UNICAST|IPV6_ADDR_MULTICAST) && |
790 | (IPV6_ADDR_UNICAST| | 814 | !((ltype|rtype) & IPV6_ADDR_LOOPBACK) && |
791 | IPV6_ADDR_LOOPBACK|IPV6_ADDR_LINKLOCAL| | 815 | (!((ltype|rtype) & IPV6_ADDR_LINKLOCAL) || p->link)) { |
792 | IPV6_ADDR_MAPPED|IPV6_ADDR_RESERVED)) == IPV6_ADDR_UNICAST) { | 816 | if (ltype&IPV6_ADDR_UNICAST) |
793 | struct net_device *ldev = NULL; | 817 | p->flags |= IP6_TNL_F_CAP_XMIT; |
794 | int l_ok = 1; | 818 | if (rtype&IPV6_ADDR_UNICAST) |
795 | int r_ok = 1; | 819 | p->flags |= IP6_TNL_F_CAP_RCV; |
796 | |||
797 | if (p->link) | ||
798 | ldev = dev_get_by_index(p->link); | ||
799 | |||
800 | if (ltype&IPV6_ADDR_UNICAST && !ipv6_chk_addr(laddr, ldev, 0)) | ||
801 | l_ok = 0; | ||
802 | |||
803 | if (rtype&IPV6_ADDR_UNICAST && ipv6_chk_addr(raddr, NULL, 0)) | ||
804 | r_ok = 0; | ||
805 | |||
806 | if (l_ok && r_ok) { | ||
807 | if (ltype&IPV6_ADDR_UNICAST) | ||
808 | p->flags |= IP6_TNL_F_CAP_XMIT; | ||
809 | if (rtype&IPV6_ADDR_UNICAST) | ||
810 | p->flags |= IP6_TNL_F_CAP_RCV; | ||
811 | } | ||
812 | if (ldev) | ||
813 | dev_put(ldev); | ||
814 | } | 820 | } |
815 | } | 821 | } |
816 | 822 | ||
@@ -844,8 +850,11 @@ static void ip6ip6_tnl_link_config(struct ip6_tnl *t) | |||
844 | dev->iflink = p->link; | 850 | dev->iflink = p->link; |
845 | 851 | ||
846 | if (p->flags & IP6_TNL_F_CAP_XMIT) { | 852 | if (p->flags & IP6_TNL_F_CAP_XMIT) { |
853 | int strict = (ipv6_addr_type(&p->raddr) & | ||
854 | (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL)); | ||
855 | |||
847 | struct rt6_info *rt = rt6_lookup(&p->raddr, &p->laddr, | 856 | struct rt6_info *rt = rt6_lookup(&p->raddr, &p->laddr, |
848 | p->link, 0); | 857 | p->link, strict); |
849 | 858 | ||
850 | if (rt == NULL) | 859 | if (rt == NULL) |
851 | return; | 860 | return; |
@@ -920,26 +929,20 @@ static int | |||
920 | ip6ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | 929 | ip6ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) |
921 | { | 930 | { |
922 | int err = 0; | 931 | int err = 0; |
923 | int create; | ||
924 | struct ip6_tnl_parm p; | 932 | struct ip6_tnl_parm p; |
925 | struct ip6_tnl *t = NULL; | 933 | struct ip6_tnl *t = NULL; |
926 | 934 | ||
927 | switch (cmd) { | 935 | switch (cmd) { |
928 | case SIOCGETTUNNEL: | 936 | case SIOCGETTUNNEL: |
929 | if (dev == ip6ip6_fb_tnl_dev) { | 937 | if (dev == ip6ip6_fb_tnl_dev) { |
930 | if (copy_from_user(&p, | 938 | if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) { |
931 | ifr->ifr_ifru.ifru_data, | ||
932 | sizeof (p))) { | ||
933 | err = -EFAULT; | 939 | err = -EFAULT; |
934 | break; | 940 | break; |
935 | } | 941 | } |
936 | if ((err = ip6ip6_tnl_locate(&p, &t, 0)) == -ENODEV) | 942 | t = ip6ip6_tnl_locate(&p, 0); |
937 | t = netdev_priv(dev); | 943 | } |
938 | else if (err) | 944 | if (t == NULL) |
939 | break; | ||
940 | } else | ||
941 | t = netdev_priv(dev); | 945 | t = netdev_priv(dev); |
942 | |||
943 | memcpy(&p, &t->parms, sizeof (p)); | 946 | memcpy(&p, &t->parms, sizeof (p)); |
944 | if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof (p))) { | 947 | if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof (p))) { |
945 | err = -EFAULT; | 948 | err = -EFAULT; |
@@ -948,35 +951,36 @@ ip6ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | |||
948 | case SIOCADDTUNNEL: | 951 | case SIOCADDTUNNEL: |
949 | case SIOCCHGTUNNEL: | 952 | case SIOCCHGTUNNEL: |
950 | err = -EPERM; | 953 | err = -EPERM; |
951 | create = (cmd == SIOCADDTUNNEL); | ||
952 | if (!capable(CAP_NET_ADMIN)) | 954 | if (!capable(CAP_NET_ADMIN)) |
953 | break; | 955 | break; |
954 | if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) { | 956 | err = -EFAULT; |
955 | err = -EFAULT; | 957 | if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) |
956 | break; | 958 | break; |
957 | } | 959 | err = -EINVAL; |
958 | if (!create && dev != ip6ip6_fb_tnl_dev) { | 960 | if (p.proto != IPPROTO_IPV6) |
959 | t = netdev_priv(dev); | ||
960 | } | ||
961 | if (!t && (err = ip6ip6_tnl_locate(&p, &t, create))) { | ||
962 | break; | 961 | break; |
963 | } | 962 | t = ip6ip6_tnl_locate(&p, cmd == SIOCADDTUNNEL); |
964 | if (cmd == SIOCCHGTUNNEL) { | 963 | if (dev != ip6ip6_fb_tnl_dev && cmd == SIOCCHGTUNNEL) { |
965 | if (t->dev != dev) { | 964 | if (t != NULL) { |
966 | err = -EEXIST; | 965 | if (t->dev != dev) { |
967 | break; | 966 | err = -EEXIST; |
968 | } | 967 | break; |
968 | } | ||
969 | } else | ||
970 | t = netdev_priv(dev); | ||
971 | |||
969 | ip6ip6_tnl_unlink(t); | 972 | ip6ip6_tnl_unlink(t); |
970 | err = ip6ip6_tnl_change(t, &p); | 973 | err = ip6ip6_tnl_change(t, &p); |
971 | ip6ip6_tnl_link(t); | 974 | ip6ip6_tnl_link(t); |
972 | netdev_state_change(dev); | 975 | netdev_state_change(dev); |
973 | } | 976 | } |
974 | if (copy_to_user(ifr->ifr_ifru.ifru_data, | 977 | if (t) { |
975 | &t->parms, sizeof (p))) { | ||
976 | err = -EFAULT; | ||
977 | } else { | ||
978 | err = 0; | 978 | err = 0; |
979 | } | 979 | if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof (p))) |
980 | err = -EFAULT; | ||
981 | |||
982 | } else | ||
983 | err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT); | ||
980 | break; | 984 | break; |
981 | case SIOCDELTUNNEL: | 985 | case SIOCDELTUNNEL: |
982 | err = -EPERM; | 986 | err = -EPERM; |
@@ -984,22 +988,18 @@ ip6ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | |||
984 | break; | 988 | break; |
985 | 989 | ||
986 | if (dev == ip6ip6_fb_tnl_dev) { | 990 | if (dev == ip6ip6_fb_tnl_dev) { |
987 | if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, | 991 | err = -EFAULT; |
988 | sizeof (p))) { | 992 | if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) |
989 | err = -EFAULT; | ||
990 | break; | 993 | break; |
991 | } | 994 | err = -ENOENT; |
992 | err = ip6ip6_tnl_locate(&p, &t, 0); | 995 | if ((t = ip6ip6_tnl_locate(&p, 0)) == NULL) |
993 | if (err) | ||
994 | break; | 996 | break; |
995 | if (t == netdev_priv(ip6ip6_fb_tnl_dev)) { | 997 | err = -EPERM; |
996 | err = -EPERM; | 998 | if (t->dev == ip6ip6_fb_tnl_dev) |
997 | break; | 999 | break; |
998 | } | 1000 | dev = t->dev; |
999 | } else { | ||
1000 | t = netdev_priv(dev); | ||
1001 | } | 1001 | } |
1002 | err = unregister_netdevice(t->dev); | 1002 | err = unregister_netdevice(dev); |
1003 | break; | 1003 | break; |
1004 | default: | 1004 | default: |
1005 | err = -EINVAL; | 1005 | err = -EINVAL; |