]>
git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blob - net/ipv4/ip_options.c
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.21 2001/09/01 00:31:50 davem Exp $
10 * Authors: A.N.Kuznetsov
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>
20 #include <linux/icmp.h>
21 #include <linux/netdevice.h>
22 #include <linux/rtnetlink.h>
26 #include <net/route.h>
27 #include <net/cipso_ipv4.h>
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.
36 * daddr is real destination address, next hop is recorded in IP header.
37 * saddr is address of outgoing interface.
40 void ip_options_build(struct sk_buff
* skb
, struct ip_options
* opt
,
41 __be32 daddr
, struct rtable
*rt
, int is_frag
)
43 unsigned char *iph
= skb_network_header(skb
);
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
);
51 memcpy(iph
+opt
->srr
+iph
[opt
->srr
+1]-4, &daddr
, 4);
55 ip_rt_get_source(iph
+opt
->rr
+iph
[opt
->rr
+2]-5, rt
);
57 ip_rt_get_source(iph
+opt
->ts
+iph
[opt
->ts
+2]-9, rt
);
58 if (opt
->ts_needtime
) {
62 midtime
= htonl((tv
.tv_sec
% 86400) * 1000 + tv
.tv_usec
/ 1000);
63 memcpy(iph
+opt
->ts
+iph
[opt
->ts
+2]-5, &midtime
, 4);
68 memset(iph
+opt
->rr
, IPOPT_NOP
, iph
[opt
->rr
+1]);
73 memset(iph
+opt
->ts
, IPOPT_NOP
, iph
[opt
->ts
+1]);
75 opt
->ts_needaddr
= opt
->ts_needtime
= 0;
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.
85 * NOTE: dopt cannot point to skb.
88 int ip_options_echo(struct ip_options
* dopt
, struct sk_buff
* skb
)
90 struct ip_options
*sopt
;
91 unsigned char *sptr
, *dptr
;
96 memset(dopt
, 0, sizeof(struct ip_options
));
100 sopt
= &(IPCB(skb
)->opt
);
102 if (sopt
->optlen
== 0) {
107 sptr
= skb_network_header(skb
);
110 daddr
= skb
->rtable
->rt_spec_dst
;
113 optlen
= sptr
[sopt
->rr
+1];
114 soffset
= sptr
[sopt
->rr
+2];
115 dopt
->rr
= dopt
->optlen
+ sizeof(struct iphdr
);
116 memcpy(dptr
, sptr
+sopt
->rr
, optlen
);
117 if (sopt
->rr_needaddr
&& soffset
<= optlen
) {
118 if (soffset
+ 3 > optlen
)
120 dptr
[2] = soffset
+ 4;
121 dopt
->rr_needaddr
= 1;
124 dopt
->optlen
+= optlen
;
127 optlen
= sptr
[sopt
->ts
+1];
128 soffset
= sptr
[sopt
->ts
+2];
129 dopt
->ts
= dopt
->optlen
+ sizeof(struct iphdr
);
130 memcpy(dptr
, sptr
+sopt
->ts
, optlen
);
131 if (soffset
<= optlen
) {
132 if (sopt
->ts_needaddr
) {
133 if (soffset
+ 3 > optlen
)
135 dopt
->ts_needaddr
= 1;
138 if (sopt
->ts_needtime
) {
139 if (soffset
+ 3 > optlen
)
141 if ((dptr
[3]&0xF) != IPOPT_TS_PRESPEC
) {
142 dopt
->ts_needtime
= 1;
145 dopt
->ts_needtime
= 0;
147 if (soffset
+ 8 <= optlen
) {
150 memcpy(&addr
, sptr
+soffset
-1, 4);
151 if (inet_addr_type(&init_net
, addr
) != RTN_LOCAL
) {
152 dopt
->ts_needtime
= 1;
161 dopt
->optlen
+= optlen
;
164 unsigned char * start
= sptr
+sopt
->srr
;
170 if (soffset
> optlen
)
171 soffset
= optlen
+ 1;
174 memcpy(&faddr
, &start
[soffset
-1], 4);
175 for (soffset
-=4, doffset
=4; soffset
> 3; soffset
-=4, doffset
+=4)
176 memcpy(&dptr
[doffset
-1], &start
[soffset
-1], 4);
178 * RFC1812 requires to fix illegal source routes.
180 if (memcmp(&ip_hdr(skb
)->saddr
,
181 &start
[soffset
+ 3], 4) == 0)
185 memcpy(&start
[doffset
-1], &daddr
, 4);
191 dopt
->srr
= dopt
->optlen
+ sizeof(struct iphdr
);
192 dopt
->optlen
+= doffset
+3;
193 dopt
->is_strictroute
= sopt
->is_strictroute
;
197 optlen
= sptr
[sopt
->cipso
+1];
198 dopt
->cipso
= dopt
->optlen
+sizeof(struct iphdr
);
199 memcpy(dptr
, sptr
+sopt
->cipso
, optlen
);
201 dopt
->optlen
+= optlen
;
203 while (dopt
->optlen
& 3) {
211 * Options "fragmenting", just fill options not
212 * allowed in fragments with NOOPs.
213 * Simple and stupid 8), but the most efficient way.
216 void ip_options_fragment(struct sk_buff
* skb
)
218 unsigned char *optptr
= skb_network_header(skb
) + sizeof(struct iphdr
);
219 struct ip_options
* opt
= &(IPCB(skb
)->opt
);
233 if (optlen
<2 || optlen
>l
)
235 if (!IPOPT_COPIED(*optptr
))
236 memset(optptr
, IPOPT_NOOP
, optlen
);
242 opt
->rr_needaddr
= 0;
243 opt
->ts_needaddr
= 0;
244 opt
->ts_needtime
= 0;
249 * Verify options and fill pointers in struct options.
250 * Caller should clear *opt, and set opt->data.
251 * If opt == NULL, then skb->data should point to IP header.
254 int ip_options_compile(struct ip_options
* opt
, struct sk_buff
* skb
)
258 unsigned char * optptr
;
260 unsigned char * pp_ptr
= NULL
;
261 struct rtable
*rt
= skb
? skb
->rtable
: NULL
;
264 opt
= &(IPCB(skb
)->opt
);
265 iph
= skb_network_header(skb
);
266 opt
->optlen
= ((struct iphdr
*)iph
)->ihl
*4 - sizeof(struct iphdr
);
267 optptr
= iph
+ sizeof(struct iphdr
);
270 optptr
= opt
->__data
;
271 iph
= optptr
- sizeof(struct iphdr
);
274 for (l
= opt
->optlen
; l
> 0; ) {
277 for (optptr
++, l
--; l
>0; optptr
++, l
--) {
278 if (*optptr
!= IPOPT_END
) {
290 if (optlen
<2 || optlen
>l
) {
305 /* NB: cf RFC-1812 5.2.4.1 */
311 if (optptr
[2] != 4 || optlen
< 7 || ((optlen
-3) & 3)) {
315 memcpy(&opt
->faddr
, &optptr
[3], 4);
317 memmove(&optptr
[3], &optptr
[7], optlen
-7);
319 opt
->is_strictroute
= (optptr
[0] == IPOPT_SSRR
);
320 opt
->srr
= optptr
- iph
;
335 if (optptr
[2] <= optlen
) {
336 if (optptr
[2]+3 > optlen
) {
341 memcpy(&optptr
[optptr
[2]-1], &rt
->rt_spec_dst
, 4);
345 opt
->rr_needaddr
= 1;
347 opt
->rr
= optptr
- iph
;
349 case IPOPT_TIMESTAMP
:
362 if (optptr
[2] <= optlen
) {
363 __be32
*timeptr
= NULL
;
364 if (optptr
[2]+3 > optptr
[1]) {
368 switch (optptr
[3]&0xF) {
369 case IPOPT_TS_TSONLY
:
370 opt
->ts
= optptr
- iph
;
372 timeptr
= (__be32
*)&optptr
[optptr
[2]-1];
373 opt
->ts_needtime
= 1;
376 case IPOPT_TS_TSANDADDR
:
377 if (optptr
[2]+7 > optptr
[1]) {
381 opt
->ts
= optptr
- iph
;
383 memcpy(&optptr
[optptr
[2]-1], &rt
->rt_spec_dst
, 4);
384 timeptr
= (__be32
*)&optptr
[optptr
[2]+3];
386 opt
->ts_needaddr
= 1;
387 opt
->ts_needtime
= 1;
390 case IPOPT_TS_PRESPEC
:
391 if (optptr
[2]+7 > optptr
[1]) {
395 opt
->ts
= optptr
- iph
;
398 memcpy(&addr
, &optptr
[optptr
[2]-1], 4);
399 if (inet_addr_type(&init_net
, addr
) == RTN_UNICAST
)
402 timeptr
= (__be32
*)&optptr
[optptr
[2]+3];
404 opt
->ts_needtime
= 1;
408 if (!skb
&& !capable(CAP_NET_RAW
)) {
417 do_gettimeofday(&tv
);
418 midtime
= htonl((tv
.tv_sec
% 86400) * 1000 + tv
.tv_usec
/ 1000);
419 memcpy(timeptr
, &midtime
, sizeof(__be32
));
423 unsigned overflow
= optptr
[3]>>4;
424 if (overflow
== 15) {
428 opt
->ts
= optptr
- iph
;
430 optptr
[3] = (optptr
[3]&0xF)|((overflow
+1)<<4);
440 if (optptr
[2] == 0 && optptr
[3] == 0)
441 opt
->router_alert
= optptr
- iph
;
444 if ((!skb
&& !capable(CAP_NET_RAW
)) || opt
->cipso
) {
448 opt
->cipso
= optptr
- iph
;
449 if (cipso_v4_validate(&optptr
)) {
457 if (!skb
&& !capable(CAP_NET_RAW
)) {
473 icmp_send(skb
, ICMP_PARAMETERPROB
, 0, htonl((pp_ptr
-iph
)<<24));
480 * Undo all the changes done by ip_options_compile().
483 void ip_options_undo(struct ip_options
* opt
)
486 unsigned char * optptr
= opt
->__data
+opt
->srr
-sizeof(struct iphdr
);
487 memmove(optptr
+7, optptr
+3, optptr
[1]-7);
488 memcpy(optptr
+3, &opt
->faddr
, 4);
490 if (opt
->rr_needaddr
) {
491 unsigned char * optptr
= opt
->__data
+opt
->rr
-sizeof(struct iphdr
);
493 memset(&optptr
[optptr
[2]-1], 0, 4);
496 unsigned char * optptr
= opt
->__data
+opt
->ts
-sizeof(struct iphdr
);
497 if (opt
->ts_needtime
) {
499 memset(&optptr
[optptr
[2]-1], 0, 4);
500 if ((optptr
[3]&0xF) == IPOPT_TS_PRESPEC
)
503 if (opt
->ts_needaddr
) {
505 memset(&optptr
[optptr
[2]-1], 0, 4);
510 static struct ip_options
*ip_options_get_alloc(const int optlen
)
512 return kzalloc(sizeof(struct ip_options
) + ((optlen
+ 3) & ~3),
516 static int ip_options_get_finish(struct ip_options
**optp
,
517 struct ip_options
*opt
, int optlen
)
520 opt
->__data
[optlen
++] = IPOPT_END
;
521 opt
->optlen
= optlen
;
523 if (optlen
&& ip_options_compile(opt
, NULL
)) {
532 int ip_options_get_from_user(struct ip_options
**optp
, unsigned char __user
*data
, int optlen
)
534 struct ip_options
*opt
= ip_options_get_alloc(optlen
);
538 if (optlen
&& copy_from_user(opt
->__data
, data
, optlen
)) {
542 return ip_options_get_finish(optp
, opt
, optlen
);
545 int ip_options_get(struct ip_options
**optp
, unsigned char *data
, int optlen
)
547 struct ip_options
*opt
= ip_options_get_alloc(optlen
);
552 memcpy(opt
->__data
, data
, optlen
);
553 return ip_options_get_finish(optp
, opt
, optlen
);
556 void ip_forward_options(struct sk_buff
*skb
)
558 struct ip_options
* opt
= &(IPCB(skb
)->opt
);
559 unsigned char * optptr
;
560 struct rtable
*rt
= skb
->rtable
;
561 unsigned char *raw
= skb_network_header(skb
);
563 if (opt
->rr_needaddr
) {
564 optptr
= (unsigned char *)raw
+ opt
->rr
;
565 ip_rt_get_source(&optptr
[optptr
[2]-5], rt
);
568 if (opt
->srr_is_hit
) {
569 int srrptr
, srrspace
;
571 optptr
= raw
+ opt
->srr
;
573 for ( srrptr
=optptr
[2], srrspace
= optptr
[1];
577 if (srrptr
+ 3 > srrspace
)
579 if (memcmp(&rt
->rt_dst
, &optptr
[srrptr
-1], 4) == 0)
582 if (srrptr
+ 3 <= srrspace
) {
584 ip_rt_get_source(&optptr
[srrptr
-1], rt
);
585 ip_hdr(skb
)->daddr
= rt
->rt_dst
;
586 optptr
[2] = srrptr
+4;
587 } else if (net_ratelimit())
588 printk(KERN_CRIT
"ip_forward(): Argh! Destination lost!\n");
589 if (opt
->ts_needaddr
) {
590 optptr
= raw
+ opt
->ts
;
591 ip_rt_get_source(&optptr
[optptr
[2]-9], rt
);
595 if (opt
->is_changed
) {
597 ip_send_check(ip_hdr(skb
));
601 int ip_options_rcv_srr(struct sk_buff
*skb
)
603 struct ip_options
*opt
= &(IPCB(skb
)->opt
);
604 int srrspace
, srrptr
;
606 struct iphdr
*iph
= ip_hdr(skb
);
607 unsigned char *optptr
= skb_network_header(skb
) + opt
->srr
;
608 struct rtable
*rt
= skb
->rtable
;
615 if (skb
->pkt_type
!= PACKET_HOST
)
617 if (rt
->rt_type
== RTN_UNICAST
) {
618 if (!opt
->is_strictroute
)
620 icmp_send(skb
, ICMP_PARAMETERPROB
, 0, htonl(16<<24));
623 if (rt
->rt_type
!= RTN_LOCAL
)
626 for (srrptr
=optptr
[2], srrspace
= optptr
[1]; srrptr
<= srrspace
; srrptr
+= 4) {
627 if (srrptr
+ 3 > srrspace
) {
628 icmp_send(skb
, ICMP_PARAMETERPROB
, 0, htonl((opt
->srr
+2)<<24));
631 memcpy(&nexthop
, &optptr
[srrptr
-1], 4);
635 err
= ip_route_input(skb
, nexthop
, iph
->saddr
, iph
->tos
, skb
->dev
);
637 if (err
|| (rt2
->rt_type
!= RTN_UNICAST
&& rt2
->rt_type
!= RTN_LOCAL
)) {
643 if (rt2
->rt_type
!= RTN_LOCAL
)
645 /* Superfast 8) loopback forward */
646 memcpy(&iph
->daddr
, &optptr
[srrptr
-1], 4);
649 if (srrptr
<= srrspace
) {