diff options
Diffstat (limited to 'net/ipv6/sit.c')
-rw-r--r-- | net/ipv6/sit.c | 133 |
1 files changed, 25 insertions, 108 deletions
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 5a6fab95569f..32e871a6c25a 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c | |||
@@ -222,15 +222,18 @@ __ipip6_tunnel_locate_prl(struct ip_tunnel *t, __be32 addr) | |||
222 | 222 | ||
223 | } | 223 | } |
224 | 224 | ||
225 | static int ipip6_tunnel_get_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a) | 225 | static int ipip6_tunnel_get_prl(struct ip_tunnel *t, |
226 | struct ip_tunnel_prl __user *a) | ||
226 | { | 227 | { |
227 | struct ip_tunnel_prl *kp; | 228 | struct ip_tunnel_prl kprl, *kp; |
228 | struct ip_tunnel_prl_entry *prl; | 229 | struct ip_tunnel_prl_entry *prl; |
229 | unsigned int cmax, c = 0, ca, len; | 230 | unsigned int cmax, c = 0, ca, len; |
230 | int ret = 0; | 231 | int ret = 0; |
231 | 232 | ||
232 | cmax = a->datalen / sizeof(*a); | 233 | if (copy_from_user(&kprl, a, sizeof(kprl))) |
233 | if (cmax > 1 && a->addr != htonl(INADDR_ANY)) | 234 | return -EFAULT; |
235 | cmax = kprl.datalen / sizeof(kprl); | ||
236 | if (cmax > 1 && kprl.addr != htonl(INADDR_ANY)) | ||
234 | cmax = 1; | 237 | cmax = 1; |
235 | 238 | ||
236 | /* For simple GET or for root users, | 239 | /* For simple GET or for root users, |
@@ -261,26 +264,25 @@ static int ipip6_tunnel_get_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a) | |||
261 | for (prl = t->prl; prl; prl = prl->next) { | 264 | for (prl = t->prl; prl; prl = prl->next) { |
262 | if (c > cmax) | 265 | if (c > cmax) |
263 | break; | 266 | break; |
264 | if (a->addr != htonl(INADDR_ANY) && prl->addr != a->addr) | 267 | if (kprl.addr != htonl(INADDR_ANY) && prl->addr != kprl.addr) |
265 | continue; | 268 | continue; |
266 | kp[c].addr = prl->addr; | 269 | kp[c].addr = prl->addr; |
267 | kp[c].flags = prl->flags; | 270 | kp[c].flags = prl->flags; |
268 | c++; | 271 | c++; |
269 | if (a->addr != htonl(INADDR_ANY)) | 272 | if (kprl.addr != htonl(INADDR_ANY)) |
270 | break; | 273 | break; |
271 | } | 274 | } |
272 | out: | 275 | out: |
273 | read_unlock(&ipip6_lock); | 276 | read_unlock(&ipip6_lock); |
274 | 277 | ||
275 | len = sizeof(*kp) * c; | 278 | len = sizeof(*kp) * c; |
276 | ret = len ? copy_to_user(a->data, kp, len) : 0; | 279 | ret = 0; |
280 | if ((len && copy_to_user(a + 1, kp, len)) || put_user(len, &a->datalen)) | ||
281 | ret = -EFAULT; | ||
277 | 282 | ||
278 | kfree(kp); | 283 | kfree(kp); |
279 | if (ret) | ||
280 | return -EFAULT; | ||
281 | 284 | ||
282 | a->datalen = len; | 285 | return ret; |
283 | return 0; | ||
284 | } | 286 | } |
285 | 287 | ||
286 | static int | 288 | static int |
@@ -403,9 +405,8 @@ static void ipip6_tunnel_uninit(struct net_device *dev) | |||
403 | 405 | ||
404 | static int ipip6_err(struct sk_buff *skb, u32 info) | 406 | static int ipip6_err(struct sk_buff *skb, u32 info) |
405 | { | 407 | { |
406 | #ifndef I_WISH_WORLD_WERE_PERFECT | ||
407 | 408 | ||
408 | /* It is not :-( All the routers (except for Linux) return only | 409 | /* All the routers (except for Linux) return only |
409 | 8 bytes of packet payload. It means, that precise relaying of | 410 | 8 bytes of packet payload. It means, that precise relaying of |
410 | ICMP in the real Internet is absolutely infeasible. | 411 | ICMP in the real Internet is absolutely infeasible. |
411 | */ | 412 | */ |
@@ -462,92 +463,6 @@ static int ipip6_err(struct sk_buff *skb, u32 info) | |||
462 | out: | 463 | out: |
463 | read_unlock(&ipip6_lock); | 464 | read_unlock(&ipip6_lock); |
464 | return err; | 465 | return err; |
465 | #else | ||
466 | struct iphdr *iph = (struct iphdr*)dp; | ||
467 | int hlen = iph->ihl<<2; | ||
468 | struct ipv6hdr *iph6; | ||
469 | const int type = icmp_hdr(skb)->type; | ||
470 | const int code = icmp_hdr(skb)->code; | ||
471 | int rel_type = 0; | ||
472 | int rel_code = 0; | ||
473 | int rel_info = 0; | ||
474 | struct sk_buff *skb2; | ||
475 | struct rt6_info *rt6i; | ||
476 | |||
477 | if (len < hlen + sizeof(struct ipv6hdr)) | ||
478 | return; | ||
479 | iph6 = (struct ipv6hdr*)(dp + hlen); | ||
480 | |||
481 | switch (type) { | ||
482 | default: | ||
483 | return; | ||
484 | case ICMP_PARAMETERPROB: | ||
485 | if (icmp_hdr(skb)->un.gateway < hlen) | ||
486 | return; | ||
487 | |||
488 | /* So... This guy found something strange INSIDE encapsulated | ||
489 | packet. Well, he is fool, but what can we do ? | ||
490 | */ | ||
491 | rel_type = ICMPV6_PARAMPROB; | ||
492 | rel_info = icmp_hdr(skb)->un.gateway - hlen; | ||
493 | break; | ||
494 | |||
495 | case ICMP_DEST_UNREACH: | ||
496 | switch (code) { | ||
497 | case ICMP_SR_FAILED: | ||
498 | case ICMP_PORT_UNREACH: | ||
499 | /* Impossible event. */ | ||
500 | return; | ||
501 | case ICMP_FRAG_NEEDED: | ||
502 | /* Too complicated case ... */ | ||
503 | return; | ||
504 | default: | ||
505 | /* All others are translated to HOST_UNREACH. | ||
506 | rfc2003 contains "deep thoughts" about NET_UNREACH, | ||
507 | I believe, it is just ether pollution. --ANK | ||
508 | */ | ||
509 | rel_type = ICMPV6_DEST_UNREACH; | ||
510 | rel_code = ICMPV6_ADDR_UNREACH; | ||
511 | break; | ||
512 | } | ||
513 | break; | ||
514 | case ICMP_TIME_EXCEEDED: | ||
515 | if (code != ICMP_EXC_TTL) | ||
516 | return; | ||
517 | rel_type = ICMPV6_TIME_EXCEED; | ||
518 | rel_code = ICMPV6_EXC_HOPLIMIT; | ||
519 | break; | ||
520 | } | ||
521 | |||
522 | /* Prepare fake skb to feed it to icmpv6_send */ | ||
523 | skb2 = skb_clone(skb, GFP_ATOMIC); | ||
524 | if (skb2 == NULL) | ||
525 | return 0; | ||
526 | dst_release(skb2->dst); | ||
527 | skb2->dst = NULL; | ||
528 | skb_pull(skb2, skb->data - (u8*)iph6); | ||
529 | skb_reset_network_header(skb2); | ||
530 | |||
531 | /* Try to guess incoming interface */ | ||
532 | rt6i = rt6_lookup(dev_net(skb->dev), &iph6->saddr, NULL, NULL, 0); | ||
533 | if (rt6i && rt6i->rt6i_dev) { | ||
534 | skb2->dev = rt6i->rt6i_dev; | ||
535 | |||
536 | rt6i = rt6_lookup(dev_net(skb->dev), | ||
537 | &iph6->daddr, &iph6->saddr, NULL, 0); | ||
538 | |||
539 | if (rt6i && rt6i->rt6i_dev && rt6i->rt6i_dev->type == ARPHRD_SIT) { | ||
540 | struct ip_tunnel *t = netdev_priv(rt6i->rt6i_dev); | ||
541 | if (rel_type == ICMPV6_TIME_EXCEED && t->parms.iph.ttl) { | ||
542 | rel_type = ICMPV6_DEST_UNREACH; | ||
543 | rel_code = ICMPV6_ADDR_UNREACH; | ||
544 | } | ||
545 | icmpv6_send(skb2, rel_type, rel_code, rel_info, skb2->dev); | ||
546 | } | ||
547 | } | ||
548 | kfree_skb(skb2); | ||
549 | return 0; | ||
550 | #endif | ||
551 | } | 466 | } |
552 | 467 | ||
553 | static inline void ipip6_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb) | 468 | static inline void ipip6_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb) |
@@ -960,11 +875,20 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd) | |||
960 | break; | 875 | break; |
961 | 876 | ||
962 | case SIOCGETPRL: | 877 | case SIOCGETPRL: |
878 | err = -EINVAL; | ||
879 | if (dev == sitn->fb_tunnel_dev) | ||
880 | goto done; | ||
881 | err = -ENOENT; | ||
882 | if (!(t = netdev_priv(dev))) | ||
883 | goto done; | ||
884 | err = ipip6_tunnel_get_prl(t, ifr->ifr_ifru.ifru_data); | ||
885 | break; | ||
886 | |||
963 | case SIOCADDPRL: | 887 | case SIOCADDPRL: |
964 | case SIOCDELPRL: | 888 | case SIOCDELPRL: |
965 | case SIOCCHGPRL: | 889 | case SIOCCHGPRL: |
966 | err = -EPERM; | 890 | err = -EPERM; |
967 | if (cmd != SIOCGETPRL && !capable(CAP_NET_ADMIN)) | 891 | if (!capable(CAP_NET_ADMIN)) |
968 | goto done; | 892 | goto done; |
969 | err = -EINVAL; | 893 | err = -EINVAL; |
970 | if (dev == sitn->fb_tunnel_dev) | 894 | if (dev == sitn->fb_tunnel_dev) |
@@ -977,12 +901,6 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd) | |||
977 | goto done; | 901 | goto done; |
978 | 902 | ||
979 | switch (cmd) { | 903 | switch (cmd) { |
980 | case SIOCGETPRL: | ||
981 | err = ipip6_tunnel_get_prl(t, &prl); | ||
982 | if (!err && copy_to_user(ifr->ifr_ifru.ifru_data, | ||
983 | &prl, sizeof(prl))) | ||
984 | err = -EFAULT; | ||
985 | break; | ||
986 | case SIOCDELPRL: | 904 | case SIOCDELPRL: |
987 | err = ipip6_tunnel_del_prl(t, &prl); | 905 | err = ipip6_tunnel_del_prl(t, &prl); |
988 | break; | 906 | break; |
@@ -991,8 +909,7 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd) | |||
991 | err = ipip6_tunnel_add_prl(t, &prl, cmd == SIOCCHGPRL); | 909 | err = ipip6_tunnel_add_prl(t, &prl, cmd == SIOCCHGPRL); |
992 | break; | 910 | break; |
993 | } | 911 | } |
994 | if (cmd != SIOCGETPRL) | 912 | netdev_state_change(dev); |
995 | netdev_state_change(dev); | ||
996 | break; | 913 | break; |
997 | 914 | ||
998 | default: | 915 | default: |