Merge branch 'e1000-fixes' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik...
[pandora-kernel.git] / net / ipv4 / ip_options.c
1 /*
2  * INET         An implementation of the TCP/IP protocol suite for the LINUX
3  *              operating system.  INET is implemented using the  BSD Socket
4  *              interface as the means of communication with the user level.
5  *
6  *              The options processing module for ip.c
7  *
8  * Version:     $Id: ip_options.c,v 1.21 2001/09/01 00:31:50 davem Exp $
9  *
10  * Authors:     A.N.Kuznetsov
11  *
12  */
13
14 #include <linux/capability.h>
15 #include <linux/module.h>
16 #include <linux/types.h>
17 #include <asm/uaccess.h>
18 #include <linux/skbuff.h>
19 #include <linux/ip.h>
20 #include <linux/icmp.h>
21 #include <linux/netdevice.h>
22 #include <linux/rtnetlink.h>
23 #include <net/sock.h>
24 #include <net/ip.h>
25 #include <net/icmp.h>
26 #include <net/route.h>
27 #include <net/cipso_ipv4.h>
28
29 /*
30  * Write options to IP header, record destination address to
31  * source route option, address of outgoing interface
32  * (we should already know it, so that this  function is allowed be
33  * called only after routing decision) and timestamp,
34  * if we originate this datagram.
35  *
36  * daddr is real destination address, next hop is recorded in IP header.
37  * saddr is address of outgoing interface.
38  */
39
40 void ip_options_build(struct sk_buff * skb, struct ip_options * opt,
41                             __be32 daddr, struct rtable *rt, int is_frag)
42 {
43         unsigned char *iph = skb_network_header(skb);
44
45         memcpy(&(IPCB(skb)->opt), opt, sizeof(struct ip_options));
46         memcpy(iph+sizeof(struct iphdr), opt->__data, opt->optlen);
47         opt = &(IPCB(skb)->opt);
48         opt->is_data = 0;
49
50         if (opt->srr)
51                 memcpy(iph+opt->srr+iph[opt->srr+1]-4, &daddr, 4);
52
53         if (!is_frag) {
54                 if (opt->rr_needaddr)
55                         ip_rt_get_source(iph+opt->rr+iph[opt->rr+2]-5, rt);
56                 if (opt->ts_needaddr)
57                         ip_rt_get_source(iph+opt->ts+iph[opt->ts+2]-9, rt);
58                 if (opt->ts_needtime) {
59                         struct timeval tv;
60                         __be32 midtime;
61                         do_gettimeofday(&tv);
62                         midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
63                         memcpy(iph+opt->ts+iph[opt->ts+2]-5, &midtime, 4);
64                 }
65                 return;
66         }
67         if (opt->rr) {
68                 memset(iph+opt->rr, IPOPT_NOP, iph[opt->rr+1]);
69                 opt->rr = 0;
70                 opt->rr_needaddr = 0;
71         }
72         if (opt->ts) {
73                 memset(iph+opt->ts, IPOPT_NOP, iph[opt->ts+1]);
74                 opt->ts = 0;
75                 opt->ts_needaddr = opt->ts_needtime = 0;
76         }
77 }
78
79 /*
80  * Provided (sopt, skb) points to received options,
81  * build in dopt compiled option set appropriate for answering.
82  * i.e. invert SRR option, copy anothers,
83  * and grab room in RR/TS options.
84  *
85  * NOTE: dopt cannot point to skb.
86  */
87
88 int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb)
89 {
90         struct ip_options *sopt;
91         unsigned char *sptr, *dptr;
92         int soffset, doffset;
93         int     optlen;
94         __be32  daddr;
95
96         memset(dopt, 0, sizeof(struct ip_options));
97
98         dopt->is_data = 1;
99
100         sopt = &(IPCB(skb)->opt);
101
102         if (sopt->optlen == 0) {
103                 dopt->optlen = 0;
104                 return 0;
105         }
106
107         sptr = skb_network_header(skb);
108         dptr = dopt->__data;
109
110         if (skb->dst)
111                 daddr = ((struct rtable*)skb->dst)->rt_spec_dst;
112         else
113                 daddr = ip_hdr(skb)->daddr;
114
115         if (sopt->rr) {
116                 optlen  = sptr[sopt->rr+1];
117                 soffset = sptr[sopt->rr+2];
118                 dopt->rr = dopt->optlen + sizeof(struct iphdr);
119                 memcpy(dptr, sptr+sopt->rr, optlen);
120                 if (sopt->rr_needaddr && soffset <= optlen) {
121                         if (soffset + 3 > optlen)
122                                 return -EINVAL;
123                         dptr[2] = soffset + 4;
124                         dopt->rr_needaddr = 1;
125                 }
126                 dptr += optlen;
127                 dopt->optlen += optlen;
128         }
129         if (sopt->ts) {
130                 optlen = sptr[sopt->ts+1];
131                 soffset = sptr[sopt->ts+2];
132                 dopt->ts = dopt->optlen + sizeof(struct iphdr);
133                 memcpy(dptr, sptr+sopt->ts, optlen);
134                 if (soffset <= optlen) {
135                         if (sopt->ts_needaddr) {
136                                 if (soffset + 3 > optlen)
137                                         return -EINVAL;
138                                 dopt->ts_needaddr = 1;
139                                 soffset += 4;
140                         }
141                         if (sopt->ts_needtime) {
142                                 if (soffset + 3 > optlen)
143                                         return -EINVAL;
144                                 if ((dptr[3]&0xF) != IPOPT_TS_PRESPEC) {
145                                         dopt->ts_needtime = 1;
146                                         soffset += 4;
147                                 } else {
148                                         dopt->ts_needtime = 0;
149
150                                         if (soffset + 8 <= optlen) {
151                                                 __be32 addr;
152
153                                                 memcpy(&addr, sptr+soffset-1, 4);
154                                                 if (inet_addr_type(addr) != RTN_LOCAL) {
155                                                         dopt->ts_needtime = 1;
156                                                         soffset += 8;
157                                                 }
158                                         }
159                                 }
160                         }
161                         dptr[2] = soffset;
162                 }
163                 dptr += optlen;
164                 dopt->optlen += optlen;
165         }
166         if (sopt->srr) {
167                 unsigned char * start = sptr+sopt->srr;
168                 __be32 faddr;
169
170                 optlen  = start[1];
171                 soffset = start[2];
172                 doffset = 0;
173                 if (soffset > optlen)
174                         soffset = optlen + 1;
175                 soffset -= 4;
176                 if (soffset > 3) {
177                         memcpy(&faddr, &start[soffset-1], 4);
178                         for (soffset-=4, doffset=4; soffset > 3; soffset-=4, doffset+=4)
179                                 memcpy(&dptr[doffset-1], &start[soffset-1], 4);
180                         /*
181                          * RFC1812 requires to fix illegal source routes.
182                          */
183                         if (memcmp(&ip_hdr(skb)->saddr,
184                                    &start[soffset + 3], 4) == 0)
185                                 doffset -= 4;
186                 }
187                 if (doffset > 3) {
188                         memcpy(&start[doffset-1], &daddr, 4);
189                         dopt->faddr = faddr;
190                         dptr[0] = start[0];
191                         dptr[1] = doffset+3;
192                         dptr[2] = 4;
193                         dptr += doffset+3;
194                         dopt->srr = dopt->optlen + sizeof(struct iphdr);
195                         dopt->optlen += doffset+3;
196                         dopt->is_strictroute = sopt->is_strictroute;
197                 }
198         }
199         if (sopt->cipso) {
200                 optlen  = sptr[sopt->cipso+1];
201                 dopt->cipso = dopt->optlen+sizeof(struct iphdr);
202                 memcpy(dptr, sptr+sopt->cipso, optlen);
203                 dptr += optlen;
204                 dopt->optlen += optlen;
205         }
206         while (dopt->optlen & 3) {
207                 *dptr++ = IPOPT_END;
208                 dopt->optlen++;
209         }
210         return 0;
211 }
212
213 /*
214  *      Options "fragmenting", just fill options not
215  *      allowed in fragments with NOOPs.
216  *      Simple and stupid 8), but the most efficient way.
217  */
218
219 void ip_options_fragment(struct sk_buff * skb)
220 {
221         unsigned char *optptr = skb_network_header(skb) + sizeof(struct iphdr);
222         struct ip_options * opt = &(IPCB(skb)->opt);
223         int  l = opt->optlen;
224         int  optlen;
225
226         while (l > 0) {
227                 switch (*optptr) {
228                 case IPOPT_END:
229                         return;
230                 case IPOPT_NOOP:
231                         l--;
232                         optptr++;
233                         continue;
234                 }
235                 optlen = optptr[1];
236                 if (optlen<2 || optlen>l)
237                   return;
238                 if (!IPOPT_COPIED(*optptr))
239                         memset(optptr, IPOPT_NOOP, optlen);
240                 l -= optlen;
241                 optptr += optlen;
242         }
243         opt->ts = 0;
244         opt->rr = 0;
245         opt->rr_needaddr = 0;
246         opt->ts_needaddr = 0;
247         opt->ts_needtime = 0;
248         return;
249 }
250
251 /*
252  * Verify options and fill pointers in struct options.
253  * Caller should clear *opt, and set opt->data.
254  * If opt == NULL, then skb->data should point to IP header.
255  */
256
257 int ip_options_compile(struct ip_options * opt, struct sk_buff * skb)
258 {
259         int l;
260         unsigned char * iph;
261         unsigned char * optptr;
262         int optlen;
263         unsigned char * pp_ptr = NULL;
264         struct rtable *rt = skb ? (struct rtable*)skb->dst : NULL;
265
266         if (!opt) {
267                 opt = &(IPCB(skb)->opt);
268                 iph = skb_network_header(skb);
269                 opt->optlen = ((struct iphdr *)iph)->ihl*4 - sizeof(struct iphdr);
270                 optptr = iph + sizeof(struct iphdr);
271                 opt->is_data = 0;
272         } else {
273                 optptr = opt->is_data ? opt->__data :
274                                         (unsigned char *)&(ip_hdr(skb)[1]);
275                 iph = optptr - sizeof(struct iphdr);
276         }
277
278         for (l = opt->optlen; l > 0; ) {
279                 switch (*optptr) {
280                       case IPOPT_END:
281                         for (optptr++, l--; l>0; optptr++, l--) {
282                                 if (*optptr != IPOPT_END) {
283                                         *optptr = IPOPT_END;
284                                         opt->is_changed = 1;
285                                 }
286                         }
287                         goto eol;
288                       case IPOPT_NOOP:
289                         l--;
290                         optptr++;
291                         continue;
292                 }
293                 optlen = optptr[1];
294                 if (optlen<2 || optlen>l) {
295                         pp_ptr = optptr;
296                         goto error;
297                 }
298                 switch (*optptr) {
299                       case IPOPT_SSRR:
300                       case IPOPT_LSRR:
301                         if (optlen < 3) {
302                                 pp_ptr = optptr + 1;
303                                 goto error;
304                         }
305                         if (optptr[2] < 4) {
306                                 pp_ptr = optptr + 2;
307                                 goto error;
308                         }
309                         /* NB: cf RFC-1812 5.2.4.1 */
310                         if (opt->srr) {
311                                 pp_ptr = optptr;
312                                 goto error;
313                         }
314                         if (!skb) {
315                                 if (optptr[2] != 4 || optlen < 7 || ((optlen-3) & 3)) {
316                                         pp_ptr = optptr + 1;
317                                         goto error;
318                                 }
319                                 memcpy(&opt->faddr, &optptr[3], 4);
320                                 if (optlen > 7)
321                                         memmove(&optptr[3], &optptr[7], optlen-7);
322                         }
323                         opt->is_strictroute = (optptr[0] == IPOPT_SSRR);
324                         opt->srr = optptr - iph;
325                         break;
326                       case IPOPT_RR:
327                         if (opt->rr) {
328                                 pp_ptr = optptr;
329                                 goto error;
330                         }
331                         if (optlen < 3) {
332                                 pp_ptr = optptr + 1;
333                                 goto error;
334                         }
335                         if (optptr[2] < 4) {
336                                 pp_ptr = optptr + 2;
337                                 goto error;
338                         }
339                         if (optptr[2] <= optlen) {
340                                 if (optptr[2]+3 > optlen) {
341                                         pp_ptr = optptr + 2;
342                                         goto error;
343                                 }
344                                 if (skb) {
345                                         memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4);
346                                         opt->is_changed = 1;
347                                 }
348                                 optptr[2] += 4;
349                                 opt->rr_needaddr = 1;
350                         }
351                         opt->rr = optptr - iph;
352                         break;
353                       case IPOPT_TIMESTAMP:
354                         if (opt->ts) {
355                                 pp_ptr = optptr;
356                                 goto error;
357                         }
358                         if (optlen < 4) {
359                                 pp_ptr = optptr + 1;
360                                 goto error;
361                         }
362                         if (optptr[2] < 5) {
363                                 pp_ptr = optptr + 2;
364                                 goto error;
365                         }
366                         if (optptr[2] <= optlen) {
367                                 __be32 *timeptr = NULL;
368                                 if (optptr[2]+3 > optptr[1]) {
369                                         pp_ptr = optptr + 2;
370                                         goto error;
371                                 }
372                                 switch (optptr[3]&0xF) {
373                                       case IPOPT_TS_TSONLY:
374                                         opt->ts = optptr - iph;
375                                         if (skb)
376                                                 timeptr = (__be32*)&optptr[optptr[2]-1];
377                                         opt->ts_needtime = 1;
378                                         optptr[2] += 4;
379                                         break;
380                                       case IPOPT_TS_TSANDADDR:
381                                         if (optptr[2]+7 > optptr[1]) {
382                                                 pp_ptr = optptr + 2;
383                                                 goto error;
384                                         }
385                                         opt->ts = optptr - iph;
386                                         if (skb) {
387                                                 memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4);
388                                                 timeptr = (__be32*)&optptr[optptr[2]+3];
389                                         }
390                                         opt->ts_needaddr = 1;
391                                         opt->ts_needtime = 1;
392                                         optptr[2] += 8;
393                                         break;
394                                       case IPOPT_TS_PRESPEC:
395                                         if (optptr[2]+7 > optptr[1]) {
396                                                 pp_ptr = optptr + 2;
397                                                 goto error;
398                                         }
399                                         opt->ts = optptr - iph;
400                                         {
401                                                 __be32 addr;
402                                                 memcpy(&addr, &optptr[optptr[2]-1], 4);
403                                                 if (inet_addr_type(addr) == RTN_UNICAST)
404                                                         break;
405                                                 if (skb)
406                                                         timeptr = (__be32*)&optptr[optptr[2]+3];
407                                         }
408                                         opt->ts_needtime = 1;
409                                         optptr[2] += 8;
410                                         break;
411                                       default:
412                                         if (!skb && !capable(CAP_NET_RAW)) {
413                                                 pp_ptr = optptr + 3;
414                                                 goto error;
415                                         }
416                                         break;
417                                 }
418                                 if (timeptr) {
419                                         struct timeval tv;
420                                         __be32  midtime;
421                                         do_gettimeofday(&tv);
422                                         midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
423                                         memcpy(timeptr, &midtime, sizeof(__be32));
424                                         opt->is_changed = 1;
425                                 }
426                         } else {
427                                 unsigned overflow = optptr[3]>>4;
428                                 if (overflow == 15) {
429                                         pp_ptr = optptr + 3;
430                                         goto error;
431                                 }
432                                 opt->ts = optptr - iph;
433                                 if (skb) {
434                                         optptr[3] = (optptr[3]&0xF)|((overflow+1)<<4);
435                                         opt->is_changed = 1;
436                                 }
437                         }
438                         break;
439                       case IPOPT_RA:
440                         if (optlen < 4) {
441                                 pp_ptr = optptr + 1;
442                                 goto error;
443                         }
444                         if (optptr[2] == 0 && optptr[3] == 0)
445                                 opt->router_alert = optptr - iph;
446                         break;
447                       case IPOPT_CIPSO:
448                         if ((!skb && !capable(CAP_NET_RAW)) || opt->cipso) {
449                                 pp_ptr = optptr;
450                                 goto error;
451                         }
452                         opt->cipso = optptr - iph;
453                         if (cipso_v4_validate(&optptr)) {
454                                 pp_ptr = optptr;
455                                 goto error;
456                         }
457                         break;
458                       case IPOPT_SEC:
459                       case IPOPT_SID:
460                       default:
461                         if (!skb && !capable(CAP_NET_RAW)) {
462                                 pp_ptr = optptr;
463                                 goto error;
464                         }
465                         break;
466                 }
467                 l -= optlen;
468                 optptr += optlen;
469         }
470
471 eol:
472         if (!pp_ptr)
473                 return 0;
474
475 error:
476         if (skb) {
477                 icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl((pp_ptr-iph)<<24));
478         }
479         return -EINVAL;
480 }
481
482
483 /*
484  *      Undo all the changes done by ip_options_compile().
485  */
486
487 void ip_options_undo(struct ip_options * opt)
488 {
489         if (opt->srr) {
490                 unsigned  char * optptr = opt->__data+opt->srr-sizeof(struct  iphdr);
491                 memmove(optptr+7, optptr+3, optptr[1]-7);
492                 memcpy(optptr+3, &opt->faddr, 4);
493         }
494         if (opt->rr_needaddr) {
495                 unsigned  char * optptr = opt->__data+opt->rr-sizeof(struct  iphdr);
496                 optptr[2] -= 4;
497                 memset(&optptr[optptr[2]-1], 0, 4);
498         }
499         if (opt->ts) {
500                 unsigned  char * optptr = opt->__data+opt->ts-sizeof(struct  iphdr);
501                 if (opt->ts_needtime) {
502                         optptr[2] -= 4;
503                         memset(&optptr[optptr[2]-1], 0, 4);
504                         if ((optptr[3]&0xF) == IPOPT_TS_PRESPEC)
505                                 optptr[2] -= 4;
506                 }
507                 if (opt->ts_needaddr) {
508                         optptr[2] -= 4;
509                         memset(&optptr[optptr[2]-1], 0, 4);
510                 }
511         }
512 }
513
514 static struct ip_options *ip_options_get_alloc(const int optlen)
515 {
516         struct ip_options *opt = kmalloc(sizeof(*opt) + ((optlen + 3) & ~3),
517                                          GFP_KERNEL);
518         if (opt)
519                 memset(opt, 0, sizeof(*opt));
520         return opt;
521 }
522
523 static int ip_options_get_finish(struct ip_options **optp,
524                                  struct ip_options *opt, int optlen)
525 {
526         while (optlen & 3)
527                 opt->__data[optlen++] = IPOPT_END;
528         opt->optlen = optlen;
529         opt->is_data = 1;
530         if (optlen && ip_options_compile(opt, NULL)) {
531                 kfree(opt);
532                 return -EINVAL;
533         }
534         kfree(*optp);
535         *optp = opt;
536         return 0;
537 }
538
539 int ip_options_get_from_user(struct ip_options **optp, unsigned char __user *data, int optlen)
540 {
541         struct ip_options *opt = ip_options_get_alloc(optlen);
542
543         if (!opt)
544                 return -ENOMEM;
545         if (optlen && copy_from_user(opt->__data, data, optlen)) {
546                 kfree(opt);
547                 return -EFAULT;
548         }
549         return ip_options_get_finish(optp, opt, optlen);
550 }
551
552 int ip_options_get(struct ip_options **optp, unsigned char *data, int optlen)
553 {
554         struct ip_options *opt = ip_options_get_alloc(optlen);
555
556         if (!opt)
557                 return -ENOMEM;
558         if (optlen)
559                 memcpy(opt->__data, data, optlen);
560         return ip_options_get_finish(optp, opt, optlen);
561 }
562
563 void ip_forward_options(struct sk_buff *skb)
564 {
565         struct   ip_options * opt       = &(IPCB(skb)->opt);
566         unsigned char * optptr;
567         struct rtable *rt = (struct rtable*)skb->dst;
568         unsigned char *raw = skb_network_header(skb);
569
570         if (opt->rr_needaddr) {
571                 optptr = (unsigned char *)raw + opt->rr;
572                 ip_rt_get_source(&optptr[optptr[2]-5], rt);
573                 opt->is_changed = 1;
574         }
575         if (opt->srr_is_hit) {
576                 int srrptr, srrspace;
577
578                 optptr = raw + opt->srr;
579
580                 for ( srrptr=optptr[2], srrspace = optptr[1];
581                      srrptr <= srrspace;
582                      srrptr += 4
583                      ) {
584                         if (srrptr + 3 > srrspace)
585                                 break;
586                         if (memcmp(&rt->rt_dst, &optptr[srrptr-1], 4) == 0)
587                                 break;
588                 }
589                 if (srrptr + 3 <= srrspace) {
590                         opt->is_changed = 1;
591                         ip_rt_get_source(&optptr[srrptr-1], rt);
592                         ip_hdr(skb)->daddr = rt->rt_dst;
593                         optptr[2] = srrptr+4;
594                 } else if (net_ratelimit())
595                         printk(KERN_CRIT "ip_forward(): Argh! Destination lost!\n");
596                 if (opt->ts_needaddr) {
597                         optptr = raw + opt->ts;
598                         ip_rt_get_source(&optptr[optptr[2]-9], rt);
599                         opt->is_changed = 1;
600                 }
601         }
602         if (opt->is_changed) {
603                 opt->is_changed = 0;
604                 ip_send_check(ip_hdr(skb));
605         }
606 }
607
608 int ip_options_rcv_srr(struct sk_buff *skb)
609 {
610         struct ip_options *opt = &(IPCB(skb)->opt);
611         int srrspace, srrptr;
612         __be32 nexthop;
613         struct iphdr *iph = ip_hdr(skb);
614         unsigned char *optptr = skb_network_header(skb) + opt->srr;
615         struct rtable *rt = (struct rtable*)skb->dst;
616         struct rtable *rt2;
617         int err;
618
619         if (!opt->srr)
620                 return 0;
621
622         if (skb->pkt_type != PACKET_HOST)
623                 return -EINVAL;
624         if (rt->rt_type == RTN_UNICAST) {
625                 if (!opt->is_strictroute)
626                         return 0;
627                 icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl(16<<24));
628                 return -EINVAL;
629         }
630         if (rt->rt_type != RTN_LOCAL)
631                 return -EINVAL;
632
633         for (srrptr=optptr[2], srrspace = optptr[1]; srrptr <= srrspace; srrptr += 4) {
634                 if (srrptr + 3 > srrspace) {
635                         icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl((opt->srr+2)<<24));
636                         return -EINVAL;
637                 }
638                 memcpy(&nexthop, &optptr[srrptr-1], 4);
639
640                 rt = (struct rtable*)skb->dst;
641                 skb->dst = NULL;
642                 err = ip_route_input(skb, nexthop, iph->saddr, iph->tos, skb->dev);
643                 rt2 = (struct rtable*)skb->dst;
644                 if (err || (rt2->rt_type != RTN_UNICAST && rt2->rt_type != RTN_LOCAL)) {
645                         ip_rt_put(rt2);
646                         skb->dst = &rt->u.dst;
647                         return -EINVAL;
648                 }
649                 ip_rt_put(rt);
650                 if (rt2->rt_type != RTN_LOCAL)
651                         break;
652                 /* Superfast 8) loopback forward */
653                 memcpy(&iph->daddr, &optptr[srrptr-1], 4);
654                 opt->is_changed = 1;
655         }
656         if (srrptr <= srrspace) {
657                 opt->srr_is_hit = 1;
658                 opt->is_changed = 1;
659         }
660         return 0;
661 }