xref: /openbmc/linux/net/ipv6/exthdrs.c (revision 4e3fd7a06dc20b2d8ec6892233ad2012968fe7b6)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *	Extension Header handling for IPv6
31da177e4SLinus Torvalds  *	Linux INET6 implementation
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *	Authors:
61da177e4SLinus Torvalds  *	Pedro Roque		<roque@di.fc.ul.pt>
71da177e4SLinus Torvalds  *	Andi Kleen		<ak@muc.de>
81da177e4SLinus Torvalds  *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru>
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  *	This program is free software; you can redistribute it and/or
111da177e4SLinus Torvalds  *      modify it under the terms of the GNU General Public License
121da177e4SLinus Torvalds  *      as published by the Free Software Foundation; either version
131da177e4SLinus Torvalds  *      2 of the License, or (at your option) any later version.
141da177e4SLinus Torvalds  */
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds /* Changes:
171da177e4SLinus Torvalds  *	yoshfuji		: ensure not to overrun while parsing
181da177e4SLinus Torvalds  *				  tlv options.
191da177e4SLinus Torvalds  *	Mitsuru KANDA @USAGI and: Remove ipv6_parse_exthdrs().
201da177e4SLinus Torvalds  *	YOSHIFUJI Hideaki @USAGI  Register inbound extension header
211da177e4SLinus Torvalds  *				  handlers as inet6_protocol{}.
221da177e4SLinus Torvalds  */
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds #include <linux/errno.h>
251da177e4SLinus Torvalds #include <linux/types.h>
261da177e4SLinus Torvalds #include <linux/socket.h>
271da177e4SLinus Torvalds #include <linux/sockios.h>
281da177e4SLinus Torvalds #include <linux/net.h>
291da177e4SLinus Torvalds #include <linux/netdevice.h>
301da177e4SLinus Torvalds #include <linux/in6.h>
311da177e4SLinus Torvalds #include <linux/icmpv6.h>
325a0e3ad6STejun Heo #include <linux/slab.h>
33bc3b2d7fSPaul Gortmaker #include <linux/export.h>
341da177e4SLinus Torvalds 
35352e512cSHerbert Xu #include <net/dst.h>
361da177e4SLinus Torvalds #include <net/sock.h>
371da177e4SLinus Torvalds #include <net/snmp.h>
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds #include <net/ipv6.h>
401da177e4SLinus Torvalds #include <net/protocol.h>
411da177e4SLinus Torvalds #include <net/transp_v6.h>
421da177e4SLinus Torvalds #include <net/rawv6.h>
431da177e4SLinus Torvalds #include <net/ndisc.h>
441da177e4SLinus Torvalds #include <net/ip6_route.h>
451da177e4SLinus Torvalds #include <net/addrconf.h>
4659fbb3a6SMasahide NAKAMURA #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
4765d4ed92SMasahide NAKAMURA #include <net/xfrm.h>
4865d4ed92SMasahide NAKAMURA #endif
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds #include <asm/uaccess.h>
511da177e4SLinus Torvalds 
52c61a4043SMasahide NAKAMURA int ipv6_find_tlv(struct sk_buff *skb, int offset, int type)
53c61a4043SMasahide NAKAMURA {
54d56f90a7SArnaldo Carvalho de Melo 	const unsigned char *nh = skb_network_header(skb);
5527a884dcSArnaldo Carvalho de Melo 	int packet_len = skb->tail - skb->network_header;
56c61a4043SMasahide NAKAMURA 	struct ipv6_opt_hdr *hdr;
57c61a4043SMasahide NAKAMURA 	int len;
58c61a4043SMasahide NAKAMURA 
59c61a4043SMasahide NAKAMURA 	if (offset + 2 > packet_len)
60c61a4043SMasahide NAKAMURA 		goto bad;
61d56f90a7SArnaldo Carvalho de Melo 	hdr = (struct ipv6_opt_hdr *)(nh + offset);
62c61a4043SMasahide NAKAMURA 	len = ((hdr->hdrlen + 1) << 3);
63c61a4043SMasahide NAKAMURA 
64c61a4043SMasahide NAKAMURA 	if (offset + len > packet_len)
65c61a4043SMasahide NAKAMURA 		goto bad;
66c61a4043SMasahide NAKAMURA 
67c61a4043SMasahide NAKAMURA 	offset += 2;
68c61a4043SMasahide NAKAMURA 	len -= 2;
69c61a4043SMasahide NAKAMURA 
70c61a4043SMasahide NAKAMURA 	while (len > 0) {
71d56f90a7SArnaldo Carvalho de Melo 		int opttype = nh[offset];
72c61a4043SMasahide NAKAMURA 		int optlen;
73c61a4043SMasahide NAKAMURA 
74c61a4043SMasahide NAKAMURA 		if (opttype == type)
75c61a4043SMasahide NAKAMURA 			return offset;
76c61a4043SMasahide NAKAMURA 
77c61a4043SMasahide NAKAMURA 		switch (opttype) {
78c61a4043SMasahide NAKAMURA 		case IPV6_TLV_PAD0:
79c61a4043SMasahide NAKAMURA 			optlen = 1;
80c61a4043SMasahide NAKAMURA 			break;
81c61a4043SMasahide NAKAMURA 		default:
82d56f90a7SArnaldo Carvalho de Melo 			optlen = nh[offset + 1] + 2;
83c61a4043SMasahide NAKAMURA 			if (optlen > len)
84c61a4043SMasahide NAKAMURA 				goto bad;
85c61a4043SMasahide NAKAMURA 			break;
86c61a4043SMasahide NAKAMURA 		}
87c61a4043SMasahide NAKAMURA 		offset += optlen;
88c61a4043SMasahide NAKAMURA 		len -= optlen;
89c61a4043SMasahide NAKAMURA 	}
90c61a4043SMasahide NAKAMURA 	/* not_found */
91c61a4043SMasahide NAKAMURA  bad:
92c61a4043SMasahide NAKAMURA 	return -1;
93c61a4043SMasahide NAKAMURA }
9459fbb3a6SMasahide NAKAMURA EXPORT_SYMBOL_GPL(ipv6_find_tlv);
95c61a4043SMasahide NAKAMURA 
961da177e4SLinus Torvalds /*
971da177e4SLinus Torvalds  *	Parsing tlv encoded headers.
981da177e4SLinus Torvalds  *
991da177e4SLinus Torvalds  *	Parsing function "func" returns 1, if parsing succeed
1001da177e4SLinus Torvalds  *	and 0, if it failed.
1011da177e4SLinus Torvalds  *	It MUST NOT touch skb->h.
1021da177e4SLinus Torvalds  */
1031da177e4SLinus Torvalds 
1041da177e4SLinus Torvalds struct tlvtype_proc {
1051da177e4SLinus Torvalds 	int	type;
106e5bbef20SHerbert Xu 	int	(*func)(struct sk_buff *skb, int offset);
1071da177e4SLinus Torvalds };
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds /*********************
1101da177e4SLinus Torvalds   Generic functions
1111da177e4SLinus Torvalds  *********************/
1121da177e4SLinus Torvalds 
1131da177e4SLinus Torvalds /* An unknown option is detected, decide what to do */
1141da177e4SLinus Torvalds 
115e5bbef20SHerbert Xu static int ip6_tlvopt_unknown(struct sk_buff *skb, int optoff)
1161da177e4SLinus Torvalds {
117d56f90a7SArnaldo Carvalho de Melo 	switch ((skb_network_header(skb)[optoff] & 0xC0) >> 6) {
1181da177e4SLinus Torvalds 	case 0: /* ignore */
1191da177e4SLinus Torvalds 		return 1;
1201da177e4SLinus Torvalds 
1211da177e4SLinus Torvalds 	case 1: /* drop packet */
1221da177e4SLinus Torvalds 		break;
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds 	case 3: /* Send ICMP if not a multicast address and drop packet */
1251da177e4SLinus Torvalds 		/* Actually, it is redundant check. icmp_send
1261da177e4SLinus Torvalds 		   will recheck in any case.
1271da177e4SLinus Torvalds 		 */
1280660e03fSArnaldo Carvalho de Melo 		if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr))
1291da177e4SLinus Torvalds 			break;
1301da177e4SLinus Torvalds 	case 2: /* send ICMP PARM PROB regardless and drop packet */
1311da177e4SLinus Torvalds 		icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, optoff);
1321da177e4SLinus Torvalds 		return 0;
1333ff50b79SStephen Hemminger 	}
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds 	kfree_skb(skb);
1361da177e4SLinus Torvalds 	return 0;
1371da177e4SLinus Torvalds }
1381da177e4SLinus Torvalds 
1391da177e4SLinus Torvalds /* Parse tlv encoded option header (hop-by-hop or destination) */
1401da177e4SLinus Torvalds 
141e5bbef20SHerbert Xu static int ip6_parse_tlv(struct tlvtype_proc *procs, struct sk_buff *skb)
1421da177e4SLinus Torvalds {
1431da177e4SLinus Torvalds 	struct tlvtype_proc *curr;
144d56f90a7SArnaldo Carvalho de Melo 	const unsigned char *nh = skb_network_header(skb);
145cfe1fc77SArnaldo Carvalho de Melo 	int off = skb_network_header_len(skb);
1469c70220bSArnaldo Carvalho de Melo 	int len = (skb_transport_header(skb)[1] + 1) << 3;
1471da177e4SLinus Torvalds 
148ea2ae17dSArnaldo Carvalho de Melo 	if (skb_transport_offset(skb) + len > skb_headlen(skb))
1491da177e4SLinus Torvalds 		goto bad;
1501da177e4SLinus Torvalds 
1511da177e4SLinus Torvalds 	off += 2;
1521da177e4SLinus Torvalds 	len -= 2;
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds 	while (len > 0) {
155d56f90a7SArnaldo Carvalho de Melo 		int optlen = nh[off + 1] + 2;
1561da177e4SLinus Torvalds 
157d56f90a7SArnaldo Carvalho de Melo 		switch (nh[off]) {
1581da177e4SLinus Torvalds 		case IPV6_TLV_PAD0:
1591da177e4SLinus Torvalds 			optlen = 1;
1601da177e4SLinus Torvalds 			break;
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds 		case IPV6_TLV_PADN:
1631da177e4SLinus Torvalds 			break;
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds 		default: /* Other TLV code so scan list */
1661da177e4SLinus Torvalds 			if (optlen > len)
1671da177e4SLinus Torvalds 				goto bad;
1681da177e4SLinus Torvalds 			for (curr=procs; curr->type >= 0; curr++) {
169d56f90a7SArnaldo Carvalho de Melo 				if (curr->type == nh[off]) {
1701da177e4SLinus Torvalds 					/* type specific length/alignment
1711da177e4SLinus Torvalds 					   checks will be performed in the
1721da177e4SLinus Torvalds 					   func(). */
173e5bbef20SHerbert Xu 					if (curr->func(skb, off) == 0)
1741da177e4SLinus Torvalds 						return 0;
1751da177e4SLinus Torvalds 					break;
1761da177e4SLinus Torvalds 				}
1771da177e4SLinus Torvalds 			}
1781da177e4SLinus Torvalds 			if (curr->type < 0) {
179e5bbef20SHerbert Xu 				if (ip6_tlvopt_unknown(skb, off) == 0)
1801da177e4SLinus Torvalds 					return 0;
1811da177e4SLinus Torvalds 			}
1821da177e4SLinus Torvalds 			break;
1831da177e4SLinus Torvalds 		}
1841da177e4SLinus Torvalds 		off += optlen;
1851da177e4SLinus Torvalds 		len -= optlen;
1861da177e4SLinus Torvalds 	}
1871da177e4SLinus Torvalds 	if (len == 0)
1881da177e4SLinus Torvalds 		return 1;
1891da177e4SLinus Torvalds bad:
1901da177e4SLinus Torvalds 	kfree_skb(skb);
1911da177e4SLinus Torvalds 	return 0;
1921da177e4SLinus Torvalds }
1931da177e4SLinus Torvalds 
1941da177e4SLinus Torvalds /*****************************
1951da177e4SLinus Torvalds   Destination options header.
1961da177e4SLinus Torvalds  *****************************/
1971da177e4SLinus Torvalds 
19859fbb3a6SMasahide NAKAMURA #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
199e5bbef20SHerbert Xu static int ipv6_dest_hao(struct sk_buff *skb, int optoff)
200a831f5bbSMasahide NAKAMURA {
201a831f5bbSMasahide NAKAMURA 	struct ipv6_destopt_hao *hao;
202a831f5bbSMasahide NAKAMURA 	struct inet6_skb_parm *opt = IP6CB(skb);
2030660e03fSArnaldo Carvalho de Melo 	struct ipv6hdr *ipv6h = ipv6_hdr(skb);
204a831f5bbSMasahide NAKAMURA 	struct in6_addr tmp_addr;
205a831f5bbSMasahide NAKAMURA 	int ret;
206a831f5bbSMasahide NAKAMURA 
207a831f5bbSMasahide NAKAMURA 	if (opt->dsthao) {
208a831f5bbSMasahide NAKAMURA 		LIMIT_NETDEBUG(KERN_DEBUG "hao duplicated\n");
209a831f5bbSMasahide NAKAMURA 		goto discard;
210a831f5bbSMasahide NAKAMURA 	}
211a831f5bbSMasahide NAKAMURA 	opt->dsthao = opt->dst1;
212a831f5bbSMasahide NAKAMURA 	opt->dst1 = 0;
213a831f5bbSMasahide NAKAMURA 
214d56f90a7SArnaldo Carvalho de Melo 	hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + optoff);
215a831f5bbSMasahide NAKAMURA 
216a831f5bbSMasahide NAKAMURA 	if (hao->length != 16) {
217a831f5bbSMasahide NAKAMURA 		LIMIT_NETDEBUG(
218a831f5bbSMasahide NAKAMURA 			KERN_DEBUG "hao invalid option length = %d\n", hao->length);
219a831f5bbSMasahide NAKAMURA 		goto discard;
220a831f5bbSMasahide NAKAMURA 	}
221a831f5bbSMasahide NAKAMURA 
222a831f5bbSMasahide NAKAMURA 	if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) {
223a831f5bbSMasahide NAKAMURA 		LIMIT_NETDEBUG(
2245b095d98SHarvey Harrison 			KERN_DEBUG "hao is not an unicast addr: %pI6\n", &hao->addr);
225a831f5bbSMasahide NAKAMURA 		goto discard;
226a831f5bbSMasahide NAKAMURA 	}
227a831f5bbSMasahide NAKAMURA 
228a831f5bbSMasahide NAKAMURA 	ret = xfrm6_input_addr(skb, (xfrm_address_t *)&ipv6h->daddr,
229a831f5bbSMasahide NAKAMURA 			       (xfrm_address_t *)&hao->addr, IPPROTO_DSTOPTS);
230a831f5bbSMasahide NAKAMURA 	if (unlikely(ret < 0))
231a831f5bbSMasahide NAKAMURA 		goto discard;
232a831f5bbSMasahide NAKAMURA 
233a831f5bbSMasahide NAKAMURA 	if (skb_cloned(skb)) {
23465c88466SHerbert Xu 		if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
235a831f5bbSMasahide NAKAMURA 			goto discard;
236a831f5bbSMasahide NAKAMURA 
237a831f5bbSMasahide NAKAMURA 		/* update all variable using below by copied skbuff */
23865c88466SHerbert Xu 		hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) +
239d56f90a7SArnaldo Carvalho de Melo 						  optoff);
24065c88466SHerbert Xu 		ipv6h = ipv6_hdr(skb);
241a831f5bbSMasahide NAKAMURA 	}
242a831f5bbSMasahide NAKAMURA 
243a831f5bbSMasahide NAKAMURA 	if (skb->ip_summed == CHECKSUM_COMPLETE)
244a831f5bbSMasahide NAKAMURA 		skb->ip_summed = CHECKSUM_NONE;
245a831f5bbSMasahide NAKAMURA 
246*4e3fd7a0SAlexey Dobriyan 	tmp_addr = ipv6h->saddr;
247*4e3fd7a0SAlexey Dobriyan 	ipv6h->saddr = hao->addr;
248*4e3fd7a0SAlexey Dobriyan 	hao->addr = tmp_addr;
249a831f5bbSMasahide NAKAMURA 
250b7aa0bf7SEric Dumazet 	if (skb->tstamp.tv64 == 0)
251a831f5bbSMasahide NAKAMURA 		__net_timestamp(skb);
252a831f5bbSMasahide NAKAMURA 
253a831f5bbSMasahide NAKAMURA 	return 1;
254a831f5bbSMasahide NAKAMURA 
255a831f5bbSMasahide NAKAMURA  discard:
256a831f5bbSMasahide NAKAMURA 	kfree_skb(skb);
257a831f5bbSMasahide NAKAMURA 	return 0;
258a831f5bbSMasahide NAKAMURA }
259a831f5bbSMasahide NAKAMURA #endif
260a831f5bbSMasahide NAKAMURA 
2611da177e4SLinus Torvalds static struct tlvtype_proc tlvprocdestopt_lst[] = {
26259fbb3a6SMasahide NAKAMURA #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
263a831f5bbSMasahide NAKAMURA 	{
264a831f5bbSMasahide NAKAMURA 		.type	= IPV6_TLV_HAO,
265a831f5bbSMasahide NAKAMURA 		.func	= ipv6_dest_hao,
266a831f5bbSMasahide NAKAMURA 	},
267a831f5bbSMasahide NAKAMURA #endif
2681da177e4SLinus Torvalds 	{-1,			NULL}
2691da177e4SLinus Torvalds };
2701da177e4SLinus Torvalds 
271e5bbef20SHerbert Xu static int ipv6_destopt_rcv(struct sk_buff *skb)
2721da177e4SLinus Torvalds {
2731da177e4SLinus Torvalds 	struct inet6_skb_parm *opt = IP6CB(skb);
27459fbb3a6SMasahide NAKAMURA #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
275a831f5bbSMasahide NAKAMURA 	__u16 dstbuf;
276a831f5bbSMasahide NAKAMURA #endif
277897dc80bSEric Dumazet 	struct dst_entry *dst = skb_dst(skb);
2781da177e4SLinus Torvalds 
279ea2ae17dSArnaldo Carvalho de Melo 	if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) ||
280ea2ae17dSArnaldo Carvalho de Melo 	    !pskb_may_pull(skb, (skb_transport_offset(skb) +
2819c70220bSArnaldo Carvalho de Melo 				 ((skb_transport_header(skb)[1] + 1) << 3)))) {
282897dc80bSEric Dumazet 		IP6_INC_STATS_BH(dev_net(dst->dev), ip6_dst_idev(dst),
283a11d206dSYOSHIFUJI Hideaki 				 IPSTATS_MIB_INHDRERRORS);
2841da177e4SLinus Torvalds 		kfree_skb(skb);
2851da177e4SLinus Torvalds 		return -1;
2861da177e4SLinus Torvalds 	}
2871da177e4SLinus Torvalds 
288cfe1fc77SArnaldo Carvalho de Melo 	opt->lastopt = opt->dst1 = skb_network_header_len(skb);
28959fbb3a6SMasahide NAKAMURA #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
290a831f5bbSMasahide NAKAMURA 	dstbuf = opt->dst1;
291a831f5bbSMasahide NAKAMURA #endif
2921da177e4SLinus Torvalds 
293e5bbef20SHerbert Xu 	if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) {
294b0e380b1SArnaldo Carvalho de Melo 		skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3;
295dc435e6dSMasahide NAKAMURA 		opt = IP6CB(skb);
29659fbb3a6SMasahide NAKAMURA #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
297a831f5bbSMasahide NAKAMURA 		opt->nhoff = dstbuf;
298a831f5bbSMasahide NAKAMURA #else
299951dbc8aSPatrick McHardy 		opt->nhoff = opt->dst1;
300a831f5bbSMasahide NAKAMURA #endif
3011da177e4SLinus Torvalds 		return 1;
3021da177e4SLinus Torvalds 	}
3031da177e4SLinus Torvalds 
304483a47d2SDenis V. Lunev 	IP6_INC_STATS_BH(dev_net(dst->dev),
305483a47d2SDenis V. Lunev 			 ip6_dst_idev(dst), IPSTATS_MIB_INHDRERRORS);
3061da177e4SLinus Torvalds 	return -1;
3071da177e4SLinus Torvalds }
3081da177e4SLinus Torvalds 
3091da177e4SLinus Torvalds /********************************
3101da177e4SLinus Torvalds   Routing header.
3111da177e4SLinus Torvalds  ********************************/
3121da177e4SLinus Torvalds 
313f6bc7d9eSEric Dumazet /* called with rcu_read_lock() */
314e5bbef20SHerbert Xu static int ipv6_rthdr_rcv(struct sk_buff *skb)
3151da177e4SLinus Torvalds {
3161da177e4SLinus Torvalds 	struct inet6_skb_parm *opt = IP6CB(skb);
31765d4ed92SMasahide NAKAMURA 	struct in6_addr *addr = NULL;
3181da177e4SLinus Torvalds 	struct in6_addr daddr;
3190bcbc926SYOSHIFUJI Hideaki 	struct inet6_dev *idev;
3201da177e4SLinus Torvalds 	int n, i;
3211da177e4SLinus Torvalds 	struct ipv6_rt_hdr *hdr;
3221da177e4SLinus Torvalds 	struct rt0_hdr *rthdr;
323483a47d2SDenis V. Lunev 	struct net *net = dev_net(skb->dev);
324483a47d2SDenis V. Lunev 	int accept_source_route = net->ipv6.devconf_all->accept_source_route;
3250bcbc926SYOSHIFUJI Hideaki 
326f6bc7d9eSEric Dumazet 	idev = __in6_dev_get(skb->dev);
327f6bc7d9eSEric Dumazet 	if (idev && accept_source_route > idev->cnf.accept_source_route)
3280bcbc926SYOSHIFUJI Hideaki 		accept_source_route = idev->cnf.accept_source_route;
3291da177e4SLinus Torvalds 
330ea2ae17dSArnaldo Carvalho de Melo 	if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) ||
331ea2ae17dSArnaldo Carvalho de Melo 	    !pskb_may_pull(skb, (skb_transport_offset(skb) +
3329c70220bSArnaldo Carvalho de Melo 				 ((skb_transport_header(skb)[1] + 1) << 3)))) {
333adf30907SEric Dumazet 		IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
334a11d206dSYOSHIFUJI Hideaki 				 IPSTATS_MIB_INHDRERRORS);
3351da177e4SLinus Torvalds 		kfree_skb(skb);
3361da177e4SLinus Torvalds 		return -1;
3371da177e4SLinus Torvalds 	}
3381da177e4SLinus Torvalds 
3399c70220bSArnaldo Carvalho de Melo 	hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb);
3401da177e4SLinus Torvalds 
3410660e03fSArnaldo Carvalho de Melo 	if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) ||
3421da177e4SLinus Torvalds 	    skb->pkt_type != PACKET_HOST) {
343adf30907SEric Dumazet 		IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
344a11d206dSYOSHIFUJI Hideaki 				 IPSTATS_MIB_INADDRERRORS);
3451da177e4SLinus Torvalds 		kfree_skb(skb);
3461da177e4SLinus Torvalds 		return -1;
3471da177e4SLinus Torvalds 	}
3481da177e4SLinus Torvalds 
3491da177e4SLinus Torvalds looped_back:
3501da177e4SLinus Torvalds 	if (hdr->segments_left == 0) {
35165d4ed92SMasahide NAKAMURA 		switch (hdr->type) {
35259fbb3a6SMasahide NAKAMURA #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
35365d4ed92SMasahide NAKAMURA 		case IPV6_SRCRT_TYPE_2:
35465d4ed92SMasahide NAKAMURA 			/* Silently discard type 2 header unless it was
35565d4ed92SMasahide NAKAMURA 			 * processed by own
35665d4ed92SMasahide NAKAMURA 			 */
35765d4ed92SMasahide NAKAMURA 			if (!addr) {
358adf30907SEric Dumazet 				IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
359a11d206dSYOSHIFUJI Hideaki 						 IPSTATS_MIB_INADDRERRORS);
36065d4ed92SMasahide NAKAMURA 				kfree_skb(skb);
36165d4ed92SMasahide NAKAMURA 				return -1;
36265d4ed92SMasahide NAKAMURA 			}
36365d4ed92SMasahide NAKAMURA 			break;
36465d4ed92SMasahide NAKAMURA #endif
36565d4ed92SMasahide NAKAMURA 		default:
36665d4ed92SMasahide NAKAMURA 			break;
36765d4ed92SMasahide NAKAMURA 		}
36865d4ed92SMasahide NAKAMURA 
369cfe1fc77SArnaldo Carvalho de Melo 		opt->lastopt = opt->srcrt = skb_network_header_len(skb);
370b0e380b1SArnaldo Carvalho de Melo 		skb->transport_header += (hdr->hdrlen + 1) << 3;
3711da177e4SLinus Torvalds 		opt->dst0 = opt->dst1;
3721da177e4SLinus Torvalds 		opt->dst1 = 0;
373d56f90a7SArnaldo Carvalho de Melo 		opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb);
3741da177e4SLinus Torvalds 		return 1;
3751da177e4SLinus Torvalds 	}
3761da177e4SLinus Torvalds 
37765d4ed92SMasahide NAKAMURA 	switch (hdr->type) {
37859fbb3a6SMasahide NAKAMURA #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
37965d4ed92SMasahide NAKAMURA 	case IPV6_SRCRT_TYPE_2:
380c382bb9dSYOSHIFUJI Hideaki 		if (accept_source_route < 0)
381c382bb9dSYOSHIFUJI Hideaki 			goto unknown_rh;
38265d4ed92SMasahide NAKAMURA 		/* Silently discard invalid RTH type 2 */
38365d4ed92SMasahide NAKAMURA 		if (hdr->hdrlen != 2 || hdr->segments_left != 1) {
384adf30907SEric Dumazet 			IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
385a11d206dSYOSHIFUJI Hideaki 					 IPSTATS_MIB_INHDRERRORS);
38665d4ed92SMasahide NAKAMURA 			kfree_skb(skb);
38765d4ed92SMasahide NAKAMURA 			return -1;
38865d4ed92SMasahide NAKAMURA 		}
38965d4ed92SMasahide NAKAMURA 		break;
39065d4ed92SMasahide NAKAMURA #endif
391c382bb9dSYOSHIFUJI Hideaki 	default:
392c382bb9dSYOSHIFUJI Hideaki 		goto unknown_rh;
39365d4ed92SMasahide NAKAMURA 	}
3941da177e4SLinus Torvalds 
3951da177e4SLinus Torvalds 	/*
3961da177e4SLinus Torvalds 	 *	This is the routing header forwarding algorithm from
3971da177e4SLinus Torvalds 	 *	RFC 2460, page 16.
3981da177e4SLinus Torvalds 	 */
3991da177e4SLinus Torvalds 
4001da177e4SLinus Torvalds 	n = hdr->hdrlen >> 1;
4011da177e4SLinus Torvalds 
4021da177e4SLinus Torvalds 	if (hdr->segments_left > n) {
403adf30907SEric Dumazet 		IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
404a11d206dSYOSHIFUJI Hideaki 				 IPSTATS_MIB_INHDRERRORS);
405d56f90a7SArnaldo Carvalho de Melo 		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
406d56f90a7SArnaldo Carvalho de Melo 				  ((&hdr->segments_left) -
407d56f90a7SArnaldo Carvalho de Melo 				   skb_network_header(skb)));
4081da177e4SLinus Torvalds 		return -1;
4091da177e4SLinus Torvalds 	}
4101da177e4SLinus Torvalds 
4111da177e4SLinus Torvalds 	/* We are about to mangle packet header. Be careful!
4121da177e4SLinus Torvalds 	   Do not damage packets queued somewhere.
4131da177e4SLinus Torvalds 	 */
4141da177e4SLinus Torvalds 	if (skb_cloned(skb)) {
4151da177e4SLinus Torvalds 		/* the copy is a forwarded packet */
41665c88466SHerbert Xu 		if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) {
417adf30907SEric Dumazet 			IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
418a11d206dSYOSHIFUJI Hideaki 					 IPSTATS_MIB_OUTDISCARDS);
419a11d206dSYOSHIFUJI Hideaki 			kfree_skb(skb);
4201da177e4SLinus Torvalds 			return -1;
4211da177e4SLinus Torvalds 		}
42265c88466SHerbert Xu 		hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb);
4231da177e4SLinus Torvalds 	}
4241da177e4SLinus Torvalds 
42584fa7933SPatrick McHardy 	if (skb->ip_summed == CHECKSUM_COMPLETE)
4261da177e4SLinus Torvalds 		skb->ip_summed = CHECKSUM_NONE;
4271da177e4SLinus Torvalds 
4281da177e4SLinus Torvalds 	i = n - --hdr->segments_left;
4291da177e4SLinus Torvalds 
4301da177e4SLinus Torvalds 	rthdr = (struct rt0_hdr *) hdr;
4311da177e4SLinus Torvalds 	addr = rthdr->addr;
4321da177e4SLinus Torvalds 	addr += i - 1;
4331da177e4SLinus Torvalds 
43465d4ed92SMasahide NAKAMURA 	switch (hdr->type) {
43559fbb3a6SMasahide NAKAMURA #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
43665d4ed92SMasahide NAKAMURA 	case IPV6_SRCRT_TYPE_2:
43765d4ed92SMasahide NAKAMURA 		if (xfrm6_input_addr(skb, (xfrm_address_t *)addr,
4380660e03fSArnaldo Carvalho de Melo 				     (xfrm_address_t *)&ipv6_hdr(skb)->saddr,
43965d4ed92SMasahide NAKAMURA 				     IPPROTO_ROUTING) < 0) {
440adf30907SEric Dumazet 			IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
441a11d206dSYOSHIFUJI Hideaki 					 IPSTATS_MIB_INADDRERRORS);
44265d4ed92SMasahide NAKAMURA 			kfree_skb(skb);
44365d4ed92SMasahide NAKAMURA 			return -1;
44465d4ed92SMasahide NAKAMURA 		}
445adf30907SEric Dumazet 		if (!ipv6_chk_home_addr(dev_net(skb_dst(skb)->dev), addr)) {
446adf30907SEric Dumazet 			IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
447a11d206dSYOSHIFUJI Hideaki 					 IPSTATS_MIB_INADDRERRORS);
44865d4ed92SMasahide NAKAMURA 			kfree_skb(skb);
44965d4ed92SMasahide NAKAMURA 			return -1;
45065d4ed92SMasahide NAKAMURA 		}
45165d4ed92SMasahide NAKAMURA 		break;
45265d4ed92SMasahide NAKAMURA #endif
45365d4ed92SMasahide NAKAMURA 	default:
45465d4ed92SMasahide NAKAMURA 		break;
45565d4ed92SMasahide NAKAMURA 	}
45665d4ed92SMasahide NAKAMURA 
4571da177e4SLinus Torvalds 	if (ipv6_addr_is_multicast(addr)) {
458adf30907SEric Dumazet 		IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
459a11d206dSYOSHIFUJI Hideaki 				 IPSTATS_MIB_INADDRERRORS);
4601da177e4SLinus Torvalds 		kfree_skb(skb);
4611da177e4SLinus Torvalds 		return -1;
4621da177e4SLinus Torvalds 	}
4631da177e4SLinus Torvalds 
464*4e3fd7a0SAlexey Dobriyan 	daddr = *addr;
465*4e3fd7a0SAlexey Dobriyan 	*addr = ipv6_hdr(skb)->daddr;
466*4e3fd7a0SAlexey Dobriyan 	ipv6_hdr(skb)->daddr = daddr;
4671da177e4SLinus Torvalds 
468adf30907SEric Dumazet 	skb_dst_drop(skb);
4691da177e4SLinus Torvalds 	ip6_route_input(skb);
470adf30907SEric Dumazet 	if (skb_dst(skb)->error) {
471d56f90a7SArnaldo Carvalho de Melo 		skb_push(skb, skb->data - skb_network_header(skb));
4721da177e4SLinus Torvalds 		dst_input(skb);
4731da177e4SLinus Torvalds 		return -1;
4741da177e4SLinus Torvalds 	}
4751da177e4SLinus Torvalds 
476adf30907SEric Dumazet 	if (skb_dst(skb)->dev->flags&IFF_LOOPBACK) {
4770660e03fSArnaldo Carvalho de Melo 		if (ipv6_hdr(skb)->hop_limit <= 1) {
478adf30907SEric Dumazet 			IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
479a11d206dSYOSHIFUJI Hideaki 					 IPSTATS_MIB_INHDRERRORS);
4801da177e4SLinus Torvalds 			icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
4813ffe533cSAlexey Dobriyan 				    0);
4821da177e4SLinus Torvalds 			kfree_skb(skb);
4831da177e4SLinus Torvalds 			return -1;
4841da177e4SLinus Torvalds 		}
4850660e03fSArnaldo Carvalho de Melo 		ipv6_hdr(skb)->hop_limit--;
4861da177e4SLinus Torvalds 		goto looped_back;
4871da177e4SLinus Torvalds 	}
4881da177e4SLinus Torvalds 
489d56f90a7SArnaldo Carvalho de Melo 	skb_push(skb, skb->data - skb_network_header(skb));
4901da177e4SLinus Torvalds 	dst_input(skb);
4911da177e4SLinus Torvalds 	return -1;
492c382bb9dSYOSHIFUJI Hideaki 
493c382bb9dSYOSHIFUJI Hideaki unknown_rh:
494adf30907SEric Dumazet 	IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_INHDRERRORS);
495c382bb9dSYOSHIFUJI Hideaki 	icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
496c382bb9dSYOSHIFUJI Hideaki 			  (&hdr->type) - skb_network_header(skb));
497c382bb9dSYOSHIFUJI Hideaki 	return -1;
4981da177e4SLinus Torvalds }
4991da177e4SLinus Torvalds 
50041135cc8SAlexey Dobriyan static const struct inet6_protocol rthdr_protocol = {
5011da177e4SLinus Torvalds 	.handler	=	ipv6_rthdr_rcv,
502adcfc7d0SHerbert Xu 	.flags		=	INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
5031da177e4SLinus Torvalds };
5041da177e4SLinus Torvalds 
50541135cc8SAlexey Dobriyan static const struct inet6_protocol destopt_protocol = {
506248b238dSDaniel Lezcano 	.handler	=	ipv6_destopt_rcv,
507248b238dSDaniel Lezcano 	.flags		=	INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
5081da177e4SLinus Torvalds };
5091da177e4SLinus Torvalds 
51041135cc8SAlexey Dobriyan static const struct inet6_protocol nodata_protocol = {
511248b238dSDaniel Lezcano 	.handler	=	dst_discard,
512248b238dSDaniel Lezcano 	.flags		=	INET6_PROTO_NOPOLICY,
513248b238dSDaniel Lezcano };
514248b238dSDaniel Lezcano 
515248b238dSDaniel Lezcano int __init ipv6_exthdrs_init(void)
516248b238dSDaniel Lezcano {
517248b238dSDaniel Lezcano 	int ret;
518248b238dSDaniel Lezcano 
519248b238dSDaniel Lezcano 	ret = inet6_add_protocol(&rthdr_protocol, IPPROTO_ROUTING);
520248b238dSDaniel Lezcano 	if (ret)
521248b238dSDaniel Lezcano 		goto out;
522248b238dSDaniel Lezcano 
523248b238dSDaniel Lezcano 	ret = inet6_add_protocol(&destopt_protocol, IPPROTO_DSTOPTS);
524248b238dSDaniel Lezcano 	if (ret)
525248b238dSDaniel Lezcano 		goto out_rthdr;
526248b238dSDaniel Lezcano 
527248b238dSDaniel Lezcano 	ret = inet6_add_protocol(&nodata_protocol, IPPROTO_NONE);
528248b238dSDaniel Lezcano 	if (ret)
529248b238dSDaniel Lezcano 		goto out_destopt;
530248b238dSDaniel Lezcano 
531248b238dSDaniel Lezcano out:
532248b238dSDaniel Lezcano 	return ret;
533248b238dSDaniel Lezcano out_rthdr:
534248b238dSDaniel Lezcano 	inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING);
535248b238dSDaniel Lezcano out_destopt:
536248b238dSDaniel Lezcano 	inet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS);
537248b238dSDaniel Lezcano 	goto out;
538248b238dSDaniel Lezcano };
539248b238dSDaniel Lezcano 
540248b238dSDaniel Lezcano void ipv6_exthdrs_exit(void)
541248b238dSDaniel Lezcano {
542248b238dSDaniel Lezcano 	inet6_del_protocol(&nodata_protocol, IPPROTO_NONE);
543248b238dSDaniel Lezcano 	inet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS);
544248b238dSDaniel Lezcano 	inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING);
545248b238dSDaniel Lezcano }
546248b238dSDaniel Lezcano 
5471da177e4SLinus Torvalds /**********************************
5481da177e4SLinus Torvalds   Hop-by-hop options.
5491da177e4SLinus Torvalds  **********************************/
5501da177e4SLinus Torvalds 
551e76b2b25SYOSHIFUJI Hideaki /*
552adf30907SEric Dumazet  * Note: we cannot rely on skb_dst(skb) before we assign it in ip6_route_input().
553e76b2b25SYOSHIFUJI Hideaki  */
554e76b2b25SYOSHIFUJI Hideaki static inline struct inet6_dev *ipv6_skb_idev(struct sk_buff *skb)
555e76b2b25SYOSHIFUJI Hideaki {
556adf30907SEric Dumazet 	return skb_dst(skb) ? ip6_dst_idev(skb_dst(skb)) : __in6_dev_get(skb->dev);
557e76b2b25SYOSHIFUJI Hideaki }
558e76b2b25SYOSHIFUJI Hideaki 
5592570a4f5SDavid S. Miller static inline struct net *ipv6_skb_net(struct sk_buff *skb)
5602570a4f5SDavid S. Miller {
5612570a4f5SDavid S. Miller 	return skb_dst(skb) ? dev_net(skb_dst(skb)->dev) : dev_net(skb->dev);
5622570a4f5SDavid S. Miller }
5632570a4f5SDavid S. Miller 
5641da177e4SLinus Torvalds /* Router Alert as of RFC 2711 */
5651da177e4SLinus Torvalds 
566e5bbef20SHerbert Xu static int ipv6_hop_ra(struct sk_buff *skb, int optoff)
5671da177e4SLinus Torvalds {
568d56f90a7SArnaldo Carvalho de Melo 	const unsigned char *nh = skb_network_header(skb);
569a80ff03eSMasahide NAKAMURA 
570d56f90a7SArnaldo Carvalho de Melo 	if (nh[optoff + 1] == 2) {
5711da177e4SLinus Torvalds 		IP6CB(skb)->ra = optoff;
5721da177e4SLinus Torvalds 		return 1;
5731da177e4SLinus Torvalds 	}
57464ce2073SPatrick McHardy 	LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n",
575d56f90a7SArnaldo Carvalho de Melo 		       nh[optoff + 1]);
5761da177e4SLinus Torvalds 	kfree_skb(skb);
5771da177e4SLinus Torvalds 	return 0;
5781da177e4SLinus Torvalds }
5791da177e4SLinus Torvalds 
5801da177e4SLinus Torvalds /* Jumbo payload */
5811da177e4SLinus Torvalds 
582e5bbef20SHerbert Xu static int ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
5831da177e4SLinus Torvalds {
584d56f90a7SArnaldo Carvalho de Melo 	const unsigned char *nh = skb_network_header(skb);
5852570a4f5SDavid S. Miller 	struct net *net = ipv6_skb_net(skb);
5861da177e4SLinus Torvalds 	u32 pkt_len;
5871da177e4SLinus Torvalds 
588d56f90a7SArnaldo Carvalho de Melo 	if (nh[optoff + 1] != 4 || (optoff & 3) != 2) {
58964ce2073SPatrick McHardy 		LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n",
590d56f90a7SArnaldo Carvalho de Melo 			       nh[optoff+1]);
591483a47d2SDenis V. Lunev 		IP6_INC_STATS_BH(net, ipv6_skb_idev(skb),
592a11d206dSYOSHIFUJI Hideaki 				 IPSTATS_MIB_INHDRERRORS);
5931da177e4SLinus Torvalds 		goto drop;
5941da177e4SLinus Torvalds 	}
5951da177e4SLinus Torvalds 
596d56f90a7SArnaldo Carvalho de Melo 	pkt_len = ntohl(*(__be32 *)(nh + optoff + 2));
5971da177e4SLinus Torvalds 	if (pkt_len <= IPV6_MAXPLEN) {
598483a47d2SDenis V. Lunev 		IP6_INC_STATS_BH(net, ipv6_skb_idev(skb),
599483a47d2SDenis V. Lunev 				 IPSTATS_MIB_INHDRERRORS);
6001da177e4SLinus Torvalds 		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2);
6011da177e4SLinus Torvalds 		return 0;
6021da177e4SLinus Torvalds 	}
6030660e03fSArnaldo Carvalho de Melo 	if (ipv6_hdr(skb)->payload_len) {
604483a47d2SDenis V. Lunev 		IP6_INC_STATS_BH(net, ipv6_skb_idev(skb),
605483a47d2SDenis V. Lunev 				 IPSTATS_MIB_INHDRERRORS);
6061da177e4SLinus Torvalds 		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
6071da177e4SLinus Torvalds 		return 0;
6081da177e4SLinus Torvalds 	}
6091da177e4SLinus Torvalds 
6101da177e4SLinus Torvalds 	if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
611483a47d2SDenis V. Lunev 		IP6_INC_STATS_BH(net, ipv6_skb_idev(skb),
612483a47d2SDenis V. Lunev 				 IPSTATS_MIB_INTRUNCATEDPKTS);
6131da177e4SLinus Torvalds 		goto drop;
6141da177e4SLinus Torvalds 	}
61542ca89c1SStephen Hemminger 
61642ca89c1SStephen Hemminger 	if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
61742ca89c1SStephen Hemminger 		goto drop;
61842ca89c1SStephen Hemminger 
6191da177e4SLinus Torvalds 	return 1;
6201da177e4SLinus Torvalds 
6211da177e4SLinus Torvalds drop:
6221da177e4SLinus Torvalds 	kfree_skb(skb);
6231da177e4SLinus Torvalds 	return 0;
6241da177e4SLinus Torvalds }
6251da177e4SLinus Torvalds 
6261da177e4SLinus Torvalds static struct tlvtype_proc tlvprochopopt_lst[] = {
6271da177e4SLinus Torvalds 	{
6281da177e4SLinus Torvalds 		.type	= IPV6_TLV_ROUTERALERT,
6291da177e4SLinus Torvalds 		.func	= ipv6_hop_ra,
6301da177e4SLinus Torvalds 	},
6311da177e4SLinus Torvalds 	{
6321da177e4SLinus Torvalds 		.type	= IPV6_TLV_JUMBO,
6331da177e4SLinus Torvalds 		.func	= ipv6_hop_jumbo,
6341da177e4SLinus Torvalds 	},
6351da177e4SLinus Torvalds 	{ -1, }
6361da177e4SLinus Torvalds };
6371da177e4SLinus Torvalds 
638e5bbef20SHerbert Xu int ipv6_parse_hopopts(struct sk_buff *skb)
6391da177e4SLinus Torvalds {
640951dbc8aSPatrick McHardy 	struct inet6_skb_parm *opt = IP6CB(skb);
641951dbc8aSPatrick McHardy 
642ec670095SYOSHIFUJI Hideaki 	/*
643d56f90a7SArnaldo Carvalho de Melo 	 * skb_network_header(skb) is equal to skb->data, and
644cfe1fc77SArnaldo Carvalho de Melo 	 * skb_network_header_len(skb) is always equal to
645ec670095SYOSHIFUJI Hideaki 	 * sizeof(struct ipv6hdr) by definition of
646ec670095SYOSHIFUJI Hideaki 	 * hop-by-hop options.
647ec670095SYOSHIFUJI Hideaki 	 */
648ec670095SYOSHIFUJI Hideaki 	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + 8) ||
6499c70220bSArnaldo Carvalho de Melo 	    !pskb_may_pull(skb, (sizeof(struct ipv6hdr) +
6509c70220bSArnaldo Carvalho de Melo 				 ((skb_transport_header(skb)[1] + 1) << 3)))) {
651ec670095SYOSHIFUJI Hideaki 		kfree_skb(skb);
652ec670095SYOSHIFUJI Hideaki 		return -1;
653ec670095SYOSHIFUJI Hideaki 	}
654ec670095SYOSHIFUJI Hideaki 
655951dbc8aSPatrick McHardy 	opt->hop = sizeof(struct ipv6hdr);
656e5bbef20SHerbert Xu 	if (ip6_parse_tlv(tlvprochopopt_lst, skb)) {
657b0e380b1SArnaldo Carvalho de Melo 		skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3;
658dc435e6dSMasahide NAKAMURA 		opt = IP6CB(skb);
659951dbc8aSPatrick McHardy 		opt->nhoff = sizeof(struct ipv6hdr);
660b809739aSYOSHIFUJI Hideaki 		return 1;
661951dbc8aSPatrick McHardy 	}
6621da177e4SLinus Torvalds 	return -1;
6631da177e4SLinus Torvalds }
6641da177e4SLinus Torvalds 
6651da177e4SLinus Torvalds /*
6661da177e4SLinus Torvalds  *	Creating outbound headers.
6671da177e4SLinus Torvalds  *
6681da177e4SLinus Torvalds  *	"build" functions work when skb is filled from head to tail (datagram)
6691da177e4SLinus Torvalds  *	"push"	functions work when headers are added from tail to head (tcp)
6701da177e4SLinus Torvalds  *
6711da177e4SLinus Torvalds  *	In both cases we assume, that caller reserved enough room
6721da177e4SLinus Torvalds  *	for headers.
6731da177e4SLinus Torvalds  */
6741da177e4SLinus Torvalds 
6751da177e4SLinus Torvalds static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
6761da177e4SLinus Torvalds 			    struct ipv6_rt_hdr *opt,
6771da177e4SLinus Torvalds 			    struct in6_addr **addr_p)
6781da177e4SLinus Torvalds {
6791da177e4SLinus Torvalds 	struct rt0_hdr *phdr, *ihdr;
6801da177e4SLinus Torvalds 	int hops;
6811da177e4SLinus Torvalds 
6821da177e4SLinus Torvalds 	ihdr = (struct rt0_hdr *) opt;
6831da177e4SLinus Torvalds 
6841da177e4SLinus Torvalds 	phdr = (struct rt0_hdr *) skb_push(skb, (ihdr->rt_hdr.hdrlen + 1) << 3);
6851da177e4SLinus Torvalds 	memcpy(phdr, ihdr, sizeof(struct rt0_hdr));
6861da177e4SLinus Torvalds 
6871da177e4SLinus Torvalds 	hops = ihdr->rt_hdr.hdrlen >> 1;
6881da177e4SLinus Torvalds 
6891da177e4SLinus Torvalds 	if (hops > 1)
6901da177e4SLinus Torvalds 		memcpy(phdr->addr, ihdr->addr + 1,
6911da177e4SLinus Torvalds 		       (hops - 1) * sizeof(struct in6_addr));
6921da177e4SLinus Torvalds 
693*4e3fd7a0SAlexey Dobriyan 	phdr->addr[hops - 1] = **addr_p;
6941da177e4SLinus Torvalds 	*addr_p = ihdr->addr;
6951da177e4SLinus Torvalds 
6961da177e4SLinus Torvalds 	phdr->rt_hdr.nexthdr = *proto;
6971da177e4SLinus Torvalds 	*proto = NEXTHDR_ROUTING;
6981da177e4SLinus Torvalds }
6991da177e4SLinus Torvalds 
7001da177e4SLinus Torvalds static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt)
7011da177e4SLinus Torvalds {
7021da177e4SLinus Torvalds 	struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ipv6_optlen(opt));
7031da177e4SLinus Torvalds 
7041da177e4SLinus Torvalds 	memcpy(h, opt, ipv6_optlen(opt));
7051da177e4SLinus Torvalds 	h->nexthdr = *proto;
7061da177e4SLinus Torvalds 	*proto = type;
7071da177e4SLinus Torvalds }
7081da177e4SLinus Torvalds 
7091da177e4SLinus Torvalds void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
7101da177e4SLinus Torvalds 			  u8 *proto,
7111da177e4SLinus Torvalds 			  struct in6_addr **daddr)
7121da177e4SLinus Torvalds {
713333fad53SYOSHIFUJI Hideaki 	if (opt->srcrt) {
7141da177e4SLinus Torvalds 		ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
715333fad53SYOSHIFUJI Hideaki 		/*
716333fad53SYOSHIFUJI Hideaki 		 * IPV6_RTHDRDSTOPTS is ignored
717333fad53SYOSHIFUJI Hideaki 		 * unless IPV6_RTHDR is set (RFC3542).
718333fad53SYOSHIFUJI Hideaki 		 */
7191da177e4SLinus Torvalds 		if (opt->dst0opt)
7201da177e4SLinus Torvalds 			ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
721333fad53SYOSHIFUJI Hideaki 	}
7221da177e4SLinus Torvalds 	if (opt->hopopt)
7231da177e4SLinus Torvalds 		ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
7241da177e4SLinus Torvalds }
7251da177e4SLinus Torvalds 
7267159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ipv6_push_nfrag_opts);
7277159039aSYOSHIFUJI Hideaki 
7281da177e4SLinus Torvalds void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto)
7291da177e4SLinus Torvalds {
7301da177e4SLinus Torvalds 	if (opt->dst1opt)
7311da177e4SLinus Torvalds 		ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt);
7321da177e4SLinus Torvalds }
7331da177e4SLinus Torvalds 
7341da177e4SLinus Torvalds struct ipv6_txoptions *
7351da177e4SLinus Torvalds ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
7361da177e4SLinus Torvalds {
7371da177e4SLinus Torvalds 	struct ipv6_txoptions *opt2;
7381da177e4SLinus Torvalds 
7391da177e4SLinus Torvalds 	opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC);
7401da177e4SLinus Torvalds 	if (opt2) {
7411da177e4SLinus Torvalds 		long dif = (char*)opt2 - (char*)opt;
7421da177e4SLinus Torvalds 		memcpy(opt2, opt, opt->tot_len);
7431da177e4SLinus Torvalds 		if (opt2->hopopt)
7441da177e4SLinus Torvalds 			*((char**)&opt2->hopopt) += dif;
7451da177e4SLinus Torvalds 		if (opt2->dst0opt)
7461da177e4SLinus Torvalds 			*((char**)&opt2->dst0opt) += dif;
7471da177e4SLinus Torvalds 		if (opt2->dst1opt)
7481da177e4SLinus Torvalds 			*((char**)&opt2->dst1opt) += dif;
7491da177e4SLinus Torvalds 		if (opt2->srcrt)
7501da177e4SLinus Torvalds 			*((char**)&opt2->srcrt) += dif;
7511da177e4SLinus Torvalds 	}
7521da177e4SLinus Torvalds 	return opt2;
7531da177e4SLinus Torvalds }
754333fad53SYOSHIFUJI Hideaki 
7553cf3dc6cSArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(ipv6_dup_options);
7563cf3dc6cSArnaldo Carvalho de Melo 
757333fad53SYOSHIFUJI Hideaki static int ipv6_renew_option(void *ohdr,
758333fad53SYOSHIFUJI Hideaki 			     struct ipv6_opt_hdr __user *newopt, int newoptlen,
759333fad53SYOSHIFUJI Hideaki 			     int inherit,
760333fad53SYOSHIFUJI Hideaki 			     struct ipv6_opt_hdr **hdr,
761333fad53SYOSHIFUJI Hideaki 			     char **p)
762333fad53SYOSHIFUJI Hideaki {
763333fad53SYOSHIFUJI Hideaki 	if (inherit) {
764333fad53SYOSHIFUJI Hideaki 		if (ohdr) {
765333fad53SYOSHIFUJI Hideaki 			memcpy(*p, ohdr, ipv6_optlen((struct ipv6_opt_hdr *)ohdr));
766333fad53SYOSHIFUJI Hideaki 			*hdr = (struct ipv6_opt_hdr *)*p;
767333fad53SYOSHIFUJI Hideaki 			*p += CMSG_ALIGN(ipv6_optlen(*(struct ipv6_opt_hdr **)hdr));
768333fad53SYOSHIFUJI Hideaki 		}
769333fad53SYOSHIFUJI Hideaki 	} else {
770333fad53SYOSHIFUJI Hideaki 		if (newopt) {
771333fad53SYOSHIFUJI Hideaki 			if (copy_from_user(*p, newopt, newoptlen))
772333fad53SYOSHIFUJI Hideaki 				return -EFAULT;
773333fad53SYOSHIFUJI Hideaki 			*hdr = (struct ipv6_opt_hdr *)*p;
774333fad53SYOSHIFUJI Hideaki 			if (ipv6_optlen(*(struct ipv6_opt_hdr **)hdr) > newoptlen)
775333fad53SYOSHIFUJI Hideaki 				return -EINVAL;
776333fad53SYOSHIFUJI Hideaki 			*p += CMSG_ALIGN(newoptlen);
777333fad53SYOSHIFUJI Hideaki 		}
778333fad53SYOSHIFUJI Hideaki 	}
779333fad53SYOSHIFUJI Hideaki 	return 0;
780333fad53SYOSHIFUJI Hideaki }
781333fad53SYOSHIFUJI Hideaki 
782333fad53SYOSHIFUJI Hideaki struct ipv6_txoptions *
783333fad53SYOSHIFUJI Hideaki ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
784333fad53SYOSHIFUJI Hideaki 		   int newtype,
785333fad53SYOSHIFUJI Hideaki 		   struct ipv6_opt_hdr __user *newopt, int newoptlen)
786333fad53SYOSHIFUJI Hideaki {
787333fad53SYOSHIFUJI Hideaki 	int tot_len = 0;
788333fad53SYOSHIFUJI Hideaki 	char *p;
789333fad53SYOSHIFUJI Hideaki 	struct ipv6_txoptions *opt2;
790333fad53SYOSHIFUJI Hideaki 	int err;
791333fad53SYOSHIFUJI Hideaki 
79299c7bc01SYOSHIFUJI Hideaki 	if (opt) {
793333fad53SYOSHIFUJI Hideaki 		if (newtype != IPV6_HOPOPTS && opt->hopopt)
794333fad53SYOSHIFUJI Hideaki 			tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt));
795333fad53SYOSHIFUJI Hideaki 		if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt)
796333fad53SYOSHIFUJI Hideaki 			tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt));
797333fad53SYOSHIFUJI Hideaki 		if (newtype != IPV6_RTHDR && opt->srcrt)
798333fad53SYOSHIFUJI Hideaki 			tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt));
799333fad53SYOSHIFUJI Hideaki 		if (newtype != IPV6_DSTOPTS && opt->dst1opt)
800333fad53SYOSHIFUJI Hideaki 			tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
80199c7bc01SYOSHIFUJI Hideaki 	}
80299c7bc01SYOSHIFUJI Hideaki 
803333fad53SYOSHIFUJI Hideaki 	if (newopt && newoptlen)
804333fad53SYOSHIFUJI Hideaki 		tot_len += CMSG_ALIGN(newoptlen);
805333fad53SYOSHIFUJI Hideaki 
806333fad53SYOSHIFUJI Hideaki 	if (!tot_len)
807333fad53SYOSHIFUJI Hideaki 		return NULL;
808333fad53SYOSHIFUJI Hideaki 
8098b8aa4b5SYOSHIFUJI Hideaki 	tot_len += sizeof(*opt2);
810333fad53SYOSHIFUJI Hideaki 	opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC);
811333fad53SYOSHIFUJI Hideaki 	if (!opt2)
812333fad53SYOSHIFUJI Hideaki 		return ERR_PTR(-ENOBUFS);
813333fad53SYOSHIFUJI Hideaki 
814333fad53SYOSHIFUJI Hideaki 	memset(opt2, 0, tot_len);
815333fad53SYOSHIFUJI Hideaki 
816333fad53SYOSHIFUJI Hideaki 	opt2->tot_len = tot_len;
817333fad53SYOSHIFUJI Hideaki 	p = (char *)(opt2 + 1);
818333fad53SYOSHIFUJI Hideaki 
81999c7bc01SYOSHIFUJI Hideaki 	err = ipv6_renew_option(opt ? opt->hopopt : NULL, newopt, newoptlen,
820333fad53SYOSHIFUJI Hideaki 				newtype != IPV6_HOPOPTS,
821333fad53SYOSHIFUJI Hideaki 				&opt2->hopopt, &p);
822333fad53SYOSHIFUJI Hideaki 	if (err)
823333fad53SYOSHIFUJI Hideaki 		goto out;
824333fad53SYOSHIFUJI Hideaki 
82599c7bc01SYOSHIFUJI Hideaki 	err = ipv6_renew_option(opt ? opt->dst0opt : NULL, newopt, newoptlen,
826333fad53SYOSHIFUJI Hideaki 				newtype != IPV6_RTHDRDSTOPTS,
827333fad53SYOSHIFUJI Hideaki 				&opt2->dst0opt, &p);
828333fad53SYOSHIFUJI Hideaki 	if (err)
829333fad53SYOSHIFUJI Hideaki 		goto out;
830333fad53SYOSHIFUJI Hideaki 
83199c7bc01SYOSHIFUJI Hideaki 	err = ipv6_renew_option(opt ? opt->srcrt : NULL, newopt, newoptlen,
832333fad53SYOSHIFUJI Hideaki 				newtype != IPV6_RTHDR,
83399c7bc01SYOSHIFUJI Hideaki 				(struct ipv6_opt_hdr **)&opt2->srcrt, &p);
834333fad53SYOSHIFUJI Hideaki 	if (err)
835333fad53SYOSHIFUJI Hideaki 		goto out;
836333fad53SYOSHIFUJI Hideaki 
83799c7bc01SYOSHIFUJI Hideaki 	err = ipv6_renew_option(opt ? opt->dst1opt : NULL, newopt, newoptlen,
838333fad53SYOSHIFUJI Hideaki 				newtype != IPV6_DSTOPTS,
839333fad53SYOSHIFUJI Hideaki 				&opt2->dst1opt, &p);
840333fad53SYOSHIFUJI Hideaki 	if (err)
841333fad53SYOSHIFUJI Hideaki 		goto out;
842333fad53SYOSHIFUJI Hideaki 
843333fad53SYOSHIFUJI Hideaki 	opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) +
844333fad53SYOSHIFUJI Hideaki 			  (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) +
845333fad53SYOSHIFUJI Hideaki 			  (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0);
846333fad53SYOSHIFUJI Hideaki 	opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0);
847333fad53SYOSHIFUJI Hideaki 
848333fad53SYOSHIFUJI Hideaki 	return opt2;
849333fad53SYOSHIFUJI Hideaki out:
8508b8aa4b5SYOSHIFUJI Hideaki 	sock_kfree_s(sk, opt2, opt2->tot_len);
851333fad53SYOSHIFUJI Hideaki 	return ERR_PTR(err);
852333fad53SYOSHIFUJI Hideaki }
853333fad53SYOSHIFUJI Hideaki 
854df9890c3SYOSHIFUJI Hideaki struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
855df9890c3SYOSHIFUJI Hideaki 					  struct ipv6_txoptions *opt)
856df9890c3SYOSHIFUJI Hideaki {
857df9890c3SYOSHIFUJI Hideaki 	/*
858df9890c3SYOSHIFUJI Hideaki 	 * ignore the dest before srcrt unless srcrt is being included.
859df9890c3SYOSHIFUJI Hideaki 	 * --yoshfuji
860df9890c3SYOSHIFUJI Hideaki 	 */
861df9890c3SYOSHIFUJI Hideaki 	if (opt && opt->dst0opt && !opt->srcrt) {
862df9890c3SYOSHIFUJI Hideaki 		if (opt_space != opt) {
863df9890c3SYOSHIFUJI Hideaki 			memcpy(opt_space, opt, sizeof(*opt_space));
864df9890c3SYOSHIFUJI Hideaki 			opt = opt_space;
865df9890c3SYOSHIFUJI Hideaki 		}
866df9890c3SYOSHIFUJI Hideaki 		opt->opt_nflen -= ipv6_optlen(opt->dst0opt);
867df9890c3SYOSHIFUJI Hideaki 		opt->dst0opt = NULL;
868df9890c3SYOSHIFUJI Hideaki 	}
869df9890c3SYOSHIFUJI Hideaki 
870df9890c3SYOSHIFUJI Hideaki 	return opt;
871df9890c3SYOSHIFUJI Hideaki }
872df9890c3SYOSHIFUJI Hideaki 
87320c59de2SArnaud Ebalard /**
87420c59de2SArnaud Ebalard  * fl6_update_dst - update flowi destination address with info given
87520c59de2SArnaud Ebalard  *                  by srcrt option, if any.
87620c59de2SArnaud Ebalard  *
8774c9483b2SDavid S. Miller  * @fl6: flowi6 for which daddr is to be updated
87820c59de2SArnaud Ebalard  * @opt: struct ipv6_txoptions in which to look for srcrt opt
8794c9483b2SDavid S. Miller  * @orig: copy of original daddr address if modified
88020c59de2SArnaud Ebalard  *
88120c59de2SArnaud Ebalard  * Returns NULL if no txoptions or no srcrt, otherwise returns orig
8824c9483b2SDavid S. Miller  * and initial value of fl6->daddr set in orig
88320c59de2SArnaud Ebalard  */
8844c9483b2SDavid S. Miller struct in6_addr *fl6_update_dst(struct flowi6 *fl6,
88520c59de2SArnaud Ebalard 				const struct ipv6_txoptions *opt,
88620c59de2SArnaud Ebalard 				struct in6_addr *orig)
88720c59de2SArnaud Ebalard {
88820c59de2SArnaud Ebalard 	if (!opt || !opt->srcrt)
88920c59de2SArnaud Ebalard 		return NULL;
89020c59de2SArnaud Ebalard 
891*4e3fd7a0SAlexey Dobriyan 	*orig = fl6->daddr;
892*4e3fd7a0SAlexey Dobriyan 	fl6->daddr = *((struct rt0_hdr *)opt->srcrt)->addr;
89320c59de2SArnaud Ebalard 	return orig;
89420c59de2SArnaud Ebalard }
89520c59de2SArnaud Ebalard 
89620c59de2SArnaud Ebalard EXPORT_SYMBOL_GPL(fl6_update_dst);
897