exthdrs.c 21.1 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>
L
Linus Torvalds 已提交
33

34
#include <net/dst.h>
L
Linus Torvalds 已提交
35 36 37 38 39 40 41 42 43 44
#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>
45
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
46 47
#include <net/xfrm.h>
#endif
L
Linus Torvalds 已提交
48 49 50

#include <asm/uaccess.h>

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

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

	if (offset + len > packet_len)
		goto bad;

	offset += 2;
	len -= 2;

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

		if (opttype == type)
			return offset;

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

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

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

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

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

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

	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.
		 */
127
		if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr))
L
Linus Torvalds 已提交
128 129 130 131
			break;
	case 2: /* send ICMP PARM PROB regardless and drop packet */
		icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, optoff);
		return 0;
132
	}
L
Linus Torvalds 已提交
133 134 135 136 137 138 139

	kfree_skb(skb);
	return 0;
}

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

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

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

	off += 2;
	len -= 2;

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

156
		switch (nh[off]) {
L
Linus Torvalds 已提交
157 158 159 160 161 162 163 164 165 166 167
		case IPV6_TLV_PAD0:
			optlen = 1;
			break;

		case IPV6_TLV_PADN:
			break;

		default: /* Other TLV code so scan list */
			if (optlen > len)
				goto bad;
			for (curr=procs; curr->type >= 0; curr++) {
168
				if (curr->type == nh[off]) {
169 170
					/* type specific length/alignment
					   checks will be performed in the
L
Linus Torvalds 已提交
171
					   func(). */
172
					if (curr->func(skb, off) == 0)
L
Linus Torvalds 已提交
173 174 175 176 177
						return 0;
					break;
				}
			}
			if (curr->type < 0) {
178
				if (ip6_tlvopt_unknown(skb, off) == 0)
L
Linus Torvalds 已提交
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
					return 0;
			}
			break;
		}
		off += optlen;
		len -= optlen;
	}
	if (len == 0)
		return 1;
bad:
	kfree_skb(skb);
	return 0;
}

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

197
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
198
static int ipv6_dest_hao(struct sk_buff *skb, int optoff)
199 200 201
{
	struct ipv6_destopt_hao *hao;
	struct inet6_skb_parm *opt = IP6CB(skb);
202
	struct ipv6hdr *ipv6h = ipv6_hdr(skb);
203 204 205 206 207 208 209 210 211 212
	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;

213
	hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + optoff);
214 215 216 217 218 219 220 221 222

	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 已提交
223
			KERN_DEBUG "hao is not an unicast addr: %pI6\n", &hao->addr);
224 225 226 227 228 229 230 231 232
		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)) {
233
		if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
234 235 236
			goto discard;

		/* update all variable using below by copied skbuff */
237
		hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) +
238
						  optoff);
239
		ipv6h = ipv6_hdr(skb);
240 241 242 243 244 245 246 247 248
	}

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

	ipv6_addr_copy(&tmp_addr, &ipv6h->saddr);
	ipv6_addr_copy(&ipv6h->saddr, &hao->addr);
	ipv6_addr_copy(&hao->addr, &tmp_addr);

249
	if (skb->tstamp.tv64 == 0)
250 251 252 253 254 255 256 257 258 259
		__net_timestamp(skb);

	return 1;

 discard:
	kfree_skb(skb);
	return 0;
}
#endif

L
Linus Torvalds 已提交
260
static struct tlvtype_proc tlvprocdestopt_lst[] = {
261
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
262 263 264 265 266
	{
		.type	= IPV6_TLV_HAO,
		.func	= ipv6_dest_hao,
	},
#endif
L
Linus Torvalds 已提交
267 268 269
	{-1,			NULL}
};

270
static int ipv6_destopt_rcv(struct sk_buff *skb)
L
Linus Torvalds 已提交
271 272
{
	struct inet6_skb_parm *opt = IP6CB(skb);
273
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
274 275
	__u16 dstbuf;
#endif
276
	struct dst_entry *dst = skb_dst(skb);
L
Linus Torvalds 已提交
277

278 279
	if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) ||
	    !pskb_may_pull(skb, (skb_transport_offset(skb) +
280
				 ((skb_transport_header(skb)[1] + 1) << 3)))) {
281
		IP6_INC_STATS_BH(dev_net(dst->dev), ip6_dst_idev(dst),
282
				 IPSTATS_MIB_INHDRERRORS);
L
Linus Torvalds 已提交
283 284 285 286
		kfree_skb(skb);
		return -1;
	}

287
	opt->lastopt = opt->dst1 = skb_network_header_len(skb);
288
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
289 290
	dstbuf = opt->dst1;
#endif
L
Linus Torvalds 已提交
291

292
	if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) {
293
		skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3;
294
		opt = IP6CB(skb);
295
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
296 297
		opt->nhoff = dstbuf;
#else
298
		opt->nhoff = opt->dst1;
299
#endif
L
Linus Torvalds 已提交
300 301 302
		return 1;
	}

303 304
	IP6_INC_STATS_BH(dev_net(dst->dev),
			 ip6_dst_idev(dst), IPSTATS_MIB_INHDRERRORS);
L
Linus Torvalds 已提交
305 306 307 308 309 310 311
	return -1;
}

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

312
/* called with rcu_read_lock() */
313
static int ipv6_rthdr_rcv(struct sk_buff *skb)
L
Linus Torvalds 已提交
314 315
{
	struct inet6_skb_parm *opt = IP6CB(skb);
316
	struct in6_addr *addr = NULL;
L
Linus Torvalds 已提交
317
	struct in6_addr daddr;
318
	struct inet6_dev *idev;
L
Linus Torvalds 已提交
319 320 321
	int n, i;
	struct ipv6_rt_hdr *hdr;
	struct rt0_hdr *rthdr;
322 323
	struct net *net = dev_net(skb->dev);
	int accept_source_route = net->ipv6.devconf_all->accept_source_route;
324

325 326 327
	idev = __in6_dev_get(skb->dev);
	if (idev && accept_source_route > idev->cnf.accept_source_route)
		accept_source_route = idev->cnf.accept_source_route;
328

329 330
	if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) ||
	    !pskb_may_pull(skb, (skb_transport_offset(skb) +
331
				 ((skb_transport_header(skb)[1] + 1) << 3)))) {
E
Eric Dumazet 已提交
332
		IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
333
				 IPSTATS_MIB_INHDRERRORS);
L
Linus Torvalds 已提交
334 335 336 337
		kfree_skb(skb);
		return -1;
	}

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

340
	if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) ||
L
Linus Torvalds 已提交
341
	    skb->pkt_type != PACKET_HOST) {
E
Eric Dumazet 已提交
342
		IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
343
				 IPSTATS_MIB_INADDRERRORS);
L
Linus Torvalds 已提交
344 345 346 347 348 349
		kfree_skb(skb);
		return -1;
	}

looped_back:
	if (hdr->segments_left == 0) {
350
		switch (hdr->type) {
351
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
352 353 354 355 356
		case IPV6_SRCRT_TYPE_2:
			/* Silently discard type 2 header unless it was
			 * processed by own
			 */
			if (!addr) {
E
Eric Dumazet 已提交
357
				IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
358
						 IPSTATS_MIB_INADDRERRORS);
359 360 361 362 363 364 365 366 367
				kfree_skb(skb);
				return -1;
			}
			break;
#endif
		default:
			break;
		}

368
		opt->lastopt = opt->srcrt = skb_network_header_len(skb);
369
		skb->transport_header += (hdr->hdrlen + 1) << 3;
L
Linus Torvalds 已提交
370 371
		opt->dst0 = opt->dst1;
		opt->dst1 = 0;
372
		opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb);
L
Linus Torvalds 已提交
373 374 375
		return 1;
	}

376
	switch (hdr->type) {
377
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
378
	case IPV6_SRCRT_TYPE_2:
379 380
		if (accept_source_route < 0)
			goto unknown_rh;
381 382
		/* Silently discard invalid RTH type 2 */
		if (hdr->hdrlen != 2 || hdr->segments_left != 1) {
E
Eric Dumazet 已提交
383
			IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
384
					 IPSTATS_MIB_INHDRERRORS);
385 386 387 388 389
			kfree_skb(skb);
			return -1;
		}
		break;
#endif
390 391
	default:
		goto unknown_rh;
L
Linus Torvalds 已提交
392 393 394 395 396 397 398 399 400 401
	}

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

	n = hdr->hdrlen >> 1;

	if (hdr->segments_left > n) {
E
Eric Dumazet 已提交
402
		IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
403
				 IPSTATS_MIB_INHDRERRORS);
404 405 406
		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
				  ((&hdr->segments_left) -
				   skb_network_header(skb)));
L
Linus Torvalds 已提交
407 408 409 410 411 412 413 414
		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 */
415
		if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) {
E
Eric Dumazet 已提交
416
			IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
417 418
					 IPSTATS_MIB_OUTDISCARDS);
			kfree_skb(skb);
L
Linus Torvalds 已提交
419 420
			return -1;
		}
421
		hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb);
L
Linus Torvalds 已提交
422 423
	}

424
	if (skb->ip_summed == CHECKSUM_COMPLETE)
L
Linus Torvalds 已提交
425 426 427 428 429 430 431 432
		skb->ip_summed = CHECKSUM_NONE;

	i = n - --hdr->segments_left;

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

433
	switch (hdr->type) {
434
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
435 436
	case IPV6_SRCRT_TYPE_2:
		if (xfrm6_input_addr(skb, (xfrm_address_t *)addr,
437
				     (xfrm_address_t *)&ipv6_hdr(skb)->saddr,
438
				     IPPROTO_ROUTING) < 0) {
E
Eric Dumazet 已提交
439
			IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
440
					 IPSTATS_MIB_INADDRERRORS);
441 442 443
			kfree_skb(skb);
			return -1;
		}
E
Eric Dumazet 已提交
444 445
		if (!ipv6_chk_home_addr(dev_net(skb_dst(skb)->dev), addr)) {
			IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
446
					 IPSTATS_MIB_INADDRERRORS);
447 448 449 450 451 452 453 454 455
			kfree_skb(skb);
			return -1;
		}
		break;
#endif
	default:
		break;
	}

L
Linus Torvalds 已提交
456
	if (ipv6_addr_is_multicast(addr)) {
E
Eric Dumazet 已提交
457
		IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
458
				 IPSTATS_MIB_INADDRERRORS);
L
Linus Torvalds 已提交
459 460 461 462 463
		kfree_skb(skb);
		return -1;
	}

	ipv6_addr_copy(&daddr, addr);
464 465
	ipv6_addr_copy(addr, &ipv6_hdr(skb)->daddr);
	ipv6_addr_copy(&ipv6_hdr(skb)->daddr, &daddr);
L
Linus Torvalds 已提交
466

E
Eric Dumazet 已提交
467
	skb_dst_drop(skb);
L
Linus Torvalds 已提交
468
	ip6_route_input(skb);
E
Eric Dumazet 已提交
469
	if (skb_dst(skb)->error) {
470
		skb_push(skb, skb->data - skb_network_header(skb));
L
Linus Torvalds 已提交
471 472 473 474
		dst_input(skb);
		return -1;
	}

E
Eric Dumazet 已提交
475
	if (skb_dst(skb)->dev->flags&IFF_LOOPBACK) {
476
		if (ipv6_hdr(skb)->hop_limit <= 1) {
E
Eric Dumazet 已提交
477
			IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
478
					 IPSTATS_MIB_INHDRERRORS);
L
Linus Torvalds 已提交
479
			icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
480
				    0);
L
Linus Torvalds 已提交
481 482 483
			kfree_skb(skb);
			return -1;
		}
484
		ipv6_hdr(skb)->hop_limit--;
L
Linus Torvalds 已提交
485 486 487
		goto looped_back;
	}

488
	skb_push(skb, skb->data - skb_network_header(skb));
L
Linus Torvalds 已提交
489 490
	dst_input(skb);
	return -1;
491 492

unknown_rh:
E
Eric Dumazet 已提交
493
	IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_INHDRERRORS);
494 495 496
	icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
			  (&hdr->type) - skb_network_header(skb));
	return -1;
L
Linus Torvalds 已提交
497 498
}

499
static const struct inet6_protocol rthdr_protocol = {
L
Linus Torvalds 已提交
500
	.handler	=	ipv6_rthdr_rcv,
H
Herbert Xu 已提交
501
	.flags		=	INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
L
Linus Torvalds 已提交
502 503
};

