exthdrs.c 21.6 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 *	Extension Header handling for IPv6
 *	Linux INET6 implementation
 *
 *	Authors:
 *	Pedro Roque		<roque@di.fc.ul.pt>
 *	Andi Kleen		<ak@muc.de>
 *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru>
 *
 *	This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 */

/* Changes:
17
 *	yoshfuji		: ensure not to overrun while parsing
L
Linus Torvalds 已提交
18 19 20 21 22 23 24 25 26 27 28 29 30 31
 *				  tlv options.
 *	Mitsuru KANDA @USAGI and: Remove ipv6_parse_exthdrs().
 *	YOSHIFUJI Hideaki @USAGI  Register inbound extension header
 *				  handlers as inet6_protocol{}.
 */

#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <linux/netdevice.h>
#include <linux/in6.h>
#include <linux/icmpv6.h>
32
#include <linux/slab.h>
33
#include <linux/export.h>
L
Linus Torvalds 已提交
34

35
#include <net/dst.h>
L
Linus Torvalds 已提交
36 37 38 39 40 41 42 43 44 45
#include <net/sock.h>
#include <net/snmp.h>

#include <net/ipv6.h>
#include <net/protocol.h>
#include <net/transp_v6.h>
#include <net/rawv6.h>
#include <net/ndisc.h>
#include <net/ip6_route.h>
#include <net/addrconf.h>
46
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
47 48
#include <net/xfrm.h>
#endif
L
Linus Torvalds 已提交
49 50 51

#include <asm/uaccess.h>

52 53
int ipv6_find_tlv(struct sk_buff *skb, int offset, int type)
{
54
	const unsigned char *nh = skb_network_header(skb);
55
	int packet_len = skb->tail - skb->network_header;
56 57 58 59 60
	struct ipv6_opt_hdr *hdr;
	int len;

	if (offset + 2 > packet_len)
		goto bad;
61
	hdr = (struct ipv6_opt_hdr *)(nh + offset);
62 63 64 65 66 67 68 69 70
	len = ((hdr->hdrlen + 1) << 3);

	if (offset + len > packet_len)
		goto bad;

	offset += 2;
	len -= 2;

	while (len > 0) {
71
		int opttype = nh[offset];
72 73 74 75 76 77
		int optlen;

		if (opttype == type)
			return offset;

		switch (opttype) {
78
		case IPV6_TLV_PAD1:
79 80 81
			optlen = 1;
			break;
		default:
82
			optlen = nh[offset + 1] + 2;
83 84 85 86 87 88 89 90 91 92 93
			if (optlen > len)
				goto bad;
			break;
		}
		offset += optlen;
		len -= optlen;
	}
	/* not_found */
 bad:
	return -1;
}
94
EXPORT_SYMBOL_GPL(ipv6_find_tlv);
95

L
Linus Torvalds 已提交
96 97 98
/*
 *	Parsing tlv encoded headers.
 *
99 100
 *	Parsing function "func" returns true, if parsing succeed
 *	and false, if it failed.
L
Linus Torvalds 已提交
101 102 103 104 105
 *	It MUST NOT touch skb->h.
 */

struct tlvtype_proc {
	int	type;
106
	bool	(*func)(struct sk_buff *skb, int offset);
L
Linus Torvalds 已提交
107 108 109 110 111 112 113 114
};

/*********************
  Generic functions
 *********************/

/* An unknown option is detected, decide what to do */

115
static bool ip6_tlvopt_unknown(struct sk_buff *skb, int optoff)
L
Linus Torvalds 已提交
116
{
117
	switch ((skb_network_header(skb)[optoff] & 0xC0) >> 6) {
L
Linus Torvalds 已提交
118
	case 0: /* ignore */
119
		return true;
L
Linus Torvalds 已提交
120 121 122 123 124 125 126 127

	case 1: /* drop packet */
		break;

	case 3: /* Send ICMP if not a multicast address and drop packet */
		/* Actually, it is redundant check. icmp_send
		   will recheck in any case.
		 */
128
		if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr))
L
Linus Torvalds 已提交
129 130 131
			break;
	case 2: /* send ICMP PARM PROB regardless and drop packet */
		icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, optoff);
132
		return false;
133
	}
L
Linus Torvalds 已提交
134 135

	kfree_skb(skb);
136
	return false;
L
Linus Torvalds 已提交
137 138 139 140
}

/* Parse tlv encoded option header (hop-by-hop or destination) */

141
static bool ip6_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb)
L
Linus Torvalds 已提交
142
{
143
	const struct tlvtype_proc *curr;
144
	const unsigned char *nh = skb_network_header(skb);
145
	int off = skb_network_header_len(skb);
146
	int len = (skb_transport_header(skb)[1] + 1) << 3;
L
Linus Torvalds 已提交
147

148
	if (skb_transport_offset(skb) + len > skb_headlen(skb))
L
Linus Torvalds 已提交
149 150 151 152 153 154
		goto bad;

	off += 2;
	len -= 2;

	while (len > 0) {
155
		int optlen = nh[off + 1] + 2;
156
		int i;
L
Linus Torvalds 已提交
157

158
		switch (nh[off]) {
159
		case IPV6_TLV_PAD1:
L
Linus Torvalds 已提交
160 161 162 163
			optlen = 1;
			break;

		case IPV6_TLV_PADN:
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
			/* RFC 2460 states that the purpose of PadN is
			 * to align the containing header to multiples
			 * of 8. 7 is therefore the highest valid value.
			 * See also RFC 4942, Section 2.1.9.5.
			 */
			if (optlen > 7)
				goto bad;
			/* RFC 4942 recommends receiving hosts to
			 * actively check PadN payload to contain
			 * only zeroes.
			 */
			for (i = 2; i < optlen; i++) {
				if (nh[off + i] != 0)
					goto bad;
			}
L
Linus Torvalds 已提交
179 180 181 182 183 184
			break;

		default: /* Other TLV code so scan list */
			if (optlen > len)
				goto bad;
			for (curr=procs; curr->type >= 0; curr++) {
185
				if (curr->type == nh[off]) {
186 187
					/* type specific length/alignment
					   checks will be performed in the
L
Linus Torvalds 已提交
188
					   func(). */
189 190
					if (curr->func(skb, off) == false)
						return false;
L
Linus Torvalds 已提交
191 192 193 194
					break;
				}
			}
			if (curr->type < 0) {
195
				if (ip6_tlvopt_unknown(skb, off) == 0)
196
					return false;
L
Linus Torvalds 已提交
197 198 199 200 201 202 203
			}
			break;
		}
		off += optlen;
		len -= optlen;
	}
	if (len == 0)
204
		return true;
L
Linus Torvalds 已提交
205 206
bad:
	kfree_skb(skb);
207
	return false;
L
Linus Torvalds 已提交
208 209 210 211 212 213
}

/*****************************
  Destination options header.
 *****************************/

214
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
215
static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
216 217 218
{
	struct ipv6_destopt_hao *hao;
	struct inet6_skb_parm *opt = IP6CB(skb);
219
	struct ipv6hdr *ipv6h = ipv6_hdr(skb);
220 221 222 223 224 225 226 227 228 229
	struct in6_addr tmp_addr;
	int ret;

	if (opt->dsthao) {
		LIMIT_NETDEBUG(KERN_DEBUG "hao duplicated\n");
		goto discard;
	}
	opt->dsthao = opt->dst1;
	opt->dst1 = 0;

230
	hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + optoff);
231 232 233 234 235 236 237 238 239

	if (hao->length != 16) {
		LIMIT_NETDEBUG(
			KERN_DEBUG "hao invalid option length = %d\n", hao->length);
		goto discard;
	}

	if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) {
		LIMIT_NETDEBUG(
H
Harvey Harrison 已提交
240
			KERN_DEBUG "hao is not an unicast addr: %pI6\n", &hao->addr);
241 242 243 244 245 246 247 248 249
		goto discard;
	}

	ret = xfrm6_input_addr(skb, (xfrm_address_t *)&ipv6h->daddr,
			       (xfrm_address_t *)&hao->addr, IPPROTO_DSTOPTS);
	if (unlikely(ret < 0))
		goto discard;

	if (skb_cloned(skb)) {
250
		if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
251 252 253
			goto discard;

		/* update all variable using below by copied skbuff */
254
		hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) +
