diff options
Diffstat (limited to 'net/ipv4/ip_options.c')
-rw-r--r-- | net/ipv4/ip_options.c | 85 |
1 files changed, 42 insertions, 43 deletions
diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index ba9836c488ed..ec93335901dd 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
15 | #include <linux/types.h> | 15 | #include <linux/types.h> |
16 | #include <asm/uaccess.h> | 16 | #include <asm/uaccess.h> |
17 | #include <asm/unaligned.h> | ||
17 | #include <linux/skbuff.h> | 18 | #include <linux/skbuff.h> |
18 | #include <linux/ip.h> | 19 | #include <linux/ip.h> |
19 | #include <linux/icmp.h> | 20 | #include <linux/icmp.h> |
@@ -36,8 +37,8 @@ | |||
36 | * saddr is address of outgoing interface. | 37 | * saddr is address of outgoing interface. |
37 | */ | 38 | */ |
38 | 39 | ||
39 | void ip_options_build(struct sk_buff * skb, struct ip_options * opt, | 40 | void ip_options_build(struct sk_buff *skb, struct ip_options *opt, |
40 | __be32 daddr, struct rtable *rt, int is_frag) | 41 | __be32 daddr, struct rtable *rt, int is_frag) |
41 | { | 42 | { |
42 | unsigned char *iph = skb_network_header(skb); | 43 | unsigned char *iph = skb_network_header(skb); |
43 | 44 | ||
@@ -50,9 +51,9 @@ void ip_options_build(struct sk_buff * skb, struct ip_options * opt, | |||
50 | 51 | ||
51 | if (!is_frag) { | 52 | if (!is_frag) { |
52 | if (opt->rr_needaddr) | 53 | if (opt->rr_needaddr) |
53 | ip_rt_get_source(iph+opt->rr+iph[opt->rr+2]-5, rt); | 54 | ip_rt_get_source(iph+opt->rr+iph[opt->rr+2]-5, skb, rt); |
54 | if (opt->ts_needaddr) | 55 | if (opt->ts_needaddr) |
55 | ip_rt_get_source(iph+opt->ts+iph[opt->ts+2]-9, rt); | 56 | ip_rt_get_source(iph+opt->ts+iph[opt->ts+2]-9, skb, rt); |
56 | if (opt->ts_needtime) { | 57 | if (opt->ts_needtime) { |
57 | struct timespec tv; | 58 | struct timespec tv; |
58 | __be32 midtime; | 59 | __be32 midtime; |
@@ -83,9 +84,9 @@ void ip_options_build(struct sk_buff * skb, struct ip_options * opt, | |||
83 | * NOTE: dopt cannot point to skb. | 84 | * NOTE: dopt cannot point to skb. |
84 | */ | 85 | */ |
85 | 86 | ||
86 | int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb) | 87 | int ip_options_echo(struct ip_options *dopt, struct sk_buff *skb) |
87 | { | 88 | { |
88 | struct ip_options *sopt; | 89 | const struct ip_options *sopt; |
89 | unsigned char *sptr, *dptr; | 90 | unsigned char *sptr, *dptr; |
90 | int soffset, doffset; | 91 | int soffset, doffset; |
91 | int optlen; | 92 | int optlen; |
@@ -95,10 +96,8 @@ int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb) | |||
95 | 96 | ||
96 | sopt = &(IPCB(skb)->opt); | 97 | sopt = &(IPCB(skb)->opt); |
97 | 98 | ||
98 | if (sopt->optlen == 0) { | 99 | if (sopt->optlen == 0) |
99 | dopt->optlen = 0; | ||
100 | return 0; | 100 | return 0; |
101 | } | ||
102 | 101 | ||
103 | sptr = skb_network_header(skb); | 102 | sptr = skb_network_header(skb); |
104 | dptr = dopt->__data; | 103 | dptr = dopt->__data; |
@@ -140,11 +139,11 @@ int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb) | |||
140 | } else { | 139 | } else { |
141 | dopt->ts_needtime = 0; | 140 | dopt->ts_needtime = 0; |
142 | 141 | ||
143 | if (soffset + 8 <= optlen) { | 142 | if (soffset + 7 <= optlen) { |
144 | __be32 addr; | 143 | __be32 addr; |
145 | 144 | ||
146 | memcpy(&addr, sptr+soffset-1, 4); | 145 | memcpy(&addr, dptr+soffset-1, 4); |
147 | if (inet_addr_type(dev_net(skb_dst(skb)->dev), addr) != RTN_LOCAL) { | 146 | if (inet_addr_type(dev_net(skb_dst(skb)->dev), addr) != RTN_UNICAST) { |
148 | dopt->ts_needtime = 1; | 147 | dopt->ts_needtime = 1; |
149 | soffset += 8; | 148 | soffset += 8; |
150 | } | 149 | } |
@@ -157,7 +156,7 @@ int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb) | |||
157 | dopt->optlen += optlen; | 156 | dopt->optlen += optlen; |
158 | } | 157 | } |
159 | if (sopt->srr) { | 158 | if (sopt->srr) { |
160 | unsigned char * start = sptr+sopt->srr; | 159 | unsigned char *start = sptr+sopt->srr; |
161 | __be32 faddr; | 160 | __be32 faddr; |
162 | 161 | ||
163 | optlen = start[1]; | 162 | optlen = start[1]; |
@@ -329,7 +328,7 @@ int ip_options_compile(struct net *net, | |||
329 | pp_ptr = optptr + 2; | 328 | pp_ptr = optptr + 2; |
330 | goto error; | 329 | goto error; |
331 | } | 330 | } |
332 | if (skb) { | 331 | if (rt) { |
333 | memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4); | 332 | memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4); |
334 | opt->is_changed = 1; | 333 | opt->is_changed = 1; |
335 | } | 334 | } |
@@ -352,7 +351,7 @@ int ip_options_compile(struct net *net, | |||
352 | goto error; | 351 | goto error; |
353 | } | 352 | } |
354 | if (optptr[2] <= optlen) { | 353 | if (optptr[2] <= optlen) { |
355 | __be32 *timeptr = NULL; | 354 | unsigned char *timeptr = NULL; |
356 | if (optptr[2]+3 > optptr[1]) { | 355 | if (optptr[2]+3 > optptr[1]) { |
357 | pp_ptr = optptr + 2; | 356 | pp_ptr = optptr + 2; |
358 | goto error; | 357 | goto error; |
@@ -361,7 +360,7 @@ int ip_options_compile(struct net *net, | |||
361 | case IPOPT_TS_TSONLY: | 360 | case IPOPT_TS_TSONLY: |
362 | opt->ts = optptr - iph; | 361 | opt->ts = optptr - iph; |
363 | if (skb) | 362 | if (skb) |
364 | timeptr = (__be32*)&optptr[optptr[2]-1]; | 363 | timeptr = &optptr[optptr[2]-1]; |
365 | opt->ts_needtime = 1; | 364 | opt->ts_needtime = 1; |
366 | optptr[2] += 4; | 365 | optptr[2] += 4; |
367 | break; | 366 | break; |
@@ -371,9 +370,9 @@ int ip_options_compile(struct net *net, | |||
371 | goto error; | 370 | goto error; |
372 | } | 371 | } |
373 | opt->ts = optptr - iph; | 372 | opt->ts = optptr - iph; |
374 | if (skb) { | 373 | if (rt) { |
375 | memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4); | 374 | memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4); |
376 | timeptr = (__be32*)&optptr[optptr[2]+3]; | 375 | timeptr = &optptr[optptr[2]+3]; |
377 | } | 376 | } |
378 | opt->ts_needaddr = 1; | 377 | opt->ts_needaddr = 1; |
379 | opt->ts_needtime = 1; | 378 | opt->ts_needtime = 1; |
@@ -391,7 +390,7 @@ int ip_options_compile(struct net *net, | |||
391 | if (inet_addr_type(net, addr) == RTN_UNICAST) | 390 | if (inet_addr_type(net, addr) == RTN_UNICAST) |
392 | break; | 391 | break; |
393 | if (skb) | 392 | if (skb) |
394 | timeptr = (__be32*)&optptr[optptr[2]+3]; | 393 | timeptr = &optptr[optptr[2]+3]; |
395 | } | 394 | } |
396 | opt->ts_needtime = 1; | 395 | opt->ts_needtime = 1; |
397 | optptr[2] += 8; | 396 | optptr[2] += 8; |
@@ -405,10 +404,10 @@ int ip_options_compile(struct net *net, | |||
405 | } | 404 | } |
406 | if (timeptr) { | 405 | if (timeptr) { |
407 | struct timespec tv; | 406 | struct timespec tv; |
408 | __be32 midtime; | 407 | u32 midtime; |
409 | getnstimeofday(&tv); | 408 | getnstimeofday(&tv); |
410 | midtime = htonl((tv.tv_sec % 86400) * MSEC_PER_SEC + tv.tv_nsec / NSEC_PER_MSEC); | 409 | midtime = (tv.tv_sec % 86400) * MSEC_PER_SEC + tv.tv_nsec / NSEC_PER_MSEC; |
411 | memcpy(timeptr, &midtime, sizeof(__be32)); | 410 | put_unaligned_be32(midtime, timeptr); |
412 | opt->is_changed = 1; | 411 | opt->is_changed = 1; |
413 | } | 412 | } |
414 | } else { | 413 | } else { |
@@ -466,7 +465,7 @@ error: | |||
466 | } | 465 | } |
467 | return -EINVAL; | 466 | return -EINVAL; |
468 | } | 467 | } |
469 | 468 | EXPORT_SYMBOL(ip_options_compile); | |
470 | 469 | ||
471 | /* | 470 | /* |
472 | * Undo all the changes done by ip_options_compile(). | 471 | * Undo all the changes done by ip_options_compile(). |
@@ -499,19 +498,19 @@ void ip_options_undo(struct ip_options * opt) | |||
499 | } | 498 | } |
500 | } | 499 | } |
501 | 500 | ||
502 | static struct ip_options *ip_options_get_alloc(const int optlen) | 501 | static struct ip_options_rcu *ip_options_get_alloc(const int optlen) |
503 | { | 502 | { |
504 | return kzalloc(sizeof(struct ip_options) + ((optlen + 3) & ~3), | 503 | return kzalloc(sizeof(struct ip_options_rcu) + ((optlen + 3) & ~3), |
505 | GFP_KERNEL); | 504 | GFP_KERNEL); |
506 | } | 505 | } |
507 | 506 | ||
508 | static int ip_options_get_finish(struct net *net, struct ip_options **optp, | 507 | static int ip_options_get_finish(struct net *net, struct ip_options_rcu **optp, |
509 | struct ip_options *opt, int optlen) | 508 | struct ip_options_rcu *opt, int optlen) |
510 | { | 509 | { |
511 | while (optlen & 3) | 510 | while (optlen & 3) |
512 | opt->__data[optlen++] = IPOPT_END; | 511 | opt->opt.__data[optlen++] = IPOPT_END; |
513 | opt->optlen = optlen; | 512 | opt->opt.optlen = optlen; |
514 | if (optlen && ip_options_compile(net, opt, NULL)) { | 513 | if (optlen && ip_options_compile(net, &opt->opt, NULL)) { |
515 | kfree(opt); | 514 | kfree(opt); |
516 | return -EINVAL; | 515 | return -EINVAL; |
517 | } | 516 | } |
@@ -520,29 +519,29 @@ static int ip_options_get_finish(struct net *net, struct ip_options **optp, | |||
520 | return 0; | 519 | return 0; |
521 | } | 520 | } |
522 | 521 | ||
523 | int ip_options_get_from_user(struct net *net, struct ip_options **optp, | 522 | int ip_options_get_from_user(struct net *net, struct ip_options_rcu **optp, |
524 | unsigned char __user *data, int optlen) | 523 | unsigned char __user *data, int optlen) |
525 | { | 524 | { |
526 | struct ip_options *opt = ip_options_get_alloc(optlen); | 525 | struct ip_options_rcu *opt = ip_options_get_alloc(optlen); |
527 | 526 | ||
528 | if (!opt) | 527 | if (!opt) |
529 | return -ENOMEM; | 528 | return -ENOMEM; |
530 | if (optlen && copy_from_user(opt->__data, data, optlen)) { | 529 | if (optlen && copy_from_user(opt->opt.__data, data, optlen)) { |
531 | kfree(opt); | 530 | kfree(opt); |
532 | return -EFAULT; | 531 | return -EFAULT; |
533 | } | 532 | } |
534 | return ip_options_get_finish(net, optp, opt, optlen); | 533 | return ip_options_get_finish(net, optp, opt, optlen); |
535 | } | 534 | } |
536 | 535 | ||
537 | int ip_options_get(struct net *net, struct ip_options **optp, | 536 | int ip_options_get(struct net *net, struct ip_options_rcu **optp, |
538 | unsigned char *data, int optlen) | 537 | unsigned char *data, int optlen) |
539 | { | 538 | { |
540 | struct ip_options *opt = ip_options_get_alloc(optlen); | 539 | struct ip_options_rcu *opt = ip_options_get_alloc(optlen); |
541 | 540 | ||
542 | if (!opt) | 541 | if (!opt) |
543 | return -ENOMEM; | 542 | return -ENOMEM; |
544 | if (optlen) | 543 | if (optlen) |
545 | memcpy(opt->__data, data, optlen); | 544 | memcpy(opt->opt.__data, data, optlen); |
546 | return ip_options_get_finish(net, optp, opt, optlen); | 545 | return ip_options_get_finish(net, optp, opt, optlen); |
547 | } | 546 | } |
548 | 547 | ||
@@ -555,7 +554,7 @@ void ip_forward_options(struct sk_buff *skb) | |||
555 | 554 | ||
556 | if (opt->rr_needaddr) { | 555 | if (opt->rr_needaddr) { |
557 | optptr = (unsigned char *)raw + opt->rr; | 556 | optptr = (unsigned char *)raw + opt->rr; |
558 | ip_rt_get_source(&optptr[optptr[2]-5], rt); | 557 | ip_rt_get_source(&optptr[optptr[2]-5], skb, rt); |
559 | opt->is_changed = 1; | 558 | opt->is_changed = 1; |
560 | } | 559 | } |
561 | if (opt->srr_is_hit) { | 560 | if (opt->srr_is_hit) { |
@@ -569,19 +568,18 @@ void ip_forward_options(struct sk_buff *skb) | |||
569 | ) { | 568 | ) { |
570 | if (srrptr + 3 > srrspace) | 569 | if (srrptr + 3 > srrspace) |
571 | break; | 570 | break; |
572 | if (memcmp(&rt->rt_dst, &optptr[srrptr-1], 4) == 0) | 571 | if (memcmp(&ip_hdr(skb)->daddr, &optptr[srrptr-1], 4) == 0) |
573 | break; | 572 | break; |
574 | } | 573 | } |
575 | if (srrptr + 3 <= srrspace) { | 574 | if (srrptr + 3 <= srrspace) { |
576 | opt->is_changed = 1; | 575 | opt->is_changed = 1; |
577 | ip_rt_get_source(&optptr[srrptr-1], rt); | 576 | ip_rt_get_source(&optptr[srrptr-1], skb, rt); |
578 | ip_hdr(skb)->daddr = rt->rt_dst; | ||
579 | optptr[2] = srrptr+4; | 577 | optptr[2] = srrptr+4; |
580 | } else if (net_ratelimit()) | 578 | } else if (net_ratelimit()) |
581 | printk(KERN_CRIT "ip_forward(): Argh! Destination lost!\n"); | 579 | printk(KERN_CRIT "ip_forward(): Argh! Destination lost!\n"); |
582 | if (opt->ts_needaddr) { | 580 | if (opt->ts_needaddr) { |
583 | optptr = raw + opt->ts; | 581 | optptr = raw + opt->ts; |
584 | ip_rt_get_source(&optptr[optptr[2]-9], rt); | 582 | ip_rt_get_source(&optptr[optptr[2]-9], skb, rt); |
585 | opt->is_changed = 1; | 583 | opt->is_changed = 1; |
586 | } | 584 | } |
587 | } | 585 | } |
@@ -603,7 +601,7 @@ int ip_options_rcv_srr(struct sk_buff *skb) | |||
603 | unsigned long orefdst; | 601 | unsigned long orefdst; |
604 | int err; | 602 | int err; |
605 | 603 | ||
606 | if (!opt->srr) | 604 | if (!rt) |
607 | return 0; | 605 | return 0; |
608 | 606 | ||
609 | if (skb->pkt_type != PACKET_HOST) | 607 | if (skb->pkt_type != PACKET_HOST) |
@@ -637,7 +635,7 @@ int ip_options_rcv_srr(struct sk_buff *skb) | |||
637 | if (rt2->rt_type != RTN_LOCAL) | 635 | if (rt2->rt_type != RTN_LOCAL) |
638 | break; | 636 | break; |
639 | /* Superfast 8) loopback forward */ | 637 | /* Superfast 8) loopback forward */ |
640 | memcpy(&iph->daddr, &optptr[srrptr-1], 4); | 638 | iph->daddr = nexthop; |
641 | opt->is_changed = 1; | 639 | opt->is_changed = 1; |
642 | } | 640 | } |
643 | if (srrptr <= srrspace) { | 641 | if (srrptr <= srrspace) { |
@@ -646,3 +644,4 @@ int ip_options_rcv_srr(struct sk_buff *skb) | |||
646 | } | 644 | } |
647 | return 0; | 645 | return 0; |
648 | } | 646 | } |
647 | EXPORT_SYMBOL(ip_options_rcv_srr); | ||