504
static const struct inet6_protocol destopt_protocol = {
505 506 507 508
	.handler	=	ipv6_destopt_rcv,
	.flags		=	INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
};

509
static const struct inet6_protocol nodata_protocol = {
510 511 512 513 514
	.handler	=	dst_discard,
	.flags		=	INET6_PROTO_NOPOLICY,
};

int __init ipv6_exthdrs_init(void)
L
Linus Torvalds 已提交
515
{
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
	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 已提交
537 538
};

539 540 541 542 543 544 545
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 已提交
546 547 548 549
/**********************************
  Hop-by-hop options.
 **********************************/

550
/*
E
Eric Dumazet 已提交
551
 * Note: we cannot rely on skb_dst(skb) before we assign it in ip6_route_input().
552 553 554
 */
static inline struct inet6_dev *ipv6_skb_idev(struct sk_buff *skb)
{
E
Eric Dumazet 已提交
555
	return skb_dst(skb) ? ip6_dst_idev(skb_dst(skb)) : __in6_dev_get(skb->dev);
556 557
}

558 559 560 561 562
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 已提交
563 564
/* Router Alert as of RFC 2711 */

565
static int ipv6_hop_ra(struct sk_buff *skb, int optoff)
L
Linus Torvalds 已提交
566
{
567
	const unsigned char *nh = skb_network_header(skb);
568

569
	if (nh[optoff + 1] == 2) {
L
Linus Torvalds 已提交
570 571 572
		IP6CB(skb)->ra = optoff;
		return 1;
	}
573
	LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n",
574
		       nh[optoff + 1]);
L
Linus Torvalds 已提交
575 576 577 578 579 580
	kfree_skb(skb);
	return 0;
}

/* Jumbo payload */

581
static int ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
L
Linus Torvalds 已提交
582
{
583
	const unsigned char *nh = skb_network_header(skb);
584
	struct net *net = ipv6_skb_net(skb);
L
Linus Torvalds 已提交
585 586
	u32 pkt_len;

587
	if (nh[optoff + 1] != 4 || (optoff & 3) != 2) {
588
		LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n",
589
			       nh[optoff+1]);
590
		IP6_INC_STATS_BH(net, ipv6_skb_idev(skb),
591
				 IPSTATS_MIB_INHDRERRORS);
L
Linus Torvalds 已提交
592 593 594
		goto drop;
	}

595
	pkt_len = ntohl(*(__be32 *)(nh + optoff + 2));
L
Linus Torvalds 已提交
596
	if (pkt_len <= IPV6_MAXPLEN) {
597 598
		IP6_INC_STATS_BH(net, ipv6_skb_idev(skb),
				 IPSTATS_MIB_INHDRERRORS);
L
Linus Torvalds 已提交
599 600 601
		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2);
		return 0;
	}
602
	if (ipv6_hdr(skb)->payload_len) {
603 604
		IP6_INC_STATS_BH(net, ipv6_skb_idev(skb),
				 IPSTATS_MIB_INHDRERRORS);
L
Linus Torvalds 已提交
605 606 607 608 609
		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
		return 0;
	}

	if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
610 611
		IP6_INC_STATS_BH(net, ipv6_skb_idev(skb),
				 IPSTATS_MIB_INTRUNCATEDPKTS);
L
Linus Torvalds 已提交
612 613
		goto drop;
	}
614 615 616 617

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

L
Linus Torvalds 已提交
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
	return 1;

drop:
	kfree_skb(skb);
	return 0;
}

static struct tlvtype_proc tlvprochopopt_lst[] = {
	{
		.type	= IPV6_TLV_ROUTERALERT,
		.func	= ipv6_hop_ra,
	},
	{
		.type	= IPV6_TLV_JUMBO,
		.func	= ipv6_hop_jumbo,
	},
	{ -1, }
};

637
int ipv6_parse_hopopts(struct sk_buff *skb)
L
Linus Torvalds 已提交
638
{
639 640
	struct inet6_skb_parm *opt = IP6CB(skb);

641
	/*
642
	 * skb_network_header(skb) is equal to skb->data, and
643
	 * skb_network_header_len(skb) is always equal to
644 645 646 647
	 * sizeof(struct ipv6hdr) by definition of
	 * hop-by-hop options.
	 */
	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + 8) ||
