~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/ipv6/exthdrs.c

Version: ~ [ linux-5.1-rc2 ] ~ [ linux-5.0.3 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.30 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.107 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.164 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.176 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.136 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.63 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.39.4 ] ~ [ linux-2.6.38.8 ] ~ [ linux-2.6.37.6 ] ~ [ linux-2.6.36.4 ] ~ [ linux-2.6.35.14 ] ~ [ linux-2.6.34.15 ] ~ [ linux-2.6.33.20 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 
 57 struct 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 
 68 static 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 
 94 static 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 perfomed 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;
141 bad:
142         kfree_skb(skb);
143         return 0;
144 }
145 
146 /*****************************
147   Destination options header.
148  *****************************/
149 
150 static struct tlvtype_proc tlvprocdestopt_lst[] = {
151         /* No destination options are defined now */
152         {-1,                    NULL}
153 };
154 
155 static int ipv6_destopt_rcv(struct sk_buff **skbp, unsigned int *nhoffp)
156 {
157         struct sk_buff *skb = *skbp;
158         struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
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                 kfree_skb(skb);
163                 return -1;
164         }
165 
166         opt->dst1 = skb->h.raw - skb->nh.raw;
167 
168         if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) {
169                 skb->h.raw += ((skb->h.raw[1]+1)<<3);
170                 *nhoffp = opt->dst1;
171                 return 1;
172         }
173 
174         return -1;
175 }
176 
177 static struct inet6_protocol destopt_protocol = {
178         .handler        =       ipv6_destopt_rcv,
179         .flags          =       INET6_PROTO_NOPOLICY,
180 };
181 
182 void __init ipv6_destopt_init(void)
183 {
184         if (inet6_add_protocol(&destopt_protocol, IPPROTO_DSTOPTS) < 0)
185                 printk(KERN_ERR "ipv6_destopt_init: Could not register protocol\n");
186 }
187 
188 /********************************
189   NONE header. No data in packet.
190  ********************************/
191 
192 static int ipv6_nodata_rcv(struct sk_buff **skbp, unsigned int *nhoffp)
193 {
194         struct sk_buff *skb = *skbp;
195 
196         kfree_skb(skb);
197         return 0;
198 }
199 
200 static struct inet6_protocol nodata_protocol = {
201         .handler        =       ipv6_nodata_rcv,
202         .flags          =       INET6_PROTO_NOPOLICY,
203 };
204 
205 void __init ipv6_nodata_init(void)
206 {
207         if (inet6_add_protocol(&nodata_protocol, IPPROTO_NONE) < 0)
208                 printk(KERN_ERR "ipv6_nodata_init: Could not register protocol\n");
209 }
210 
211 /********************************
212   Routing header.
213  ********************************/
214 
215 static int ipv6_rthdr_rcv(struct sk_buff **skbp, unsigned int *nhoffp)
216 {
217         struct sk_buff *skb = *skbp;
218         struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
219         struct in6_addr *addr;
220         struct in6_addr daddr;
221         int addr_type;
222         int n, i;
223 
224         struct ipv6_rt_hdr *hdr;
225         struct rt0_hdr *rthdr;
226 
227         if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||
228             !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
229                 IP6_INC_STATS_BH(Ip6InHdrErrors);
230                 kfree_skb(skb);
231                 return -1;
232         }
233 
234         hdr = (struct ipv6_rt_hdr *) skb->h.raw;
235 
236         if ((ipv6_addr_type(&skb->nh.ipv6h->daddr)&IPV6_ADDR_MULTICAST) ||
237             skb->pkt_type != PACKET_HOST) {
238                 kfree_skb(skb);
239                 return -1;
240         }
241 
242 looped_back:
243         if (hdr->segments_left == 0) {
244                 opt->srcrt = skb->h.raw - skb->nh.raw;
245                 skb->h.raw += (hdr->hdrlen + 1) << 3;
246                 opt->dst0 = opt->dst1;
247                 opt->dst1 = 0;
248                 *nhoffp = (&hdr->nexthdr) - skb->nh.raw;
249                 return 1;
250         }
251 
252         if (hdr->type != IPV6_SRCRT_TYPE_0) {
253                 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb->nh.raw);
254                 return -1;
255         }
256         
257         if (hdr->hdrlen & 0x01) {
258                 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->hdrlen) - skb->nh.raw);
259                 return -1;
260         }
261 
262         /*
263          *      This is the routing header forwarding algorithm from
264          *      RFC 2460, page 16.
265          */
266 
267         n = hdr->hdrlen >> 1;
268 
269         if (hdr->segments_left > n) {
270                 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->segments_left) - skb->nh.raw);
271                 return -1;
272         }
273 
274         /* We are about to mangle packet header. Be careful!
275            Do not damage packets queued somewhere.
276          */
277         if (skb_cloned(skb)) {
278                 struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
279                 kfree_skb(skb);
280                 if (skb2 == NULL)
281                         return -1;
282                 *skbp = skb = skb2;
283                 opt = (struct inet6_skb_parm *)skb2->cb;
284                 hdr = (struct ipv6_rt_hdr *) skb2->h.raw;
285         }
286 
287         if (skb->ip_summed == CHECKSUM_HW)
288                 skb->ip_summed = CHECKSUM_NONE;
289 
290         i = n - --hdr->segments_left;
291 
292         rthdr = (struct rt0_hdr *) hdr;
293         addr = rthdr->addr;
294         addr += i - 1;
295 
296         addr_type = ipv6_addr_type(addr);
297 
298         if (addr_type&IPV6_ADDR_MULTICAST) {
299                 kfree_skb(skb);
300                 return -1;
301         }
302 
303         ipv6_addr_copy(&daddr, addr);
304         ipv6_addr_copy(addr, &skb->nh.ipv6h->daddr);
305         ipv6_addr_copy(&skb->nh.ipv6h->daddr, &daddr);
306 
307         dst_release(xchg(&skb->dst, NULL));
308         ip6_route_input(skb);
309         if (skb->dst->error) {
310                 dst_input(skb);
311                 return -1;
312         }
313         if (skb->dst->dev->flags&IFF_LOOPBACK) {
314                 if (skb->nh.ipv6h->hop_limit <= 1) {
315                         icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
316                                     0, skb->dev);
317                         kfree_skb(skb);
318                         return -1;
319                 }
320                 skb->nh.ipv6h->hop_limit--;
321                 goto looped_back;
322         }
323 
324         dst_input(skb);
325         return -1;
326 }
327 
328 static struct inet6_protocol rthdr_protocol = {
329         .handler        =       ipv6_rthdr_rcv,
330         .flags          =       INET6_PROTO_NOPOLICY,
331 };
332 
333 void __init ipv6_rthdr_init(void)
334 {
335         if (inet6_add_protocol(&rthdr_protocol, IPPROTO_ROUTING) < 0)
336                 printk(KERN_ERR "ipv6_rthdr_init: Could not register protocol\n");
337 };
338 
339 /*
340    This function inverts received rthdr.
341    NOTE: specs allow to make it automatically only if
342    packet authenticated.
343 
344    I will not discuss it here (though, I am really pissed off at
345    this stupid requirement making rthdr idea useless)
346 
347    Actually, it creates severe problems  for us.
348    Embryonic requests has no associated sockets,
349    so that user have no control over it and
350    cannot not only to set reply options, but
351    even to know, that someone wants to connect
352    without success. :-(
353 
354    For now we need to test the engine, so that I created
355    temporary (or permanent) backdoor.
356    If listening socket set IPV6_RTHDR to 2, then we invert header.
357                                                    --ANK (980729)
358  */
359 
360 struct ipv6_txoptions *
361 ipv6_invert_rthdr(struct sock *sk, struct ipv6_rt_hdr *hdr)
362 {
363         /* Received rthdr:
364 
365            [ H1 -> H2 -> ... H_prev ]  daddr=ME
366 
367            Inverted result:
368            [ H_prev -> ... -> H1 ] daddr =sender
369 
370            Note, that IP output engine will rewrire this rthdr
371            by rotating it left by one addr.
372          */
373 
374         int n, i;
375         struct rt0_hdr *rthdr = (struct rt0_hdr*)hdr;
376         struct rt0_hdr *irthdr;
377         struct ipv6_txoptions *opt;
378         int hdrlen = ipv6_optlen(hdr);
379 
380         if (hdr->segments_left ||
381             hdr->type != IPV6_SRCRT_TYPE_0 ||
382             hdr->hdrlen & 0x01)
383                 return NULL;
384 
385         n = hdr->hdrlen >> 1;
386         opt = sock_kmalloc(sk, sizeof(*opt) + hdrlen, GFP_ATOMIC);
387         if (opt == NULL)
388                 return NULL;
389         memset(opt, 0, sizeof(*opt));
390         opt->tot_len = sizeof(*opt) + hdrlen;
391         opt->srcrt = (void*)(opt+1);
392         opt->opt_nflen = hdrlen;
393 
394         memcpy(opt->srcrt, hdr, sizeof(*hdr));
395         irthdr = (struct rt0_hdr*)opt->srcrt;
396         /* Obsolete field, MBZ, when originated by us */
397         irthdr->bitmap = 0;
398         opt->srcrt->segments_left = n;
399         for (i=0; i<n; i++)
400                 memcpy(irthdr->addr+i, rthdr->addr+(n-1-i), 16);
401         return opt;
402 }
403 
404 /**********************************
405   Hop-by-hop options.
406  **********************************/
407 
408 /* Router Alert as of RFC 2711 */
409 
410 static int ipv6_hop_ra(struct sk_buff *skb, int optoff)
411 {
412         if (skb->nh.raw[optoff+1] == 2) {
413                 ((struct inet6_skb_parm*)skb->cb)->ra = optoff;
414                 return 1;
415         }
416         if (net_ratelimit())
417                 printk(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n", skb->nh.raw[optoff+1]);
418         kfree_skb(skb);
419         return 0;
420 }
421 
422 /* Jumbo payload */
423 
424 static int ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
425 {
426         u32 pkt_len;
427 
428         if (skb->nh.raw[optoff+1] != 4 || (optoff&3) != 2) {
429                 if (net_ratelimit())
430                         printk(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n", skb->nh.raw[optoff+1]);
431                 goto drop;
432         }
433 
434         pkt_len = ntohl(*(u32*)(skb->nh.raw+optoff+2));
435         if (pkt_len <= IPV6_MAXPLEN) {
436                 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2);
437                 return 0;
438         }
439         if (skb->nh.ipv6h->payload_len) {
440                 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
441                 return 0;
442         }
443 
444         if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
445                 IP6_INC_STATS_BH(Ip6InTruncatedPkts);
446                 goto drop;
447         }
448         if (pkt_len + sizeof(struct ipv6hdr) < skb->len) {
449                 __pskb_trim(skb, pkt_len + sizeof(struct ipv6hdr));
450                 if (skb->ip_summed == CHECKSUM_HW)
451                         skb->ip_summed = CHECKSUM_NONE;
452         }
453         return 1;
454 
455 drop:
456         kfree_skb(skb);
457         return 0;
458 }
459 
460 static struct tlvtype_proc tlvprochopopt_lst[] = {
461         {
462                 .type   = IPV6_TLV_ROUTERALERT,
463                 .func   = ipv6_hop_ra,
464         },
465         {
466                 .type   = IPV6_TLV_JUMBO,
467                 .func   = ipv6_hop_jumbo,
468         },
469         { -1, }
470 };
471 
472 int ipv6_parse_hopopts(struct sk_buff *skb, int nhoff)
473 {
474         ((struct inet6_skb_parm*)skb->cb)->hop = sizeof(struct ipv6hdr);
475         if (ip6_parse_tlv(tlvprochopopt_lst, skb))
476                 return sizeof(struct ipv6hdr);
477         return -1;
478 }
479 
480 /*
481  *      Creating outbound headers.
482  *
483  *      "build" functions work when skb is filled from head to tail (datagram)
484  *      "push"  functions work when headers are added from tail to head (tcp)
485  *
486  *      In both cases we assume, that caller reserved enough room
487  *      for headers.
488  */
489 
490 static u8 *ipv6_build_rthdr(struct sk_buff *skb, u8 *prev_hdr,
491                      struct ipv6_rt_hdr *opt, struct in6_addr *addr)
492 {
493         struct rt0_hdr *phdr, *ihdr;
494         int hops;
495 
496         ihdr = (struct rt0_hdr *) opt;
497         
498         phdr = (struct rt0_hdr *) skb_put(skb, (ihdr->rt_hdr.hdrlen + 1) << 3);
499         memcpy(phdr, ihdr, sizeof(struct rt0_hdr));
500 
501         hops = ihdr->rt_hdr.hdrlen >> 1;
502 
503         if (hops > 1)
504                 memcpy(phdr->addr, ihdr->addr + 1,
505                        (hops - 1) * sizeof(struct in6_addr));
506 
507         ipv6_addr_copy(phdr->addr + (hops - 1), addr);
508 
509         phdr->rt_hdr.nexthdr = *prev_hdr;
510         *prev_hdr = NEXTHDR_ROUTING;
511         return &phdr->rt_hdr.nexthdr;
512 }
513 
514 static u8 *ipv6_build_exthdr(struct sk_buff *skb, u8 *prev_hdr, u8 type, struct ipv6_opt_hdr *opt)
515 {
516         struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_put(skb, ipv6_optlen(opt));
517 
518         memcpy(h, opt, ipv6_optlen(opt));
519         h->nexthdr = *prev_hdr;
520         *prev_hdr = type;
521         return &h->nexthdr;
522 }
523 
524 static u8 *ipv6_build_authhdr(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_opt_hdr *opt)
525 {
526         struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_put(skb, (opt->hdrlen+2)<<2);
527 
528         memcpy(h, opt, (opt->hdrlen+2)<<2);
529         h->nexthdr = *prev_hdr;
530         *prev_hdr = NEXTHDR_AUTH;
531         return &h->nexthdr;
532 }
533 
534 
535 u8 *ipv6_build_nfrag_opts(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_txoptions *opt,
536                           struct in6_addr *daddr, u32 jumbolen)
537 {
538         struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb->data;
539 
540         if (opt && opt->hopopt)
541                 prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_HOP, opt->hopopt);
542 
543         if (jumbolen) {
544                 u8 *jumboopt = (u8 *)skb_put(skb, 8);
545 
546                 if (opt && opt->hopopt) {
547                         *jumboopt++ = IPV6_TLV_PADN;
548                         *jumboopt++ = 0;
549                         h->hdrlen++;
550                 } else {
551                         h = (struct ipv6_opt_hdr *)jumboopt;
552                         h->nexthdr = *prev_hdr;
553                         h->hdrlen = 0;
554                         jumboopt += 2;
555                         *prev_hdr = NEXTHDR_HOP;
556                         prev_hdr = &h->nexthdr;
557                 }
558                 jumboopt[0] = IPV6_TLV_JUMBO;
559                 jumboopt[1] = 4;
560                 *(u32*)(jumboopt+2) = htonl(jumbolen);
561         }
562         if (opt) {
563                 if (opt->dst0opt)
564                         prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_DEST, opt->dst0opt);
565                 if (opt->srcrt)
566                         prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt, daddr);
567         }
568         return prev_hdr;
569 }
570 
571 u8 *ipv6_build_frag_opts(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_txoptions *opt)
572 {
573         if (opt->auth)
574                 prev_hdr = ipv6_build_authhdr(skb, prev_hdr, opt->auth);
575         if (opt->dst1opt)
576                 prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_DEST, opt->dst1opt);
577         return prev_hdr;
578 }
579 
580 static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
581                             struct ipv6_rt_hdr *opt,
582                             struct in6_addr **addr_p)
583 {
584         struct rt0_hdr *phdr, *ihdr;
585         int hops;
586 
587         ihdr = (struct rt0_hdr *) opt;
588         
589         phdr = (struct rt0_hdr *) skb_push(skb, (ihdr->rt_hdr.hdrlen + 1) << 3);
590         memcpy(phdr, ihdr, sizeof(struct rt0_hdr));
591 
592         hops = ihdr->rt_hdr.hdrlen >> 1;
593 
594         if (hops > 1)
595                 memcpy(phdr->addr, ihdr->addr + 1,
596                        (hops - 1) * sizeof(struct in6_addr));
597 
598         ipv6_addr_copy(phdr->addr + (hops - 1), *addr_p);
599         *addr_p = ihdr->addr;
600 
601         phdr->rt_hdr.nexthdr = *proto;
602         *proto = NEXTHDR_ROUTING;
603 }
604 
605 static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt)
606 {
607         struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ipv6_optlen(opt));
608 
609         memcpy(h, opt, ipv6_optlen(opt));
610         h->nexthdr = *proto;
611         *proto = type;
612 }
613 
614 static void ipv6_push_authhdr(struct sk_buff *skb, u8 *proto, struct ipv6_opt_hdr *opt)
615 {
616         struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, (opt->hdrlen+2)<<2);
617 
618         memcpy(h, opt, (opt->hdrlen+2)<<2);
619         h->nexthdr = *proto;
620         *proto = NEXTHDR_AUTH;
621 }
622 
623 void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
624                           u8 *proto,
625                           struct in6_addr **daddr)
626 {
627         if (opt->srcrt)
628                 ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
629         if (opt->dst0opt)
630                 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
631         if (opt->hopopt)
632                 ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
633 }
634 
635 void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto)
636 {
637         if (opt->dst1opt)
638                 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt);
639         if (opt->auth)
640                 ipv6_push_authhdr(skb, proto, opt->auth);
641 }
642 
643 struct ipv6_txoptions *
644 ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
645 {
646         struct ipv6_txoptions *opt2;
647 
648         opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC);
649         if (opt2) {
650                 long dif = (char*)opt2 - (char*)opt;
651                 memcpy(opt2, opt, opt->tot_len);
652                 if (opt2->hopopt)
653                         *((char**)&opt2->hopopt) += dif;
654                 if (opt2->dst0opt)
655                         *((char**)&opt2->dst0opt) += dif;
656                 if (opt2->dst1opt)
657                         *((char**)&opt2->dst1opt) += dif;
658                 if (opt2->auth)
659                         *((char**)&opt2->auth) += dif;
660                 if (opt2->srcrt)
661                         *((char**)&opt2->srcrt) += dif;
662         }
663         return opt2;
664 }
665 
666 
667 /* 
668  * find out if nexthdr is a well-known extension header or a protocol
669  */
670 
671 int ipv6_ext_hdr(u8 nexthdr)
672 {
673         /* 
674          * find out if nexthdr is an extension header or a protocol
675          */
676         return ( (nexthdr == NEXTHDR_HOP)       ||
677                  (nexthdr == NEXTHDR_ROUTING)   ||
678                  (nexthdr == NEXTHDR_FRAGMENT)  ||
679                  (nexthdr == NEXTHDR_AUTH)      ||
680                  (nexthdr == NEXTHDR_NONE)      ||
681                  (nexthdr == NEXTHDR_DEST) );
682 }
683 
684 /*
685  * Skip any extension headers. This is used by the ICMP module.
686  *
687  * Note that strictly speaking this conflicts with RFC 2460 4.0:
688  * ...The contents and semantics of each extension header determine whether 
689  * or not to proceed to the next header.  Therefore, extension headers must
690  * be processed strictly in the order they appear in the packet; a
691  * receiver must not, for example, scan through a packet looking for a
692  * particular kind of extension header and process that header prior to
693  * processing all preceding ones.
694  * 
695  * We do exactly this. This is a protocol bug. We can't decide after a
696  * seeing an unknown discard-with-error flavour TLV option if it's a 
697  * ICMP error message or not (errors should never be send in reply to
698  * ICMP error messages).
699  * 
700  * But I see no other way to do this. This might need to be reexamined
701  * when Linux implements ESP (and maybe AUTH) headers.
702  * --AK
703  *
704  * This function parses (probably truncated) exthdr set "hdr"
705  * of length "len". "nexthdrp" initially points to some place,
706  * where type of the first header can be found.
707  *
708  * It skips all well-known exthdrs, and returns pointer to the start
709  * of unparsable area i.e. the first header with unknown type.
710  * If it is not NULL *nexthdr is updated by type/protocol of this header.
711  *
712  * NOTES: - if packet terminated with NEXTHDR_NONE it returns NULL.
713  *        - it may return pointer pointing beyond end of packet,
714  *          if the last recognized header is truncated in the middle.
715  *        - if packet is truncated, so that all parsed headers are skipped,
716  *          it returns NULL.
717  *        - First fragment header is skipped, not-first ones
718  *          are considered as unparsable.
719  *        - ESP is unparsable for now and considered like
720  *          normal payload protocol.
721  *        - Note also special handling of AUTH header. Thanks to IPsec wizards.
722  *
723  * --ANK (980726)
724  */
725 
726 int ipv6_skip_exthdr(struct sk_buff *skb, int start, u8 *nexthdrp, int len)
727 {
728         u8 nexthdr = *nexthdrp;
729 
730         while (ipv6_ext_hdr(nexthdr)) {
731                 struct ipv6_opt_hdr hdr;
732                 int hdrlen;
733 
734                 if (len < (int)sizeof(struct ipv6_opt_hdr))
735                         return -1;
736                 if (nexthdr == NEXTHDR_NONE)
737                         return -1;
738                 if (skb_copy_bits(skb, start, &hdr, sizeof(hdr)))
739                         BUG();
740                 if (nexthdr == NEXTHDR_FRAGMENT) {
741                         struct frag_hdr *fhdr = (struct frag_hdr *) &hdr;
742                         if (ntohs(fhdr->frag_off) & ~0x7)
743                                 break;
744                         hdrlen = 8;
745                 } else if (nexthdr == NEXTHDR_AUTH)
746                         hdrlen = (hdr.hdrlen+2)<<2; 
747                 else
748                         hdrlen = ipv6_optlen(&hdr); 
749 
750                 nexthdr = hdr.nexthdr;
751                 len -= hdrlen;
752                 start += hdrlen;
753         }
754 
755         *nexthdrp = nexthdr;
756         return start;
757 }
758 
759 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp