aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/ip_options.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4/ip_options.c')
-rw-r--r--net/ipv4/ip_options.c85
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
39void ip_options_build(struct sk_buff * skb, struct ip_options * opt, 40void 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
86int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb) 87int 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 468EXPORT_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
502static struct ip_options *ip_options_get_alloc(const int optlen) 501static 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
508static int ip_options_get_finish(struct net *net, struct ip_options **optp, 507static 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
523int ip_options_get_from_user(struct net *net, struct ip_options **optp, 522int 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
537int ip_options_get(struct net *net, struct ip_options **optp, 536int 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}
647EXPORT_SYMBOL(ip_options_rcv_srr);