648 649
	    !pskb_may_pull(skb, (sizeof(struct ipv6hdr) +
				 ((skb_transport_header(skb)[1] + 1) << 3)))) {
650 651 652 653
		kfree_skb(skb);
		return -1;
	}

654
	opt->hop = sizeof(struct ipv6hdr);
655
	if (ip6_parse_tlv(tlvprochopopt_lst, skb)) {
656
		skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3;
657
		opt = IP6CB(skb);
658
		opt->nhoff = sizeof(struct ipv6hdr);
659
		return 1;
660
	}
L
Linus Torvalds 已提交
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681
	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;
682

L
Linus Torvalds 已提交
683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711
	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));

	ipv6_addr_copy(phdr->addr + (hops - 1), *addr_p);
	*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)
{
712
	if (opt->srcrt) {
L
Linus Torvalds 已提交
713
		ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
714 715 716 717 718 719 720
		/*
		 * 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 已提交
721 722 723 724
	if (opt->hopopt)
		ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
}

725 726
EXPORT_SYMBOL(ipv6_push_nfrag_opts);

L
Linus Torvalds 已提交
727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752
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) {
		long dif = (char*)opt2 - (char*)opt;
		memcpy(opt2, opt, opt->tot_len);
		if (opt2->hopopt)
			*((char**)&opt2->hopopt) += dif;
		if (opt2->dst0opt)
			*((char**)&opt2->dst0opt) += dif;
		if (opt2->dst1opt)
			*((char**)&opt2->dst1opt) += dif;
		if (opt2->srcrt)
			*((char**)&opt2->srcrt) += dif;
	}
	return opt2;
}
753

754 755
EXPORT_SYMBOL_GPL(ipv6_dup_options);

756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
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;

791 792 793 794 795 796 797 798 799 800 801
	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));
	}

802 803 804 805 806 807
	if (newopt && newoptlen)
		tot_len += CMSG_ALIGN(newoptlen);

	if (!tot_len)
		return NULL;

808
	tot_len += sizeof(*opt2);
809 810 811 812 813 814 815 816 817
	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);

818
	err = ipv6_renew_option(opt ? opt->hopopt : NULL, newopt, newoptlen,
819 820 821 822 823
				newtype != IPV6_HOPOPTS,
				&opt2->hopopt, &p);
	if (err)
		goto out;

824
	err = ipv6_renew_option(opt ? opt->dst0opt : NULL, newopt, newoptlen,
825 826 827 828 829
				newtype != IPV6_RTHDRDSTOPTS,
				&opt2->dst0opt, &p);
	if (err)
		goto out;

830
	err = ipv6_renew_option(opt ? opt->srcrt : NULL, newopt, newoptlen,
831
				newtype != IPV6_RTHDR,
832
				(struct ipv6_opt_hdr **)&opt2->srcrt, &p);
833 834 835
	if (err)
		goto out;

836
	err = ipv6_renew_option(opt ? opt->dst1opt : NULL, newopt, newoptlen,
837 838 839 840 841 842 843 844 845 846 847 848
				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:
849
	sock_kfree_s(sk, opt2, opt2->tot_len);
850 851 852
	return ERR_PTR(err);
}

853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871
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;
}

872 873 874 875
/**
 * fl6_update_dst - update flowi destination address with info given
 *                  by srcrt option, if any.
 *
876
 * @fl6: flowi6 for which daddr is to be updated
877
 * @opt: struct ipv6_txoptions in which to look for srcrt opt
878
 * @orig: copy of original daddr address if modified
879 880
 *
 * Returns NULL if no txoptions or no srcrt, otherwise returns orig
881
 * and initial value of fl6->daddr set in orig
882
 */
883
struct in6_addr *fl6_update_dst(struct flowi6 *fl6,
884 885 886 887 888 889
				const struct ipv6_txoptions *opt,
				struct in6_addr *orig)
{
	if (!opt || !opt->srcrt)
		return NULL;

890 891
	ipv6_addr_copy(orig, &fl6->daddr);
	ipv6_addr_copy(&fl6->daddr, ((struct rt0_hdr *)opt->srcrt)->addr);
892 893 894 895
	return orig;
}

EXPORT_SYMBOL_GPL(fl6_update_dst);