]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 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; | |
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 = 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 | ||
179 | static struct inet6_protocol destopt_protocol = { | |
180 | .handler = ipv6_destopt_rcv, | |
181 | .flags = INET6_PROTO_NOPOLICY, | |
182 | }; | |
183 | ||
184 | void __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 | ||
194 | static 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 | ||
202 | static struct inet6_protocol nodata_protocol = { | |
203 | .handler = ipv6_nodata_rcv, | |
204 | .flags = INET6_PROTO_NOPOLICY, | |
205 | }; | |
206 | ||
207 | void __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 | ||
217 | static 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 | ||
244 | looped_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 | ||
339 | static struct inet6_protocol rthdr_protocol = { | |
340 | .handler = ipv6_rthdr_rcv, | |
341 | .flags = INET6_PROTO_NOPOLICY, | |
342 | }; | |
343 | ||
344 | void __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 | ||
371 | struct ipv6_txoptions * | |
372 | ipv6_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 | ||
421 | static 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 | ||
435 | static 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 | ||
469 | drop: | |
470 | kfree_skb(skb); | |
471 | return 0; | |
472 | } | |
473 | ||
474 | static 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 | ||
486 | int 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 | ||
504 | static 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 | ||
529 | static 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 | ||
538 | void 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 | ||
550 | void 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 | ||
556 | struct ipv6_txoptions * | |
557 | ipv6_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 | } |