255
						  optoff);
256
		ipv6h = ipv6_hdr(skb);
257 258 259 260 261
	}

	if (skb->ip_summed == CHECKSUM_COMPLETE)
		skb->ip_summed = CHECKSUM_NONE;

A
Alexey Dobriyan 已提交
262 263 264
	tmp_addr = ipv6h->saddr;
	ipv6h->saddr = hao->addr;
	hao->addr = tmp_addr;
265

266
	if (skb->tstamp.tv64 == 0)
267 268
		__net_timestamp(skb);

269
	return true;
270 271 272

 discard:
	kfree_skb(skb);
273
	return false;
274 275 276
}
#endif

277
static const struct tlvtype_proc tlvprocdestopt_lst[] = {
278
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
279 280 281 282 283
	{
		.type	= IPV6_TLV_HAO,
		.func	= ipv6_dest_hao,
	},
#endif
L
Linus Torvalds 已提交
284 285 286
	{-1,			NULL}
};

287
static int ipv6_destopt_rcv(struct sk_buff *skb)
L
Linus Torvalds 已提交
288 289
{
	struct inet6_skb_parm *opt = IP6CB(skb);
290
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
291 292
	__u16 dstbuf;
#endif
293
	struct dst_entry *dst = skb_dst(skb);
L
Linus Torvalds 已提交
294

295 296
	if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) ||
	    !pskb_may_pull(skb, (skb_transport_offset(skb) +
297
				 ((skb_transport_header(skb)[1] + 1) << 3)))) {
298
		IP6_INC_STATS_BH(dev_net(dst->dev), ip6_dst_idev(dst),
299
				 IPSTATS_MIB_INHDRERRORS);
L
Linus Torvalds 已提交
300 301 302 303
		kfree_skb(skb);
		return -1;
	}

304
	opt->lastopt = opt->dst1 = skb_network_header_len(skb);
305
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
306 307
	dstbuf = opt->dst1;
#endif
L
Linus Torvalds 已提交
308

309
	if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) {
310
		skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3;
311
		opt = IP6CB(skb);
312
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
313 314
		opt->nhoff = dstbuf;
#else
315
		opt->nhoff = opt->dst1;
316
#endif
L
Linus Torvalds 已提交
317 318 319
		return 1;
	}

320 321
	IP6_INC_STATS_BH(dev_net(dst->dev),
			 ip6_dst_idev(dst), IPSTATS_MIB_INHDRERRORS);
L
Linus Torvalds 已提交
322 323 324 325 326 327 328
	return -1;
}

/********************************
  Routing header.
 ********************************/

329
/* called with rcu_read_lock() */
330
static int ipv6_rthdr_rcv(struct sk_buff *skb)
L
Linus Torvalds 已提交
331 332
{
	struct inet6_skb_parm *opt = IP6CB(skb);
333
	struct in6_addr *addr = NULL;
L
Linus Torvalds 已提交
334
	struct in6_addr daddr;
335
	struct inet6_dev *idev;
L
Linus Torvalds 已提交
336 337 338
	int n, i;
	struct ipv6_rt_hdr *hdr;
	struct rt0_hdr *rthdr;
339 340
	struct net *net = dev_net(skb->dev);
	int accept_source_route = net->ipv6.devconf_all->accept_source_route;
341

342 343 344
	idev = __in6_dev_get(skb->dev);
	if (idev && accept_source_route > idev->cnf.accept_source_route)
		accept_source_route = idev->cnf.accept_source_route;
345

346 347
	if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) ||
	    !pskb_may_pull(skb, (skb_transport_offset(skb) +
348
				 ((skb_transport_header(skb)[1] + 1) << 3)))) {
E
Eric Dumazet 已提交
349
		IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
350
				 IPSTATS_MIB_INHDRERRORS);
L
Linus Torvalds 已提交
351 352 353 354
		kfree_skb(skb);
		return -1;
	}

355
	hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb);
L
Linus Torvalds 已提交
356

357
	if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) ||
