blob: 4aa7fb024b3a1eccdf874bdc6289e73fba0c247c [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:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +090019 * yoshfuji : ensure not to overrun while parsing
Linus Torvalds1da177e2005-04-16 15:20:36 -070020 * 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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/net.h>
31#include <linux/netdevice.h>
32#include <linux/in6.h>
33#include <linux/icmpv6.h>
34
35#include <net/sock.h>
36#include <net/snmp.h>
37
38#include <net/ipv6.h>
39#include <net/protocol.h>
40#include <net/transp_v6.h>
41#include <net/rawv6.h>
42#include <net/ndisc.h>
43#include <net/ip6_route.h>
44#include <net/addrconf.h>
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -070045#ifdef CONFIG_IPV6_MIP6
46#include <net/xfrm.h>
47#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070048
49#include <asm/uaccess.h>
50
Masahide NAKAMURAc61a40432006-08-23 19:18:35 -070051int ipv6_find_tlv(struct sk_buff *skb, int offset, int type)
52{
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -070053 const unsigned char *nh = skb_network_header(skb);
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -070054 int packet_len = skb->tail - skb->network_header;
Masahide NAKAMURAc61a40432006-08-23 19:18:35 -070055 struct ipv6_opt_hdr *hdr;
56 int len;
57
58 if (offset + 2 > packet_len)
59 goto bad;
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -070060 hdr = (struct ipv6_opt_hdr *)(nh + offset);
Masahide NAKAMURAc61a40432006-08-23 19:18:35 -070061 len = ((hdr->hdrlen + 1) << 3);
62
63 if (offset + len > packet_len)
64 goto bad;
65
66 offset += 2;
67 len -= 2;
68
69 while (len > 0) {
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -070070 int opttype = nh[offset];
Masahide NAKAMURAc61a40432006-08-23 19:18:35 -070071 int optlen;
72
73 if (opttype == type)
74 return offset;
75
76 switch (opttype) {
77 case IPV6_TLV_PAD0:
78 optlen = 1;
79 break;
80 default:
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -070081 optlen = nh[offset + 1] + 2;
Masahide NAKAMURAc61a40432006-08-23 19:18:35 -070082 if (optlen > len)
83 goto bad;
84 break;
85 }
86 offset += optlen;
87 len -= optlen;
88 }
89 /* not_found */
Masahide NAKAMURAc61a40432006-08-23 19:18:35 -070090 bad:
91 return -1;
92}
93
Linus Torvalds1da177e2005-04-16 15:20:36 -070094/*
95 * Parsing tlv encoded headers.
96 *
97 * Parsing function "func" returns 1, if parsing succeed
98 * and 0, if it failed.
99 * It MUST NOT touch skb->h.
100 */
101
102struct tlvtype_proc {
103 int type;
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700104 int (*func)(struct sk_buff **skbp, int offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105};
106
107/*********************
108 Generic functions
109 *********************/
110
111/* An unknown option is detected, decide what to do */
112
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700113static int ip6_tlvopt_unknown(struct sk_buff **skbp, int optoff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114{
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700115 struct sk_buff *skb = *skbp;
116
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700117 switch ((skb_network_header(skb)[optoff] & 0xC0) >> 6) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118 case 0: /* ignore */
119 return 1;
120
121 case 1: /* drop packet */
122 break;
123
124 case 3: /* Send ICMP if not a multicast address and drop packet */
125 /* Actually, it is redundant check. icmp_send
126 will recheck in any case.
127 */
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700128 if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 break;
130 case 2: /* send ICMP PARM PROB regardless and drop packet */
131 icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, optoff);
132 return 0;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700133 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134
135 kfree_skb(skb);
136 return 0;
137}
138
139/* Parse tlv encoded option header (hop-by-hop or destination) */
140
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700141static int ip6_parse_tlv(struct tlvtype_proc *procs, struct sk_buff **skbp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142{
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700143 struct sk_buff *skb = *skbp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 struct tlvtype_proc *curr;
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700145 const unsigned char *nh = skb_network_header(skb);
Arnaldo Carvalho de Melocfe1fc72007-03-16 17:26:39 -0300146 int off = skb_network_header_len(skb);
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700147 int len = (skb_transport_header(skb)[1] + 1) << 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148
Arnaldo Carvalho de Meloea2ae172007-04-25 17:55:53 -0700149 if (skb_transport_offset(skb) + len > skb_headlen(skb))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 goto bad;
151
152 off += 2;
153 len -= 2;
154
155 while (len > 0) {
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700156 int optlen = nh[off + 1] + 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700158 switch (nh[off]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 case IPV6_TLV_PAD0:
160 optlen = 1;
161 break;
162
163 case IPV6_TLV_PADN:
164 break;
165
166 default: /* Other TLV code so scan list */
167 if (optlen > len)
168 goto bad;
169 for (curr=procs; curr->type >= 0; curr++) {
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700170 if (curr->type == nh[off]) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900171 /* type specific length/alignment
172 checks will be performed in the
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 func(). */
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700174 if (curr->func(skbp, off) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 return 0;
176 break;
177 }
178 }
179 if (curr->type < 0) {
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700180 if (ip6_tlvopt_unknown(skbp, off) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 return 0;
182 }
183 break;
184 }
185 off += optlen;
186 len -= optlen;
187 }
188 if (len == 0)
189 return 1;
190bad:
191 kfree_skb(skb);
192 return 0;
193}
194
195/*****************************
196 Destination options header.
197 *****************************/
198
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700199#ifdef CONFIG_IPV6_MIP6
200static int ipv6_dest_hao(struct sk_buff **skbp, int optoff)
201{
202 struct sk_buff *skb = *skbp;
203 struct ipv6_destopt_hao *hao;
204 struct inet6_skb_parm *opt = IP6CB(skb);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700205 struct ipv6hdr *ipv6h = ipv6_hdr(skb);
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700206 struct in6_addr tmp_addr;
207 int ret;
208
209 if (opt->dsthao) {
210 LIMIT_NETDEBUG(KERN_DEBUG "hao duplicated\n");
211 goto discard;
212 }
213 opt->dsthao = opt->dst1;
214 opt->dst1 = 0;
215
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700216 hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + optoff);
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700217
218 if (hao->length != 16) {
219 LIMIT_NETDEBUG(
220 KERN_DEBUG "hao invalid option length = %d\n", hao->length);
221 goto discard;
222 }
223
224 if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) {
225 LIMIT_NETDEBUG(
226 KERN_DEBUG "hao is not an unicast addr: " NIP6_FMT "\n", NIP6(hao->addr));
227 goto discard;
228 }
229
230 ret = xfrm6_input_addr(skb, (xfrm_address_t *)&ipv6h->daddr,
231 (xfrm_address_t *)&hao->addr, IPPROTO_DSTOPTS);
232 if (unlikely(ret < 0))
233 goto discard;
234
235 if (skb_cloned(skb)) {
236 struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
Masahide NAKAMURAdc435e62006-08-31 15:18:49 -0700237 struct inet6_skb_parm *opt2;
238
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700239 if (skb2 == NULL)
240 goto discard;
241
Masahide NAKAMURAdc435e62006-08-31 15:18:49 -0700242 opt2 = IP6CB(skb2);
243 memcpy(opt2, opt, sizeof(*opt2));
244
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700245 kfree_skb(skb);
246
247 /* update all variable using below by copied skbuff */
248 *skbp = skb = skb2;
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700249 hao = (struct ipv6_destopt_hao *)(skb_network_header(skb2) +
250 optoff);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700251 ipv6h = ipv6_hdr(skb2);
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700252 }
253
254 if (skb->ip_summed == CHECKSUM_COMPLETE)
255 skb->ip_summed = CHECKSUM_NONE;
256
257 ipv6_addr_copy(&tmp_addr, &ipv6h->saddr);
258 ipv6_addr_copy(&ipv6h->saddr, &hao->addr);
259 ipv6_addr_copy(&hao->addr, &tmp_addr);
260
Eric Dumazetb7aa0bf2007-04-19 16:16:32 -0700261 if (skb->tstamp.tv64 == 0)
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700262 __net_timestamp(skb);
263
264 return 1;
265
266 discard:
267 kfree_skb(skb);
268 return 0;
269}
270#endif
271
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272static struct tlvtype_proc tlvprocdestopt_lst[] = {
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700273#ifdef CONFIG_IPV6_MIP6
274 {
275 .type = IPV6_TLV_HAO,
276 .func = ipv6_dest_hao,
277 },
278#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 {-1, NULL}
280};
281
Patrick McHardy951dbc82006-01-06 23:02:34 -0800282static int ipv6_destopt_rcv(struct sk_buff **skbp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283{
284 struct sk_buff *skb = *skbp;
285 struct inet6_skb_parm *opt = IP6CB(skb);
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700286#ifdef CONFIG_IPV6_MIP6
287 __u16 dstbuf;
288#endif
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900289 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290
Arnaldo Carvalho de Meloea2ae172007-04-25 17:55:53 -0700291 if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) ||
292 !pskb_may_pull(skb, (skb_transport_offset(skb) +
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700293 ((skb_transport_header(skb)[1] + 1) << 3)))) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900294 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
295 IPSTATS_MIB_INHDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 kfree_skb(skb);
297 return -1;
298 }
299
Arnaldo Carvalho de Melocfe1fc72007-03-16 17:26:39 -0300300 opt->lastopt = opt->dst1 = skb_network_header_len(skb);
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700301#ifdef CONFIG_IPV6_MIP6
302 dstbuf = opt->dst1;
303#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900305 dst = dst_clone(skb->dst);
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700306 if (ip6_parse_tlv(tlvprocdestopt_lst, skbp)) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900307 dst_release(dst);
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700308 skb = *skbp;
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -0700309 skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3;
Masahide NAKAMURAdc435e62006-08-31 15:18:49 -0700310 opt = IP6CB(skb);
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700311#ifdef CONFIG_IPV6_MIP6
312 opt->nhoff = dstbuf;
313#else
Patrick McHardy951dbc82006-01-06 23:02:34 -0800314 opt->nhoff = opt->dst1;
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700315#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 return 1;
317 }
318
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900319 IP6_INC_STATS_BH(ip6_dst_idev(dst), IPSTATS_MIB_INHDRERRORS);
320 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321 return -1;
322}
323
324static struct inet6_protocol destopt_protocol = {
325 .handler = ipv6_destopt_rcv,
Herbert Xuadcfc7d2006-06-30 13:36:15 -0700326 .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327};
328
329void __init ipv6_destopt_init(void)
330{
331 if (inet6_add_protocol(&destopt_protocol, IPPROTO_DSTOPTS) < 0)
332 printk(KERN_ERR "ipv6_destopt_init: Could not register protocol\n");
333}
334
335/********************************
336 NONE header. No data in packet.
337 ********************************/
338
Patrick McHardy951dbc82006-01-06 23:02:34 -0800339static int ipv6_nodata_rcv(struct sk_buff **skbp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340{
341 struct sk_buff *skb = *skbp;
342
343 kfree_skb(skb);
344 return 0;
345}
346
347static struct inet6_protocol nodata_protocol = {
348 .handler = ipv6_nodata_rcv,
349 .flags = INET6_PROTO_NOPOLICY,
350};
351
352void __init ipv6_nodata_init(void)
353{
354 if (inet6_add_protocol(&nodata_protocol, IPPROTO_NONE) < 0)
355 printk(KERN_ERR "ipv6_nodata_init: Could not register protocol\n");
356}
357
358/********************************
359 Routing header.
360 ********************************/
361
Patrick McHardy951dbc82006-01-06 23:02:34 -0800362static int ipv6_rthdr_rcv(struct sk_buff **skbp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363{
364 struct sk_buff *skb = *skbp;
365 struct inet6_skb_parm *opt = IP6CB(skb);
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700366 struct in6_addr *addr = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 struct in6_addr daddr;
YOSHIFUJI Hideaki0bcbc922007-04-24 14:58:30 -0700368 struct inet6_dev *idev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 int n, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 struct ipv6_rt_hdr *hdr;
371 struct rt0_hdr *rthdr;
YOSHIFUJI Hideaki0bcbc922007-04-24 14:58:30 -0700372 int accept_source_route = ipv6_devconf.accept_source_route;
373
374 if (accept_source_route < 0 ||
375 ((idev = in6_dev_get(skb->dev)) == NULL)) {
376 kfree_skb(skb);
377 return -1;
378 }
379 if (idev->cnf.accept_source_route < 0) {
380 in6_dev_put(idev);
381 kfree_skb(skb);
382 return -1;
383 }
384
385 if (accept_source_route > idev->cnf.accept_source_route)
386 accept_source_route = idev->cnf.accept_source_route;
387
388 in6_dev_put(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389
Arnaldo Carvalho de Meloea2ae172007-04-25 17:55:53 -0700390 if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) ||
391 !pskb_may_pull(skb, (skb_transport_offset(skb) +
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700392 ((skb_transport_header(skb)[1] + 1) << 3)))) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900393 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
394 IPSTATS_MIB_INHDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 kfree_skb(skb);
396 return -1;
397 }
398
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700399 hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400
YOSHIFUJI Hideaki0bcbc922007-04-24 14:58:30 -0700401 switch (hdr->type) {
402#ifdef CONFIG_IPV6_MIP6
403 break;
404#endif
405 case IPV6_SRCRT_TYPE_0:
YOSHIFUJI Hideakia23cf142007-04-25 11:13:49 +0900406 if (accept_source_route > 0)
YOSHIFUJI Hideaki0bcbc922007-04-24 14:58:30 -0700407 break;
408 kfree_skb(skb);
409 return -1;
410 default:
411 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
412 IPSTATS_MIB_INHDRERRORS);
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700413 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
414 (&hdr->type) - skb_network_header(skb));
YOSHIFUJI Hideaki0bcbc922007-04-24 14:58:30 -0700415 return -1;
416 }
417
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700418 if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 skb->pkt_type != PACKET_HOST) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900420 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
421 IPSTATS_MIB_INADDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 kfree_skb(skb);
423 return -1;
424 }
425
426looped_back:
427 if (hdr->segments_left == 0) {
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700428 switch (hdr->type) {
429#ifdef CONFIG_IPV6_MIP6
430 case IPV6_SRCRT_TYPE_2:
431 /* Silently discard type 2 header unless it was
432 * processed by own
433 */
434 if (!addr) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900435 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
436 IPSTATS_MIB_INADDRERRORS);
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700437 kfree_skb(skb);
438 return -1;
439 }
440 break;
441#endif
442 default:
443 break;
444 }
445
Arnaldo Carvalho de Melocfe1fc72007-03-16 17:26:39 -0300446 opt->lastopt = opt->srcrt = skb_network_header_len(skb);
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -0700447 skb->transport_header += (hdr->hdrlen + 1) << 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 opt->dst0 = opt->dst1;
449 opt->dst1 = 0;
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700450 opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 return 1;
452 }
453
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700454 switch (hdr->type) {
455 case IPV6_SRCRT_TYPE_0:
456 if (hdr->hdrlen & 0x01) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900457 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
458 IPSTATS_MIB_INHDRERRORS);
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700459 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
460 ((&hdr->hdrlen) -
461 skb_network_header(skb)));
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700462 return -1;
463 }
464 break;
465#ifdef CONFIG_IPV6_MIP6
466 case IPV6_SRCRT_TYPE_2:
467 /* Silently discard invalid RTH type 2 */
468 if (hdr->hdrlen != 2 || hdr->segments_left != 1) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900469 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
470 IPSTATS_MIB_INHDRERRORS);
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700471 kfree_skb(skb);
472 return -1;
473 }
474 break;
475#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477
478 /*
479 * This is the routing header forwarding algorithm from
480 * RFC 2460, page 16.
481 */
482
483 n = hdr->hdrlen >> 1;
484
485 if (hdr->segments_left > n) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900486 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
487 IPSTATS_MIB_INHDRERRORS);
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700488 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
489 ((&hdr->segments_left) -
490 skb_network_header(skb)));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 return -1;
492 }
493
494 /* We are about to mangle packet header. Be careful!
495 Do not damage packets queued somewhere.
496 */
497 if (skb_cloned(skb)) {
498 struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499 /* the copy is a forwarded packet */
500 if (skb2 == NULL) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900501 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
502 IPSTATS_MIB_OUTDISCARDS);
503 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 return -1;
505 }
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900506 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 *skbp = skb = skb2;
508 opt = IP6CB(skb2);
Arnaldo Carvalho de Melobff9b612007-03-16 17:19:57 -0300509 hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 }
511
Patrick McHardy84fa7932006-08-29 16:44:56 -0700512 if (skb->ip_summed == CHECKSUM_COMPLETE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 skb->ip_summed = CHECKSUM_NONE;
514
515 i = n - --hdr->segments_left;
516
517 rthdr = (struct rt0_hdr *) hdr;
518 addr = rthdr->addr;
519 addr += i - 1;
520
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700521 switch (hdr->type) {
522#ifdef CONFIG_IPV6_MIP6
523 case IPV6_SRCRT_TYPE_2:
524 if (xfrm6_input_addr(skb, (xfrm_address_t *)addr,
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700525 (xfrm_address_t *)&ipv6_hdr(skb)->saddr,
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700526 IPPROTO_ROUTING) < 0) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900527 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
528 IPSTATS_MIB_INADDRERRORS);
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700529 kfree_skb(skb);
530 return -1;
531 }
532 if (!ipv6_chk_home_addr(addr)) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900533 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
534 IPSTATS_MIB_INADDRERRORS);
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700535 kfree_skb(skb);
536 return -1;
537 }
538 break;
539#endif
540 default:
541 break;
542 }
543
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 if (ipv6_addr_is_multicast(addr)) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900545 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
546 IPSTATS_MIB_INADDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 kfree_skb(skb);
548 return -1;
549 }
550
551 ipv6_addr_copy(&daddr, addr);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700552 ipv6_addr_copy(addr, &ipv6_hdr(skb)->daddr);
553 ipv6_addr_copy(&ipv6_hdr(skb)->daddr, &daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554
555 dst_release(xchg(&skb->dst, NULL));
556 ip6_route_input(skb);
557 if (skb->dst->error) {
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700558 skb_push(skb, skb->data - skb_network_header(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 dst_input(skb);
560 return -1;
561 }
562
563 if (skb->dst->dev->flags&IFF_LOOPBACK) {
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700564 if (ipv6_hdr(skb)->hop_limit <= 1) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900565 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
566 IPSTATS_MIB_INHDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
568 0, skb->dev);
569 kfree_skb(skb);
570 return -1;
571 }
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700572 ipv6_hdr(skb)->hop_limit--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 goto looped_back;
574 }
575
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700576 skb_push(skb, skb->data - skb_network_header(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 dst_input(skb);
578 return -1;
579}
580
581static struct inet6_protocol rthdr_protocol = {
582 .handler = ipv6_rthdr_rcv,
Herbert Xuadcfc7d2006-06-30 13:36:15 -0700583 .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584};
585
586void __init ipv6_rthdr_init(void)
587{
588 if (inet6_add_protocol(&rthdr_protocol, IPPROTO_ROUTING) < 0)
589 printk(KERN_ERR "ipv6_rthdr_init: Could not register protocol\n");
590};
591
592/*
593 This function inverts received rthdr.
594 NOTE: specs allow to make it automatically only if
595 packet authenticated.
596
597 I will not discuss it here (though, I am really pissed off at
598 this stupid requirement making rthdr idea useless)
599
600 Actually, it creates severe problems for us.
601 Embryonic requests has no associated sockets,
602 so that user have no control over it and
603 cannot not only to set reply options, but
604 even to know, that someone wants to connect
605 without success. :-(
606
607 For now we need to test the engine, so that I created
608 temporary (or permanent) backdoor.
609 If listening socket set IPV6_RTHDR to 2, then we invert header.
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900610 --ANK (980729)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611 */
612
613struct ipv6_txoptions *
614ipv6_invert_rthdr(struct sock *sk, struct ipv6_rt_hdr *hdr)
615{
616 /* Received rthdr:
617
618 [ H1 -> H2 -> ... H_prev ] daddr=ME
619
620 Inverted result:
621 [ H_prev -> ... -> H1 ] daddr =sender
622
623 Note, that IP output engine will rewrite this rthdr
624 by rotating it left by one addr.
625 */
626
627 int n, i;
628 struct rt0_hdr *rthdr = (struct rt0_hdr*)hdr;
629 struct rt0_hdr *irthdr;
630 struct ipv6_txoptions *opt;
631 int hdrlen = ipv6_optlen(hdr);
632
633 if (hdr->segments_left ||
634 hdr->type != IPV6_SRCRT_TYPE_0 ||
635 hdr->hdrlen & 0x01)
636 return NULL;
637
638 n = hdr->hdrlen >> 1;
639 opt = sock_kmalloc(sk, sizeof(*opt) + hdrlen, GFP_ATOMIC);
640 if (opt == NULL)
641 return NULL;
642 memset(opt, 0, sizeof(*opt));
643 opt->tot_len = sizeof(*opt) + hdrlen;
644 opt->srcrt = (void*)(opt+1);
645 opt->opt_nflen = hdrlen;
646
647 memcpy(opt->srcrt, hdr, sizeof(*hdr));
648 irthdr = (struct rt0_hdr*)opt->srcrt;
Brian Haleye6df4392005-09-10 00:15:06 -0700649 irthdr->reserved = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 opt->srcrt->segments_left = n;
651 for (i=0; i<n; i++)
652 memcpy(irthdr->addr+i, rthdr->addr+(n-1-i), 16);
653 return opt;
654}
655
Arnaldo Carvalho de Melo3cf3dc62005-12-13 23:23:20 -0800656EXPORT_SYMBOL_GPL(ipv6_invert_rthdr);
657
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658/**********************************
659 Hop-by-hop options.
660 **********************************/
661
662/* Router Alert as of RFC 2711 */
663
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700664static int ipv6_hop_ra(struct sk_buff **skbp, int optoff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665{
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700666 struct sk_buff *skb = *skbp;
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700667 const unsigned char *nh = skb_network_header(skb);
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700668
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700669 if (nh[optoff + 1] == 2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 IP6CB(skb)->ra = optoff;
671 return 1;
672 }
Patrick McHardy64ce2072005-08-09 20:50:53 -0700673 LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n",
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700674 nh[optoff + 1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 kfree_skb(skb);
676 return 0;
677}
678
679/* Jumbo payload */
680
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700681static int ipv6_hop_jumbo(struct sk_buff **skbp, int optoff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682{
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700683 struct sk_buff *skb = *skbp;
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700684 const unsigned char *nh = skb_network_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 u32 pkt_len;
686
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700687 if (nh[optoff + 1] != 4 || (optoff & 3) != 2) {
Patrick McHardy64ce2072005-08-09 20:50:53 -0700688 LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n",
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700689 nh[optoff+1]);
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900690 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
691 IPSTATS_MIB_INHDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 goto drop;
693 }
694
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700695 pkt_len = ntohl(*(__be32 *)(nh + optoff + 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 if (pkt_len <= IPV6_MAXPLEN) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900697 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2);
699 return 0;
700 }
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700701 if (ipv6_hdr(skb)->payload_len) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900702 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
704 return 0;
705 }
706
707 if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900708 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INTRUNCATEDPKTS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 goto drop;
710 }
Stephen Hemminger42ca89c2005-09-08 12:57:43 -0700711
712 if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
713 goto drop;
714
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 return 1;
716
717drop:
718 kfree_skb(skb);
719 return 0;
720}
721
722static struct tlvtype_proc tlvprochopopt_lst[] = {
723 {
724 .type = IPV6_TLV_ROUTERALERT,
725 .func = ipv6_hop_ra,
726 },
727 {
728 .type = IPV6_TLV_JUMBO,
729 .func = ipv6_hop_jumbo,
730 },
731 { -1, }
732};
733
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700734int ipv6_parse_hopopts(struct sk_buff **skbp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735{
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700736 struct sk_buff *skb = *skbp;
Patrick McHardy951dbc82006-01-06 23:02:34 -0800737 struct inet6_skb_parm *opt = IP6CB(skb);
738
YOSHIFUJI Hideakiec670092006-04-18 14:46:26 -0700739 /*
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700740 * skb_network_header(skb) is equal to skb->data, and
Arnaldo Carvalho de Melocfe1fc72007-03-16 17:26:39 -0300741 * skb_network_header_len(skb) is always equal to
YOSHIFUJI Hideakiec670092006-04-18 14:46:26 -0700742 * sizeof(struct ipv6hdr) by definition of
743 * hop-by-hop options.
744 */
745 if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + 8) ||
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700746 !pskb_may_pull(skb, (sizeof(struct ipv6hdr) +
747 ((skb_transport_header(skb)[1] + 1) << 3)))) {
YOSHIFUJI Hideakiec670092006-04-18 14:46:26 -0700748 kfree_skb(skb);
749 return -1;
750 }
751
Patrick McHardy951dbc82006-01-06 23:02:34 -0800752 opt->hop = sizeof(struct ipv6hdr);
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700753 if (ip6_parse_tlv(tlvprochopopt_lst, skbp)) {
754 skb = *skbp;
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -0700755 skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3;
Masahide NAKAMURAdc435e62006-08-31 15:18:49 -0700756 opt = IP6CB(skb);
Patrick McHardy951dbc82006-01-06 23:02:34 -0800757 opt->nhoff = sizeof(struct ipv6hdr);
YOSHIFUJI Hideakib8097392006-04-18 14:48:45 -0700758 return 1;
Patrick McHardy951dbc82006-01-06 23:02:34 -0800759 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 return -1;
761}
762
763/*
764 * Creating outbound headers.
765 *
766 * "build" functions work when skb is filled from head to tail (datagram)
767 * "push" functions work when headers are added from tail to head (tcp)
768 *
769 * In both cases we assume, that caller reserved enough room
770 * for headers.
771 */
772
773static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
774 struct ipv6_rt_hdr *opt,
775 struct in6_addr **addr_p)
776{
777 struct rt0_hdr *phdr, *ihdr;
778 int hops;
779
780 ihdr = (struct rt0_hdr *) opt;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900781
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 phdr = (struct rt0_hdr *) skb_push(skb, (ihdr->rt_hdr.hdrlen + 1) << 3);
783 memcpy(phdr, ihdr, sizeof(struct rt0_hdr));
784
785 hops = ihdr->rt_hdr.hdrlen >> 1;
786
787 if (hops > 1)
788 memcpy(phdr->addr, ihdr->addr + 1,
789 (hops - 1) * sizeof(struct in6_addr));
790
791 ipv6_addr_copy(phdr->addr + (hops - 1), *addr_p);
792 *addr_p = ihdr->addr;
793
794 phdr->rt_hdr.nexthdr = *proto;
795 *proto = NEXTHDR_ROUTING;
796}
797
798static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt)
799{
800 struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ipv6_optlen(opt));
801
802 memcpy(h, opt, ipv6_optlen(opt));
803 h->nexthdr = *proto;
804 *proto = type;
805}
806
807void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
808 u8 *proto,
809 struct in6_addr **daddr)
810{
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900811 if (opt->srcrt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900813 /*
814 * IPV6_RTHDRDSTOPTS is ignored
815 * unless IPV6_RTHDR is set (RFC3542).
816 */
817 if (opt->dst0opt)
818 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
819 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 if (opt->hopopt)
821 ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
822}
823
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900824EXPORT_SYMBOL(ipv6_push_nfrag_opts);
825
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto)
827{
828 if (opt->dst1opt)
829 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt);
830}
831
832struct ipv6_txoptions *
833ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
834{
835 struct ipv6_txoptions *opt2;
836
837 opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC);
838 if (opt2) {
839 long dif = (char*)opt2 - (char*)opt;
840 memcpy(opt2, opt, opt->tot_len);
841 if (opt2->hopopt)
842 *((char**)&opt2->hopopt) += dif;
843 if (opt2->dst0opt)
844 *((char**)&opt2->dst0opt) += dif;
845 if (opt2->dst1opt)
846 *((char**)&opt2->dst1opt) += dif;
847 if (opt2->srcrt)
848 *((char**)&opt2->srcrt) += dif;
849 }
850 return opt2;
851}
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900852
Arnaldo Carvalho de Melo3cf3dc62005-12-13 23:23:20 -0800853EXPORT_SYMBOL_GPL(ipv6_dup_options);
854
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900855static int ipv6_renew_option(void *ohdr,
856 struct ipv6_opt_hdr __user *newopt, int newoptlen,
857 int inherit,
858 struct ipv6_opt_hdr **hdr,
859 char **p)
860{
861 if (inherit) {
862 if (ohdr) {
863 memcpy(*p, ohdr, ipv6_optlen((struct ipv6_opt_hdr *)ohdr));
864 *hdr = (struct ipv6_opt_hdr *)*p;
865 *p += CMSG_ALIGN(ipv6_optlen(*(struct ipv6_opt_hdr **)hdr));
866 }
867 } else {
868 if (newopt) {
869 if (copy_from_user(*p, newopt, newoptlen))
870 return -EFAULT;
871 *hdr = (struct ipv6_opt_hdr *)*p;
872 if (ipv6_optlen(*(struct ipv6_opt_hdr **)hdr) > newoptlen)
873 return -EINVAL;
874 *p += CMSG_ALIGN(newoptlen);
875 }
876 }
877 return 0;
878}
879
880struct ipv6_txoptions *
881ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
882 int newtype,
883 struct ipv6_opt_hdr __user *newopt, int newoptlen)
884{
885 int tot_len = 0;
886 char *p;
887 struct ipv6_txoptions *opt2;
888 int err;
889
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700890 if (opt) {
891 if (newtype != IPV6_HOPOPTS && opt->hopopt)
892 tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt));
893 if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt)
894 tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt));
895 if (newtype != IPV6_RTHDR && opt->srcrt)
896 tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt));
897 if (newtype != IPV6_DSTOPTS && opt->dst1opt)
898 tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
899 }
900
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900901 if (newopt && newoptlen)
902 tot_len += CMSG_ALIGN(newoptlen);
903
904 if (!tot_len)
905 return NULL;
906
YOSHIFUJI Hideaki8b8aa4b2005-11-20 12:18:17 +0900907 tot_len += sizeof(*opt2);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900908 opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC);
909 if (!opt2)
910 return ERR_PTR(-ENOBUFS);
911
912 memset(opt2, 0, tot_len);
913
914 opt2->tot_len = tot_len;
915 p = (char *)(opt2 + 1);
916
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700917 err = ipv6_renew_option(opt ? opt->hopopt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900918 newtype != IPV6_HOPOPTS,
919 &opt2->hopopt, &p);
920 if (err)
921 goto out;
922
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700923 err = ipv6_renew_option(opt ? opt->dst0opt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900924 newtype != IPV6_RTHDRDSTOPTS,
925 &opt2->dst0opt, &p);
926 if (err)
927 goto out;
928
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700929 err = ipv6_renew_option(opt ? opt->srcrt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900930 newtype != IPV6_RTHDR,
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700931 (struct ipv6_opt_hdr **)&opt2->srcrt, &p);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900932 if (err)
933 goto out;
934
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700935 err = ipv6_renew_option(opt ? opt->dst1opt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900936 newtype != IPV6_DSTOPTS,
937 &opt2->dst1opt, &p);
938 if (err)
939 goto out;
940
941 opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) +
942 (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) +
943 (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0);
944 opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0);
945
946 return opt2;
947out:
YOSHIFUJI Hideaki8b8aa4b2005-11-20 12:18:17 +0900948 sock_kfree_s(sk, opt2, opt2->tot_len);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900949 return ERR_PTR(err);
950}
951
YOSHIFUJI Hideakidf9890c2005-11-20 12:23:18 +0900952struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
953 struct ipv6_txoptions *opt)
954{
955 /*
956 * ignore the dest before srcrt unless srcrt is being included.
957 * --yoshfuji
958 */
959 if (opt && opt->dst0opt && !opt->srcrt) {
960 if (opt_space != opt) {
961 memcpy(opt_space, opt, sizeof(*opt_space));
962 opt = opt_space;
963 }
964 opt->opt_nflen -= ipv6_optlen(opt->dst0opt);
965 opt->dst0opt = NULL;
966 }
967
968 return opt;
969}
970