diff options
Diffstat (limited to 'net/ipv6/exthdrs.c')
-rw-r--r-- | net/ipv6/exthdrs.c | 575 |
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 | |||
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 | } | ||