blob: 14be0b9b77a5f998614351e1c5dbc92a31f94df0 [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
YOSHIFUJI Hideakie76b2b22007-05-09 14:01:59 -0700663/*
664 * Note: we cannot rely on skb->dst before we assign it in ip6_route_input().
665 */
666static inline struct inet6_dev *ipv6_skb_idev(struct sk_buff *skb)
667{
668 return skb->dst ? ip6_dst_idev(skb->dst) : __in6_dev_get(skb->dev);
669}
670
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671/* Router Alert as of RFC 2711 */
672
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700673static int ipv6_hop_ra(struct sk_buff **skbp, int optoff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674{
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700675 struct sk_buff *skb = *skbp;
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700676 const unsigned char *nh = skb_network_header(skb);
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700677
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700678 if (nh[optoff + 1] == 2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 IP6CB(skb)->ra = optoff;
680 return 1;
681 }
Patrick McHardy64ce2072005-08-09 20:50:53 -0700682 LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n",
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700683 nh[optoff + 1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 kfree_skb(skb);
685 return 0;
686}
687
688/* Jumbo payload */
689
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700690static int ipv6_hop_jumbo(struct sk_buff **skbp, int optoff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691{
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700692 struct sk_buff *skb = *skbp;
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700693 const unsigned char *nh = skb_network_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 u32 pkt_len;
695
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700696 if (nh[optoff + 1] != 4 || (optoff & 3) != 2) {
Patrick McHardy64ce2072005-08-09 20:50:53 -0700697 LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n",
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700698 nh[optoff+1]);
YOSHIFUJI Hideakie76b2b22007-05-09 14:01:59 -0700699 IP6_INC_STATS_BH(ipv6_skb_idev(skb),
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900700 IPSTATS_MIB_INHDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 goto drop;
702 }
703
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700704 pkt_len = ntohl(*(__be32 *)(nh + optoff + 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 if (pkt_len <= IPV6_MAXPLEN) {
YOSHIFUJI Hideakie76b2b22007-05-09 14:01:59 -0700706 IP6_INC_STATS_BH(ipv6_skb_idev(skb), IPSTATS_MIB_INHDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2);
708 return 0;
709 }
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700710 if (ipv6_hdr(skb)->payload_len) {
YOSHIFUJI Hideakie76b2b22007-05-09 14:01:59 -0700711 IP6_INC_STATS_BH(ipv6_skb_idev(skb), IPSTATS_MIB_INHDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
713 return 0;
714 }
715
716 if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
YOSHIFUJI Hideakie76b2b22007-05-09 14:01:59 -0700717 IP6_INC_STATS_BH(ipv6_skb_idev(skb), IPSTATS_MIB_INTRUNCATEDPKTS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718 goto drop;
719 }
Stephen Hemminger42ca89c2005-09-08 12:57:43 -0700720
721 if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
722 goto drop;
723
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 return 1;
725
726drop:
727 kfree_skb(skb);
728 return 0;
729}
730
731static struct tlvtype_proc tlvprochopopt_lst[] = {
732 {
733 .type = IPV6_TLV_ROUTERALERT,
734 .func = ipv6_hop_ra,
735 },
736 {
737 .type = IPV6_TLV_JUMBO,
738 .func = ipv6_hop_jumbo,
739 },
740 { -1, }
741};
742
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700743int ipv6_parse_hopopts(struct sk_buff **skbp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744{
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700745 struct sk_buff *skb = *skbp;
Patrick McHardy951dbc82006-01-06 23:02:34 -0800746 struct inet6_skb_parm *opt = IP6CB(skb);
747
YOSHIFUJI Hideakiec670092006-04-18 14:46:26 -0700748 /*
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700749 * skb_network_header(skb) is equal to skb->data, and
Arnaldo Carvalho de Melocfe1fc72007-03-16 17:26:39 -0300750 * skb_network_header_len(skb) is always equal to
YOSHIFUJI Hideakiec670092006-04-18 14:46:26 -0700751 * sizeof(struct ipv6hdr) by definition of
752 * hop-by-hop options.
753 */
754 if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + 8) ||
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700755 !pskb_may_pull(skb, (sizeof(struct ipv6hdr) +
756 ((skb_transport_header(skb)[1] + 1) << 3)))) {
YOSHIFUJI Hideakiec670092006-04-18 14:46:26 -0700757 kfree_skb(skb);
758 return -1;
759 }
760
Patrick McHardy951dbc82006-01-06 23:02:34 -0800761 opt->hop = sizeof(struct ipv6hdr);
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700762 if (ip6_parse_tlv(tlvprochopopt_lst, skbp)) {
763 skb = *skbp;
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -0700764 skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3;
Masahide NAKAMURAdc435e62006-08-31 15:18:49 -0700765 opt = IP6CB(skb);
Patrick McHardy951dbc82006-01-06 23:02:34 -0800766 opt->nhoff = sizeof(struct ipv6hdr);
YOSHIFUJI Hideakib8097392006-04-18 14:48:45 -0700767 return 1;
Patrick McHardy951dbc82006-01-06 23:02:34 -0800768 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 return -1;
770}
771
772/*
773 * Creating outbound headers.
774 *
775 * "build" functions work when skb is filled from head to tail (datagram)
776 * "push" functions work when headers are added from tail to head (tcp)
777 *
778 * In both cases we assume, that caller reserved enough room
779 * for headers.
780 */
781
782static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
783 struct ipv6_rt_hdr *opt,
784 struct in6_addr **addr_p)
785{
786 struct rt0_hdr *phdr, *ihdr;
787 int hops;
788
789 ihdr = (struct rt0_hdr *) opt;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900790
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 phdr = (struct rt0_hdr *) skb_push(skb, (ihdr->rt_hdr.hdrlen + 1) << 3);
792 memcpy(phdr, ihdr, sizeof(struct rt0_hdr));
793
794 hops = ihdr->rt_hdr.hdrlen >> 1;
795
796 if (hops > 1)
797 memcpy(phdr->addr, ihdr->addr + 1,
798 (hops - 1) * sizeof(struct in6_addr));
799
800 ipv6_addr_copy(phdr->addr + (hops - 1), *addr_p);
801 *addr_p = ihdr->addr;
802
803 phdr->rt_hdr.nexthdr = *proto;
804 *proto = NEXTHDR_ROUTING;
805}
806
807static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt)
808{
809 struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ipv6_optlen(opt));
810
811 memcpy(h, opt, ipv6_optlen(opt));
812 h->nexthdr = *proto;
813 *proto = type;
814}
815
816void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
817 u8 *proto,
818 struct in6_addr **daddr)
819{
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900820 if (opt->srcrt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900822 /*
823 * IPV6_RTHDRDSTOPTS is ignored
824 * unless IPV6_RTHDR is set (RFC3542).
825 */
826 if (opt->dst0opt)
827 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
828 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 if (opt->hopopt)
830 ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
831}
832
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900833EXPORT_SYMBOL(ipv6_push_nfrag_opts);
834
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto)
836{
837 if (opt->dst1opt)
838 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt);
839}
840
841struct ipv6_txoptions *
842ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
843{
844 struct ipv6_txoptions *opt2;
845
846 opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC);
847 if (opt2) {
848 long dif = (char*)opt2 - (char*)opt;
849 memcpy(opt2, opt, opt->tot_len);
850 if (opt2->hopopt)
851 *((char**)&opt2->hopopt) += dif;
852 if (opt2->dst0opt)
853 *((char**)&opt2->dst0opt) += dif;
854 if (opt2->dst1opt)
855 *((char**)&opt2->dst1opt) += dif;
856 if (opt2->srcrt)
857 *((char**)&opt2->srcrt) += dif;
858 }
859 return opt2;
860}
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900861
Arnaldo Carvalho de Melo3cf3dc62005-12-13 23:23:20 -0800862EXPORT_SYMBOL_GPL(ipv6_dup_options);
863
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900864static int ipv6_renew_option(void *ohdr,
865 struct ipv6_opt_hdr __user *newopt, int newoptlen,
866 int inherit,
867 struct ipv6_opt_hdr **hdr,
868 char **p)
869{
870 if (inherit) {
871 if (ohdr) {
872 memcpy(*p, ohdr, ipv6_optlen((struct ipv6_opt_hdr *)ohdr));
873 *hdr = (struct ipv6_opt_hdr *)*p;
874 *p += CMSG_ALIGN(ipv6_optlen(*(struct ipv6_opt_hdr **)hdr));
875 }
876 } else {
877 if (newopt) {
878 if (copy_from_user(*p, newopt, newoptlen))
879 return -EFAULT;
880 *hdr = (struct ipv6_opt_hdr *)*p;
881 if (ipv6_optlen(*(struct ipv6_opt_hdr **)hdr) > newoptlen)
882 return -EINVAL;
883 *p += CMSG_ALIGN(newoptlen);
884 }
885 }
886 return 0;
887}
888
889struct ipv6_txoptions *
890ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
891 int newtype,
892 struct ipv6_opt_hdr __user *newopt, int newoptlen)
893{
894 int tot_len = 0;
895 char *p;
896 struct ipv6_txoptions *opt2;
897 int err;
898
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700899 if (opt) {
900 if (newtype != IPV6_HOPOPTS && opt->hopopt)
901 tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt));
902 if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt)
903 tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt));
904 if (newtype != IPV6_RTHDR && opt->srcrt)
905 tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt));
906 if (newtype != IPV6_DSTOPTS && opt->dst1opt)
907 tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
908 }
909
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900910 if (newopt && newoptlen)
911 tot_len += CMSG_ALIGN(newoptlen);
912
913 if (!tot_len)
914 return NULL;
915
YOSHIFUJI Hideaki8b8aa4b2005-11-20 12:18:17 +0900916 tot_len += sizeof(*opt2);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900917 opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC);
918 if (!opt2)
919 return ERR_PTR(-ENOBUFS);
920
921 memset(opt2, 0, tot_len);
922
923 opt2->tot_len = tot_len;
924 p = (char *)(opt2 + 1);
925
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700926 err = ipv6_renew_option(opt ? opt->hopopt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900927 newtype != IPV6_HOPOPTS,
928 &opt2->hopopt, &p);
929 if (err)
930 goto out;
931
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700932 err = ipv6_renew_option(opt ? opt->dst0opt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900933 newtype != IPV6_RTHDRDSTOPTS,
934 &opt2->dst0opt, &p);
935 if (err)
936 goto out;
937
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700938 err = ipv6_renew_option(opt ? opt->srcrt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900939 newtype != IPV6_RTHDR,
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700940 (struct ipv6_opt_hdr **)&opt2->srcrt, &p);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900941 if (err)
942 goto out;
943
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700944 err = ipv6_renew_option(opt ? opt->dst1opt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900945 newtype != IPV6_DSTOPTS,
946 &opt2->dst1opt, &p);
947 if (err)
948 goto out;
949
950 opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) +
951 (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) +
952 (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0);
953 opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0);
954
955 return opt2;
956out:
YOSHIFUJI Hideaki8b8aa4b2005-11-20 12:18:17 +0900957 sock_kfree_s(sk, opt2, opt2->tot_len);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900958 return ERR_PTR(err);
959}
960
YOSHIFUJI Hideakidf9890c2005-11-20 12:23:18 +0900961struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
962 struct ipv6_txoptions *opt)
963{
964 /*
965 * ignore the dest before srcrt unless srcrt is being included.
966 * --yoshfuji
967 */
968 if (opt && opt->dst0opt && !opt->srcrt) {
969 if (opt_space != opt) {
970 memcpy(opt_space, opt, sizeof(*opt_space));
971 opt = opt_space;
972 }
973 opt->opt_nflen -= ipv6_optlen(opt->dst0opt);
974 opt->dst0opt = NULL;
975 }
976
977 return opt;
978}
979