2 * Extension Header handling for IPv6
3 * Linux INET6 implementation
6 * Pedro Roque <roque@di.fc.ul.pt>
7 * Andi Kleen <ak@muc.de>
8 * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
10 * $Id: exthdrs.c,v 1.13 2001/06/19 15:58:56 davem Exp $
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version
15 * 2 of the License, or (at your option) any later version.
19 * yoshfuji : ensure not to overrun while parsing
21 * Mitsuru KANDA @USAGI and: Remove ipv6_parse_exthdrs().
22 * YOSHIFUJI Hideaki @USAGI Register inbound extension header
23 * handlers as inet6_protocol{}.
26 #include <linux/errno.h>
27 #include <linux/types.h>
28 #include <linux/socket.h>
29 #include <linux/sockios.h>
30 #include <linux/sched.h>
31 #include <linux/net.h>
32 #include <linux/netdevice.h>
33 #include <linux/in6.h>
34 #include <linux/icmpv6.h>
40 #include <net/protocol.h>
41 #include <net/transp_v6.h>
42 #include <net/rawv6.h>
43 #include <net/ndisc.h>
44 #include <net/ip6_route.h>
45 #include <net/addrconf.h>
47 #include <asm/uaccess.h>
50 * Parsing tlv encoded headers.
52 * Parsing function "func" returns 1, if parsing succeed
53 * and 0, if it failed.
54 * It MUST NOT touch skb->h.
59 int (*func
)(struct sk_buff
*skb
, int offset
);
62 /*********************
64 *********************/
66 /* An unknown option is detected, decide what to do */
68 static int ip6_tlvopt_unknown(struct sk_buff
*skb
, int optoff
)
70 switch ((skb
->nh
.raw
[optoff
] & 0xC0) >> 6) {
74 case 1: /* drop packet */
77 case 3: /* Send ICMP if not a multicast address and drop packet */
78 /* Actually, it is redundant check. icmp_send
79 will recheck in any case.
81 if (ipv6_addr_is_multicast(&skb
->nh
.ipv6h
->daddr
))
83 case 2: /* send ICMP PARM PROB regardless and drop packet */
84 icmpv6_param_prob(skb
, ICMPV6_UNK_OPTION
, optoff
);
92 /* Parse tlv encoded option header (hop-by-hop or destination) */
94 static int ip6_parse_tlv(struct tlvtype_proc
*procs
, struct sk_buff
*skb
)
96 struct tlvtype_proc
*curr
;
97 int off
= skb
->h
.raw
- skb
->nh
.raw
;
98 int len
= ((skb
->h
.raw
[1]+1)<<3);
100 if ((skb
->h
.raw
+ len
) - skb
->data
> skb_headlen(skb
))
107 int optlen
= skb
->nh
.raw
[off
+1]+2;
109 switch (skb
->nh
.raw
[off
]) {
117 default: /* Other TLV code so scan list */
120 for (curr
=procs
; curr
->type
>= 0; curr
++) {
121 if (curr
->type
== skb
->nh
.raw
[off
]) {
122 /* type specific length/alignment
123 checks will be performed in the
125 if (curr
->func(skb
, off
) == 0)
130 if (curr
->type
< 0) {
131 if (ip6_tlvopt_unknown(skb
, off
) == 0)
146 /*****************************
147 Destination options header.
148 *****************************/
150 static struct tlvtype_proc tlvprocdestopt_lst
[] = {
151 /* No destination options are defined now */
155 static int ipv6_destopt_rcv(struct sk_buff
**skbp
, unsigned int *nhoffp
)
157 struct sk_buff
*skb
= *skbp
;
158 struct inet6_skb_parm
*opt
= IP6CB(skb
);
160 if (!pskb_may_pull(skb
, (skb
->h
.raw
-skb
->data
)+8) ||
161 !pskb_may_pull(skb
, (skb
->h
.raw
-skb
->data
)+((skb
->h
.raw
[1]+1)<<3))) {
162 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS
);
167 opt
->dst1
= skb
->h
.raw
- skb
->nh
.raw
;
169 if (ip6_parse_tlv(tlvprocdestopt_lst
, skb
)) {
170 skb
->h
.raw
+= ((skb
->h
.raw
[1]+1)<<3);
175 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS
);
179 static struct inet6_protocol destopt_protocol
= {
180 .handler
= ipv6_destopt_rcv
,
181 .flags
= INET6_PROTO_NOPOLICY
,
184 void __init
ipv6_destopt_init(void)
186 if (inet6_add_protocol(&destopt_protocol
, IPPROTO_DSTOPTS
) < 0)
187 printk(KERN_ERR
"ipv6_destopt_init: Could not register protocol\n");
190 /********************************
191 NONE header. No data in packet.
192 ********************************/
194 static int ipv6_nodata_rcv(struct sk_buff
**skbp
, unsigned int *nhoffp
)
196 struct sk_buff
*skb
= *skbp
;
202 static struct inet6_protocol nodata_protocol
= {
203 .handler
= ipv6_nodata_rcv
,
204 .flags
= INET6_PROTO_NOPOLICY
,
207 void __init
ipv6_nodata_init(void)
209 if (inet6_add_protocol(&nodata_protocol
, IPPROTO_NONE
) < 0)
210 printk(KERN_ERR
"ipv6_nodata_init: Could not register protocol\n");
213 /********************************
215 ********************************/
217 static int ipv6_rthdr_rcv(struct sk_buff
**skbp
, unsigned int *nhoffp
)
219 struct sk_buff
*skb
= *skbp
;
220 struct inet6_skb_parm
*opt
= IP6CB(skb
);
221 struct in6_addr
*addr
;
222 struct in6_addr daddr
;
225 struct ipv6_rt_hdr
*hdr
;
226 struct rt0_hdr
*rthdr
;
228 if (!pskb_may_pull(skb
, (skb
->h
.raw
-skb
->data
)+8) ||
229 !pskb_may_pull(skb
, (skb
->h
.raw
-skb
->data
)+((skb
->h
.raw
[1]+1)<<3))) {
230 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS
);
235 hdr
= (struct ipv6_rt_hdr
*) skb
->h
.raw
;
237 if (ipv6_addr_is_multicast(&skb
->nh
.ipv6h
->daddr
) ||
238 skb
->pkt_type
!= PACKET_HOST
) {
239 IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS
);
245 if (hdr
->segments_left
== 0) {
246 opt
->srcrt
= skb
->h
.raw
- skb
->nh
.raw
;
247 skb
->h
.raw
+= (hdr
->hdrlen
+ 1) << 3;
248 opt
->dst0
= opt
->dst1
;
250 *nhoffp
= (&hdr
->nexthdr
) - skb
->nh
.raw
;
254 if (hdr
->type
!= IPV6_SRCRT_TYPE_0
) {
255 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS
);
256 icmpv6_param_prob(skb
, ICMPV6_HDR_FIELD
, (&hdr
->type
) - skb
->nh
.raw
);
260 if (hdr
->hdrlen
& 0x01) {
261 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS
);
262 icmpv6_param_prob(skb
, ICMPV6_HDR_FIELD
, (&hdr
->hdrlen
) - skb
->nh
.raw
);
267 * This is the routing header forwarding algorithm from
271 n
= hdr
->hdrlen
>> 1;
273 if (hdr
->segments_left
> n
) {
274 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS
);
275 icmpv6_param_prob(skb
, ICMPV6_HDR_FIELD
, (&hdr
->segments_left
) - skb
->nh
.raw
);
279 /* We are about to mangle packet header. Be careful!
280 Do not damage packets queued somewhere.
282 if (skb_cloned(skb
)) {
283 struct sk_buff
*skb2
= skb_copy(skb
, GFP_ATOMIC
);
285 /* the copy is a forwarded packet */
287 IP6_INC_STATS_BH(IPSTATS_MIB_OUTDISCARDS
);
292 hdr
= (struct ipv6_rt_hdr
*) skb2
->h
.raw
;
295 if (skb
->ip_summed
== CHECKSUM_HW
)
296 skb
->ip_summed
= CHECKSUM_NONE
;
298 i
= n
- --hdr
->segments_left
;
300 rthdr
= (struct rt0_hdr
*) hdr
;
304 if (ipv6_addr_is_multicast(addr
)) {
305 IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS
);
310 ipv6_addr_copy(&daddr
, addr
);
311 ipv6_addr_copy(addr
, &skb
->nh
.ipv6h
->daddr
);
312 ipv6_addr_copy(&skb
->nh
.ipv6h
->daddr
, &daddr
);
314 dst_release(xchg(&skb
->dst
, NULL
));
315 ip6_route_input(skb
);
316 if (skb
->dst
->error
) {
317 skb_push(skb
, skb
->data
- skb
->nh
.raw
);
322 if (skb
->dst
->dev
->flags
&IFF_LOOPBACK
) {
323 if (skb
->nh
.ipv6h
->hop_limit
<= 1) {
324 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS
);
325 icmpv6_send(skb
, ICMPV6_TIME_EXCEED
, ICMPV6_EXC_HOPLIMIT
,
330 skb
->nh
.ipv6h
->hop_limit
--;
334 skb_push(skb
, skb
->data
- skb
->nh
.raw
);
339 static struct inet6_protocol rthdr_protocol
= {
340 .handler
= ipv6_rthdr_rcv
,
341 .flags
= INET6_PROTO_NOPOLICY
,
344 void __init
ipv6_rthdr_init(void)
346 if (inet6_add_protocol(&rthdr_protocol
, IPPROTO_ROUTING
) < 0)
347 printk(KERN_ERR
"ipv6_rthdr_init: Could not register protocol\n");
351 This function inverts received rthdr.
352 NOTE: specs allow to make it automatically only if
353 packet authenticated.
355 I will not discuss it here (though, I am really pissed off at
356 this stupid requirement making rthdr idea useless)
358 Actually, it creates severe problems for us.
359 Embryonic requests has no associated sockets,
360 so that user have no control over it and
361 cannot not only to set reply options, but
362 even to know, that someone wants to connect
365 For now we need to test the engine, so that I created
366 temporary (or permanent) backdoor.
367 If listening socket set IPV6_RTHDR to 2, then we invert header.
371 struct ipv6_txoptions
*
372 ipv6_invert_rthdr(struct sock
*sk
, struct ipv6_rt_hdr
*hdr
)
376 [ H1 -> H2 -> ... H_prev ] daddr=ME
379 [ H_prev -> ... -> H1 ] daddr =sender
381 Note, that IP output engine will rewrite this rthdr
382 by rotating it left by one addr.
386 struct rt0_hdr
*rthdr
= (struct rt0_hdr
*)hdr
;
387 struct rt0_hdr
*irthdr
;
388 struct ipv6_txoptions
*opt
;
389 int hdrlen
= ipv6_optlen(hdr
);
391 if (hdr
->segments_left
||
392 hdr
->type
!= IPV6_SRCRT_TYPE_0
||
396 n
= hdr
->hdrlen
>> 1;
397 opt
= sock_kmalloc(sk
, sizeof(*opt
) + hdrlen
, GFP_ATOMIC
);
400 memset(opt
, 0, sizeof(*opt
));
401 opt
->tot_len
= sizeof(*opt
) + hdrlen
;
402 opt
->srcrt
= (void*)(opt
+1);
403 opt
->opt_nflen
= hdrlen
;
405 memcpy(opt
->srcrt
, hdr
, sizeof(*hdr
));
406 irthdr
= (struct rt0_hdr
*)opt
->srcrt
;
407 /* Obsolete field, MBZ, when originated by us */
409 opt
->srcrt
->segments_left
= n
;
411 memcpy(irthdr
->addr
+i
, rthdr
->addr
+(n
-1-i
), 16);
415 /**********************************
417 **********************************/
419 /* Router Alert as of RFC 2711 */
421 static int ipv6_hop_ra(struct sk_buff
*skb
, int optoff
)
423 if (skb
->nh
.raw
[optoff
+1] == 2) {
424 IP6CB(skb
)->ra
= optoff
;
428 printk(KERN_DEBUG
"ipv6_hop_ra: wrong RA length %d\n", skb
->nh
.raw
[optoff
+1]));
435 static int ipv6_hop_jumbo(struct sk_buff
*skb
, int optoff
)
439 if (skb
->nh
.raw
[optoff
+1] != 4 || (optoff
&3) != 2) {
441 printk(KERN_DEBUG
"ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n", skb
->nh
.raw
[optoff
+1]));
442 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS
);
446 pkt_len
= ntohl(*(u32
*)(skb
->nh
.raw
+optoff
+2));
447 if (pkt_len
<= IPV6_MAXPLEN
) {
448 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS
);
449 icmpv6_param_prob(skb
, ICMPV6_HDR_FIELD
, optoff
+2);
452 if (skb
->nh
.ipv6h
->payload_len
) {
453 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS
);
454 icmpv6_param_prob(skb
, ICMPV6_HDR_FIELD
, optoff
);
458 if (pkt_len
> skb
->len
- sizeof(struct ipv6hdr
)) {
459 IP6_INC_STATS_BH(IPSTATS_MIB_INTRUNCATEDPKTS
);
462 if (pkt_len
+ sizeof(struct ipv6hdr
) < skb
->len
) {
463 __pskb_trim(skb
, pkt_len
+ sizeof(struct ipv6hdr
));
464 if (skb
->ip_summed
== CHECKSUM_HW
)
465 skb
->ip_summed
= CHECKSUM_NONE
;
474 static struct tlvtype_proc tlvprochopopt_lst
[] = {
476 .type
= IPV6_TLV_ROUTERALERT
,
480 .type
= IPV6_TLV_JUMBO
,
481 .func
= ipv6_hop_jumbo
,
486 int ipv6_parse_hopopts(struct sk_buff
*skb
, int nhoff
)
488 IP6CB(skb
)->hop
= sizeof(struct ipv6hdr
);
489 if (ip6_parse_tlv(tlvprochopopt_lst
, skb
))
490 return sizeof(struct ipv6hdr
);
495 * Creating outbound headers.
497 * "build" functions work when skb is filled from head to tail (datagram)
498 * "push" functions work when headers are added from tail to head (tcp)
500 * In both cases we assume, that caller reserved enough room
504 static void ipv6_push_rthdr(struct sk_buff
*skb
, u8
*proto
,
505 struct ipv6_rt_hdr
*opt
,
506 struct in6_addr
**addr_p
)
508 struct rt0_hdr
*phdr
, *ihdr
;
511 ihdr
= (struct rt0_hdr
*) opt
;
513 phdr
= (struct rt0_hdr
*) skb_push(skb
, (ihdr
->rt_hdr
.hdrlen
+ 1) << 3);
514 memcpy(phdr
, ihdr
, sizeof(struct rt0_hdr
));
516 hops
= ihdr
->rt_hdr
.hdrlen
>> 1;
519 memcpy(phdr
->addr
, ihdr
->addr
+ 1,
520 (hops
- 1) * sizeof(struct in6_addr
));
522 ipv6_addr_copy(phdr
->addr
+ (hops
- 1), *addr_p
);
523 *addr_p
= ihdr
->addr
;
525 phdr
->rt_hdr
.nexthdr
= *proto
;
526 *proto
= NEXTHDR_ROUTING
;
529 static void ipv6_push_exthdr(struct sk_buff
*skb
, u8
*proto
, u8 type
, struct ipv6_opt_hdr
*opt
)
531 struct ipv6_opt_hdr
*h
= (struct ipv6_opt_hdr
*)skb_push(skb
, ipv6_optlen(opt
));
533 memcpy(h
, opt
, ipv6_optlen(opt
));
538 void ipv6_push_nfrag_opts(struct sk_buff
*skb
, struct ipv6_txoptions
*opt
,
540 struct in6_addr
**daddr
)
543 ipv6_push_rthdr(skb
, proto
, opt
->srcrt
, daddr
);
545 ipv6_push_exthdr(skb
, proto
, NEXTHDR_DEST
, opt
->dst0opt
);
547 ipv6_push_exthdr(skb
, proto
, NEXTHDR_HOP
, opt
->hopopt
);
550 void ipv6_push_frag_opts(struct sk_buff
*skb
, struct ipv6_txoptions
*opt
, u8
*proto
)
553 ipv6_push_exthdr(skb
, proto
, NEXTHDR_DEST
, opt
->dst1opt
);
556 struct ipv6_txoptions
*
557 ipv6_dup_options(struct sock
*sk
, struct ipv6_txoptions
*opt
)
559 struct ipv6_txoptions
*opt2
;
561 opt2
= sock_kmalloc(sk
, opt
->tot_len
, GFP_ATOMIC
);
563 long dif
= (char*)opt2
- (char*)opt
;
564 memcpy(opt2
, opt
, opt
->tot_len
);
566 *((char**)&opt2
->hopopt
) += dif
;
568 *((char**)&opt2
->dst0opt
) += dif
;
570 *((char**)&opt2
->dst1opt
) += dif
;
572 *((char**)&opt2
->srcrt
) += dif
;