blob: fc3a961fc5ba7209d5a03f511dd287c4951ad3e2 [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
YOSHIFUJI Hideakic382bb92007-07-10 22:47:58 -0700375 idev = in6_dev_get(skb->dev);
376 if (idev) {
377 if (accept_source_route > idev->cnf.accept_source_route)
378 accept_source_route = idev->cnf.accept_source_route;
YOSHIFUJI Hideaki0bcbc922007-04-24 14:58:30 -0700379 in6_dev_put(idev);
YOSHIFUJI Hideaki0bcbc922007-04-24 14:58:30 -0700380 }
381
Arnaldo Carvalho de Meloea2ae172007-04-25 17:55:53 -0700382 if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) ||
383 !pskb_may_pull(skb, (skb_transport_offset(skb) +
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700384 ((skb_transport_header(skb)[1] + 1) << 3)))) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900385 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
386 IPSTATS_MIB_INHDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387 kfree_skb(skb);
388 return -1;
389 }
390
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700391 hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700393 if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 skb->pkt_type != PACKET_HOST) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900395 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
396 IPSTATS_MIB_INADDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 kfree_skb(skb);
398 return -1;
399 }
400
401looped_back:
402 if (hdr->segments_left == 0) {
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700403 switch (hdr->type) {
Masahide NAKAMURA59fbb3a62007-06-26 23:56:32 -0700404#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700405 case IPV6_SRCRT_TYPE_2:
406 /* Silently discard type 2 header unless it was
407 * processed by own
408 */
409 if (!addr) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900410 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
411 IPSTATS_MIB_INADDRERRORS);
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700412 kfree_skb(skb);
413 return -1;
414 }
415 break;
416#endif
417 default:
418 break;
419 }
420
Arnaldo Carvalho de Melocfe1fc72007-03-16 17:26:39 -0300421 opt->lastopt = opt->srcrt = skb_network_header_len(skb);
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -0700422 skb->transport_header += (hdr->hdrlen + 1) << 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423 opt->dst0 = opt->dst1;
424 opt->dst1 = 0;
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700425 opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 return 1;
427 }
428
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700429 switch (hdr->type) {
430 case IPV6_SRCRT_TYPE_0:
YOSHIFUJI Hideakic382bb92007-07-10 22:47:58 -0700431 if (accept_source_route <= 0)
432 goto unknown_rh;
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700433 if (hdr->hdrlen & 0x01) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900434 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
435 IPSTATS_MIB_INHDRERRORS);
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700436 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
437 ((&hdr->hdrlen) -
438 skb_network_header(skb)));
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700439 return -1;
440 }
441 break;
Masahide NAKAMURA59fbb3a62007-06-26 23:56:32 -0700442#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700443 case IPV6_SRCRT_TYPE_2:
YOSHIFUJI Hideakic382bb92007-07-10 22:47:58 -0700444 if (accept_source_route < 0)
445 goto unknown_rh;
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700446 /* Silently discard invalid RTH type 2 */
447 if (hdr->hdrlen != 2 || hdr->segments_left != 1) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900448 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
449 IPSTATS_MIB_INHDRERRORS);
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700450 kfree_skb(skb);
451 return -1;
452 }
453 break;
454#endif
YOSHIFUJI Hideakic382bb92007-07-10 22:47:58 -0700455 default:
456 goto unknown_rh;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458
459 /*
460 * This is the routing header forwarding algorithm from
461 * RFC 2460, page 16.
462 */
463
464 n = hdr->hdrlen >> 1;
465
466 if (hdr->segments_left > n) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900467 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
468 IPSTATS_MIB_INHDRERRORS);
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700469 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
470 ((&hdr->segments_left) -
471 skb_network_header(skb)));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 return -1;
473 }
474
475 /* We are about to mangle packet header. Be careful!
476 Do not damage packets queued somewhere.
477 */
478 if (skb_cloned(skb)) {
479 struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 /* the copy is a forwarded packet */
481 if (skb2 == NULL) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900482 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
483 IPSTATS_MIB_OUTDISCARDS);
484 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 return -1;
486 }
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900487 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 *skbp = skb = skb2;
489 opt = IP6CB(skb2);
Arnaldo Carvalho de Melobff9b612007-03-16 17:19:57 -0300490 hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 }
492
Patrick McHardy84fa7932006-08-29 16:44:56 -0700493 if (skb->ip_summed == CHECKSUM_COMPLETE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 skb->ip_summed = CHECKSUM_NONE;
495
496 i = n - --hdr->segments_left;
497
498 rthdr = (struct rt0_hdr *) hdr;
499 addr = rthdr->addr;
500 addr += i - 1;
501
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700502 switch (hdr->type) {
Masahide NAKAMURA59fbb3a62007-06-26 23:56:32 -0700503#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700504 case IPV6_SRCRT_TYPE_2:
505 if (xfrm6_input_addr(skb, (xfrm_address_t *)addr,
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700506 (xfrm_address_t *)&ipv6_hdr(skb)->saddr,
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700507 IPPROTO_ROUTING) < 0) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900508 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
509 IPSTATS_MIB_INADDRERRORS);
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700510 kfree_skb(skb);
511 return -1;
512 }
513 if (!ipv6_chk_home_addr(addr)) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900514 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
515 IPSTATS_MIB_INADDRERRORS);
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700516 kfree_skb(skb);
517 return -1;
518 }
519 break;
520#endif
521 default:
522 break;
523 }
524
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 if (ipv6_addr_is_multicast(addr)) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900526 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
527 IPSTATS_MIB_INADDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 kfree_skb(skb);
529 return -1;
530 }
531
532 ipv6_addr_copy(&daddr, addr);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700533 ipv6_addr_copy(addr, &ipv6_hdr(skb)->daddr);
534 ipv6_addr_copy(&ipv6_hdr(skb)->daddr, &daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535
536 dst_release(xchg(&skb->dst, NULL));
537 ip6_route_input(skb);
538 if (skb->dst->error) {
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700539 skb_push(skb, skb->data - skb_network_header(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 dst_input(skb);
541 return -1;
542 }
543
544 if (skb->dst->dev->flags&IFF_LOOPBACK) {
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700545 if (ipv6_hdr(skb)->hop_limit <= 1) {
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900546 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
547 IPSTATS_MIB_INHDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
549 0, skb->dev);
550 kfree_skb(skb);
551 return -1;
552 }
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700553 ipv6_hdr(skb)->hop_limit--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 goto looped_back;
555 }
556
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700557 skb_push(skb, skb->data - skb_network_header(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 dst_input(skb);
559 return -1;
YOSHIFUJI Hideakic382bb92007-07-10 22:47:58 -0700560
561unknown_rh:
562 IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS);
563 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
564 (&hdr->type) - skb_network_header(skb));
565 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566}
567
568static struct inet6_protocol rthdr_protocol = {
569 .handler = ipv6_rthdr_rcv,
Herbert Xuadcfc7d2006-06-30 13:36:15 -0700570 .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571};
572
573void __init ipv6_rthdr_init(void)
574{
575 if (inet6_add_protocol(&rthdr_protocol, IPPROTO_ROUTING) < 0)
576 printk(KERN_ERR "ipv6_rthdr_init: Could not register protocol\n");
577};
578
579/*
580 This function inverts received rthdr.
581 NOTE: specs allow to make it automatically only if
582 packet authenticated.
583
584 I will not discuss it here (though, I am really pissed off at
585 this stupid requirement making rthdr idea useless)
586
587 Actually, it creates severe problems for us.
588 Embryonic requests has no associated sockets,
589 so that user have no control over it and
590 cannot not only to set reply options, but
591 even to know, that someone wants to connect
592 without success. :-(
593
594 For now we need to test the engine, so that I created
595 temporary (or permanent) backdoor.
596 If listening socket set IPV6_RTHDR to 2, then we invert header.
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900597 --ANK (980729)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 */
599
600struct ipv6_txoptions *
601ipv6_invert_rthdr(struct sock *sk, struct ipv6_rt_hdr *hdr)
602{
603 /* Received rthdr:
604
605 [ H1 -> H2 -> ... H_prev ] daddr=ME
606
607 Inverted result:
608 [ H_prev -> ... -> H1 ] daddr =sender
609
610 Note, that IP output engine will rewrite this rthdr
611 by rotating it left by one addr.
612 */
613
614 int n, i;
615 struct rt0_hdr *rthdr = (struct rt0_hdr*)hdr;
616 struct rt0_hdr *irthdr;
617 struct ipv6_txoptions *opt;
618 int hdrlen = ipv6_optlen(hdr);
619
620 if (hdr->segments_left ||
621 hdr->type != IPV6_SRCRT_TYPE_0 ||
622 hdr->hdrlen & 0x01)
623 return NULL;
624
625 n = hdr->hdrlen >> 1;
626 opt = sock_kmalloc(sk, sizeof(*opt) + hdrlen, GFP_ATOMIC);
627 if (opt == NULL)
628 return NULL;
629 memset(opt, 0, sizeof(*opt));
630 opt->tot_len = sizeof(*opt) + hdrlen;
631 opt->srcrt = (void*)(opt+1);
632 opt->opt_nflen = hdrlen;
633
634 memcpy(opt->srcrt, hdr, sizeof(*hdr));
635 irthdr = (struct rt0_hdr*)opt->srcrt;
Brian Haleye6df4392005-09-10 00:15:06 -0700636 irthdr->reserved = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637 opt->srcrt->segments_left = n;
638 for (i=0; i<n; i++)
639 memcpy(irthdr->addr+i, rthdr->addr+(n-1-i), 16);
640 return opt;
641}
642
Arnaldo Carvalho de Melo3cf3dc62005-12-13 23:23:20 -0800643EXPORT_SYMBOL_GPL(ipv6_invert_rthdr);
644
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645/**********************************
646 Hop-by-hop options.
647 **********************************/
648
YOSHIFUJI Hideakie76b2b22007-05-09 14:01:59 -0700649/*
650 * Note: we cannot rely on skb->dst before we assign it in ip6_route_input().
651 */
652static inline struct inet6_dev *ipv6_skb_idev(struct sk_buff *skb)
653{
654 return skb->dst ? ip6_dst_idev(skb->dst) : __in6_dev_get(skb->dev);
655}
656
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657/* Router Alert as of RFC 2711 */
658
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700659static int ipv6_hop_ra(struct sk_buff **skbp, int optoff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660{
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700661 struct sk_buff *skb = *skbp;
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700662 const unsigned char *nh = skb_network_header(skb);
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700663
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700664 if (nh[optoff + 1] == 2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 IP6CB(skb)->ra = optoff;
666 return 1;
667 }
Patrick McHardy64ce2072005-08-09 20:50:53 -0700668 LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n",
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700669 nh[optoff + 1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 kfree_skb(skb);
671 return 0;
672}
673
674/* Jumbo payload */
675
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700676static int ipv6_hop_jumbo(struct sk_buff **skbp, int optoff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677{
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700678 struct sk_buff *skb = *skbp;
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700679 const unsigned char *nh = skb_network_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 u32 pkt_len;
681
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700682 if (nh[optoff + 1] != 4 || (optoff & 3) != 2) {
Patrick McHardy64ce2072005-08-09 20:50:53 -0700683 LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n",
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700684 nh[optoff+1]);
YOSHIFUJI Hideakie76b2b22007-05-09 14:01:59 -0700685 IP6_INC_STATS_BH(ipv6_skb_idev(skb),
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +0900686 IPSTATS_MIB_INHDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 goto drop;
688 }
689
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700690 pkt_len = ntohl(*(__be32 *)(nh + optoff + 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 if (pkt_len <= IPV6_MAXPLEN) {
YOSHIFUJI Hideakie76b2b22007-05-09 14:01:59 -0700692 IP6_INC_STATS_BH(ipv6_skb_idev(skb), IPSTATS_MIB_INHDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2);
694 return 0;
695 }
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700696 if (ipv6_hdr(skb)->payload_len) {
YOSHIFUJI Hideakie76b2b22007-05-09 14:01:59 -0700697 IP6_INC_STATS_BH(ipv6_skb_idev(skb), IPSTATS_MIB_INHDRERRORS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
699 return 0;
700 }
701
702 if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
YOSHIFUJI Hideakie76b2b22007-05-09 14:01:59 -0700703 IP6_INC_STATS_BH(ipv6_skb_idev(skb), IPSTATS_MIB_INTRUNCATEDPKTS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704 goto drop;
705 }
Stephen Hemminger42ca89c2005-09-08 12:57:43 -0700706
707 if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
708 goto drop;
709
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710 return 1;
711
712drop:
713 kfree_skb(skb);
714 return 0;
715}
716
717static struct tlvtype_proc tlvprochopopt_lst[] = {
718 {
719 .type = IPV6_TLV_ROUTERALERT,
720 .func = ipv6_hop_ra,
721 },
722 {
723 .type = IPV6_TLV_JUMBO,
724 .func = ipv6_hop_jumbo,
725 },
726 { -1, }
727};
728
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700729int ipv6_parse_hopopts(struct sk_buff **skbp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730{
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700731 struct sk_buff *skb = *skbp;
Patrick McHardy951dbc82006-01-06 23:02:34 -0800732 struct inet6_skb_parm *opt = IP6CB(skb);
733
YOSHIFUJI Hideakiec670092006-04-18 14:46:26 -0700734 /*
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700735 * skb_network_header(skb) is equal to skb->data, and
Arnaldo Carvalho de Melocfe1fc72007-03-16 17:26:39 -0300736 * skb_network_header_len(skb) is always equal to
YOSHIFUJI Hideakiec670092006-04-18 14:46:26 -0700737 * sizeof(struct ipv6hdr) by definition of
738 * hop-by-hop options.
739 */
740 if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + 8) ||
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700741 !pskb_may_pull(skb, (sizeof(struct ipv6hdr) +
742 ((skb_transport_header(skb)[1] + 1) << 3)))) {
YOSHIFUJI Hideakiec670092006-04-18 14:46:26 -0700743 kfree_skb(skb);
744 return -1;
745 }
746
Patrick McHardy951dbc82006-01-06 23:02:34 -0800747 opt->hop = sizeof(struct ipv6hdr);
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700748 if (ip6_parse_tlv(tlvprochopopt_lst, skbp)) {
749 skb = *skbp;
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -0700750 skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3;
Masahide NAKAMURAdc435e62006-08-31 15:18:49 -0700751 opt = IP6CB(skb);
Patrick McHardy951dbc82006-01-06 23:02:34 -0800752 opt->nhoff = sizeof(struct ipv6hdr);
YOSHIFUJI Hideakib8097392006-04-18 14:48:45 -0700753 return 1;
Patrick McHardy951dbc82006-01-06 23:02:34 -0800754 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 return -1;
756}
757
758/*
759 * Creating outbound headers.
760 *
761 * "build" functions work when skb is filled from head to tail (datagram)
762 * "push" functions work when headers are added from tail to head (tcp)
763 *
764 * In both cases we assume, that caller reserved enough room
765 * for headers.
766 */
767
768static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
769 struct ipv6_rt_hdr *opt,
770 struct in6_addr **addr_p)
771{
772 struct rt0_hdr *phdr, *ihdr;
773 int hops;
774
775 ihdr = (struct rt0_hdr *) opt;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900776
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 phdr = (struct rt0_hdr *) skb_push(skb, (ihdr->rt_hdr.hdrlen + 1) << 3);
778 memcpy(phdr, ihdr, sizeof(struct rt0_hdr));
779
780 hops = ihdr->rt_hdr.hdrlen >> 1;
781
782 if (hops > 1)
783 memcpy(phdr->addr, ihdr->addr + 1,
784 (hops - 1) * sizeof(struct in6_addr));
785
786 ipv6_addr_copy(phdr->addr + (hops - 1), *addr_p);
787 *addr_p = ihdr->addr;
788
789 phdr->rt_hdr.nexthdr = *proto;
790 *proto = NEXTHDR_ROUTING;
791}
792
793static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt)
794{
795 struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ipv6_optlen(opt));
796
797 memcpy(h, opt, ipv6_optlen(opt));
798 h->nexthdr = *proto;
799 *proto = type;
800}
801
802void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
803 u8 *proto,
804 struct in6_addr **daddr)
805{
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900806 if (opt->srcrt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900808 /*
809 * IPV6_RTHDRDSTOPTS is ignored
810 * unless IPV6_RTHDR is set (RFC3542).
811 */
812 if (opt->dst0opt)
813 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
814 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 if (opt->hopopt)
816 ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
817}
818
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900819EXPORT_SYMBOL(ipv6_push_nfrag_opts);
820
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto)
822{
823 if (opt->dst1opt)
824 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt);
825}
826
827struct ipv6_txoptions *
828ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
829{
830 struct ipv6_txoptions *opt2;
831
832 opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC);
833 if (opt2) {
834 long dif = (char*)opt2 - (char*)opt;
835 memcpy(opt2, opt, opt->tot_len);
836 if (opt2->hopopt)
837 *((char**)&opt2->hopopt) += dif;
838 if (opt2->dst0opt)
839 *((char**)&opt2->dst0opt) += dif;
840 if (opt2->dst1opt)
841 *((char**)&opt2->dst1opt) += dif;
842 if (opt2->srcrt)
843 *((char**)&opt2->srcrt) += dif;
844 }
845 return opt2;
846}
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900847
Arnaldo Carvalho de Melo3cf3dc62005-12-13 23:23:20 -0800848EXPORT_SYMBOL_GPL(ipv6_dup_options);
849
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900850static int ipv6_renew_option(void *ohdr,
851 struct ipv6_opt_hdr __user *newopt, int newoptlen,
852 int inherit,
853 struct ipv6_opt_hdr **hdr,
854 char **p)
855{
856 if (inherit) {
857 if (ohdr) {
858 memcpy(*p, ohdr, ipv6_optlen((struct ipv6_opt_hdr *)ohdr));
859 *hdr = (struct ipv6_opt_hdr *)*p;
860 *p += CMSG_ALIGN(ipv6_optlen(*(struct ipv6_opt_hdr **)hdr));
861 }
862 } else {
863 if (newopt) {
864 if (copy_from_user(*p, newopt, newoptlen))
865 return -EFAULT;
866 *hdr = (struct ipv6_opt_hdr *)*p;
867 if (ipv6_optlen(*(struct ipv6_opt_hdr **)hdr) > newoptlen)
868 return -EINVAL;
869 *p += CMSG_ALIGN(newoptlen);
870 }
871 }
872 return 0;
873}
874
875struct ipv6_txoptions *
876ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
877 int newtype,
878 struct ipv6_opt_hdr __user *newopt, int newoptlen)
879{
880 int tot_len = 0;
881 char *p;
882 struct ipv6_txoptions *opt2;
883 int err;
884
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700885 if (opt) {
886 if (newtype != IPV6_HOPOPTS && opt->hopopt)
887 tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt));
888 if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt)
889 tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt));
890 if (newtype != IPV6_RTHDR && opt->srcrt)
891 tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt));
892 if (newtype != IPV6_DSTOPTS && opt->dst1opt)
893 tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
894 }
895
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900896 if (newopt && newoptlen)
897 tot_len += CMSG_ALIGN(newoptlen);
898
899 if (!tot_len)
900 return NULL;
901
YOSHIFUJI Hideaki8b8aa4b2005-11-20 12:18:17 +0900902 tot_len += sizeof(*opt2);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900903 opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC);
904 if (!opt2)
905 return ERR_PTR(-ENOBUFS);
906
907 memset(opt2, 0, tot_len);
908
909 opt2->tot_len = tot_len;
910 p = (char *)(opt2 + 1);
911
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700912 err = ipv6_renew_option(opt ? opt->hopopt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900913 newtype != IPV6_HOPOPTS,
914 &opt2->hopopt, &p);
915 if (err)
916 goto out;
917
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700918 err = ipv6_renew_option(opt ? opt->dst0opt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900919 newtype != IPV6_RTHDRDSTOPTS,
920 &opt2->dst0opt, &p);
921 if (err)
922 goto out;
923
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700924 err = ipv6_renew_option(opt ? opt->srcrt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900925 newtype != IPV6_RTHDR,
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700926 (struct ipv6_opt_hdr **)&opt2->srcrt, &p);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900927 if (err)
928 goto out;
929
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700930 err = ipv6_renew_option(opt ? opt->dst1opt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900931 newtype != IPV6_DSTOPTS,
932 &opt2->dst1opt, &p);
933 if (err)
934 goto out;
935
936 opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) +
937 (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) +
938 (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0);
939 opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0);
940
941 return opt2;
942out:
YOSHIFUJI Hideaki8b8aa4b2005-11-20 12:18:17 +0900943 sock_kfree_s(sk, opt2, opt2->tot_len);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900944 return ERR_PTR(err);
945}
946
YOSHIFUJI Hideakidf9890c2005-11-20 12:23:18 +0900947struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
948 struct ipv6_txoptions *opt)
949{
950 /*
951 * ignore the dest before srcrt unless srcrt is being included.
952 * --yoshfuji
953 */
954 if (opt && opt->dst0opt && !opt->srcrt) {
955 if (opt_space != opt) {
956 memcpy(opt_space, opt, sizeof(*opt_space));
957 opt = opt_space;
958 }
959 opt->opt_nflen -= ipv6_optlen(opt->dst0opt);
960 opt->dst0opt = NULL;
961 }
962
963 return opt;
964}
965