L
Linus Torvalds 已提交
358
	    skb->pkt_type != PACKET_HOST) {
E
Eric Dumazet 已提交
359
		IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
360
				 IPSTATS_MIB_INADDRERRORS);
L
Linus Torvalds 已提交
361 362 363 364 365 366
		kfree_skb(skb);
		return -1;
	}

looped_back:
	if (hdr->segments_left == 0) {
367
		switch (hdr->type) {
368
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
369 370 371 372 373
		case IPV6_SRCRT_TYPE_2:
			/* Silently discard type 2 header unless it was
			 * processed by own
			 */
			if (!addr) {
E
Eric Dumazet 已提交
374
				IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
375
						 IPSTATS_MIB_INADDRERRORS);
376 377 378 379 380 381 382 383 384
				kfree_skb(skb);
				return -1;
			}
			break;
#endif
		default:
			break;
		}

385
		opt->lastopt = opt->srcrt = skb_network_header_len(skb);
386
		skb->transport_header += (hdr->hdrlen + 1) << 3;
L
Linus Torvalds 已提交
387 388
		opt->dst0 = opt->dst1;
		opt->dst1 = 0;
389
		opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb);
L
Linus Torvalds 已提交
390 391 392
		return 1;
	}

393
	switch (hdr->type) {
394
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
395
	case IPV6_SRCRT_TYPE_2:
396 397
		if (accept_source_route < 0)
			goto unknown_rh;
398 399
		/* Silently discard invalid RTH type 2 */
		if (hdr->hdrlen != 2 || hdr->segments_left != 1) {
E
Eric Dumazet 已提交
400
			IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
401
					 IPSTATS_MIB_INHDRERRORS);
402 403 404 405 406
			kfree_skb(skb);
			return -1;
		}
		break;
#endif
407 408
	default:
		goto unknown_rh;
L
Linus Torvalds 已提交
409 410 411 412 413 414 415 416 417 418
	}

	/*
	 *	This is the routing header forwarding algorithm from
	 *	RFC 2460, page 16.
	 */

	n = hdr->hdrlen >> 1;

	if (hdr->segments_left > n) {
E
Eric Dumazet 已提交
419
		IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
420
				 IPSTATS_MIB_INHDRERRORS);
421 422 423
		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
				  ((&hdr->segments_left) -
				   skb_network_header(skb)));
L
Linus Torvalds 已提交
424 425 426 427 428 429 430 431
		return -1;
	}

	/* We are about to mangle packet header. Be careful!
	   Do not damage packets queued somewhere.
	 */
	if (skb_cloned(skb)) {
		/* the copy is a forwarded packet */
432
		if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) {
E
Eric Dumazet 已提交
433
			IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
434 435
					 IPSTATS_MIB_OUTDISCARDS);
			kfree_skb(skb);
L
Linus Torvalds 已提交
436 437
			return -1;
		}
438
		hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb);
L
Linus Torvalds 已提交
439 440
	}

441
	if (skb->ip_summed == CHECKSUM_COMPLETE)
L
Linus Torvalds 已提交
442 443 444 445 446 447 448 449
		skb->ip_summed = CHECKSUM_NONE;

	i = n - --hdr->segments_left;

	rthdr = (struct rt0_hdr *) hdr;
	addr = rthdr->addr;
	addr += i - 1;

450
	switch (hdr->type) {
451
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
452 453
	case IPV6_SRCRT_TYPE_2:
		if (xfrm6_input_addr(skb, (xfrm_address_t *)addr,
454
				     (xfrm_address_t *)&ipv6_hdr(skb)->saddr,
455
				     IPPROTO_ROUTING) < 0) {
E
Eric Dumazet 已提交
456
			IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
457
					 IPSTATS_MIB_INADDRERRORS);
458 459 460
			kfree_skb(skb);
			return -1;
		}
E
Eric Dumazet 已提交
461 462
		if (!ipv6_chk_home_addr(dev_net(skb_dst(skb)->dev), addr)) {
			IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
463
					 IPSTATS_MIB_INADDRERRORS);
464 465 466 467 468 469 470 471 472
			kfree_skb(skb);
			return -1;
		}
		break;
#endif
	default:
		break;
	}

