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.
6 * The options processing module for ip.c
8 * Version: $Id: ip_options.c,v 1.19 2000/07/26 01:04:17 davem Exp $
10 * Authors: A.N.Kuznetsov
14 #include <linux/types.h>
15 #include <asm/uaccess.h>
16 #include <linux/skbuff.h>
18 #include <linux/icmp.h>
19 #include <linux/netdevice.h>
20 #include <linux/rtnetlink.h>
26 * Write options to IP header, record destination address to
27 * source route option, address of outgoing interface
28 * (we should already know it, so that this function is allowed be
29 * called only after routing decision) and timestamp,
30 * if we originate this datagram.
32 * daddr is real destination address, next hop is recorded in IP header.
33 * saddr is address of outgoing interface.
36 void ip_options_build(struct sk_buff
* skb
, struct ip_options
* opt
,
37 u32 daddr
, struct rtable
*rt
, int is_frag
)
39 unsigned char * iph
= skb
->nh
.raw
;
41 memcpy(&(IPCB(skb
)->opt
), opt
, sizeof(struct ip_options
));
42 memcpy(iph
+sizeof(struct iphdr
), opt
->__data
, opt
->optlen
);
43 opt
= &(IPCB(skb
)->opt
);
47 memcpy(iph
+opt
->srr
+iph
[opt
->srr
+1]-4, &daddr
, 4);
51 ip_rt_get_source(iph
+opt
->rr
+iph
[opt
->rr
+2]-5, rt
);
53 ip_rt_get_source(iph
+opt
->ts
+iph
[opt
->ts
+2]-9, rt
);
54 if (opt
->ts_needtime
) {
58 midtime
= htonl((tv
.tv_sec
% 86400) * 1000 + tv
.tv_usec
/ 1000);
59 memcpy(iph
+opt
->ts
+iph
[opt
->ts
+2]-5, &midtime
, 4);
64 memset(iph
+opt
->rr
, IPOPT_NOP
, iph
[opt
->rr
+1]);
69 memset(iph
+opt
->ts
, IPOPT_NOP
, iph
[opt
->ts
+1]);
71 opt
->ts_needaddr
= opt
->ts_needtime
= 0;
76 * Provided (sopt, skb) points to received options,
77 * build in dopt compiled option set appropriate for answering.
78 * i.e. invert SRR option, copy anothers,
79 * and grab room in RR/TS options.
81 * NOTE: dopt cannot point to skb.
84 int ip_options_echo(struct ip_options
* dopt
, struct sk_buff
* skb
)
86 struct ip_options
*sopt
;
87 unsigned char *sptr
, *dptr
;
92 memset(dopt
, 0, sizeof(struct ip_options
));
96 sopt
= &(IPCB(skb
)->opt
);
98 if (sopt
->optlen
== 0) {
107 daddr
= ((struct rtable
*)skb
->dst
)->rt_spec_dst
;
109 daddr
= skb
->nh
.iph
->daddr
;
112 optlen
= sptr
[sopt
->rr
+1];
113 soffset
= sptr
[sopt
->rr
+2];
114 dopt
->rr
= dopt
->optlen
+ sizeof(struct iphdr
);
115 memcpy(dptr
, sptr
+sopt
->rr
, optlen
);
116 if (sopt
->rr_needaddr
&& soffset
<= optlen
) {
117 if (soffset
+ 3 > optlen
)
119 dptr
[2] = soffset
+ 4;
120 dopt
->rr_needaddr
= 1;
123 dopt
->optlen
+= optlen
;
126 optlen
= sptr
[sopt
->ts
+1];
127 soffset
= sptr
[sopt
->ts
+2];
128 dopt
->ts
= dopt
->optlen
+ sizeof(struct iphdr
);
129 memcpy(dptr
, sptr
+sopt
->ts
, optlen
);
130 if (soffset
<= optlen
) {
131 if (sopt
->ts_needaddr
) {
132 if (soffset
+ 3 > optlen
)
134 dopt
->ts_needaddr
= 1;
137 if (sopt
->ts_needtime
) {
138 if (soffset
+ 3 > optlen
)
140 if ((dptr
[3]&0xF) != IPOPT_TS_PRESPEC
) {
141 dopt
->ts_needtime
= 1;
144 dopt
->ts_needtime
= 0;
146 if (soffset
+ 8 <= optlen
) {
149 memcpy(&addr
, sptr
+soffset
-1, 4);
150 if (inet_addr_type(addr
) != RTN_LOCAL
) {
151 dopt
->ts_needtime
= 1;
160 dopt
->optlen
+= optlen
;
163 unsigned char * start
= sptr
+sopt
->srr
;
169 if (soffset
> optlen
)
170 soffset
= optlen
+ 1;
173 memcpy(&faddr
, &start
[soffset
-1], 4);
174 for (soffset
-=4, doffset
=4; soffset
> 3; soffset
-=4, doffset
+=4)
175 memcpy(&dptr
[doffset
-1], &start
[soffset
-1], 4);
177 * RFC1812 requires to fix illegal source routes.
179 if (memcmp(&skb
->nh
.iph
->saddr
, &start
[soffset
+3], 4) == 0)
183 memcpy(&start
[doffset
-1], &daddr
, 4);
189 dopt
->srr
= dopt
->optlen
+ sizeof(struct iphdr
);
190 dopt
->optlen
+= doffset
+3;
191 dopt
->is_strictroute
= sopt
->is_strictroute
;
194 while (dopt
->optlen
& 3) {
202 * Options "fragmenting", just fill options not
203 * allowed in fragments with NOOPs.
204 * Simple and stupid 8), but the most efficient way.
207 void ip_options_fragment(struct sk_buff
* skb
)
209 unsigned char * optptr
= skb
->nh
.raw
;
210 struct ip_options
* opt
= &(IPCB(skb
)->opt
);
224 if (optlen
<2 || optlen
>l
)
226 if (!IPOPT_COPIED(*optptr
))
227 memset(optptr
, IPOPT_NOOP
, optlen
);
233 opt
->rr_needaddr
= 0;
234 opt
->ts_needaddr
= 0;
235 opt
->ts_needtime
= 0;
240 * Verify options and fill pointers in struct options.
241 * Caller should clear *opt, and set opt->data.
242 * If opt == NULL, then skb->data should point to IP header.
245 int ip_options_compile(struct ip_options
* opt
, struct sk_buff
* skb
)
249 unsigned char * optptr
;
251 unsigned char * pp_ptr
= NULL
;
252 struct rtable
*rt
= skb
? (struct rtable
*)skb
->dst
: NULL
;
255 opt
= &(IPCB(skb
)->opt
);
256 memset(opt
, 0, sizeof(struct ip_options
));
258 opt
->optlen
= ((struct iphdr
*)iph
)->ihl
*4 - sizeof(struct iphdr
);
259 optptr
= iph
+ sizeof(struct iphdr
);
262 optptr
= opt
->is_data
? opt
->__data
: (unsigned char*)&(skb
->nh
.iph
[1]);
263 iph
= optptr
- sizeof(struct iphdr
);
266 for (l
= opt
->optlen
; l
> 0; ) {
269 for (optptr
++, l
--; l
>0; l
--) {
270 if (*optptr
!= IPOPT_END
) {
282 if (optlen
<2 || optlen
>l
) {
297 /* NB: cf RFC-1812 5.2.4.1 */
303 if (optptr
[2] != 4 || optlen
< 7 || ((optlen
-3) & 3)) {
307 memcpy(&opt
->faddr
, &optptr
[3], 4);
309 memmove(&optptr
[3], &optptr
[7], optlen
-7);
311 opt
->is_strictroute
= (optptr
[0] == IPOPT_SSRR
);
312 opt
->srr
= optptr
- iph
;
327 if (optptr
[2] <= optlen
) {
328 if (optptr
[2]+3 > optlen
) {
333 memcpy(&optptr
[optptr
[2]-1], &rt
->rt_spec_dst
, 4);
337 opt
->rr_needaddr
= 1;
339 opt
->rr
= optptr
- iph
;
341 case IPOPT_TIMESTAMP
:
354 if (optptr
[2] <= optlen
) {
355 __u32
* timeptr
= NULL
;
356 if (optptr
[2]+3 > optptr
[1]) {
360 switch (optptr
[3]&0xF) {
361 case IPOPT_TS_TSONLY
:
362 opt
->ts
= optptr
- iph
;
364 timeptr
= (__u32
*)&optptr
[optptr
[2]-1];
365 opt
->ts_needtime
= 1;
368 case IPOPT_TS_TSANDADDR
:
369 if (optptr
[2]+7 > optptr
[1]) {
373 opt
->ts
= optptr
- iph
;
375 memcpy(&optptr
[optptr
[2]-1], &rt
->rt_spec_dst
, 4);
376 timeptr
= (__u32
*)&optptr
[optptr
[2]+3];
378 opt
->ts_needaddr
= 1;
379 opt
->ts_needtime
= 1;
382 case IPOPT_TS_PRESPEC
:
383 if (optptr
[2]+7 > optptr
[1]) {
387 opt
->ts
= optptr
- iph
;
390 memcpy(&addr
, &optptr
[optptr
[2]-1], 4);
391 if (inet_addr_type(addr
) == RTN_UNICAST
)
394 timeptr
= (__u32
*)&optptr
[optptr
[2]+3];
396 opt
->ts_needtime
= 1;
400 if (!skb
&& !capable(CAP_NET_RAW
)) {
409 do_gettimeofday(&tv
);
410 midtime
= htonl((tv
.tv_sec
% 86400) * 1000 + tv
.tv_usec
/ 1000);
411 memcpy(timeptr
, &midtime
, sizeof(__u32
));
415 unsigned overflow
= optptr
[3]>>4;
416 if (overflow
== 15) {
420 opt
->ts
= optptr
- iph
;
422 optptr
[3] = (optptr
[3]&0xF)|((overflow
+1)<<4);
432 if (optptr
[2] == 0 && optptr
[3] == 0)
433 opt
->router_alert
= optptr
- iph
;
438 if (!skb
&& !capable(CAP_NET_RAW
)) {
454 icmp_send(skb
, ICMP_PARAMETERPROB
, 0, htonl((pp_ptr
-iph
)<<24));
461 * Undo all the changes done by ip_options_compile().
464 void ip_options_undo(struct ip_options
* opt
)
467 unsigned char * optptr
= opt
->__data
+opt
->srr
-sizeof(struct iphdr
);
468 memmove(optptr
+7, optptr
+3, optptr
[1]-7);
469 memcpy(optptr
+3, &opt
->faddr
, 4);
471 if (opt
->rr_needaddr
) {
472 unsigned char * optptr
= opt
->__data
+opt
->rr
-sizeof(struct iphdr
);
474 memset(&optptr
[optptr
[2]-1], 0, 4);
477 unsigned char * optptr
= opt
->__data
+opt
->ts
-sizeof(struct iphdr
);
478 if (opt
->ts_needtime
) {
480 memset(&optptr
[optptr
[2]-1], 0, 4);
481 if ((optptr
[3]&0xF) == IPOPT_TS_PRESPEC
)
484 if (opt
->ts_needaddr
) {
486 memset(&optptr
[optptr
[2]-1], 0, 4);
491 int ip_options_get(struct ip_options
**optp
, unsigned char *data
, int optlen
, int user
)
493 struct ip_options
*opt
;
495 opt
= kmalloc(sizeof(struct ip_options
)+((optlen
+3)&~3), GFP_KERNEL
);
498 memset(opt
, 0, sizeof(struct ip_options
));
501 if (copy_from_user(opt
->__data
, data
, optlen
))
504 memcpy(opt
->__data
, data
, optlen
);
507 opt
->__data
[optlen
++] = IPOPT_END
;
508 opt
->optlen
= optlen
;
510 opt
->is_setbyuser
= 1;
511 if (optlen
&& ip_options_compile(opt
, NULL
)) {
519 void ip_forward_options(struct sk_buff
*skb
)
521 struct ip_options
* opt
= &(IPCB(skb
)->opt
);
522 unsigned char * optptr
;
523 struct rtable
*rt
= (struct rtable
*)skb
->dst
;
524 unsigned char *raw
= skb
->nh
.raw
;
526 if (opt
->rr_needaddr
) {
527 optptr
= (unsigned char *)raw
+ opt
->rr
;
528 ip_rt_get_source(&optptr
[optptr
[2]-5], rt
);
531 if (opt
->srr_is_hit
) {
532 int srrptr
, srrspace
;
534 optptr
= raw
+ opt
->srr
;
536 for ( srrptr
=optptr
[2], srrspace
= optptr
[1];
540 if (srrptr
+ 3 > srrspace
)
542 if (memcmp(&rt
->rt_dst
, &optptr
[srrptr
-1], 4) == 0)
545 if (srrptr
+ 3 <= srrspace
) {
547 ip_rt_get_source(&optptr
[srrptr
-1], rt
);
548 skb
->nh
.iph
->daddr
= rt
->rt_dst
;
549 optptr
[2] = srrptr
+4;
551 printk(KERN_CRIT
"ip_forward(): Argh! Destination lost!\n");
552 if (opt
->ts_needaddr
) {
553 optptr
= raw
+ opt
->ts
;
554 ip_rt_get_source(&optptr
[optptr
[2]-9], rt
);
558 if (opt
->is_changed
) {
560 ip_send_check(skb
->nh
.iph
);
564 int ip_options_rcv_srr(struct sk_buff
*skb
)
566 struct ip_options
*opt
= &(IPCB(skb
)->opt
);
567 int srrspace
, srrptr
;
569 struct iphdr
*iph
= skb
->nh
.iph
;
570 unsigned char * optptr
= skb
->nh
.raw
+ opt
->srr
;
571 struct rtable
*rt
= (struct rtable
*)skb
->dst
;
578 if (skb
->pkt_type
!= PACKET_HOST
)
580 if (rt
->rt_type
== RTN_UNICAST
) {
581 if (!opt
->is_strictroute
)
583 icmp_send(skb
, ICMP_PARAMETERPROB
, 0, htonl(16<<24));
586 if (rt
->rt_type
!= RTN_LOCAL
)
589 for (srrptr
=optptr
[2], srrspace
= optptr
[1]; srrptr
<= srrspace
; srrptr
+= 4) {
590 if (srrptr
+ 3 > srrspace
) {
591 icmp_send(skb
, ICMP_PARAMETERPROB
, 0, htonl((opt
->srr
+2)<<24));
594 memcpy(&nexthop
, &optptr
[srrptr
-1], 4);
596 rt
= (struct rtable
*)skb
->dst
;
598 err
= ip_route_input(skb
, nexthop
, iph
->saddr
, iph
->tos
, skb
->dev
);
599 rt2
= (struct rtable
*)skb
->dst
;
600 if (err
|| (rt2
->rt_type
!= RTN_UNICAST
&& rt2
->rt_type
!= RTN_LOCAL
)) {
602 skb
->dst
= &rt
->u
.dst
;
606 if (rt2
->rt_type
!= RTN_LOCAL
)
608 /* Superfast 8) loopback forward */
609 memcpy(&iph
->daddr
, &optptr
[srrptr
-1], 4);
612 if (srrptr
<= srrspace
) {