aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/exthdrs.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /net/ipv6/exthdrs.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'net/ipv6/exthdrs.c')
-rw-r--r--net/ipv6/exthdrs.c575
1 files changed, 575 insertions, 0 deletions
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
new file mode 100644
index 000000000000..e0839eafc3a9
--- /dev/null
+++ b/net/ipv6/exthdrs.c
@@ -0,0 +1,575 @@
1/*
2 * Extension Header handling for IPv6
3 * Linux INET6 implementation
4 *
5 * Authors:
6 * Pedro Roque <roque@di.fc.ul.pt>
7 * Andi Kleen <ak@muc.de>
8 * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
9 *
10 * $Id: exthdrs.c,v 1.13 2001/06/19 15:58:56 davem Exp $
11 *
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.
16 */
17
18/* Changes:
19 * yoshfuji : ensure not to overrun while parsing
20 * tlv options.
21 * Mitsuru KANDA @USAGI and: Remove ipv6_parse_exthdrs().
22 * YOSHIFUJI Hideaki @USAGI Register inbound extension header
23 * handlers as inet6_protocol{}.
24 */
25
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>
35
36#include <net/sock.h>
37#include <net/snmp.h>
38
39#include <net/ipv6.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>
46
47#include <asm/uaccess.h>
48
49/*
50 * Parsing tlv encoded headers.
51 *
52 * Parsing function "func" returns 1, if parsing succeed
53 * and 0, if it failed.
54 * It MUST NOT touch skb->h.
55 */
56
57struct tlvtype_proc {
58 int type;
59 int (*func)(struct sk_buff *skb, int offset);
60};
61
62/*********************
63 Generic functions
64 *********************/
65
66/* An unknown option is detected, decide what to do */
67
68static int ip6_tlvopt_unknown(struct sk_buff *skb, int optoff)
69{
70 switch ((skb->nh.raw[optoff] & 0xC0) >> 6) {
71 case 0: /* ignore */
72 return 1;
73
74 case 1: /* drop packet */
75 break;
76
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.
80 */
81 if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr))
82 break;
83 case 2: /* send ICMP PARM PROB regardless and drop packet */
84 icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, optoff);
85 return 0;
86 };
87
88 kfree_skb(skb);
89 return 0;
90}
91
92/* Parse tlv encoded option header (hop-by-hop or destination) */
93
94static int ip6_parse_tlv(struct tlvtype_proc *procs, struct sk_buff *skb)
95{
96 struct tlvtype_proc *curr;
97 int off = skb->h.raw - skb->nh.raw;
98 int len = ((skb->h.raw[1]+1)<<3);
99
100 if ((skb->h.raw + len) - skb->data > skb_headlen(skb))
101 goto bad;
102
103 off += 2;
104 len -= 2;
105
106 while (len > 0) {
107 int optlen = skb->nh.raw[off+1]+2;
108
109 switch (skb->nh.raw[off]) {
110 case IPV6_TLV_PAD0:
111 optlen = 1;
112 break;
113
114 case IPV6_TLV_PADN:
115 break;
116
117 default: /* Other TLV code so scan list */
118 if (optlen > len)
119 goto bad;
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
124 func(). */
125 if (curr->func(skb, off) == 0)
126 return 0;
127 break;
128 }
129 }
130 if (curr->type < 0) {
131 if (ip6_tlvopt_unknown(skb, off) == 0)
132 return 0;
133 }
134 break;
135 }
136 off += optlen;
137 len -= optlen;
138 }
139 if (len == 0)
140 return 1;
141bad:
142 kfree_skb(skb);
143 return 0;
144}
145
146/*****************************
147 Destination options header.
148 *****************************/
149
150static struct tlvtype_proc tlvprocdestopt_lst[] = {
151 /* No destination options are defined now */
152 {-1, NULL}
153};
154
155static int ipv6_destopt_rcv(struct sk_buff **skbp, unsigned int *nhoffp)
156{
157 struct sk_buff *skb = *skbp;
158 struct inet6_skb_parm *opt = IP6CB(skb);
159
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);
163 kfree_skb(skb);
164 return -1;
165 }
166
167 opt->dst1 = skb->h.raw - skb->nh.raw;
168
169 if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) {
170 skb->h.raw += ((skb->h.raw[1]+1)<<3);
171 *nhoffp = opt->dst1;
172 return 1;
173 }
174
175 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
176 return -1;
177}
178
179static struct inet6_protocol destopt_protocol = {
180 .handler = ipv6_destopt_rcv,
181 .flags = INET6_PROTO_NOPOLICY,
182};
183
184void __init ipv6_destopt_init(void)
185{
186 if (inet6_add_protocol(&destopt_protocol, IPPROTO_DSTOPTS) < 0)
187 printk(KERN_ERR "ipv6_destopt_init: Could not register protocol\n");
188}
189
190/********************************
191 NONE header. No data in packet.
192 ********************************/
193
194static int ipv6_nodata_rcv(struct sk_buff **skbp, unsigned int *nhoffp)
195{
196 struct sk_buff *skb = *skbp;
197
198 kfree_skb(skb);
199 return 0;
200}
201
202static struct inet6_protocol nodata_protocol = {
203 .handler = ipv6_nodata_rcv,
204 .flags = INET6_PROTO_NOPOLICY,
205};
206
207void __init ipv6_nodata_init(void)
208{
209 if (inet6_add_protocol(&nodata_protocol, IPPROTO_NONE) < 0)
210 printk(KERN_ERR "ipv6_nodata_init: Could not register protocol\n");
211}
212
213/********************************
214 Routing header.
215 ********************************/
216
217static int ipv6_rthdr_rcv(struct sk_buff **skbp, unsigned int *nhoffp)
218{
219 struct sk_buff *skb = *skbp;
220 struct inet6_skb_parm *opt = IP6CB(skb);
221 struct in6_addr *addr;
222 struct in6_addr daddr;
223 int n, i;
224
225 struct ipv6_rt_hdr *hdr;
226 struct rt0_hdr *rthdr;
227
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);
231 kfree_skb(skb);
232 return -1;
233 }
234
235 hdr = (struct ipv6_rt_hdr *) skb->h.raw;
236
237 if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr) ||
238 skb->pkt_type != PACKET_HOST) {
239 IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS);
240 kfree_skb(skb);
241 return -1;
242 }
243
244looped_back:
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;
249 opt->dst1 = 0;
250 *nhoffp = (&hdr->nexthdr) - skb->nh.raw;
251 return 1;
252 }
253
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);
257 return -1;
258 }
259
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);
263 return -1;
264 }
265
266 /*
267 * This is the routing header forwarding algorithm from
268 * RFC 2460, page 16.
269 */
270
271 n = hdr->hdrlen >> 1;
272
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);
276 return -1;
277 }
278
279 /* We are about to mangle packet header. Be careful!
280 Do not damage packets queued somewhere.
281 */
282 if (skb_cloned(skb)) {
283 struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
284 kfree_skb(skb);
285 /* the copy is a forwarded packet */
286 if (skb2 == NULL) {
287 IP6_INC_STATS_BH(IPSTATS_MIB_OUTDISCARDS);
288 return -1;
289 }
290 *skbp = skb = skb2;
291 opt = IP6CB(skb2);
292 hdr = (struct ipv6_rt_hdr *) skb2->h.raw;
293 }
294
295 if (skb->ip_summed == CHECKSUM_HW)
296 skb->ip_summed = CHECKSUM_NONE;
297
298 i = n - --hdr->segments_left;
299
300 rthdr = (struct rt0_hdr *) hdr;
301 addr = rthdr->addr;
302 addr += i - 1;
303
304 if (ipv6_addr_is_multicast(addr)) {
305 IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS);
306 kfree_skb(skb);
307 return -1;
308 }
309
310 ipv6_addr_copy(&daddr, addr);
311 ipv6_addr_copy(addr, &skb->nh.ipv6h->daddr);
312 ipv6_addr_copy(&skb->nh.ipv6h->daddr, &daddr);
313
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);
318 dst_input(skb);
319 return -1;
320 }
321
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,
326 0, skb->dev);
327 kfree_skb(skb);
328 return -1;
329 }
330 skb->nh.ipv6h->hop_limit--;
331 goto looped_back;
332 }
333
334 skb_push(skb, skb->data - skb->nh.raw);
335 dst_input(skb);
336 return -1;
337}
338
339static struct inet6_protocol rthdr_protocol = {
340 .handler = ipv6_rthdr_rcv,
341 .flags = INET6_PROTO_NOPOLICY,
342};
343
344void __init ipv6_rthdr_init(void)
345{
346 if (inet6_add_protocol(&rthdr_protocol, IPPROTO_ROUTING) < 0)
347 printk(KERN_ERR "ipv6_rthdr_init: Could not register protocol\n");
348};
349
350/*
351 This function inverts received rthdr.
352 NOTE: specs allow to make it automatically only if
353 packet authenticated.
354
355 I will not discuss it here (though, I am really pissed off at
356 this stupid requirement making rthdr idea useless)
357
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
363 without success. :-(
364
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.
368 --ANK (980729)
369 */
370
371struct ipv6_txoptions *
372ipv6_invert_rthdr(struct sock *sk, struct ipv6_rt_hdr *hdr)
373{
374 /* Received rthdr:
375
376 [ H1 -> H2 -> ... H_prev ] daddr=ME
377
378 Inverted result:
379 [ H_prev -> ... -> H1 ] daddr =sender
380
381 Note, that IP output engine will rewrite this rthdr
382 by rotating it left by one addr.
383 */
384
385 int n, i;
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);
390
391 if (hdr->segments_left ||
392 hdr->type != IPV6_SRCRT_TYPE_0 ||
393 hdr->hdrlen & 0x01)
394 return NULL;
395
396 n = hdr->hdrlen >> 1;
397 opt = sock_kmalloc(sk, sizeof(*opt) + hdrlen, GFP_ATOMIC);
398 if (opt == NULL)
399 return NULL;
400 memset(opt, 0, sizeof(*opt));
401 opt->tot_len = sizeof(*opt) + hdrlen;
402 opt->srcrt = (void*)(opt+1);
403 opt->opt_nflen = hdrlen;
404
405 memcpy(opt->srcrt, hdr, sizeof(*hdr));
406 irthdr = (struct rt0_hdr*)opt->srcrt;
407 /* Obsolete field, MBZ, when originated by us */
408 irthdr->bitmap = 0;
409 opt->srcrt->segments_left = n;
410 for (i=0; i<n; i++)
411 memcpy(irthdr->addr+i, rthdr->addr+(n-1-i), 16);
412 return opt;
413}
414
415/**********************************
416 Hop-by-hop options.
417 **********************************/
418
419/* Router Alert as of RFC 2711 */
420
421static int ipv6_hop_ra(struct sk_buff *skb, int optoff)
422{
423 if (skb->nh.raw[optoff+1] == 2) {
424 IP6CB(skb)->ra = optoff;
425 return 1;
426 }
427 LIMIT_NETDEBUG(
428 printk(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n", skb->nh.raw[optoff+1]));
429 kfree_skb(skb);
430 return 0;
431}
432
433/* Jumbo payload */
434
435static int ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
436{
437 u32 pkt_len;
438
439 if (skb->nh.raw[optoff+1] != 4 || (optoff&3) != 2) {
440 LIMIT_NETDEBUG(
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);
443 goto drop;
444 }
445
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);
450 return 0;
451 }
452 if (skb->nh.ipv6h->payload_len) {
453 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
454 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
455 return 0;
456 }
457
458 if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
459 IP6_INC_STATS_BH(IPSTATS_MIB_INTRUNCATEDPKTS);
460 goto drop;
461 }
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;
466 }
467 return 1;
468
469drop:
470 kfree_skb(skb);
471 return 0;
472}
473
474static struct tlvtype_proc tlvprochopopt_lst[] = {
475 {
476 .type = IPV6_TLV_ROUTERALERT,
477 .func = ipv6_hop_ra,
478 },
479 {
480 .type = IPV6_TLV_JUMBO,
481 .func = ipv6_hop_jumbo,
482 },
483 { -1, }
484};
485
486int ipv6_parse_hopopts(struct sk_buff *skb, int nhoff)
487{
488 IP6CB(skb)->hop = sizeof(struct ipv6hdr);
489 if (ip6_parse_tlv(tlvprochopopt_lst, skb))
490 return sizeof(struct ipv6hdr);
491 return -1;
492}
493
494/*
495 * Creating outbound headers.
496 *
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)
499 *
500 * In both cases we assume, that caller reserved enough room
501 * for headers.
502 */
503
504static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
505 struct ipv6_rt_hdr *opt,
506 struct in6_addr **addr_p)
507{
508 struct rt0_hdr *phdr, *ihdr;
509 int hops;
510
511 ihdr = (struct rt0_hdr *) opt;
512
513 phdr = (struct rt0_hdr *) skb_push(skb, (ihdr->rt_hdr.hdrlen + 1) << 3);
514 memcpy(phdr, ihdr, sizeof(struct rt0_hdr));
515
516 hops = ihdr->rt_hdr.hdrlen >> 1;
517
518 if (hops > 1)
519 memcpy(phdr->addr, ihdr->addr + 1,
520 (hops - 1) * sizeof(struct in6_addr));
521
522 ipv6_addr_copy(phdr->addr + (hops - 1), *addr_p);
523 *addr_p = ihdr->addr;
524
525 phdr->rt_hdr.nexthdr = *proto;
526 *proto = NEXTHDR_ROUTING;
527}
528
529static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt)
530{
531 struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ipv6_optlen(opt));
532
533 memcpy(h, opt, ipv6_optlen(opt));
534 h->nexthdr = *proto;
535 *proto = type;
536}
537
538void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
539 u8 *proto,
540 struct in6_addr **daddr)
541{
542 if (opt->srcrt)
543 ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
544 if (opt->dst0opt)
545 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
546 if (opt->hopopt)
547 ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
548}
549
550void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto)
551{
552 if (opt->dst1opt)
553 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt);
554}
555
556struct ipv6_txoptions *
557ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
558{
559 struct ipv6_txoptions *opt2;
560
561 opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC);
562 if (opt2) {
563 long dif = (char*)opt2 - (char*)opt;
564 memcpy(opt2, opt, opt->tot_len);
565 if (opt2->hopopt)
566 *((char**)&opt2->hopopt) += dif;
567 if (opt2->dst0opt)
568 *((char**)&opt2->dst0opt) += dif;
569 if (opt2->dst1opt)
570 *((char**)&opt2->dst1opt) += dif;
571 if (opt2->srcrt)
572 *((char**)&opt2->srcrt) += dif;
573 }
574 return opt2;
575}