L
Linus Torvalds 已提交
473
	if (ipv6_addr_is_multicast(addr)) {
E
Eric Dumazet 已提交
474
		IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
475
				 IPSTATS_MIB_INADDRERRORS);
L
Linus Torvalds 已提交
476 477 478 479
		kfree_skb(skb);
		return -1;
	}

A
Alexey Dobriyan 已提交
480 481 482
	daddr = *addr;
	*addr = ipv6_hdr(skb)->daddr;
	ipv6_hdr(skb)->daddr = daddr;
L
Linus Torvalds 已提交
483

E
Eric Dumazet 已提交
484
	skb_dst_drop(skb);
L
Linus Torvalds 已提交
485
	ip6_route_input(skb);
E
Eric Dumazet 已提交
486
	if (skb_dst(skb)->error) {
487
		skb_push(skb, skb->data - skb_network_header(skb));
L
Linus Torvalds 已提交
488 489 490 491
		dst_input(skb);
		return -1;
	}

E
Eric Dumazet 已提交
492
	if (skb_dst(skb)->dev->flags&IFF_LOOPBACK) {
493
		if (ipv6_hdr(skb)->hop_limit <= 1) {
E
Eric Dumazet 已提交
494
			IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
495
					 IPSTATS_MIB_INHDRERRORS);
L
Linus Torvalds 已提交
496
			icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
497
				    0);
L
Linus Torvalds 已提交
498 499 500
			kfree_skb(skb);
			return -1;
		}
501
		ipv6_hdr(skb)->hop_limit--;
L
Linus Torvalds 已提交
502 503 504
		goto looped_back;
	}

505
	skb_push(skb, skb->data - skb_network_header(skb));
L
Linus Torvalds 已提交
506 507
	dst_input(skb);
	return -1;
508 509

unknown_rh:
E
Eric Dumazet 已提交
510
	IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_INHDRERRORS);
511 512 513
	icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
			  (&hdr->type) - skb_network_header(skb));
	return -1;
L
Linus Torvalds 已提交
514 515
}

516
static const struct inet6_protocol rthdr_protocol = {
L
Linus Torvalds 已提交
517
	.handler	=	ipv6_rthdr_rcv,
H
Herbert Xu 已提交
518
	.flags		=	INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
L
Linus Torvalds 已提交
519 520
};

521
static const struct inet6_protocol destopt_protocol = {
522 523 524 525
	.handler	=	ipv6_destopt_rcv,
	.flags		=	INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
};

526
static const struct inet6_protocol nodata_protocol = {
527 528 529 530 531
	.handler	=	dst_discard,
	.flags		=	INET6_PROTO_NOPOLICY,
};

int __init ipv6_exthdrs_init(void)
L
Linus Torvalds 已提交
532
{
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
	int ret;

	ret = inet6_add_protocol(&rthdr_protocol, IPPROTO_ROUTING);
	if (ret)
		goto out;

	ret = inet6_add_protocol(&destopt_protocol, IPPROTO_DSTOPTS);
	if (ret)
		goto out_rthdr;

	ret = inet6_add_protocol(&nodata_protocol, IPPROTO_NONE);
	if (ret)
		goto out_destopt;

out:
	return ret;
out_rthdr:
	inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING);
out_destopt:
	inet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS);
	goto out;
L
Linus Torvalds 已提交
554 555
};

556 557 558 559 560 561 562
void ipv6_exthdrs_exit(void)
{
	inet6_del_protocol(&nodata_protocol, IPPROTO_NONE);
	inet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS);
	inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING);
}

L
Linus Torvalds 已提交
563 564 565 566
/**********************************
  Hop-by-hop options.
 **********************************/

567
/*
E
Eric Dumazet 已提交
568
 * Note: we cannot rely on skb_dst(skb) before we assign it in ip6_route_input().
569 570 571
 */
static inline struct inet6_dev *ipv6_skb_idev(struct sk_buff *skb)
{
E
Eric Dumazet 已提交
572
	return skb_dst(skb) ? ip6_dst_idev(skb_dst(skb)) : __in6_dev_get(skb->dev);
573 574
}

