blob: a18d4256372cf988e66d1feef51507b7163b8103 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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
Patrick McHardy951dbc82006-01-06 23:02:34 -0800155static int ipv6_destopt_rcv(struct sk_buff **skbp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156{
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
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900167 opt->lastopt = skb->h.raw - skb->nh.raw;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 opt->dst1 = skb->h.raw - skb->nh.raw;
169
170 if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) {
171 skb->h.raw += ((skb->h.raw[1]+1)<<3);
Patrick McHardy951dbc82006-01-06 23:02:34 -0800172 opt->nhoff = opt->dst1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 return 1;
174 }
175
176 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
177 return -1;
178}
179
180static struct inet6_protocol destopt_protocol = {
181 .handler = ipv6_destopt_rcv,
182 .flags = INET6_PROTO_NOPOLICY,
183};
184
185void __init ipv6_destopt_init(void)
186{
187 if (inet6_add_protocol(&destopt_protocol, IPPROTO_DSTOPTS) < 0)
188 printk(KERN_ERR "ipv6_destopt_init: Could not register protocol\n");
189}
190
191/********************************
192 NONE header. No data in packet.
193 ********************************/
194
Patrick McHardy951dbc82006-01-06 23:02:34 -0800195static int ipv6_nodata_rcv(struct sk_buff **skbp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196{
197 struct sk_buff *skb = *skbp;
198
199 kfree_skb(skb);
200 return 0;
201}
202
203static struct inet6_protocol nodata_protocol = {
204 .handler = ipv6_nodata_rcv,
205 .flags = INET6_PROTO_NOPOLICY,
206};
207
208void __init ipv6_nodata_init(void)
209{
210 if (inet6_add_protocol(&nodata_protocol, IPPROTO_NONE) < 0)
211 printk(KERN_ERR "ipv6_nodata_init: Could not register protocol\n");
212}
213
214/********************************
215 Routing header.
216 ********************************/
217
Patrick McHardy951dbc82006-01-06 23:02:34 -0800218static int ipv6_rthdr_rcv(struct sk_buff **skbp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219{
220 struct sk_buff *skb = *skbp;
221 struct inet6_skb_parm *opt = IP6CB(skb);
222 struct in6_addr *addr;
223 struct in6_addr daddr;
224 int n, i;
225
226 struct ipv6_rt_hdr *hdr;
227 struct rt0_hdr *rthdr;
228
229 if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||
230 !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
231 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
232 kfree_skb(skb);
233 return -1;
234 }
235
236 hdr = (struct ipv6_rt_hdr *) skb->h.raw;
237
238 if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr) ||
239 skb->pkt_type != PACKET_HOST) {
240 IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS);
241 kfree_skb(skb);
242 return -1;
243 }
244
245looped_back:
246 if (hdr->segments_left == 0) {
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900247 opt->lastopt = skb->h.raw - skb->nh.raw;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 opt->srcrt = skb->h.raw - skb->nh.raw;
249 skb->h.raw += (hdr->hdrlen + 1) << 3;
250 opt->dst0 = opt->dst1;
251 opt->dst1 = 0;
Patrick McHardy951dbc82006-01-06 23:02:34 -0800252 opt->nhoff = (&hdr->nexthdr) - skb->nh.raw;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 return 1;
254 }
255
256 if (hdr->type != IPV6_SRCRT_TYPE_0) {
257 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
258 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb->nh.raw);
259 return -1;
260 }
261
262 if (hdr->hdrlen & 0x01) {
263 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
264 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->hdrlen) - skb->nh.raw);
265 return -1;
266 }
267
268 /*
269 * This is the routing header forwarding algorithm from
270 * RFC 2460, page 16.
271 */
272
273 n = hdr->hdrlen >> 1;
274
275 if (hdr->segments_left > n) {
276 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
277 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->segments_left) - skb->nh.raw);
278 return -1;
279 }
280
281 /* We are about to mangle packet header. Be careful!
282 Do not damage packets queued somewhere.
283 */
284 if (skb_cloned(skb)) {
285 struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
286 kfree_skb(skb);
287 /* the copy is a forwarded packet */
288 if (skb2 == NULL) {
289 IP6_INC_STATS_BH(IPSTATS_MIB_OUTDISCARDS);
290 return -1;
291 }
292 *skbp = skb = skb2;
293 opt = IP6CB(skb2);
294 hdr = (struct ipv6_rt_hdr *) skb2->h.raw;
295 }
296
297 if (skb->ip_summed == CHECKSUM_HW)
298 skb->ip_summed = CHECKSUM_NONE;
299
300 i = n - --hdr->segments_left;
301
302 rthdr = (struct rt0_hdr *) hdr;
303 addr = rthdr->addr;
304 addr += i - 1;
305
306 if (ipv6_addr_is_multicast(addr)) {
307 IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS);
308 kfree_skb(skb);
309 return -1;
310 }
311
312 ipv6_addr_copy(&daddr, addr);
313 ipv6_addr_copy(addr, &skb->nh.ipv6h->daddr);
314 ipv6_addr_copy(&skb->nh.ipv6h->daddr, &daddr);
315
316 dst_release(xchg(&skb->dst, NULL));
317 ip6_route_input(skb);
318 if (skb->dst->error) {
319 skb_push(skb, skb->data - skb->nh.raw);
320 dst_input(skb);
321 return -1;
322 }
323
324 if (skb->dst->dev->flags&IFF_LOOPBACK) {
325 if (skb->nh.ipv6h->hop_limit <= 1) {
326 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
327 icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
328 0, skb->dev);
329 kfree_skb(skb);
330 return -1;
331 }
332 skb->nh.ipv6h->hop_limit--;
333 goto looped_back;
334 }
335
336 skb_push(skb, skb->data - skb->nh.raw);
337 dst_input(skb);
338 return -1;
339}
340
341static struct inet6_protocol rthdr_protocol = {
342 .handler = ipv6_rthdr_rcv,
343 .flags = INET6_PROTO_NOPOLICY,
344};
345
346void __init ipv6_rthdr_init(void)
347{
348 if (inet6_add_protocol(&rthdr_protocol, IPPROTO_ROUTING) < 0)
349 printk(KERN_ERR "ipv6_rthdr_init: Could not register protocol\n");
350};
351
352/*
353 This function inverts received rthdr.
354 NOTE: specs allow to make it automatically only if
355 packet authenticated.
356
357 I will not discuss it here (though, I am really pissed off at
358 this stupid requirement making rthdr idea useless)
359
360 Actually, it creates severe problems for us.
361 Embryonic requests has no associated sockets,
362 so that user have no control over it and
363 cannot not only to set reply options, but
364 even to know, that someone wants to connect
365 without success. :-(
366
367 For now we need to test the engine, so that I created
368 temporary (or permanent) backdoor.
369 If listening socket set IPV6_RTHDR to 2, then we invert header.
370 --ANK (980729)
371 */
372
373struct ipv6_txoptions *
374ipv6_invert_rthdr(struct sock *sk, struct ipv6_rt_hdr *hdr)
375{
376 /* Received rthdr:
377
378 [ H1 -> H2 -> ... H_prev ] daddr=ME
379
380 Inverted result:
381 [ H_prev -> ... -> H1 ] daddr =sender
382
383 Note, that IP output engine will rewrite this rthdr
384 by rotating it left by one addr.
385 */
386
387 int n, i;
388 struct rt0_hdr *rthdr = (struct rt0_hdr*)hdr;
389 struct rt0_hdr *irthdr;
390 struct ipv6_txoptions *opt;
391 int hdrlen = ipv6_optlen(hdr);
392
393 if (hdr->segments_left ||
394 hdr->type != IPV6_SRCRT_TYPE_0 ||
395 hdr->hdrlen & 0x01)
396 return NULL;
397
398 n = hdr->hdrlen >> 1;
399 opt = sock_kmalloc(sk, sizeof(*opt) + hdrlen, GFP_ATOMIC);
400 if (opt == NULL)
401 return NULL;
402 memset(opt, 0, sizeof(*opt));
403 opt->tot_len = sizeof(*opt) + hdrlen;
404 opt->srcrt = (void*)(opt+1);
405 opt->opt_nflen = hdrlen;
406
407 memcpy(opt->srcrt, hdr, sizeof(*hdr));
408 irthdr = (struct rt0_hdr*)opt->srcrt;
Brian Haleye6df4392005-09-10 00:15:06 -0700409 irthdr->reserved = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 opt->srcrt->segments_left = n;
411 for (i=0; i<n; i++)
412 memcpy(irthdr->addr+i, rthdr->addr+(n-1-i), 16);
413 return opt;
414}
415
Arnaldo Carvalho de Melo3cf3dc62005-12-13 23:23:20 -0800416EXPORT_SYMBOL_GPL(ipv6_invert_rthdr);
417
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418/**********************************
419 Hop-by-hop options.
420 **********************************/
421
422/* Router Alert as of RFC 2711 */
423
424static int ipv6_hop_ra(struct sk_buff *skb, int optoff)
425{
426 if (skb->nh.raw[optoff+1] == 2) {
427 IP6CB(skb)->ra = optoff;
428 return 1;
429 }
Patrick McHardy64ce2072005-08-09 20:50:53 -0700430 LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n",
431 skb->nh.raw[optoff+1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 kfree_skb(skb);
433 return 0;
434}
435
436/* Jumbo payload */
437
438static int ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
439{
440 u32 pkt_len;
441
442 if (skb->nh.raw[optoff+1] != 4 || (optoff&3) != 2) {
Patrick McHardy64ce2072005-08-09 20:50:53 -0700443 LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n",
444 skb->nh.raw[optoff+1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
446 goto drop;
447 }
448
449 pkt_len = ntohl(*(u32*)(skb->nh.raw+optoff+2));
450 if (pkt_len <= IPV6_MAXPLEN) {
451 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
452 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2);
453 return 0;
454 }
455 if (skb->nh.ipv6h->payload_len) {
456 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
457 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
458 return 0;
459 }
460
461 if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
462 IP6_INC_STATS_BH(IPSTATS_MIB_INTRUNCATEDPKTS);
463 goto drop;
464 }
Stephen Hemminger42ca89c2005-09-08 12:57:43 -0700465
466 if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
467 goto drop;
468
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 return 1;
470
471drop:
472 kfree_skb(skb);
473 return 0;
474}
475
476static struct tlvtype_proc tlvprochopopt_lst[] = {
477 {
478 .type = IPV6_TLV_ROUTERALERT,
479 .func = ipv6_hop_ra,
480 },
481 {
482 .type = IPV6_TLV_JUMBO,
483 .func = ipv6_hop_jumbo,
484 },
485 { -1, }
486};
487
YOSHIFUJI Hideakib8097392006-04-18 14:48:45 -0700488int ipv6_parse_hopopts(struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489{
Patrick McHardy951dbc82006-01-06 23:02:34 -0800490 struct inet6_skb_parm *opt = IP6CB(skb);
491
YOSHIFUJI Hideakiec670092006-04-18 14:46:26 -0700492 /*
493 * skb->nh.raw is equal to skb->data, and
494 * skb->h.raw - skb->nh.raw is always equal to
495 * sizeof(struct ipv6hdr) by definition of
496 * hop-by-hop options.
497 */
498 if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + 8) ||
499 !pskb_may_pull(skb, sizeof(struct ipv6hdr) + ((skb->h.raw[1] + 1) << 3))) {
500 kfree_skb(skb);
501 return -1;
502 }
503
Patrick McHardy951dbc82006-01-06 23:02:34 -0800504 opt->hop = sizeof(struct ipv6hdr);
505 if (ip6_parse_tlv(tlvprochopopt_lst, skb)) {
506 skb->h.raw += (skb->h.raw[1]+1)<<3;
507 opt->nhoff = sizeof(struct ipv6hdr);
YOSHIFUJI Hideakib8097392006-04-18 14:48:45 -0700508 return 1;
Patrick McHardy951dbc82006-01-06 23:02:34 -0800509 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 return -1;
511}
512
513/*
514 * Creating outbound headers.
515 *
516 * "build" functions work when skb is filled from head to tail (datagram)
517 * "push" functions work when headers are added from tail to head (tcp)
518 *
519 * In both cases we assume, that caller reserved enough room
520 * for headers.
521 */
522
523static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
524 struct ipv6_rt_hdr *opt,
525 struct in6_addr **addr_p)
526{
527 struct rt0_hdr *phdr, *ihdr;
528 int hops;
529
530 ihdr = (struct rt0_hdr *) opt;
531
532 phdr = (struct rt0_hdr *) skb_push(skb, (ihdr->rt_hdr.hdrlen + 1) << 3);
533 memcpy(phdr, ihdr, sizeof(struct rt0_hdr));
534
535 hops = ihdr->rt_hdr.hdrlen >> 1;
536
537 if (hops > 1)
538 memcpy(phdr->addr, ihdr->addr + 1,
539 (hops - 1) * sizeof(struct in6_addr));
540
541 ipv6_addr_copy(phdr->addr + (hops - 1), *addr_p);
542 *addr_p = ihdr->addr;
543
544 phdr->rt_hdr.nexthdr = *proto;
545 *proto = NEXTHDR_ROUTING;
546}
547
548static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt)
549{
550 struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ipv6_optlen(opt));
551
552 memcpy(h, opt, ipv6_optlen(opt));
553 h->nexthdr = *proto;
554 *proto = type;
555}
556
557void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
558 u8 *proto,
559 struct in6_addr **daddr)
560{
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900561 if (opt->srcrt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900563 /*
564 * IPV6_RTHDRDSTOPTS is ignored
565 * unless IPV6_RTHDR is set (RFC3542).
566 */
567 if (opt->dst0opt)
568 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
569 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 if (opt->hopopt)
571 ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
572}
573
574void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto)
575{
576 if (opt->dst1opt)
577 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt);
578}
579
580struct ipv6_txoptions *
581ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
582{
583 struct ipv6_txoptions *opt2;
584
585 opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC);
586 if (opt2) {
587 long dif = (char*)opt2 - (char*)opt;
588 memcpy(opt2, opt, opt->tot_len);
589 if (opt2->hopopt)
590 *((char**)&opt2->hopopt) += dif;
591 if (opt2->dst0opt)
592 *((char**)&opt2->dst0opt) += dif;
593 if (opt2->dst1opt)
594 *((char**)&opt2->dst1opt) += dif;
595 if (opt2->srcrt)
596 *((char**)&opt2->srcrt) += dif;
597 }
598 return opt2;
599}
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900600
Arnaldo Carvalho de Melo3cf3dc62005-12-13 23:23:20 -0800601EXPORT_SYMBOL_GPL(ipv6_dup_options);
602
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900603static int ipv6_renew_option(void *ohdr,
604 struct ipv6_opt_hdr __user *newopt, int newoptlen,
605 int inherit,
606 struct ipv6_opt_hdr **hdr,
607 char **p)
608{
609 if (inherit) {
610 if (ohdr) {
611 memcpy(*p, ohdr, ipv6_optlen((struct ipv6_opt_hdr *)ohdr));
612 *hdr = (struct ipv6_opt_hdr *)*p;
613 *p += CMSG_ALIGN(ipv6_optlen(*(struct ipv6_opt_hdr **)hdr));
614 }
615 } else {
616 if (newopt) {
617 if (copy_from_user(*p, newopt, newoptlen))
618 return -EFAULT;
619 *hdr = (struct ipv6_opt_hdr *)*p;
620 if (ipv6_optlen(*(struct ipv6_opt_hdr **)hdr) > newoptlen)
621 return -EINVAL;
622 *p += CMSG_ALIGN(newoptlen);
623 }
624 }
625 return 0;
626}
627
628struct ipv6_txoptions *
629ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
630 int newtype,
631 struct ipv6_opt_hdr __user *newopt, int newoptlen)
632{
633 int tot_len = 0;
634 char *p;
635 struct ipv6_txoptions *opt2;
636 int err;
637
638 if (newtype != IPV6_HOPOPTS && opt->hopopt)
639 tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt));
640 if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt)
641 tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt));
642 if (newtype != IPV6_RTHDR && opt->srcrt)
643 tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt));
644 if (newtype != IPV6_DSTOPTS && opt->dst1opt)
645 tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
646 if (newopt && newoptlen)
647 tot_len += CMSG_ALIGN(newoptlen);
648
649 if (!tot_len)
650 return NULL;
651
YOSHIFUJI Hideaki8b8aa4b2005-11-20 12:18:17 +0900652 tot_len += sizeof(*opt2);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900653 opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC);
654 if (!opt2)
655 return ERR_PTR(-ENOBUFS);
656
657 memset(opt2, 0, tot_len);
658
659 opt2->tot_len = tot_len;
660 p = (char *)(opt2 + 1);
661
662 err = ipv6_renew_option(opt->hopopt, newopt, newoptlen,
663 newtype != IPV6_HOPOPTS,
664 &opt2->hopopt, &p);
665 if (err)
666 goto out;
667
668 err = ipv6_renew_option(opt->dst0opt, newopt, newoptlen,
669 newtype != IPV6_RTHDRDSTOPTS,
670 &opt2->dst0opt, &p);
671 if (err)
672 goto out;
673
674 err = ipv6_renew_option(opt->srcrt, newopt, newoptlen,
675 newtype != IPV6_RTHDR,
676 (struct ipv6_opt_hdr **)opt2->srcrt, &p);
677 if (err)
678 goto out;
679
680 err = ipv6_renew_option(opt->dst1opt, newopt, newoptlen,
681 newtype != IPV6_DSTOPTS,
682 &opt2->dst1opt, &p);
683 if (err)
684 goto out;
685
686 opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) +
687 (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) +
688 (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0);
689 opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0);
690
691 return opt2;
692out:
YOSHIFUJI Hideaki8b8aa4b2005-11-20 12:18:17 +0900693 sock_kfree_s(sk, opt2, opt2->tot_len);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900694 return ERR_PTR(err);
695}
696
YOSHIFUJI Hideakidf9890c2005-11-20 12:23:18 +0900697struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
698 struct ipv6_txoptions *opt)
699{
700 /*
701 * ignore the dest before srcrt unless srcrt is being included.
702 * --yoshfuji
703 */
704 if (opt && opt->dst0opt && !opt->srcrt) {
705 if (opt_space != opt) {
706 memcpy(opt_space, opt, sizeof(*opt_space));
707 opt = opt_space;
708 }
709 opt->opt_nflen -= ipv6_optlen(opt->dst0opt);
710 opt->dst0opt = NULL;
711 }
712
713 return opt;
714}
715