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.c63
1 files changed, 27 insertions, 36 deletions
diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c
index 4d315158fd3c..d107543d3f81 100644
--- a/net/ipv4/ip_options.c
+++ b/net/ipv4/ip_options.c
@@ -45,7 +45,6 @@ void ip_options_build(struct sk_buff * skb, struct ip_options * opt,
45 memcpy(&(IPCB(skb)->opt), opt, sizeof(struct ip_options)); 45 memcpy(&(IPCB(skb)->opt), opt, sizeof(struct ip_options));
46 memcpy(iph+sizeof(struct iphdr), opt->__data, opt->optlen); 46 memcpy(iph+sizeof(struct iphdr), opt->__data, opt->optlen);
47 opt = &(IPCB(skb)->opt); 47 opt = &(IPCB(skb)->opt);
48 opt->is_data = 0;
49 48
50 if (opt->srr) 49 if (opt->srr)
51 memcpy(iph+opt->srr+iph[opt->srr+1]-4, &daddr, 4); 50 memcpy(iph+opt->srr+iph[opt->srr+1]-4, &daddr, 4);
@@ -95,8 +94,6 @@ int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb)
95 94
96 memset(dopt, 0, sizeof(struct ip_options)); 95 memset(dopt, 0, sizeof(struct ip_options));
97 96
98 dopt->is_data = 1;
99
100 sopt = &(IPCB(skb)->opt); 97 sopt = &(IPCB(skb)->opt);
101 98
102 if (sopt->optlen == 0) { 99 if (sopt->optlen == 0) {
@@ -107,10 +104,7 @@ int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb)
107 sptr = skb_network_header(skb); 104 sptr = skb_network_header(skb);
108 dptr = dopt->__data; 105 dptr = dopt->__data;
109 106
110 if (skb->dst) 107 daddr = skb->rtable->rt_spec_dst;
111 daddr = ((struct rtable*)skb->dst)->rt_spec_dst;
112 else
113 daddr = ip_hdr(skb)->daddr;
114 108
115 if (sopt->rr) { 109 if (sopt->rr) {
116 optlen = sptr[sopt->rr+1]; 110 optlen = sptr[sopt->rr+1];
@@ -151,7 +145,7 @@ int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb)
151 __be32 addr; 145 __be32 addr;
152 146
153 memcpy(&addr, sptr+soffset-1, 4); 147 memcpy(&addr, sptr+soffset-1, 4);
154 if (inet_addr_type(&init_net, addr) != RTN_LOCAL) { 148 if (inet_addr_type(dev_net(skb->dst->dev), addr) != RTN_LOCAL) {
155 dopt->ts_needtime = 1; 149 dopt->ts_needtime = 1;
156 soffset += 8; 150 soffset += 8;
157 } 151 }
@@ -254,26 +248,22 @@ void ip_options_fragment(struct sk_buff * skb)
254 * If opt == NULL, then skb->data should point to IP header. 248 * If opt == NULL, then skb->data should point to IP header.
255 */ 249 */
256 250
257int ip_options_compile(struct ip_options * opt, struct sk_buff * skb) 251int ip_options_compile(struct net *net,
252 struct ip_options * opt, struct sk_buff * skb)
258{ 253{
259 int l; 254 int l;
260 unsigned char * iph; 255 unsigned char * iph;
261 unsigned char * optptr; 256 unsigned char * optptr;
262 int optlen; 257 int optlen;
263 unsigned char * pp_ptr = NULL; 258 unsigned char * pp_ptr = NULL;
264 struct rtable *rt = skb ? (struct rtable*)skb->dst : NULL; 259 struct rtable *rt = NULL;
265 260
266 if (!opt) { 261 if (skb != NULL) {
267 opt = &(IPCB(skb)->opt); 262 rt = skb->rtable;
268 iph = skb_network_header(skb); 263 optptr = (unsigned char *)&(ip_hdr(skb)[1]);
269 opt->optlen = ((struct iphdr *)iph)->ihl*4 - sizeof(struct iphdr); 264 } else
270 optptr = iph + sizeof(struct iphdr); 265 optptr = opt->__data;
271 opt->is_data = 0; 266 iph = optptr - sizeof(struct iphdr);
272 } else {
273 optptr = opt->is_data ? opt->__data :
274 (unsigned char *)&(ip_hdr(skb)[1]);
275 iph = optptr - sizeof(struct iphdr);
276 }
277 267
278 for (l = opt->optlen; l > 0; ) { 268 for (l = opt->optlen; l > 0; ) {
279 switch (*optptr) { 269 switch (*optptr) {
@@ -400,7 +390,7 @@ int ip_options_compile(struct ip_options * opt, struct sk_buff * skb)
400 { 390 {
401 __be32 addr; 391 __be32 addr;
402 memcpy(&addr, &optptr[optptr[2]-1], 4); 392 memcpy(&addr, &optptr[optptr[2]-1], 4);
403 if (inet_addr_type(&init_net, addr) == RTN_UNICAST) 393 if (inet_addr_type(net, addr) == RTN_UNICAST)
404 break; 394 break;
405 if (skb) 395 if (skb)
406 timeptr = (__be32*)&optptr[optptr[2]+3]; 396 timeptr = (__be32*)&optptr[optptr[2]+3];
@@ -517,14 +507,13 @@ static struct ip_options *ip_options_get_alloc(const int optlen)
517 GFP_KERNEL); 507 GFP_KERNEL);
518} 508}
519 509
520static int ip_options_get_finish(struct ip_options **optp, 510static int ip_options_get_finish(struct net *net, struct ip_options **optp,
521 struct ip_options *opt, int optlen) 511 struct ip_options *opt, int optlen)
522{ 512{
523 while (optlen & 3) 513 while (optlen & 3)
524 opt->__data[optlen++] = IPOPT_END; 514 opt->__data[optlen++] = IPOPT_END;
525 opt->optlen = optlen; 515 opt->optlen = optlen;
526 opt->is_data = 1; 516 if (optlen && ip_options_compile(net, opt, NULL)) {
527 if (optlen && ip_options_compile(opt, NULL)) {
528 kfree(opt); 517 kfree(opt);
529 return -EINVAL; 518 return -EINVAL;
530 } 519 }
@@ -533,7 +522,8 @@ static int ip_options_get_finish(struct ip_options **optp,
533 return 0; 522 return 0;
534} 523}
535 524
536int ip_options_get_from_user(struct ip_options **optp, unsigned char __user *data, int optlen) 525int ip_options_get_from_user(struct net *net, struct ip_options **optp,
526 unsigned char __user *data, int optlen)
537{ 527{
538 struct ip_options *opt = ip_options_get_alloc(optlen); 528 struct ip_options *opt = ip_options_get_alloc(optlen);
539 529
@@ -543,10 +533,11 @@ int ip_options_get_from_user(struct ip_options **optp, unsigned char __user *dat
543 kfree(opt); 533 kfree(opt);
544 return -EFAULT; 534 return -EFAULT;
545 } 535 }
546 return ip_options_get_finish(optp, opt, optlen); 536 return ip_options_get_finish(net, optp, opt, optlen);
547} 537}
548 538
549int ip_options_get(struct ip_options **optp, unsigned char *data, int optlen) 539int ip_options_get(struct net *net, struct ip_options **optp,
540 unsigned char *data, int optlen)
550{ 541{
551 struct ip_options *opt = ip_options_get_alloc(optlen); 542 struct ip_options *opt = ip_options_get_alloc(optlen);
552 543
@@ -554,14 +545,14 @@ int ip_options_get(struct ip_options **optp, unsigned char *data, int optlen)
554 return -ENOMEM; 545 return -ENOMEM;
555 if (optlen) 546 if (optlen)
556 memcpy(opt->__data, data, optlen); 547 memcpy(opt->__data, data, optlen);
557 return ip_options_get_finish(optp, opt, optlen); 548 return ip_options_get_finish(net, optp, opt, optlen);
558} 549}
559 550
560void ip_forward_options(struct sk_buff *skb) 551void ip_forward_options(struct sk_buff *skb)
561{ 552{
562 struct ip_options * opt = &(IPCB(skb)->opt); 553 struct ip_options * opt = &(IPCB(skb)->opt);
563 unsigned char * optptr; 554 unsigned char * optptr;
564 struct rtable *rt = (struct rtable*)skb->dst; 555 struct rtable *rt = skb->rtable;
565 unsigned char *raw = skb_network_header(skb); 556 unsigned char *raw = skb_network_header(skb);
566 557
567 if (opt->rr_needaddr) { 558 if (opt->rr_needaddr) {
@@ -609,7 +600,7 @@ int ip_options_rcv_srr(struct sk_buff *skb)
609 __be32 nexthop; 600 __be32 nexthop;
610 struct iphdr *iph = ip_hdr(skb); 601 struct iphdr *iph = ip_hdr(skb);
611 unsigned char *optptr = skb_network_header(skb) + opt->srr; 602 unsigned char *optptr = skb_network_header(skb) + opt->srr;
612 struct rtable *rt = (struct rtable*)skb->dst; 603 struct rtable *rt = skb->rtable;
613 struct rtable *rt2; 604 struct rtable *rt2;
614 int err; 605 int err;
615 606
@@ -634,13 +625,13 @@ int ip_options_rcv_srr(struct sk_buff *skb)
634 } 625 }
635 memcpy(&nexthop, &optptr[srrptr-1], 4); 626 memcpy(&nexthop, &optptr[srrptr-1], 4);
636 627
637 rt = (struct rtable*)skb->dst; 628 rt = skb->rtable;
638 skb->dst = NULL; 629 skb->rtable = NULL;
639 err = ip_route_input(skb, nexthop, iph->saddr, iph->tos, skb->dev); 630 err = ip_route_input(skb, nexthop, iph->saddr, iph->tos, skb->dev);
640 rt2 = (struct rtable*)skb->dst; 631 rt2 = skb->rtable;
641 if (err || (rt2->rt_type != RTN_UNICAST && rt2->rt_type != RTN_LOCAL)) { 632 if (err || (rt2->rt_type != RTN_UNICAST && rt2->rt_type != RTN_LOCAL)) {
642 ip_rt_put(rt2); 633 ip_rt_put(rt2);
643 skb->dst = &rt->u.dst; 634 skb->rtable = rt;
644 return -EINVAL; 635 return -EINVAL;
645 } 636 }
646 ip_rt_put(rt); 637 ip_rt_put(rt);