575 576 577 578 579
static inline struct net *ipv6_skb_net(struct sk_buff *skb)
{
	return skb_dst(skb) ? dev_net(skb_dst(skb)->dev) : dev_net(skb->dev);
}

L
Linus Torvalds 已提交
580 581
/* Router Alert as of RFC 2711 */

582
static bool ipv6_hop_ra(struct sk_buff *skb, int optoff)
L
Linus Torvalds 已提交
583
{
584
	const unsigned char *nh = skb_network_header(skb);
585

586
	if (nh[optoff + 1] == 2) {
L
Linus Torvalds 已提交
587
		IP6CB(skb)->ra = optoff;
588
		return true;
L
Linus Torvalds 已提交
589
	}
590
	LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n",
591
		       nh[optoff + 1]);
L
Linus Torvalds 已提交
592
	kfree_skb(skb);
593
	return false;
L
Linus Torvalds 已提交
594 595 596 597
}

/* Jumbo payload */

598
static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
L
Linus Torvalds 已提交
599
{
600
	const unsigned char *nh = skb_network_header(skb);
601
	struct net *net = ipv6_skb_net(skb);
L
Linus Torvalds 已提交
602 603
	u32 pkt_len;

604
	if (nh[optoff + 1] != 4 || (optoff & 3) != 2) {
605
		LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n",
606
			       nh[optoff+1]);
607
		IP6_INC_STATS_BH(net, ipv6_skb_idev(skb),
608
				 IPSTATS_MIB_INHDRERRORS);
L
Linus Torvalds 已提交
609 610 611
		goto drop;
	}

612
	pkt_len = ntohl(*(__be32 *)(nh + optoff + 2));
L
Linus Torvalds 已提交
613
	if (pkt_len <= IPV6_MAXPLEN) {
614 615
		IP6_INC_STATS_BH(net, ipv6_skb_idev(skb),
				 IPSTATS_MIB_INHDRERRORS);
L
Linus Torvalds 已提交
616
		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2);
617
		return false;
L
Linus Torvalds 已提交
618
	}
619
	if (ipv6_hdr(skb)->payload_len) {
620 621
		IP6_INC_STATS_BH(net, ipv6_skb_idev(skb),
				 IPSTATS_MIB_INHDRERRORS);
L
Linus Torvalds 已提交
622
		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
623
		return false;
L
Linus Torvalds 已提交
624 625 626
	}

	if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
627 628
		IP6_INC_STATS_BH(net, ipv6_skb_idev(skb),
				 IPSTATS_MIB_INTRUNCATEDPKTS);
L
Linus Torvalds 已提交
629 630
		goto drop;
	}
631 632 633 634

	if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
		goto drop;

635
	return true;
L
Linus Torvalds 已提交
636 637 638

drop:
	kfree_skb(skb);
639
	return false;
L
Linus Torvalds 已提交
640 641
}

642
static const struct tlvtype_proc tlvprochopopt_lst[] = {
L
Linus Torvalds 已提交
643 644 645 646 647 648 649 650 651 652 653
	{
		.type	= IPV6_TLV_ROUTERALERT,
		.func	= ipv6_hop_ra,
	},
	{
		.type	= IPV6_TLV_JUMBO,
		.func	= ipv6_hop_jumbo,
	},
	{ -1, }
};

654
int ipv6_parse_hopopts(struct sk_buff *skb)
L
Linus Torvalds 已提交
655
{
656 657
	struct inet6_skb_parm *opt = IP6CB(skb);

658
	/*
659
	 * skb_network_header(skb) is equal to skb->data, and
660
	 * skb_network_header_len(skb) is always equal to
661 662 663 664
	 * sizeof(struct ipv6hdr) by definition of
	 * hop-by-hop options.
	 */
	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + 8) ||
665 666
	    !pskb_may_pull(skb, (sizeof(struct ipv6hdr) +
				 ((skb_transport_header(skb)[1] + 1) << 3)))) {
667 668 669 670
		kfree_skb(skb);
		return -1;
	}

671
	opt->hop = sizeof(struct ipv6hdr);
672
	if (ip6_parse_tlv(tlvprochopopt_lst, skb)) {
673
		skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3;
674
		opt = IP6CB(skb);
675
		opt->nhoff = sizeof(struct ipv6hdr);
676
		return 1;
677
	}
