blob: 6d8e4ac7bdad84a7631bb9c3eabca4bb66c5b7c2 [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 NAKAMURAc61a4042006-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 NAKAMURAc61a4042006-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 NAKAMURAc61a4042006-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 NAKAMURAc61a4042006-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 NAKAMURAc61a4042006-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 NAKAMURAc61a4042006-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
YOSHIFUJI Hideakiebbd90a2007-04-27 02:13:39 -0700403 case IPV6_SRCRT_TYPE_2:
YOSHIFUJI Hideaki0bcbc922007-04-24 14:58:30 -0700404 break;
405#endif
406 case IPV6_SRCRT_TYPE_0:
YOSHIFUJI Hideakia23cf142007-04-25 11:13:49 +0900407 if (accept_source_route > 0)
YOSHIFUJI Hideaki0bcbc922007-04-24 14:58:30 -0700408 break;
409 kfree_skb(skb);
410 return -1;
411 default:
412 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
413 IPSTATS_MIB_INHDRERRORS);
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700414 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
415 (&hdr->type) - skb_network_header(skb));
YOSHIFUJI Hideaki0bcbc922007-04-24 14:58:30 -0700416 return -1;
417 }
418
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700419 if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 skb->pkt_type != PACKET_HOST) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900421 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
422 IPSTATS_MIB_INADDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423 kfree_skb(skb);
424 return -1;
425 }
426
427looped_back:
428 if (hdr->segments_left == 0) {
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700429 switch (hdr->type) {
430#ifdef CONFIG_IPV6_MIP6
431 case IPV6_SRCRT_TYPE_2:
432 /* Silently discard type 2 header unless it was
433 * processed by own
434 */
435 if (!addr) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900436 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
437 IPSTATS_MIB_INADDRERRORS);
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700438 kfree_skb(skb);
439 return -1;
440 }
441 break;
442#endif
443 default:
444 break;
445 }
446
Arnaldo Carvalho de Melocfe1fc72007-03-16 17:26:39 -0300447 opt->lastopt = opt->srcrt = skb_network_header_len(skb);
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -0700448 skb->transport_header += (hdr->hdrlen + 1) << 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 opt->dst0 = opt->dst1;
450 opt->dst1 = 0;
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700451 opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 return 1;
453 }
454
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700455 switch (hdr->type) {
456 case IPV6_SRCRT_TYPE_0:
457 if (hdr->hdrlen & 0x01) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900458 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
459 IPSTATS_MIB_INHDRERRORS);
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700460 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
461 ((&hdr->hdrlen) -
462 skb_network_header(skb)));
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700463 return -1;
464 }
465 break;
466#ifdef CONFIG_IPV6_MIP6
467 case IPV6_SRCRT_TYPE_2:
468 /* Silently discard invalid RTH type 2 */
469 if (hdr->hdrlen != 2 || hdr->segments_left != 1) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900470 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
471 IPSTATS_MIB_INHDRERRORS);
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700472 kfree_skb(skb);
473 return -1;
474 }
475 break;
476#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478
479 /*
480 * This is the routing header forwarding algorithm from
481 * RFC 2460, page 16.
482 */
483
484 n = hdr->hdrlen >> 1;
485
486 if (hdr->segments_left > n) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900487 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
488 IPSTATS_MIB_INHDRERRORS);
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700489 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
490 ((&hdr->segments_left) -
491 skb_network_header(skb)));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 return -1;
493 }
494
495 /* We are about to mangle packet header. Be careful!
496 Do not damage packets queued somewhere.
497 */
498 if (skb_cloned(skb)) {
499 struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 /* the copy is a forwarded packet */
501 if (skb2 == NULL) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900502 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
503 IPSTATS_MIB_OUTDISCARDS);
504 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 return -1;
506 }
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900507 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 *skbp = skb = skb2;
509 opt = IP6CB(skb2);
Arnaldo Carvalho de Melobff9b612007-03-16 17:19:57 -0300510 hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 }
512
Patrick McHardy84fa7932006-08-29 16:44:56 -0700513 if (skb->ip_summed == CHECKSUM_COMPLETE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 skb->ip_summed = CHECKSUM_NONE;
515
516 i = n - --hdr->segments_left;
517
518 rthdr = (struct rt0_hdr *) hdr;
519 addr = rthdr->addr;
520 addr += i - 1;
521
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700522 switch (hdr->type) {
523#ifdef CONFIG_IPV6_MIP6
524 case IPV6_SRCRT_TYPE_2:
525 if (xfrm6_input_addr(skb, (xfrm_address_t *)addr,
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700526 (xfrm_address_t *)&ipv6_hdr(skb)->saddr,
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700527 IPPROTO_ROUTING) < 0) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900528 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
529 IPSTATS_MIB_INADDRERRORS);
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700530 kfree_skb(skb);
531 return -1;
532 }
533 if (!ipv6_chk_home_addr(addr)) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900534 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
535 IPSTATS_MIB_INADDRERRORS);
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700536 kfree_skb(skb);
537 return -1;
538 }
539 break;
540#endif
541 default:
542 break;
543 }
544
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 if (ipv6_addr_is_multicast(addr)) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900546 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
547 IPSTATS_MIB_INADDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 kfree_skb(skb);
549 return -1;
550 }
551
552 ipv6_addr_copy(&daddr, addr);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700553 ipv6_addr_copy(addr, &ipv6_hdr(skb)->daddr);
554 ipv6_addr_copy(&ipv6_hdr(skb)->daddr, &daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555
556 dst_release(xchg(&skb->dst, NULL));
557 ip6_route_input(skb);
558 if (skb->dst->error) {
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700559 skb_push(skb, skb->data - skb_network_header(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 dst_input(skb);
561 return -1;
562 }
563
564 if (skb->dst->dev->flags&IFF_LOOPBACK) {
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700565 if (ipv6_hdr(skb)->hop_limit <= 1) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900566 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
567 IPSTATS_MIB_INHDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
569 0, skb->dev);
570 kfree_skb(skb);
571 return -1;
572 }
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700573 ipv6_hdr(skb)->hop_limit--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 goto looped_back;
575 }
576
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700577 skb_push(skb, skb->data - skb_network_header(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 dst_input(skb);
579 return -1;
580}
581
582static struct inet6_protocol rthdr_protocol = {
583 .handler = ipv6_rthdr_rcv,
Herbert Xuadcfc7d2006-06-30 13:36:15 -0700584 .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585};
586
587void __init ipv6_rthdr_init(void)
588{
589 if (inet6_add_protocol(&rthdr_protocol, IPPROTO_ROUTING) < 0)
590 printk(KERN_ERR "ipv6_rthdr_init: Could not register protocol\n");
591};
592
593/*
594 This function inverts received rthdr.
595 NOTE: specs allow to make it automatically only if
596 packet authenticated.
597
598 I will not discuss it here (though, I am really pissed off at
599 this stupid requirement making rthdr idea useless)
600
601 Actually, it creates severe problems for us.
602 Embryonic requests has no associated sockets,
603 so that user have no control over it and
604 cannot not only to set reply options, but
605 even to know, that someone wants to connect
606 without success. :-(
607
608 For now we need to test the engine, so that I created
609 temporary (or permanent) backdoor.
610 If listening socket set IPV6_RTHDR to 2, then we invert header.
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900611 --ANK (980729)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 */
613
614struct ipv6_txoptions *
615ipv6_invert_rthdr(struct sock *sk, struct ipv6_rt_hdr *hdr)
616{
617 /* Received rthdr:
618
619 [ H1 -> H2 -> ... H_prev ] daddr=ME
620
621 Inverted result:
622 [ H_prev -> ... -> H1 ] daddr =sender
623
624 Note, that IP output engine will rewrite this rthdr
625 by rotating it left by one addr.
626 */
627
628 int n, i;
629 struct rt0_hdr *rthdr = (struct rt0_hdr*)hdr;
630 struct rt0_hdr *irthdr;
631 struct ipv6_txoptions *opt;
632 int hdrlen = ipv6_optlen(hdr);
633
634 if (hdr->segments_left ||
635 hdr->type != IPV6_SRCRT_TYPE_0 ||
636 hdr->hdrlen & 0x01)
637 return NULL;
638
639 n = hdr->hdrlen >> 1;
640 opt = sock_kmalloc(sk, sizeof(*opt) + hdrlen, GFP_ATOMIC);
641 if (opt == NULL)
642 return NULL;
643 memset(opt, 0, sizeof(*opt));
644 opt->tot_len = sizeof(*opt) + hdrlen;
645 opt->srcrt = (void*)(opt+1);
646 opt->opt_nflen = hdrlen;
647
648 memcpy(opt->srcrt, hdr, sizeof(*hdr));
649 irthdr = (struct rt0_hdr*)opt->srcrt;
Brian Haleye6df4392005-09-10 00:15:06 -0700650 irthdr->reserved = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 opt->srcrt->segments_left = n;
652 for (i=0; i<n; i++)
653 memcpy(irthdr->addr+i, rthdr->addr+(n-1-i), 16);
654 return opt;
655}
656
Arnaldo Carvalho de Melo3cf3dc62005-12-13 23:23:20 -0800657EXPORT_SYMBOL_GPL(ipv6_invert_rthdr);
658
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659/**********************************
660 Hop-by-hop options.
661 **********************************/
662
663/* Router Alert as of RFC 2711 */
664
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700665static int ipv6_hop_ra(struct sk_buff **skbp, int optoff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666{
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700667 struct sk_buff *skb = *skbp;
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700668 const unsigned char *nh = skb_network_header(skb);
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700669
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700670 if (nh[optoff + 1] == 2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 IP6CB(skb)->ra = optoff;
672 return 1;
673 }
Patrick McHardy64ce2072005-08-09 20:50:53 -0700674 LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n",
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700675 nh[optoff + 1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 kfree_skb(skb);
677 return 0;
678}
679
680/* Jumbo payload */
681
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700682static int ipv6_hop_jumbo(struct sk_buff **skbp, int optoff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683{
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700684 struct sk_buff *skb = *skbp;
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700685 const unsigned char *nh = skb_network_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 u32 pkt_len;
687
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700688 if (nh[optoff + 1] != 4 || (optoff & 3) != 2) {
Patrick McHardy64ce2072005-08-09 20:50:53 -0700689 LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n",
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700690 nh[optoff+1]);
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900691 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
692 IPSTATS_MIB_INHDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 goto drop;
694 }
695
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700696 pkt_len = ntohl(*(__be32 *)(nh + optoff + 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697 if (pkt_len <= IPV6_MAXPLEN) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900698 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2);
700 return 0;
701 }
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700702 if (ipv6_hdr(skb)->payload_len) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900703 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
705 return 0;
706 }
707
708 if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900709 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INTRUNCATEDPKTS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710 goto drop;
711 }
Stephen Hemminger42ca89c2005-09-08 12:57:43 -0700712
713 if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
714 goto drop;
715
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 return 1;
717
718drop:
719 kfree_skb(skb);
720 return 0;
721}
722
723static struct tlvtype_proc tlvprochopopt_lst[] = {
724 {
725 .type = IPV6_TLV_ROUTERALERT,
726 .func = ipv6_hop_ra,
727 },
728 {
729 .type = IPV6_TLV_JUMBO,
730 .func = ipv6_hop_jumbo,
731 },
732 { -1, }
733};
734
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700735int ipv6_parse_hopopts(struct sk_buff **skbp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736{
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700737 struct sk_buff *skb = *skbp;
Patrick McHardy951dbc82006-01-06 23:02:34 -0800738 struct inet6_skb_parm *opt = IP6CB(skb);
739
YOSHIFUJI Hideakiec670092006-04-18 14:46:26 -0700740 /*
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700741 * skb_network_header(skb) is equal to skb->data, and
Arnaldo Carvalho de Melocfe1fc72007-03-16 17:26:39 -0300742 * skb_network_header_len(skb) is always equal to
YOSHIFUJI Hideakiec670092006-04-18 14:46:26 -0700743 * sizeof(struct ipv6hdr) by definition of
744 * hop-by-hop options.
745 */
746 if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + 8) ||
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700747 !pskb_may_pull(skb, (sizeof(struct ipv6hdr) +
748 ((skb_transport_header(skb)[1] + 1) << 3)))) {
YOSHIFUJI Hideakiec670092006-04-18 14:46:26 -0700749 kfree_skb(skb);
750 return -1;
751 }
752
Patrick McHardy951dbc82006-01-06 23:02:34 -0800753 opt->hop = sizeof(struct ipv6hdr);
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700754 if (ip6_parse_tlv(tlvprochopopt_lst, skbp)) {
755 skb = *skbp;
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -0700756 skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3;
Masahide NAKAMURAdc435e62006-08-31 15:18:49 -0700757 opt = IP6CB(skb);
Patrick McHardy951dbc82006-01-06 23:02:34 -0800758 opt->nhoff = sizeof(struct ipv6hdr);
YOSHIFUJI Hideakib8097392006-04-18 14:48:45 -0700759 return 1;
Patrick McHardy951dbc82006-01-06 23:02:34 -0800760 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 return -1;
762}
763
764/*
765 * Creating outbound headers.
766 *
767 * "build" functions work when skb is filled from head to tail (datagram)
768 * "push" functions work when headers are added from tail to head (tcp)
769 *
770 * In both cases we assume, that caller reserved enough room
771 * for headers.
772 */
773
774static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
775 struct ipv6_rt_hdr *opt,
776 struct in6_addr **addr_p)
777{
778 struct rt0_hdr *phdr, *ihdr;
779 int hops;
780
781 ihdr = (struct rt0_hdr *) opt;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900782
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 phdr = (struct rt0_hdr *) skb_push(skb, (ihdr->rt_hdr.hdrlen + 1) << 3);
784 memcpy(phdr, ihdr, sizeof(struct rt0_hdr));
785
786 hops = ihdr->rt_hdr.hdrlen >> 1;
787
788 if (hops > 1)
789 memcpy(phdr->addr, ihdr->addr + 1,
790 (hops - 1) * sizeof(struct in6_addr));
791
792 ipv6_addr_copy(phdr->addr + (hops - 1), *addr_p);
793 *addr_p = ihdr->addr;
794
795 phdr->rt_hdr.nexthdr = *proto;
796 *proto = NEXTHDR_ROUTING;
797}
798
799static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt)
800{
801 struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ipv6_optlen(opt));
802
803 memcpy(h, opt, ipv6_optlen(opt));
804 h->nexthdr = *proto;
805 *proto = type;
806}
807
808void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
809 u8 *proto,
810 struct in6_addr **daddr)
811{
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900812 if (opt->srcrt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900814 /*
815 * IPV6_RTHDRDSTOPTS is ignored
816 * unless IPV6_RTHDR is set (RFC3542).
817 */
818 if (opt->dst0opt)
819 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
820 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 if (opt->hopopt)
822 ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
823}
824
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900825EXPORT_SYMBOL(ipv6_push_nfrag_opts);
826
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto)
828{
829 if (opt->dst1opt)
830 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt);
831}
832
833struct ipv6_txoptions *
834ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
835{
836 struct ipv6_txoptions *opt2;
837
838 opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC);
839 if (opt2) {
840 long dif = (char*)opt2 - (char*)opt;
841 memcpy(opt2, opt, opt->tot_len);
842 if (opt2->hopopt)
843 *((char**)&opt2->hopopt) += dif;
844 if (opt2->dst0opt)
845 *((char**)&opt2->dst0opt) += dif;
846 if (opt2->dst1opt)
847 *((char**)&opt2->dst1opt) += dif;
848 if (opt2->srcrt)
849 *((char**)&opt2->srcrt) += dif;
850 }
851 return opt2;
852}
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900853
Arnaldo Carvalho de Melo3cf3dc62005-12-13 23:23:20 -0800854EXPORT_SYMBOL_GPL(ipv6_dup_options);
855
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900856static int ipv6_renew_option(void *ohdr,
857 struct ipv6_opt_hdr __user *newopt, int newoptlen,
858 int inherit,
859 struct ipv6_opt_hdr **hdr,
860 char **p)
861{
862 if (inherit) {
863 if (ohdr) {
864 memcpy(*p, ohdr, ipv6_optlen((struct ipv6_opt_hdr *)ohdr));
865 *hdr = (struct ipv6_opt_hdr *)*p;
866 *p += CMSG_ALIGN(ipv6_optlen(*(struct ipv6_opt_hdr **)hdr));
867 }
868 } else {
869 if (newopt) {
870 if (copy_from_user(*p, newopt, newoptlen))
871 return -EFAULT;
872 *hdr = (struct ipv6_opt_hdr *)*p;
873 if (ipv6_optlen(*(struct ipv6_opt_hdr **)hdr) > newoptlen)
874 return -EINVAL;
875 *p += CMSG_ALIGN(newoptlen);
876 }
877 }
878 return 0;
879}
880
881struct ipv6_txoptions *
882ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
883 int newtype,
884 struct ipv6_opt_hdr __user *newopt, int newoptlen)
885{
886 int tot_len = 0;
887 char *p;
888 struct ipv6_txoptions *opt2;
889 int err;
890
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700891 if (opt) {
892 if (newtype != IPV6_HOPOPTS && opt->hopopt)
893 tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt));
894 if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt)
895 tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt));
896 if (newtype != IPV6_RTHDR && opt->srcrt)
897 tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt));
898 if (newtype != IPV6_DSTOPTS && opt->dst1opt)
899 tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
900 }
901
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900902 if (newopt && newoptlen)
903 tot_len += CMSG_ALIGN(newoptlen);
904
905 if (!tot_len)
906 return NULL;
907
YOSHIFUJI Hideaki8b8aa4b2005-11-20 12:18:17 +0900908 tot_len += sizeof(*opt2);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900909 opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC);
910 if (!opt2)
911 return ERR_PTR(-ENOBUFS);
912
913 memset(opt2, 0, tot_len);
914
915 opt2->tot_len = tot_len;
916 p = (char *)(opt2 + 1);
917
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700918 err = ipv6_renew_option(opt ? opt->hopopt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900919 newtype != IPV6_HOPOPTS,
920 &opt2->hopopt, &p);
921 if (err)
922 goto out;
923
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700924 err = ipv6_renew_option(opt ? opt->dst0opt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900925 newtype != IPV6_RTHDRDSTOPTS,
926 &opt2->dst0opt, &p);
927 if (err)
928 goto out;
929
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700930 err = ipv6_renew_option(opt ? opt->srcrt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900931 newtype != IPV6_RTHDR,
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700932 (struct ipv6_opt_hdr **)&opt2->srcrt, &p);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900933 if (err)
934 goto out;
935
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700936 err = ipv6_renew_option(opt ? opt->dst1opt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900937 newtype != IPV6_DSTOPTS,
938 &opt2->dst1opt, &p);
939 if (err)
940 goto out;
941
942 opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) +
943 (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) +
944 (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0);
945 opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0);
946
947 return opt2;
948out:
YOSHIFUJI Hideaki8b8aa4b2005-11-20 12:18:17 +0900949 sock_kfree_s(sk, opt2, opt2->tot_len);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900950 return ERR_PTR(err);
951}
952
YOSHIFUJI Hideakidf9890c2005-11-20 12:23:18 +0900953struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
954 struct ipv6_txoptions *opt)
955{
956 /*
957 * ignore the dest before srcrt unless srcrt is being included.
958 * --yoshfuji
959 */
960 if (opt && opt->dst0opt && !opt->srcrt) {
961 if (opt_space != opt) {
962 memcpy(opt_space, opt, sizeof(*opt_space));
963 opt = opt_space;
964 }
965 opt->opt_nflen -= ipv6_optlen(opt->dst0opt);
966 opt->dst0opt = NULL;
967 }
968
969 return opt;
970}
971