blob: 173a4bb52255d57a9e7f2b105b95a52c32f1cda2 [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 NAKAMURA59fbb3a62007-06-26 23:56:32 -070045#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -070046#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}
Masahide NAKAMURA59fbb3a62007-06-26 23:56:32 -070093EXPORT_SYMBOL_GPL(ipv6_find_tlv);
Masahide NAKAMURAc61a4042006-08-23 19:18:35 -070094
Linus Torvalds1da177e2005-04-16 15:20:36 -070095/*
96 * Parsing tlv encoded headers.
97 *
98 * Parsing function "func" returns 1, if parsing succeed
99 * and 0, if it failed.
100 * It MUST NOT touch skb->h.
101 */
102
103struct tlvtype_proc {
104 int type;
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700105 int (*func)(struct sk_buff **skbp, int offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106};
107
108/*********************
109 Generic functions
110 *********************/
111
112/* An unknown option is detected, decide what to do */
113
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700114static int ip6_tlvopt_unknown(struct sk_buff **skbp, int optoff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115{
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700116 struct sk_buff *skb = *skbp;
117
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700118 switch ((skb_network_header(skb)[optoff] & 0xC0) >> 6) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 case 0: /* ignore */
120 return 1;
121
122 case 1: /* drop packet */
123 break;
124
125 case 3: /* Send ICMP if not a multicast address and drop packet */
126 /* Actually, it is redundant check. icmp_send
127 will recheck in any case.
128 */
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700129 if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 break;
131 case 2: /* send ICMP PARM PROB regardless and drop packet */
132 icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, optoff);
133 return 0;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700134 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135
136 kfree_skb(skb);
137 return 0;
138}
139
140/* Parse tlv encoded option header (hop-by-hop or destination) */
141
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700142static int ip6_parse_tlv(struct tlvtype_proc *procs, struct sk_buff **skbp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143{
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700144 struct sk_buff *skb = *skbp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 struct tlvtype_proc *curr;
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700146 const unsigned char *nh = skb_network_header(skb);
Arnaldo Carvalho de Melocfe1fc72007-03-16 17:26:39 -0300147 int off = skb_network_header_len(skb);
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700148 int len = (skb_transport_header(skb)[1] + 1) << 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149
Arnaldo Carvalho de Meloea2ae172007-04-25 17:55:53 -0700150 if (skb_transport_offset(skb) + len > skb_headlen(skb))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 goto bad;
152
153 off += 2;
154 len -= 2;
155
156 while (len > 0) {
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700157 int optlen = nh[off + 1] + 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700159 switch (nh[off]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 case IPV6_TLV_PAD0:
161 optlen = 1;
162 break;
163
164 case IPV6_TLV_PADN:
165 break;
166
167 default: /* Other TLV code so scan list */
168 if (optlen > len)
169 goto bad;
170 for (curr=procs; curr->type >= 0; curr++) {
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700171 if (curr->type == nh[off]) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900172 /* type specific length/alignment
173 checks will be performed in the
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 func(). */
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700175 if (curr->func(skbp, off) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 return 0;
177 break;
178 }
179 }
180 if (curr->type < 0) {
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700181 if (ip6_tlvopt_unknown(skbp, off) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 return 0;
183 }
184 break;
185 }
186 off += optlen;
187 len -= optlen;
188 }
189 if (len == 0)
190 return 1;
191bad:
192 kfree_skb(skb);
193 return 0;
194}
195
196/*****************************
197 Destination options header.
198 *****************************/
199
Masahide NAKAMURA59fbb3a62007-06-26 23:56:32 -0700200#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700201static int ipv6_dest_hao(struct sk_buff **skbp, int optoff)
202{
203 struct sk_buff *skb = *skbp;
204 struct ipv6_destopt_hao *hao;
205 struct inet6_skb_parm *opt = IP6CB(skb);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700206 struct ipv6hdr *ipv6h = ipv6_hdr(skb);
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700207 struct in6_addr tmp_addr;
208 int ret;
209
210 if (opt->dsthao) {
211 LIMIT_NETDEBUG(KERN_DEBUG "hao duplicated\n");
212 goto discard;
213 }
214 opt->dsthao = opt->dst1;
215 opt->dst1 = 0;
216
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700217 hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + optoff);
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700218
219 if (hao->length != 16) {
220 LIMIT_NETDEBUG(
221 KERN_DEBUG "hao invalid option length = %d\n", hao->length);
222 goto discard;
223 }
224
225 if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) {
226 LIMIT_NETDEBUG(
227 KERN_DEBUG "hao is not an unicast addr: " NIP6_FMT "\n", NIP6(hao->addr));
228 goto discard;
229 }
230
231 ret = xfrm6_input_addr(skb, (xfrm_address_t *)&ipv6h->daddr,
232 (xfrm_address_t *)&hao->addr, IPPROTO_DSTOPTS);
233 if (unlikely(ret < 0))
234 goto discard;
235
236 if (skb_cloned(skb)) {
237 struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
Masahide NAKAMURAdc435e62006-08-31 15:18:49 -0700238 struct inet6_skb_parm *opt2;
239
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700240 if (skb2 == NULL)
241 goto discard;
242
Masahide NAKAMURAdc435e62006-08-31 15:18:49 -0700243 opt2 = IP6CB(skb2);
244 memcpy(opt2, opt, sizeof(*opt2));
245
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700246 kfree_skb(skb);
247
248 /* update all variable using below by copied skbuff */
249 *skbp = skb = skb2;
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700250 hao = (struct ipv6_destopt_hao *)(skb_network_header(skb2) +
251 optoff);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700252 ipv6h = ipv6_hdr(skb2);
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700253 }
254
255 if (skb->ip_summed == CHECKSUM_COMPLETE)
256 skb->ip_summed = CHECKSUM_NONE;
257
258 ipv6_addr_copy(&tmp_addr, &ipv6h->saddr);
259 ipv6_addr_copy(&ipv6h->saddr, &hao->addr);
260 ipv6_addr_copy(&hao->addr, &tmp_addr);
261
Eric Dumazetb7aa0bf2007-04-19 16:16:32 -0700262 if (skb->tstamp.tv64 == 0)
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700263 __net_timestamp(skb);
264
265 return 1;
266
267 discard:
268 kfree_skb(skb);
269 return 0;
270}
271#endif
272
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273static struct tlvtype_proc tlvprocdestopt_lst[] = {
Masahide NAKAMURA59fbb3a62007-06-26 23:56:32 -0700274#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700275 {
276 .type = IPV6_TLV_HAO,
277 .func = ipv6_dest_hao,
278 },
279#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 {-1, NULL}
281};
282
Patrick McHardy951dbc82006-01-06 23:02:34 -0800283static int ipv6_destopt_rcv(struct sk_buff **skbp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284{
285 struct sk_buff *skb = *skbp;
286 struct inet6_skb_parm *opt = IP6CB(skb);
Masahide NAKAMURA59fbb3a62007-06-26 23:56:32 -0700287#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700288 __u16 dstbuf;
289#endif
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900290 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291
Arnaldo Carvalho de Meloea2ae172007-04-25 17:55:53 -0700292 if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) ||
293 !pskb_may_pull(skb, (skb_transport_offset(skb) +
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700294 ((skb_transport_header(skb)[1] + 1) << 3)))) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900295 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
296 IPSTATS_MIB_INHDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 kfree_skb(skb);
298 return -1;
299 }
300
Arnaldo Carvalho de Melocfe1fc72007-03-16 17:26:39 -0300301 opt->lastopt = opt->dst1 = skb_network_header_len(skb);
Masahide NAKAMURA59fbb3a62007-06-26 23:56:32 -0700302#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700303 dstbuf = opt->dst1;
304#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900306 dst = dst_clone(skb->dst);
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700307 if (ip6_parse_tlv(tlvprocdestopt_lst, skbp)) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900308 dst_release(dst);
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700309 skb = *skbp;
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -0700310 skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3;
Masahide NAKAMURAdc435e62006-08-31 15:18:49 -0700311 opt = IP6CB(skb);
Masahide NAKAMURA59fbb3a62007-06-26 23:56:32 -0700312#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700313 opt->nhoff = dstbuf;
314#else
Patrick McHardy951dbc82006-01-06 23:02:34 -0800315 opt->nhoff = opt->dst1;
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700316#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 return 1;
318 }
319
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900320 IP6_INC_STATS_BH(ip6_dst_idev(dst), IPSTATS_MIB_INHDRERRORS);
321 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 return -1;
323}
324
325static struct inet6_protocol destopt_protocol = {
326 .handler = ipv6_destopt_rcv,
Herbert Xuadcfc7d2006-06-30 13:36:15 -0700327 .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328};
329
330void __init ipv6_destopt_init(void)
331{
332 if (inet6_add_protocol(&destopt_protocol, IPPROTO_DSTOPTS) < 0)
333 printk(KERN_ERR "ipv6_destopt_init: Could not register protocol\n");
334}
335
336/********************************
337 NONE header. No data in packet.
338 ********************************/
339
Patrick McHardy951dbc82006-01-06 23:02:34 -0800340static int ipv6_nodata_rcv(struct sk_buff **skbp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341{
342 struct sk_buff *skb = *skbp;
343
344 kfree_skb(skb);
345 return 0;
346}
347
348static struct inet6_protocol nodata_protocol = {
349 .handler = ipv6_nodata_rcv,
350 .flags = INET6_PROTO_NOPOLICY,
351};
352
353void __init ipv6_nodata_init(void)
354{
355 if (inet6_add_protocol(&nodata_protocol, IPPROTO_NONE) < 0)
356 printk(KERN_ERR "ipv6_nodata_init: Could not register protocol\n");
357}
358
359/********************************
360 Routing header.
361 ********************************/
362
Patrick McHardy951dbc82006-01-06 23:02:34 -0800363static int ipv6_rthdr_rcv(struct sk_buff **skbp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364{
365 struct sk_buff *skb = *skbp;
366 struct inet6_skb_parm *opt = IP6CB(skb);
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700367 struct in6_addr *addr = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 struct in6_addr daddr;
YOSHIFUJI Hideaki0bcbc922007-04-24 14:58:30 -0700369 struct inet6_dev *idev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 int n, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 struct ipv6_rt_hdr *hdr;
372 struct rt0_hdr *rthdr;
YOSHIFUJI Hideaki0bcbc922007-04-24 14:58:30 -0700373 int accept_source_route = ipv6_devconf.accept_source_route;
374
375 if (accept_source_route < 0 ||
376 ((idev = in6_dev_get(skb->dev)) == NULL)) {
377 kfree_skb(skb);
378 return -1;
379 }
380 if (idev->cnf.accept_source_route < 0) {
381 in6_dev_put(idev);
382 kfree_skb(skb);
383 return -1;
384 }
385
386 if (accept_source_route > idev->cnf.accept_source_route)
387 accept_source_route = idev->cnf.accept_source_route;
388
389 in6_dev_put(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390
Arnaldo Carvalho de Meloea2ae172007-04-25 17:55:53 -0700391 if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) ||
392 !pskb_may_pull(skb, (skb_transport_offset(skb) +
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700393 ((skb_transport_header(skb)[1] + 1) << 3)))) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900394 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
395 IPSTATS_MIB_INHDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 kfree_skb(skb);
397 return -1;
398 }
399
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700400 hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401
YOSHIFUJI Hideaki0bcbc922007-04-24 14:58:30 -0700402 switch (hdr->type) {
403#ifdef CONFIG_IPV6_MIP6
YOSHIFUJI Hideakiebbd90a2007-04-27 02:13:39 -0700404 case IPV6_SRCRT_TYPE_2:
YOSHIFUJI Hideaki0bcbc922007-04-24 14:58:30 -0700405 break;
406#endif
407 case IPV6_SRCRT_TYPE_0:
YOSHIFUJI Hideakia23cf142007-04-25 11:13:49 +0900408 if (accept_source_route > 0)
YOSHIFUJI Hideaki0bcbc922007-04-24 14:58:30 -0700409 break;
410 kfree_skb(skb);
411 return -1;
412 default:
413 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
414 IPSTATS_MIB_INHDRERRORS);
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700415 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
416 (&hdr->type) - skb_network_header(skb));
YOSHIFUJI Hideaki0bcbc922007-04-24 14:58:30 -0700417 return -1;
418 }
419
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700420 if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 skb->pkt_type != PACKET_HOST) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900422 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
423 IPSTATS_MIB_INADDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 kfree_skb(skb);
425 return -1;
426 }
427
428looped_back:
429 if (hdr->segments_left == 0) {
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700430 switch (hdr->type) {
Masahide NAKAMURA59fbb3a62007-06-26 23:56:32 -0700431#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700432 case IPV6_SRCRT_TYPE_2:
433 /* Silently discard type 2 header unless it was
434 * processed by own
435 */
436 if (!addr) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900437 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
438 IPSTATS_MIB_INADDRERRORS);
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700439 kfree_skb(skb);
440 return -1;
441 }
442 break;
443#endif
444 default:
445 break;
446 }
447
Arnaldo Carvalho de Melocfe1fc72007-03-16 17:26:39 -0300448 opt->lastopt = opt->srcrt = skb_network_header_len(skb);
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -0700449 skb->transport_header += (hdr->hdrlen + 1) << 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450 opt->dst0 = opt->dst1;
451 opt->dst1 = 0;
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700452 opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 return 1;
454 }
455
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700456 switch (hdr->type) {
457 case IPV6_SRCRT_TYPE_0:
458 if (hdr->hdrlen & 0x01) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900459 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
460 IPSTATS_MIB_INHDRERRORS);
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700461 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
462 ((&hdr->hdrlen) -
463 skb_network_header(skb)));
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700464 return -1;
465 }
466 break;
Masahide NAKAMURA59fbb3a62007-06-26 23:56:32 -0700467#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700468 case IPV6_SRCRT_TYPE_2:
469 /* Silently discard invalid RTH type 2 */
470 if (hdr->hdrlen != 2 || hdr->segments_left != 1) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900471 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
472 IPSTATS_MIB_INHDRERRORS);
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700473 kfree_skb(skb);
474 return -1;
475 }
476 break;
477#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479
480 /*
481 * This is the routing header forwarding algorithm from
482 * RFC 2460, page 16.
483 */
484
485 n = hdr->hdrlen >> 1;
486
487 if (hdr->segments_left > n) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900488 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
489 IPSTATS_MIB_INHDRERRORS);
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700490 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
491 ((&hdr->segments_left) -
492 skb_network_header(skb)));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 return -1;
494 }
495
496 /* We are about to mangle packet header. Be careful!
497 Do not damage packets queued somewhere.
498 */
499 if (skb_cloned(skb)) {
500 struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 /* the copy is a forwarded packet */
502 if (skb2 == NULL) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900503 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
504 IPSTATS_MIB_OUTDISCARDS);
505 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 return -1;
507 }
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900508 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 *skbp = skb = skb2;
510 opt = IP6CB(skb2);
Arnaldo Carvalho de Melobff9b612007-03-16 17:19:57 -0300511 hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 }
513
Patrick McHardy84fa7932006-08-29 16:44:56 -0700514 if (skb->ip_summed == CHECKSUM_COMPLETE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 skb->ip_summed = CHECKSUM_NONE;
516
517 i = n - --hdr->segments_left;
518
519 rthdr = (struct rt0_hdr *) hdr;
520 addr = rthdr->addr;
521 addr += i - 1;
522
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700523 switch (hdr->type) {
Masahide NAKAMURA59fbb3a62007-06-26 23:56:32 -0700524#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700525 case IPV6_SRCRT_TYPE_2:
526 if (xfrm6_input_addr(skb, (xfrm_address_t *)addr,
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700527 (xfrm_address_t *)&ipv6_hdr(skb)->saddr,
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700528 IPPROTO_ROUTING) < 0) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900529 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
530 IPSTATS_MIB_INADDRERRORS);
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700531 kfree_skb(skb);
532 return -1;
533 }
534 if (!ipv6_chk_home_addr(addr)) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900535 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
536 IPSTATS_MIB_INADDRERRORS);
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700537 kfree_skb(skb);
538 return -1;
539 }
540 break;
541#endif
542 default:
543 break;
544 }
545
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 if (ipv6_addr_is_multicast(addr)) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900547 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
548 IPSTATS_MIB_INADDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 kfree_skb(skb);
550 return -1;
551 }
552
553 ipv6_addr_copy(&daddr, addr);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700554 ipv6_addr_copy(addr, &ipv6_hdr(skb)->daddr);
555 ipv6_addr_copy(&ipv6_hdr(skb)->daddr, &daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556
557 dst_release(xchg(&skb->dst, NULL));
558 ip6_route_input(skb);
559 if (skb->dst->error) {
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700560 skb_push(skb, skb->data - skb_network_header(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 dst_input(skb);
562 return -1;
563 }
564
565 if (skb->dst->dev->flags&IFF_LOOPBACK) {
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700566 if (ipv6_hdr(skb)->hop_limit <= 1) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900567 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
568 IPSTATS_MIB_INHDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
570 0, skb->dev);
571 kfree_skb(skb);
572 return -1;
573 }
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700574 ipv6_hdr(skb)->hop_limit--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 goto looped_back;
576 }
577
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700578 skb_push(skb, skb->data - skb_network_header(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 dst_input(skb);
580 return -1;
581}
582
583static struct inet6_protocol rthdr_protocol = {
584 .handler = ipv6_rthdr_rcv,
Herbert Xuadcfc7d2006-06-30 13:36:15 -0700585 .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586};
587
588void __init ipv6_rthdr_init(void)
589{
590 if (inet6_add_protocol(&rthdr_protocol, IPPROTO_ROUTING) < 0)
591 printk(KERN_ERR "ipv6_rthdr_init: Could not register protocol\n");
592};
593
594/*
595 This function inverts received rthdr.
596 NOTE: specs allow to make it automatically only if
597 packet authenticated.
598
599 I will not discuss it here (though, I am really pissed off at
600 this stupid requirement making rthdr idea useless)
601
602 Actually, it creates severe problems for us.
603 Embryonic requests has no associated sockets,
604 so that user have no control over it and
605 cannot not only to set reply options, but
606 even to know, that someone wants to connect
607 without success. :-(
608
609 For now we need to test the engine, so that I created
610 temporary (or permanent) backdoor.
611 If listening socket set IPV6_RTHDR to 2, then we invert header.
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900612 --ANK (980729)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 */
614
615struct ipv6_txoptions *
616ipv6_invert_rthdr(struct sock *sk, struct ipv6_rt_hdr *hdr)
617{
618 /* Received rthdr:
619
620 [ H1 -> H2 -> ... H_prev ] daddr=ME
621
622 Inverted result:
623 [ H_prev -> ... -> H1 ] daddr =sender
624
625 Note, that IP output engine will rewrite this rthdr
626 by rotating it left by one addr.
627 */
628
629 int n, i;
630 struct rt0_hdr *rthdr = (struct rt0_hdr*)hdr;
631 struct rt0_hdr *irthdr;
632 struct ipv6_txoptions *opt;
633 int hdrlen = ipv6_optlen(hdr);
634
635 if (hdr->segments_left ||
636 hdr->type != IPV6_SRCRT_TYPE_0 ||
637 hdr->hdrlen & 0x01)
638 return NULL;
639
640 n = hdr->hdrlen >> 1;
641 opt = sock_kmalloc(sk, sizeof(*opt) + hdrlen, GFP_ATOMIC);
642 if (opt == NULL)
643 return NULL;
644 memset(opt, 0, sizeof(*opt));
645 opt->tot_len = sizeof(*opt) + hdrlen;
646 opt->srcrt = (void*)(opt+1);
647 opt->opt_nflen = hdrlen;
648
649 memcpy(opt->srcrt, hdr, sizeof(*hdr));
650 irthdr = (struct rt0_hdr*)opt->srcrt;
Brian Haleye6df4392005-09-10 00:15:06 -0700651 irthdr->reserved = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652 opt->srcrt->segments_left = n;
653 for (i=0; i<n; i++)
654 memcpy(irthdr->addr+i, rthdr->addr+(n-1-i), 16);
655 return opt;
656}
657
Arnaldo Carvalho de Melo3cf3dc62005-12-13 23:23:20 -0800658EXPORT_SYMBOL_GPL(ipv6_invert_rthdr);
659
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660/**********************************
661 Hop-by-hop options.
662 **********************************/
663
YOSHIFUJI Hideakie76b2b22007-05-09 14:01:59 -0700664/*
665 * Note: we cannot rely on skb->dst before we assign it in ip6_route_input().
666 */
667static inline struct inet6_dev *ipv6_skb_idev(struct sk_buff *skb)
668{
669 return skb->dst ? ip6_dst_idev(skb->dst) : __in6_dev_get(skb->dev);
670}
671
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672/* Router Alert as of RFC 2711 */
673
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700674static int ipv6_hop_ra(struct sk_buff **skbp, int optoff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675{
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700676 struct sk_buff *skb = *skbp;
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700677 const unsigned char *nh = skb_network_header(skb);
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700678
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700679 if (nh[optoff + 1] == 2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 IP6CB(skb)->ra = optoff;
681 return 1;
682 }
Patrick McHardy64ce2072005-08-09 20:50:53 -0700683 LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n",
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700684 nh[optoff + 1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 kfree_skb(skb);
686 return 0;
687}
688
689/* Jumbo payload */
690
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700691static int ipv6_hop_jumbo(struct sk_buff **skbp, int optoff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692{
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700693 struct sk_buff *skb = *skbp;
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700694 const unsigned char *nh = skb_network_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 u32 pkt_len;
696
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700697 if (nh[optoff + 1] != 4 || (optoff & 3) != 2) {
Patrick McHardy64ce2072005-08-09 20:50:53 -0700698 LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n",
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700699 nh[optoff+1]);
YOSHIFUJI Hideakie76b2b22007-05-09 14:01:59 -0700700 IP6_INC_STATS_BH(ipv6_skb_idev(skb),
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900701 IPSTATS_MIB_INHDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 goto drop;
703 }
704
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700705 pkt_len = ntohl(*(__be32 *)(nh + optoff + 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 if (pkt_len <= IPV6_MAXPLEN) {
YOSHIFUJI Hideakie76b2b22007-05-09 14:01:59 -0700707 IP6_INC_STATS_BH(ipv6_skb_idev(skb), IPSTATS_MIB_INHDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2);
709 return 0;
710 }
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700711 if (ipv6_hdr(skb)->payload_len) {
YOSHIFUJI Hideakie76b2b22007-05-09 14:01:59 -0700712 IP6_INC_STATS_BH(ipv6_skb_idev(skb), IPSTATS_MIB_INHDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
714 return 0;
715 }
716
717 if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
YOSHIFUJI Hideakie76b2b22007-05-09 14:01:59 -0700718 IP6_INC_STATS_BH(ipv6_skb_idev(skb), IPSTATS_MIB_INTRUNCATEDPKTS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 goto drop;
720 }
Stephen Hemminger42ca89c2005-09-08 12:57:43 -0700721
722 if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
723 goto drop;
724
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 return 1;
726
727drop:
728 kfree_skb(skb);
729 return 0;
730}
731
732static struct tlvtype_proc tlvprochopopt_lst[] = {
733 {
734 .type = IPV6_TLV_ROUTERALERT,
735 .func = ipv6_hop_ra,
736 },
737 {
738 .type = IPV6_TLV_JUMBO,
739 .func = ipv6_hop_jumbo,
740 },
741 { -1, }
742};
743
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700744int ipv6_parse_hopopts(struct sk_buff **skbp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745{
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700746 struct sk_buff *skb = *skbp;
Patrick McHardy951dbc82006-01-06 23:02:34 -0800747 struct inet6_skb_parm *opt = IP6CB(skb);
748
YOSHIFUJI Hideakiec670092006-04-18 14:46:26 -0700749 /*
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700750 * skb_network_header(skb) is equal to skb->data, and
Arnaldo Carvalho de Melocfe1fc72007-03-16 17:26:39 -0300751 * skb_network_header_len(skb) is always equal to
YOSHIFUJI Hideakiec670092006-04-18 14:46:26 -0700752 * sizeof(struct ipv6hdr) by definition of
753 * hop-by-hop options.
754 */
755 if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + 8) ||
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700756 !pskb_may_pull(skb, (sizeof(struct ipv6hdr) +
757 ((skb_transport_header(skb)[1] + 1) << 3)))) {
YOSHIFUJI Hideakiec670092006-04-18 14:46:26 -0700758 kfree_skb(skb);
759 return -1;
760 }
761
Patrick McHardy951dbc82006-01-06 23:02:34 -0800762 opt->hop = sizeof(struct ipv6hdr);
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700763 if (ip6_parse_tlv(tlvprochopopt_lst, skbp)) {
764 skb = *skbp;
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -0700765 skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3;
Masahide NAKAMURAdc435e62006-08-31 15:18:49 -0700766 opt = IP6CB(skb);
Patrick McHardy951dbc82006-01-06 23:02:34 -0800767 opt->nhoff = sizeof(struct ipv6hdr);
YOSHIFUJI Hideakib8097392006-04-18 14:48:45 -0700768 return 1;
Patrick McHardy951dbc82006-01-06 23:02:34 -0800769 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 return -1;
771}
772
773/*
774 * Creating outbound headers.
775 *
776 * "build" functions work when skb is filled from head to tail (datagram)
777 * "push" functions work when headers are added from tail to head (tcp)
778 *
779 * In both cases we assume, that caller reserved enough room
780 * for headers.
781 */
782
783static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
784 struct ipv6_rt_hdr *opt,
785 struct in6_addr **addr_p)
786{
787 struct rt0_hdr *phdr, *ihdr;
788 int hops;
789
790 ihdr = (struct rt0_hdr *) opt;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900791
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 phdr = (struct rt0_hdr *) skb_push(skb, (ihdr->rt_hdr.hdrlen + 1) << 3);
793 memcpy(phdr, ihdr, sizeof(struct rt0_hdr));
794
795 hops = ihdr->rt_hdr.hdrlen >> 1;
796
797 if (hops > 1)
798 memcpy(phdr->addr, ihdr->addr + 1,
799 (hops - 1) * sizeof(struct in6_addr));
800
801 ipv6_addr_copy(phdr->addr + (hops - 1), *addr_p);
802 *addr_p = ihdr->addr;
803
804 phdr->rt_hdr.nexthdr = *proto;
805 *proto = NEXTHDR_ROUTING;
806}
807
808static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt)
809{
810 struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ipv6_optlen(opt));
811
812 memcpy(h, opt, ipv6_optlen(opt));
813 h->nexthdr = *proto;
814 *proto = type;
815}
816
817void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
818 u8 *proto,
819 struct in6_addr **daddr)
820{
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900821 if (opt->srcrt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900823 /*
824 * IPV6_RTHDRDSTOPTS is ignored
825 * unless IPV6_RTHDR is set (RFC3542).
826 */
827 if (opt->dst0opt)
828 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
829 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 if (opt->hopopt)
831 ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
832}
833
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900834EXPORT_SYMBOL(ipv6_push_nfrag_opts);
835
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto)
837{
838 if (opt->dst1opt)
839 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt);
840}
841
842struct ipv6_txoptions *
843ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
844{
845 struct ipv6_txoptions *opt2;
846
847 opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC);
848 if (opt2) {
849 long dif = (char*)opt2 - (char*)opt;
850 memcpy(opt2, opt, opt->tot_len);
851 if (opt2->hopopt)
852 *((char**)&opt2->hopopt) += dif;
853 if (opt2->dst0opt)
854 *((char**)&opt2->dst0opt) += dif;
855 if (opt2->dst1opt)
856 *((char**)&opt2->dst1opt) += dif;
857 if (opt2->srcrt)
858 *((char**)&opt2->srcrt) += dif;
859 }
860 return opt2;
861}
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900862
Arnaldo Carvalho de Melo3cf3dc62005-12-13 23:23:20 -0800863EXPORT_SYMBOL_GPL(ipv6_dup_options);
864
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900865static int ipv6_renew_option(void *ohdr,
866 struct ipv6_opt_hdr __user *newopt, int newoptlen,
867 int inherit,
868 struct ipv6_opt_hdr **hdr,
869 char **p)
870{
871 if (inherit) {
872 if (ohdr) {
873 memcpy(*p, ohdr, ipv6_optlen((struct ipv6_opt_hdr *)ohdr));
874 *hdr = (struct ipv6_opt_hdr *)*p;
875 *p += CMSG_ALIGN(ipv6_optlen(*(struct ipv6_opt_hdr **)hdr));
876 }
877 } else {
878 if (newopt) {
879 if (copy_from_user(*p, newopt, newoptlen))
880 return -EFAULT;
881 *hdr = (struct ipv6_opt_hdr *)*p;
882 if (ipv6_optlen(*(struct ipv6_opt_hdr **)hdr) > newoptlen)
883 return -EINVAL;
884 *p += CMSG_ALIGN(newoptlen);
885 }
886 }
887 return 0;
888}
889
890struct ipv6_txoptions *
891ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
892 int newtype,
893 struct ipv6_opt_hdr __user *newopt, int newoptlen)
894{
895 int tot_len = 0;
896 char *p;
897 struct ipv6_txoptions *opt2;
898 int err;
899
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700900 if (opt) {
901 if (newtype != IPV6_HOPOPTS && opt->hopopt)
902 tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt));
903 if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt)
904 tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt));
905 if (newtype != IPV6_RTHDR && opt->srcrt)
906 tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt));
907 if (newtype != IPV6_DSTOPTS && opt->dst1opt)
908 tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
909 }
910
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900911 if (newopt && newoptlen)
912 tot_len += CMSG_ALIGN(newoptlen);
913
914 if (!tot_len)
915 return NULL;
916
YOSHIFUJI Hideaki8b8aa4b2005-11-20 12:18:17 +0900917 tot_len += sizeof(*opt2);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900918 opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC);
919 if (!opt2)
920 return ERR_PTR(-ENOBUFS);
921
922 memset(opt2, 0, tot_len);
923
924 opt2->tot_len = tot_len;
925 p = (char *)(opt2 + 1);
926
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700927 err = ipv6_renew_option(opt ? opt->hopopt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900928 newtype != IPV6_HOPOPTS,
929 &opt2->hopopt, &p);
930 if (err)
931 goto out;
932
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700933 err = ipv6_renew_option(opt ? opt->dst0opt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900934 newtype != IPV6_RTHDRDSTOPTS,
935 &opt2->dst0opt, &p);
936 if (err)
937 goto out;
938
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700939 err = ipv6_renew_option(opt ? opt->srcrt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900940 newtype != IPV6_RTHDR,
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700941 (struct ipv6_opt_hdr **)&opt2->srcrt, &p);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900942 if (err)
943 goto out;
944
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700945 err = ipv6_renew_option(opt ? opt->dst1opt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900946 newtype != IPV6_DSTOPTS,
947 &opt2->dst1opt, &p);
948 if (err)
949 goto out;
950
951 opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) +
952 (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) +
953 (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0);
954 opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0);
955
956 return opt2;
957out:
YOSHIFUJI Hideaki8b8aa4b2005-11-20 12:18:17 +0900958 sock_kfree_s(sk, opt2, opt2->tot_len);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900959 return ERR_PTR(err);
960}
961
YOSHIFUJI Hideakidf9890c2005-11-20 12:23:18 +0900962struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
963 struct ipv6_txoptions *opt)
964{
965 /*
966 * ignore the dest before srcrt unless srcrt is being included.
967 * --yoshfuji
968 */
969 if (opt && opt->dst0opt && !opt->srcrt) {
970 if (opt_space != opt) {
971 memcpy(opt_space, opt, sizeof(*opt_space));
972 opt = opt_space;
973 }
974 opt->opt_nflen -= ipv6_optlen(opt->dst0opt);
975 opt->dst0opt = NULL;
976 }
977
978 return opt;
979}
980