L
Linus Torvalds 已提交
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698
	return -1;
}

/*
 *	Creating outbound headers.
 *
 *	"build" functions work when skb is filled from head to tail (datagram)
 *	"push"	functions work when headers are added from tail to head (tcp)
 *
 *	In both cases we assume, that caller reserved enough room
 *	for headers.
 */

static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
			    struct ipv6_rt_hdr *opt,
			    struct in6_addr **addr_p)
{
	struct rt0_hdr *phdr, *ihdr;
	int hops;

	ihdr = (struct rt0_hdr *) opt;
699

L
Linus Torvalds 已提交
700 701 702 703 704 705 706 707 708
	phdr = (struct rt0_hdr *) skb_push(skb, (ihdr->rt_hdr.hdrlen + 1) << 3);
	memcpy(phdr, ihdr, sizeof(struct rt0_hdr));

	hops = ihdr->rt_hdr.hdrlen >> 1;

	if (hops > 1)
		memcpy(phdr->addr, ihdr->addr + 1,
		       (hops - 1) * sizeof(struct in6_addr));

A
Alexey Dobriyan 已提交
709
	phdr->addr[hops - 1] = **addr_p;
L
Linus Torvalds 已提交
710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728
	*addr_p = ihdr->addr;

	phdr->rt_hdr.nexthdr = *proto;
	*proto = NEXTHDR_ROUTING;
}

static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt)
{
	struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ipv6_optlen(opt));

	memcpy(h, opt, ipv6_optlen(opt));
	h->nexthdr = *proto;
	*proto = type;
}

void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
			  u8 *proto,
			  struct in6_addr **daddr)
{
729
	if (opt->srcrt) {
L
Linus Torvalds 已提交
730
		ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
731 732 733 734 735 736 737
		/*
		 * IPV6_RTHDRDSTOPTS is ignored
		 * unless IPV6_RTHDR is set (RFC3542).
		 */
		if (opt->dst0opt)
			ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
	}
L
Linus Torvalds 已提交
738 739 740
	if (opt->hopopt)
		ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
}
741 742
EXPORT_SYMBOL(ipv6_push_nfrag_opts);

L
Linus Torvalds 已提交
743 744 745 746 747 748 749 750 751 752 753 754 755
void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto)
{
	if (opt->dst1opt)
		ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt);
}

struct ipv6_txoptions *
ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
{
	struct ipv6_txoptions *opt2;

	opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC);
	if (opt2) {
756
		long dif = (char *)opt2 - (char *)opt;
L
Linus Torvalds 已提交
757 758
		memcpy(opt2, opt, opt->tot_len);
		if (opt2->hopopt)
759
			*((char **)&opt2->hopopt) += dif;
L
Linus Torvalds 已提交
760
		if (opt2->dst0opt)
761
			*((char **)&opt2->dst0opt) += dif;
L
Linus Torvalds 已提交
762
		if (opt2->dst1opt)
763
			*((char **)&opt2->dst1opt) += dif;
L
Linus Torvalds 已提交
764
		if (opt2->srcrt)
765
			*((char **)&opt2->srcrt) += dif;
L
Linus Torvalds 已提交
766 767 768
	}
	return opt2;
}
769 770
EXPORT_SYMBOL_GPL(ipv6_dup_options);

771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805
static int ipv6_renew_option(void *ohdr,
			     struct ipv6_opt_hdr __user *newopt, int newoptlen,
			     int inherit,
			     struct ipv6_opt_hdr **hdr,
			     char **p)
{
	if (inherit) {
		if (ohdr) {
			memcpy(*p, ohdr, ipv6_optlen((struct ipv6_opt_hdr *)ohdr));
			*hdr = (struct ipv6_opt_hdr *)*p;
			*p += CMSG_ALIGN(ipv6_optlen(*(struct ipv6_opt_hdr **)hdr));
		}
	} else {
		if (newopt) {
			if (copy_from_user(*p, newopt, newoptlen))
				return -EFAULT;
			*hdr = (struct ipv6_opt_hdr *)*p;
			if (ipv6_optlen(*(struct ipv6_opt_hdr **)hdr) > newoptlen)
				return -EINVAL;
			*p += CMSG_ALIGN(newoptlen);
		}
	}
	return 0;
}

struct ipv6_txoptions *
ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
		   int newtype,
		   struct ipv6_opt_hdr __user *newopt, int newoptlen)
{
	int tot_len = 0;
	char *p;
	struct ipv6_txoptions *opt2;
	int err;

806 807 808 809 810 811 812 813 814 815 816
	if (opt) {
		if (newtype != IPV6_HOPOPTS && opt->hopopt)
			tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt));
		if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt)
			tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt));
		if (newtype != IPV6_RTHDR && opt->srcrt)
			tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt));
		if (newtype != IPV6_DSTOPTS && opt->dst1opt)
			tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
	}

817 818 819 820 821 822
	if (newopt && newoptlen)
		tot_len += CMSG_ALIGN(newoptlen);

	if (!tot_len)
		return NULL;

823
	tot_len += sizeof(*opt2);
824 825 826 827 828 829 830 831 832
	opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC);
	if (!opt2)
		return ERR_PTR(-ENOBUFS);

	memset(opt2, 0, tot_len);

	opt2->tot_len = tot_len;
	p = (char *)(opt2 + 1);

833
	err = ipv6_renew_option(opt ? opt->hopopt : NULL, newopt, newoptlen,
834 835 836 837 838
				newtype != IPV6_HOPOPTS,
				&opt2->hopopt, &p);
	if (err)
		goto out;

839
	err = ipv6_renew_option(opt ? opt->dst0opt : NULL, newopt, newoptlen,
840 841 842 843 844
				newtype != IPV6_RTHDRDSTOPTS,
				&opt2->dst0opt, &p);
	if (err)
		goto out;

845
	err = ipv6_renew_option(opt ? opt->srcrt : NULL, newopt, newoptlen,
846
				newtype != IPV6_RTHDR,
847
				(struct ipv6_opt_hdr **)&opt2->srcrt, &p);
848 849 850
	if (err)
		goto out;

851
	err = ipv6_renew_option(opt ? opt->dst1opt : NULL, newopt, newoptlen,
852 853 854 855 856 857 858 859 860 861 862 863
				newtype != IPV6_DSTOPTS,
				&opt2->dst1opt, &p);
	if (err)
		goto out;

	opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) +
			  (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) +
			  (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0);
	opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0);

	return opt2;
out:
864
	sock_kfree_s(sk, opt2, opt2->tot_len);
865 866 867
	return ERR_PTR(err);
}

868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885
struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
					  struct ipv6_txoptions *opt)
{
	/*
	 * ignore the dest before srcrt unless srcrt is being included.
	 * --yoshfuji
	 */
	if (opt && opt->dst0opt && !opt->srcrt) {
		if (opt_space != opt) {
			memcpy(opt_space, opt, sizeof(*opt_space));
			opt = opt_space;
		}
		opt->opt_nflen -= ipv6_optlen(opt->dst0opt);
		opt->dst0opt = NULL;
	}

	return opt;
}
886
EXPORT_SYMBOL_GPL(ipv6_fixup_options);
887

888 889 890 891
/**
 * fl6_update_dst - update flowi destination address with info given
 *                  by srcrt option, if any.
 *
892
 * @fl6: flowi6 for which daddr is to be updated
893
 * @opt: struct ipv6_txoptions in which to look for srcrt opt
894
 * @orig: copy of original daddr address if modified
895 896
 *
 * Returns NULL if no txoptions or no srcrt, otherwise returns orig
897
 * and initial value of fl6->daddr set in orig
898
 */
899
struct in6_addr *fl6_update_dst(struct flowi6 *fl6,
900 901 902 903 904 905
				const struct ipv6_txoptions *opt,
				struct in6_addr *orig)
{
	if (!opt || !opt->srcrt)
		return NULL;

A
Alexey Dobriyan 已提交
906 907
	*orig = fl6->daddr;
	fl6->daddr = *((struct rt0_hdr *)opt->srcrt)->addr;
908 909 910
	return orig;
}
EXPORT_SYMBOL_GPL(fl6_update_dst);