xref: /openbmc/linux/net/ipv6/exthdrs.c (revision 96d3c5a7d20ec546e44695983fe0508c6f904248)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *	Extension Header handling for IPv6
41da177e4SLinus Torvalds  *	Linux INET6 implementation
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  *	Authors:
71da177e4SLinus Torvalds  *	Pedro Roque		<roque@di.fc.ul.pt>
81da177e4SLinus Torvalds  *	Andi Kleen		<ak@muc.de>
91da177e4SLinus Torvalds  *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru>
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds /* Changes:
131da177e4SLinus Torvalds  *	yoshfuji		: ensure not to overrun while parsing
141da177e4SLinus Torvalds  *				  tlv options.
151da177e4SLinus Torvalds  *	Mitsuru KANDA @USAGI and: Remove ipv6_parse_exthdrs().
161da177e4SLinus Torvalds  *	YOSHIFUJI Hideaki @USAGI  Register inbound extension header
171da177e4SLinus Torvalds  *				  handlers as inet6_protocol{}.
181da177e4SLinus Torvalds  */
191da177e4SLinus Torvalds 
201da177e4SLinus Torvalds #include <linux/errno.h>
211da177e4SLinus Torvalds #include <linux/types.h>
221da177e4SLinus Torvalds #include <linux/socket.h>
231da177e4SLinus Torvalds #include <linux/sockios.h>
241da177e4SLinus Torvalds #include <linux/net.h>
251da177e4SLinus Torvalds #include <linux/netdevice.h>
261da177e4SLinus Torvalds #include <linux/in6.h>
271da177e4SLinus Torvalds #include <linux/icmpv6.h>
285a0e3ad6STejun Heo #include <linux/slab.h>
29bc3b2d7fSPaul Gortmaker #include <linux/export.h>
301da177e4SLinus Torvalds 
31352e512cSHerbert Xu #include <net/dst.h>
321da177e4SLinus Torvalds #include <net/sock.h>
331da177e4SLinus Torvalds #include <net/snmp.h>
341da177e4SLinus Torvalds 
351da177e4SLinus Torvalds #include <net/ipv6.h>
361da177e4SLinus Torvalds #include <net/protocol.h>
371da177e4SLinus Torvalds #include <net/transp_v6.h>
381da177e4SLinus Torvalds #include <net/rawv6.h>
391da177e4SLinus Torvalds #include <net/ndisc.h>
401da177e4SLinus Torvalds #include <net/ip6_route.h>
411da177e4SLinus Torvalds #include <net/addrconf.h>
422e532b70SHuw Davies #include <net/calipso.h>
4307a93626SAmerigo Wang #if IS_ENABLED(CONFIG_IPV6_MIP6)
4465d4ed92SMasahide NAKAMURA #include <net/xfrm.h>
4565d4ed92SMasahide NAKAMURA #endif
461ababebaSDavid Lebrun #include <linux/seg6.h>
471ababebaSDavid Lebrun #include <net/seg6.h>
489baee834SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC
499baee834SDavid Lebrun #include <net/seg6_hmac.h>
509baee834SDavid Lebrun #endif
518610c7c6SAlexander Aring #include <net/rpl.h>
529ee11f0fSJustin Iurman #include <linux/ioam6.h>
539ee11f0fSJustin Iurman #include <net/ioam6.h>
549ee11f0fSJustin Iurman #include <net/dst_metadata.h>
551da177e4SLinus Torvalds 
56ce256981SFabian Frederick #include <linux/uaccess.h>
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds /*********************
591da177e4SLinus Torvalds   Generic functions
601da177e4SLinus Torvalds  *********************/
611da177e4SLinus Torvalds 
621da177e4SLinus Torvalds /* An unknown option is detected, decide what to do */
631da177e4SLinus Torvalds 
ip6_tlvopt_unknown(struct sk_buff * skb,int optoff,bool disallow_unknowns)6447d3d7acSTom Herbert static bool ip6_tlvopt_unknown(struct sk_buff *skb, int optoff,
6547d3d7acSTom Herbert 			       bool disallow_unknowns)
661da177e4SLinus Torvalds {
6747d3d7acSTom Herbert 	if (disallow_unknowns) {
6847d3d7acSTom Herbert 		/* If unknown TLVs are disallowed by configuration
6947d3d7acSTom Herbert 		 * then always silently drop packet. Note this also
7047d3d7acSTom Herbert 		 * means no ICMP parameter problem is sent which
7147d3d7acSTom Herbert 		 * could be a good property to mitigate a reflection DOS
7247d3d7acSTom Herbert 		 * attack.
7347d3d7acSTom Herbert 		 */
7447d3d7acSTom Herbert 
7547d3d7acSTom Herbert 		goto drop;
7647d3d7acSTom Herbert 	}
7747d3d7acSTom Herbert 
78d56f90a7SArnaldo Carvalho de Melo 	switch ((skb_network_header(skb)[optoff] & 0xC0) >> 6) {
791da177e4SLinus Torvalds 	case 0: /* ignore */
80a50feda5SEric Dumazet 		return true;
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds 	case 1: /* drop packet */
831da177e4SLinus Torvalds 		break;
841da177e4SLinus Torvalds 
851da177e4SLinus Torvalds 	case 3: /* Send ICMP if not a multicast address and drop packet */
861da177e4SLinus Torvalds 		/* Actually, it is redundant check. icmp_send
871da177e4SLinus Torvalds 		   will recheck in any case.
881da177e4SLinus Torvalds 		 */
890660e03fSArnaldo Carvalho de Melo 		if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr))
901da177e4SLinus Torvalds 			break;
91a8eceea8SJoe Perches 		fallthrough;
921da177e4SLinus Torvalds 	case 2: /* send ICMP PARM PROB regardless and drop packet */
937d9dbdfbSMenglong Dong 		icmpv6_param_prob_reason(skb, ICMPV6_UNK_OPTION, optoff,
947d9dbdfbSMenglong Dong 					 SKB_DROP_REASON_UNHANDLED_PROTO);
95a50feda5SEric Dumazet 		return false;
963ff50b79SStephen Hemminger 	}
971da177e4SLinus Torvalds 
9847d3d7acSTom Herbert drop:
997d9dbdfbSMenglong Dong 	kfree_skb_reason(skb, SKB_DROP_REASON_UNHANDLED_PROTO);
100a50feda5SEric Dumazet 	return false;
1011da177e4SLinus Torvalds }
1021da177e4SLinus Torvalds 
10351b8f812SEric Dumazet static bool ipv6_hop_ra(struct sk_buff *skb, int optoff);
10451b8f812SEric Dumazet static bool ipv6_hop_ioam(struct sk_buff *skb, int optoff);
10551b8f812SEric Dumazet static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff);
10651b8f812SEric Dumazet static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff);
10751b8f812SEric Dumazet #if IS_ENABLED(CONFIG_IPV6_MIP6)
10851b8f812SEric Dumazet static bool ipv6_dest_hao(struct sk_buff *skb, int optoff);
10951b8f812SEric Dumazet #endif
11051b8f812SEric Dumazet 
1111da177e4SLinus Torvalds /* Parse tlv encoded option header (hop-by-hop or destination) */
1121da177e4SLinus Torvalds 
ip6_parse_tlv(bool hopbyhop,struct sk_buff * skb,int max_count)11351b8f812SEric Dumazet static bool ip6_parse_tlv(bool hopbyhop,
11447d3d7acSTom Herbert 			  struct sk_buff *skb,
11547d3d7acSTom Herbert 			  int max_count)
1161da177e4SLinus Torvalds {
11747d3d7acSTom Herbert 	int len = (skb_transport_header(skb)[1] + 1) << 3;
118d56f90a7SArnaldo Carvalho de Melo 	const unsigned char *nh = skb_network_header(skb);
119cfe1fc77SArnaldo Carvalho de Melo 	int off = skb_network_header_len(skb);
12047d3d7acSTom Herbert 	bool disallow_unknowns = false;
12147d3d7acSTom Herbert 	int tlv_count = 0;
1229b905fe6SEldad Zack 	int padlen = 0;
1231da177e4SLinus Torvalds 
12447d3d7acSTom Herbert 	if (unlikely(max_count < 0)) {
12547d3d7acSTom Herbert 		disallow_unknowns = true;
12647d3d7acSTom Herbert 		max_count = -max_count;
12747d3d7acSTom Herbert 	}
12847d3d7acSTom Herbert 
1291da177e4SLinus Torvalds 	off += 2;
1301da177e4SLinus Torvalds 	len -= 2;
1311da177e4SLinus Torvalds 
1321da177e4SLinus Torvalds 	while (len > 0) {
133624085a3SEric Dumazet 		int optlen, i;
1341da177e4SLinus Torvalds 
135624085a3SEric Dumazet 		if (nh[off] == IPV6_TLV_PAD1) {
1369b905fe6SEldad Zack 			padlen++;
1379b905fe6SEldad Zack 			if (padlen > 7)
1389b905fe6SEldad Zack 				goto bad;
139624085a3SEric Dumazet 			off++;
140624085a3SEric Dumazet 			len--;
141624085a3SEric Dumazet 			continue;
142624085a3SEric Dumazet 		}
143624085a3SEric Dumazet 		if (len < 2)
144624085a3SEric Dumazet 			goto bad;
145624085a3SEric Dumazet 		optlen = nh[off + 1] + 2;
146624085a3SEric Dumazet 		if (optlen > len)
147624085a3SEric Dumazet 			goto bad;
1481da177e4SLinus Torvalds 
149624085a3SEric Dumazet 		if (nh[off] == IPV6_TLV_PADN) {
150c1412fceSEldad Zack 			/* RFC 2460 states that the purpose of PadN is
151c1412fceSEldad Zack 			 * to align the containing header to multiples
152c1412fceSEldad Zack 			 * of 8. 7 is therefore the highest valid value.
153c1412fceSEldad Zack 			 * See also RFC 4942, Section 2.1.9.5.
154c1412fceSEldad Zack 			 */
1559b905fe6SEldad Zack 			padlen += optlen;
1569b905fe6SEldad Zack 			if (padlen > 7)
157c1412fceSEldad Zack 				goto bad;
158c1412fceSEldad Zack 			/* RFC 4942 recommends receiving hosts to
159c1412fceSEldad Zack 			 * actively check PadN payload to contain
160c1412fceSEldad Zack 			 * only zeroes.
161c1412fceSEldad Zack 			 */
162c1412fceSEldad Zack 			for (i = 2; i < optlen; i++) {
163c1412fceSEldad Zack 				if (nh[off + i] != 0)
164c1412fceSEldad Zack 					goto bad;
165c1412fceSEldad Zack 			}
166624085a3SEric Dumazet 		} else {
16747d3d7acSTom Herbert 			tlv_count++;
16847d3d7acSTom Herbert 			if (tlv_count > max_count)
16947d3d7acSTom Herbert 				goto bad;
17047d3d7acSTom Herbert 
17151b8f812SEric Dumazet 			if (hopbyhop) {
17251b8f812SEric Dumazet 				switch (nh[off]) {
17351b8f812SEric Dumazet 				case IPV6_TLV_ROUTERALERT:
17451b8f812SEric Dumazet 					if (!ipv6_hop_ra(skb, off))
17551b8f812SEric Dumazet 						return false;
17651b8f812SEric Dumazet 					break;
17751b8f812SEric Dumazet 				case IPV6_TLV_IOAM:
17851b8f812SEric Dumazet 					if (!ipv6_hop_ioam(skb, off))
17951b8f812SEric Dumazet 						return false;
180*8fbc1919SJustin Iurman 
181*8fbc1919SJustin Iurman 					nh = skb_network_header(skb);
18251b8f812SEric Dumazet 					break;
18351b8f812SEric Dumazet 				case IPV6_TLV_JUMBO:
18451b8f812SEric Dumazet 					if (!ipv6_hop_jumbo(skb, off))
18551b8f812SEric Dumazet 						return false;
18651b8f812SEric Dumazet 					break;
18751b8f812SEric Dumazet 				case IPV6_TLV_CALIPSO:
18851b8f812SEric Dumazet 					if (!ipv6_hop_calipso(skb, off))
18951b8f812SEric Dumazet 						return false;
19051b8f812SEric Dumazet 					break;
19151b8f812SEric Dumazet 				default:
19251b8f812SEric Dumazet 					if (!ip6_tlvopt_unknown(skb, off,
19351b8f812SEric Dumazet 								disallow_unknowns))
19451b8f812SEric Dumazet 						return false;
19551b8f812SEric Dumazet 					break;
19651b8f812SEric Dumazet 				}
19751b8f812SEric Dumazet 			} else {
19851b8f812SEric Dumazet 				switch (nh[off]) {
19951b8f812SEric Dumazet #if IS_ENABLED(CONFIG_IPV6_MIP6)
20051b8f812SEric Dumazet 				case IPV6_TLV_HAO:
20151b8f812SEric Dumazet 					if (!ipv6_dest_hao(skb, off))
20251b8f812SEric Dumazet 						return false;
20351b8f812SEric Dumazet 					break;
20451b8f812SEric Dumazet #endif
20551b8f812SEric Dumazet 				default:
20651b8f812SEric Dumazet 					if (!ip6_tlvopt_unknown(skb, off,
20751b8f812SEric Dumazet 								disallow_unknowns))
208a50feda5SEric Dumazet 						return false;
2091da177e4SLinus Torvalds 					break;
2101da177e4SLinus Torvalds 				}
2111da177e4SLinus Torvalds 			}
2129b905fe6SEldad Zack 			padlen = 0;
2131da177e4SLinus Torvalds 		}
2141da177e4SLinus Torvalds 		off += optlen;
2151da177e4SLinus Torvalds 		len -= optlen;
2161da177e4SLinus Torvalds 	}
2179b905fe6SEldad Zack 
2181da177e4SLinus Torvalds 	if (len == 0)
219a50feda5SEric Dumazet 		return true;
2201da177e4SLinus Torvalds bad:
2217d9dbdfbSMenglong Dong 	kfree_skb_reason(skb, SKB_DROP_REASON_IP_INHDR);
222a50feda5SEric Dumazet 	return false;
2231da177e4SLinus Torvalds }
2241da177e4SLinus Torvalds 
2251da177e4SLinus Torvalds /*****************************
2261da177e4SLinus Torvalds   Destination options header.
2271da177e4SLinus Torvalds  *****************************/
2281da177e4SLinus Torvalds 
22907a93626SAmerigo Wang #if IS_ENABLED(CONFIG_IPV6_MIP6)
ipv6_dest_hao(struct sk_buff * skb,int optoff)230a50feda5SEric Dumazet static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
231a831f5bbSMasahide NAKAMURA {
232a831f5bbSMasahide NAKAMURA 	struct ipv6_destopt_hao *hao;
233a831f5bbSMasahide NAKAMURA 	struct inet6_skb_parm *opt = IP6CB(skb);
2340660e03fSArnaldo Carvalho de Melo 	struct ipv6hdr *ipv6h = ipv6_hdr(skb);
2357d9dbdfbSMenglong Dong 	SKB_DR(reason);
236a831f5bbSMasahide NAKAMURA 	int ret;
237a831f5bbSMasahide NAKAMURA 
238a831f5bbSMasahide NAKAMURA 	if (opt->dsthao) {
239ba7a46f1SJoe Perches 		net_dbg_ratelimited("hao duplicated\n");
240a831f5bbSMasahide NAKAMURA 		goto discard;
241a831f5bbSMasahide NAKAMURA 	}
242a831f5bbSMasahide NAKAMURA 	opt->dsthao = opt->dst1;
243a831f5bbSMasahide NAKAMURA 	opt->dst1 = 0;
244a831f5bbSMasahide NAKAMURA 
245d56f90a7SArnaldo Carvalho de Melo 	hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + optoff);
246a831f5bbSMasahide NAKAMURA 
247a831f5bbSMasahide NAKAMURA 	if (hao->length != 16) {
248ba7a46f1SJoe Perches 		net_dbg_ratelimited("hao invalid option length = %d\n",
249ba7a46f1SJoe Perches 				    hao->length);
2507d9dbdfbSMenglong Dong 		SKB_DR_SET(reason, IP_INHDR);
251a831f5bbSMasahide NAKAMURA 		goto discard;
252a831f5bbSMasahide NAKAMURA 	}
253a831f5bbSMasahide NAKAMURA 
254a831f5bbSMasahide NAKAMURA 	if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) {
255ba7a46f1SJoe Perches 		net_dbg_ratelimited("hao is not an unicast addr: %pI6\n",
256ba7a46f1SJoe Perches 				    &hao->addr);
2577d9dbdfbSMenglong Dong 		SKB_DR_SET(reason, INVALID_PROTO);
258a831f5bbSMasahide NAKAMURA 		goto discard;
259a831f5bbSMasahide NAKAMURA 	}
260a831f5bbSMasahide NAKAMURA 
261a831f5bbSMasahide NAKAMURA 	ret = xfrm6_input_addr(skb, (xfrm_address_t *)&ipv6h->daddr,
262a831f5bbSMasahide NAKAMURA 			       (xfrm_address_t *)&hao->addr, IPPROTO_DSTOPTS);
2637d9dbdfbSMenglong Dong 	if (unlikely(ret < 0)) {
2647d9dbdfbSMenglong Dong 		SKB_DR_SET(reason, XFRM_POLICY);
265a831f5bbSMasahide NAKAMURA 		goto discard;
2667d9dbdfbSMenglong Dong 	}
267a831f5bbSMasahide NAKAMURA 
268a831f5bbSMasahide NAKAMURA 	if (skb_cloned(skb)) {
26965c88466SHerbert Xu 		if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
270a831f5bbSMasahide NAKAMURA 			goto discard;
271a831f5bbSMasahide NAKAMURA 
272a831f5bbSMasahide NAKAMURA 		/* update all variable using below by copied skbuff */
27365c88466SHerbert Xu 		hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) +
274d56f90a7SArnaldo Carvalho de Melo 						  optoff);
27565c88466SHerbert Xu 		ipv6h = ipv6_hdr(skb);
276a831f5bbSMasahide NAKAMURA 	}
277a831f5bbSMasahide NAKAMURA 
278a831f5bbSMasahide NAKAMURA 	if (skb->ip_summed == CHECKSUM_COMPLETE)
279a831f5bbSMasahide NAKAMURA 		skb->ip_summed = CHECKSUM_NONE;
280a831f5bbSMasahide NAKAMURA 
28174b6551bSGustavo A. R. Silva 	swap(ipv6h->saddr, hao->addr);
282a831f5bbSMasahide NAKAMURA 
2832456e855SThomas Gleixner 	if (skb->tstamp == 0)
284a831f5bbSMasahide NAKAMURA 		__net_timestamp(skb);
285a831f5bbSMasahide NAKAMURA 
286a50feda5SEric Dumazet 	return true;
287a831f5bbSMasahide NAKAMURA 
288a831f5bbSMasahide NAKAMURA  discard:
2897d9dbdfbSMenglong Dong 	kfree_skb_reason(skb, reason);
290a50feda5SEric Dumazet 	return false;
291a831f5bbSMasahide NAKAMURA }
292a831f5bbSMasahide NAKAMURA #endif
293a831f5bbSMasahide NAKAMURA 
ipv6_destopt_rcv(struct sk_buff * skb)294e5bbef20SHerbert Xu static int ipv6_destopt_rcv(struct sk_buff *skb)
2951da177e4SLinus Torvalds {
296bdb7cc64SStephen Suryaputra 	struct inet6_dev *idev = __in6_dev_get(skb->dev);
2971da177e4SLinus Torvalds 	struct inet6_skb_parm *opt = IP6CB(skb);
29807a93626SAmerigo Wang #if IS_ENABLED(CONFIG_IPV6_MIP6)
299a831f5bbSMasahide NAKAMURA 	__u16 dstbuf;
300a831f5bbSMasahide NAKAMURA #endif
301897dc80bSEric Dumazet 	struct dst_entry *dst = skb_dst(skb);
30247d3d7acSTom Herbert 	struct net *net = dev_net(skb->dev);
30347d3d7acSTom Herbert 	int extlen;
3041da177e4SLinus Torvalds 
305ea2ae17dSArnaldo Carvalho de Melo 	if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) ||
306ea2ae17dSArnaldo Carvalho de Melo 	    !pskb_may_pull(skb, (skb_transport_offset(skb) +
3079c70220bSArnaldo Carvalho de Melo 				 ((skb_transport_header(skb)[1] + 1) << 3)))) {
308bdb7cc64SStephen Suryaputra 		__IP6_INC_STATS(dev_net(dst->dev), idev,
309a11d206dSYOSHIFUJI Hideaki 				IPSTATS_MIB_INHDRERRORS);
31047d3d7acSTom Herbert fail_and_free:
3111da177e4SLinus Torvalds 		kfree_skb(skb);
3121da177e4SLinus Torvalds 		return -1;
3131da177e4SLinus Torvalds 	}
3141da177e4SLinus Torvalds 
31547d3d7acSTom Herbert 	extlen = (skb_transport_header(skb)[1] + 1) << 3;
31647d3d7acSTom Herbert 	if (extlen > net->ipv6.sysctl.max_dst_opts_len)
31747d3d7acSTom Herbert 		goto fail_and_free;
31847d3d7acSTom Herbert 
319cfe1fc77SArnaldo Carvalho de Melo 	opt->lastopt = opt->dst1 = skb_network_header_len(skb);
32007a93626SAmerigo Wang #if IS_ENABLED(CONFIG_IPV6_MIP6)
321a831f5bbSMasahide NAKAMURA 	dstbuf = opt->dst1;
322a831f5bbSMasahide NAKAMURA #endif
3231da177e4SLinus Torvalds 
32451b8f812SEric Dumazet 	if (ip6_parse_tlv(false, skb, net->ipv6.sysctl.max_dst_opts_cnt)) {
32547d3d7acSTom Herbert 		skb->transport_header += extlen;
326dc435e6dSMasahide NAKAMURA 		opt = IP6CB(skb);
32707a93626SAmerigo Wang #if IS_ENABLED(CONFIG_IPV6_MIP6)
328a831f5bbSMasahide NAKAMURA 		opt->nhoff = dstbuf;
329a831f5bbSMasahide NAKAMURA #else
330951dbc8aSPatrick McHardy 		opt->nhoff = opt->dst1;
331a831f5bbSMasahide NAKAMURA #endif
3321da177e4SLinus Torvalds 		return 1;
3331da177e4SLinus Torvalds 	}
3341da177e4SLinus Torvalds 
335bdb7cc64SStephen Suryaputra 	__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
3361da177e4SLinus Torvalds 	return -1;
3371da177e4SLinus Torvalds }
3381da177e4SLinus Torvalds 
seg6_update_csum(struct sk_buff * skb)3391ababebaSDavid Lebrun static void seg6_update_csum(struct sk_buff *skb)
3401ababebaSDavid Lebrun {
3411ababebaSDavid Lebrun 	struct ipv6_sr_hdr *hdr;
3421ababebaSDavid Lebrun 	struct in6_addr *addr;
3431ababebaSDavid Lebrun 	__be32 from, to;
3441ababebaSDavid Lebrun 
3451ababebaSDavid Lebrun 	/* srh is at transport offset and seg_left is already decremented
3461ababebaSDavid Lebrun 	 * but daddr is not yet updated with next segment
3471ababebaSDavid Lebrun 	 */
3481ababebaSDavid Lebrun 
3491ababebaSDavid Lebrun 	hdr = (struct ipv6_sr_hdr *)skb_transport_header(skb);
3501ababebaSDavid Lebrun 	addr = hdr->segments + hdr->segments_left;
3511ababebaSDavid Lebrun 
3521ababebaSDavid Lebrun 	hdr->segments_left++;
3531ababebaSDavid Lebrun 	from = *(__be32 *)hdr;
3541ababebaSDavid Lebrun 
3551ababebaSDavid Lebrun 	hdr->segments_left--;
3561ababebaSDavid Lebrun 	to = *(__be32 *)hdr;
3571ababebaSDavid Lebrun 
3581ababebaSDavid Lebrun 	/* update skb csum with diff resulting from seg_left decrement */
3591ababebaSDavid Lebrun 
3601ababebaSDavid Lebrun 	update_csum_diff4(skb, from, to);
3611ababebaSDavid Lebrun 
3621ababebaSDavid Lebrun 	/* compute csum diff between current and next segment and update */
3631ababebaSDavid Lebrun 
3641ababebaSDavid Lebrun 	update_csum_diff16(skb, (__be32 *)(&ipv6_hdr(skb)->daddr),
3651ababebaSDavid Lebrun 			   (__be32 *)addr);
3661ababebaSDavid Lebrun }
3671ababebaSDavid Lebrun 
ipv6_srh_rcv(struct sk_buff * skb)3681ababebaSDavid Lebrun static int ipv6_srh_rcv(struct sk_buff *skb)
3691ababebaSDavid Lebrun {
3701ababebaSDavid Lebrun 	struct inet6_skb_parm *opt = IP6CB(skb);
3711ababebaSDavid Lebrun 	struct net *net = dev_net(skb->dev);
3721ababebaSDavid Lebrun 	struct ipv6_sr_hdr *hdr;
3731ababebaSDavid Lebrun 	struct inet6_dev *idev;
3741ababebaSDavid Lebrun 	struct in6_addr *addr;
3751ababebaSDavid Lebrun 	int accept_seg6;
3761ababebaSDavid Lebrun 
3771ababebaSDavid Lebrun 	hdr = (struct ipv6_sr_hdr *)skb_transport_header(skb);
3781ababebaSDavid Lebrun 
3791ababebaSDavid Lebrun 	idev = __in6_dev_get(skb->dev);
3801ababebaSDavid Lebrun 
3811ababebaSDavid Lebrun 	accept_seg6 = net->ipv6.devconf_all->seg6_enabled;
3821ababebaSDavid Lebrun 	if (accept_seg6 > idev->cnf.seg6_enabled)
3831ababebaSDavid Lebrun 		accept_seg6 = idev->cnf.seg6_enabled;
3841ababebaSDavid Lebrun 
3851ababebaSDavid Lebrun 	if (!accept_seg6) {
3861ababebaSDavid Lebrun 		kfree_skb(skb);
3871ababebaSDavid Lebrun 		return -1;
3881ababebaSDavid Lebrun 	}
3891ababebaSDavid Lebrun 
3909baee834SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC
3919baee834SDavid Lebrun 	if (!seg6_hmac_validate_skb(skb)) {
3929baee834SDavid Lebrun 		kfree_skb(skb);
3939baee834SDavid Lebrun 		return -1;
3949baee834SDavid Lebrun 	}
3959baee834SDavid Lebrun #endif
3969baee834SDavid Lebrun 
3971ababebaSDavid Lebrun looped_back:
398013e8167SDavid Lebrun 	if (hdr->segments_left == 0) {
399ee90c6baSJulien Massonneau 		if (hdr->nexthdr == NEXTHDR_IPV6 || hdr->nexthdr == NEXTHDR_IPV4) {
4001ababebaSDavid Lebrun 			int offset = (hdr->hdrlen + 1) << 3;
4011ababebaSDavid Lebrun 
4021ababebaSDavid Lebrun 			skb_postpull_rcsum(skb, skb_network_header(skb),
4031ababebaSDavid Lebrun 					   skb_network_header_len(skb));
4040d2e27b8SKuniyuki Iwashima 			skb_pull(skb, offset);
4051ababebaSDavid Lebrun 			skb_postpull_rcsum(skb, skb_transport_header(skb),
4061ababebaSDavid Lebrun 					   offset);
4071ababebaSDavid Lebrun 
4081ababebaSDavid Lebrun 			skb_reset_network_header(skb);
4091ababebaSDavid Lebrun 			skb_reset_transport_header(skb);
4101ababebaSDavid Lebrun 			skb->encapsulation = 0;
411ee90c6baSJulien Massonneau 			if (hdr->nexthdr == NEXTHDR_IPV4)
412ee90c6baSJulien Massonneau 				skb->protocol = htons(ETH_P_IP);
4131ababebaSDavid Lebrun 			__skb_tunnel_rx(skb, skb->dev, net);
4141ababebaSDavid Lebrun 
4151ababebaSDavid Lebrun 			netif_rx(skb);
4161ababebaSDavid Lebrun 			return -1;
4171ababebaSDavid Lebrun 		}
4181ababebaSDavid Lebrun 
4191ababebaSDavid Lebrun 		opt->srcrt = skb_network_header_len(skb);
4201ababebaSDavid Lebrun 		opt->lastopt = opt->srcrt;
4211ababebaSDavid Lebrun 		skb->transport_header += (hdr->hdrlen + 1) << 3;
4221ababebaSDavid Lebrun 		opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb);
4231ababebaSDavid Lebrun 
4241ababebaSDavid Lebrun 		return 1;
4251ababebaSDavid Lebrun 	}
4261ababebaSDavid Lebrun 
4271ababebaSDavid Lebrun 	if (hdr->segments_left >= (hdr->hdrlen >> 1)) {
428bdb7cc64SStephen Suryaputra 		__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
4291ababebaSDavid Lebrun 		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
4301ababebaSDavid Lebrun 				  ((&hdr->segments_left) -
4311ababebaSDavid Lebrun 				   skb_network_header(skb)));
4321ababebaSDavid Lebrun 		return -1;
4331ababebaSDavid Lebrun 	}
4341ababebaSDavid Lebrun 
4351ababebaSDavid Lebrun 	if (skb_cloned(skb)) {
4361ababebaSDavid Lebrun 		if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) {
4371ababebaSDavid Lebrun 			__IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
4381ababebaSDavid Lebrun 					IPSTATS_MIB_OUTDISCARDS);
4391ababebaSDavid Lebrun 			kfree_skb(skb);
4401ababebaSDavid Lebrun 			return -1;
4411ababebaSDavid Lebrun 		}
4421ababebaSDavid Lebrun 
4431ababebaSDavid Lebrun 		hdr = (struct ipv6_sr_hdr *)skb_transport_header(skb);
444b83d50f4SKuniyuki Iwashima 	}
4451ababebaSDavid Lebrun 
4461ababebaSDavid Lebrun 	hdr->segments_left--;
4471ababebaSDavid Lebrun 	addr = hdr->segments + hdr->segments_left;
4481ababebaSDavid Lebrun 
4491ababebaSDavid Lebrun 	skb_push(skb, sizeof(struct ipv6hdr));
4501ababebaSDavid Lebrun 
4511ababebaSDavid Lebrun 	if (skb->ip_summed == CHECKSUM_COMPLETE)
4521ababebaSDavid Lebrun 		seg6_update_csum(skb);
4531ababebaSDavid Lebrun 
4541ababebaSDavid Lebrun 	ipv6_hdr(skb)->daddr = *addr;
4551ababebaSDavid Lebrun 
4561ababebaSDavid Lebrun 	ip6_route_input(skb);
4571ababebaSDavid Lebrun 
4581ababebaSDavid Lebrun 	if (skb_dst(skb)->error) {
4591ababebaSDavid Lebrun 		dst_input(skb);
4601ababebaSDavid Lebrun 		return -1;
4611ababebaSDavid Lebrun 	}
4621ababebaSDavid Lebrun 
4631ababebaSDavid Lebrun 	if (skb_dst(skb)->dev->flags & IFF_LOOPBACK) {
4641ababebaSDavid Lebrun 		if (ipv6_hdr(skb)->hop_limit <= 1) {
465bdb7cc64SStephen Suryaputra 			__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
4661ababebaSDavid Lebrun 			icmpv6_send(skb, ICMPV6_TIME_EXCEED,
4671ababebaSDavid Lebrun 				    ICMPV6_EXC_HOPLIMIT, 0);
4681ababebaSDavid Lebrun 			kfree_skb(skb);
4691ababebaSDavid Lebrun 			return -1;
4701ababebaSDavid Lebrun 		}
4711ababebaSDavid Lebrun 		ipv6_hdr(skb)->hop_limit--;
4721ababebaSDavid Lebrun 
4731ababebaSDavid Lebrun 		skb_pull(skb, sizeof(struct ipv6hdr));
4741ababebaSDavid Lebrun 		goto looped_back;
4751ababebaSDavid Lebrun 	}
4761ababebaSDavid Lebrun 
4771ababebaSDavid Lebrun 	dst_input(skb);
4781ababebaSDavid Lebrun 
4791ababebaSDavid Lebrun 	return -1;
4801ababebaSDavid Lebrun }
4811ababebaSDavid Lebrun 
ipv6_rpl_srh_rcv(struct sk_buff * skb)4828610c7c6SAlexander Aring static int ipv6_rpl_srh_rcv(struct sk_buff *skb)
4838610c7c6SAlexander Aring {
4848610c7c6SAlexander Aring 	struct ipv6_rpl_sr_hdr *hdr, *ohdr, *chdr;
4858610c7c6SAlexander Aring 	struct inet6_skb_parm *opt = IP6CB(skb);
4868610c7c6SAlexander Aring 	struct net *net = dev_net(skb->dev);
4878610c7c6SAlexander Aring 	struct inet6_dev *idev;
4888610c7c6SAlexander Aring 	struct ipv6hdr *oldhdr;
4898610c7c6SAlexander Aring 	unsigned char *buf;
4908610c7c6SAlexander Aring 	int accept_rpl_seg;
4918610c7c6SAlexander Aring 	int i, err;
4928610c7c6SAlexander Aring 	u64 n = 0;
4938610c7c6SAlexander Aring 	u32 r;
4948610c7c6SAlexander Aring 
4958610c7c6SAlexander Aring 	idev = __in6_dev_get(skb->dev);
4968610c7c6SAlexander Aring 
4978610c7c6SAlexander Aring 	accept_rpl_seg = net->ipv6.devconf_all->rpl_seg_enabled;
4988610c7c6SAlexander Aring 	if (accept_rpl_seg > idev->cnf.rpl_seg_enabled)
4998610c7c6SAlexander Aring 		accept_rpl_seg = idev->cnf.rpl_seg_enabled;
5008610c7c6SAlexander Aring 
5018610c7c6SAlexander Aring 	if (!accept_rpl_seg) {
5028610c7c6SAlexander Aring 		kfree_skb(skb);
5038610c7c6SAlexander Aring 		return -1;
5048610c7c6SAlexander Aring 	}
5058610c7c6SAlexander Aring 
5068610c7c6SAlexander Aring looped_back:
5078610c7c6SAlexander Aring 	hdr = (struct ipv6_rpl_sr_hdr *)skb_transport_header(skb);
5088610c7c6SAlexander Aring 
5098610c7c6SAlexander Aring 	if (hdr->segments_left == 0) {
5108610c7c6SAlexander Aring 		if (hdr->nexthdr == NEXTHDR_IPV6) {
5118610c7c6SAlexander Aring 			int offset = (hdr->hdrlen + 1) << 3;
5128610c7c6SAlexander Aring 
5138610c7c6SAlexander Aring 			skb_postpull_rcsum(skb, skb_network_header(skb),
5148610c7c6SAlexander Aring 					   skb_network_header_len(skb));
515ac9d8a66SKuniyuki Iwashima 			skb_pull(skb, offset);
5168610c7c6SAlexander Aring 			skb_postpull_rcsum(skb, skb_transport_header(skb),
5178610c7c6SAlexander Aring 					   offset);
5188610c7c6SAlexander Aring 
5198610c7c6SAlexander Aring 			skb_reset_network_header(skb);
5208610c7c6SAlexander Aring 			skb_reset_transport_header(skb);
5218610c7c6SAlexander Aring 			skb->encapsulation = 0;
5228610c7c6SAlexander Aring 
5238610c7c6SAlexander Aring 			__skb_tunnel_rx(skb, skb->dev, net);
5248610c7c6SAlexander Aring 
5258610c7c6SAlexander Aring 			netif_rx(skb);
5268610c7c6SAlexander Aring 			return -1;
5278610c7c6SAlexander Aring 		}
5288610c7c6SAlexander Aring 
5298610c7c6SAlexander Aring 		opt->srcrt = skb_network_header_len(skb);
5308610c7c6SAlexander Aring 		opt->lastopt = opt->srcrt;
5318610c7c6SAlexander Aring 		skb->transport_header += (hdr->hdrlen + 1) << 3;
5328610c7c6SAlexander Aring 		opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb);
5338610c7c6SAlexander Aring 
5348610c7c6SAlexander Aring 		return 1;
5358610c7c6SAlexander Aring 	}
5368610c7c6SAlexander Aring 
5378610c7c6SAlexander Aring 	n = (hdr->hdrlen << 3) - hdr->pad - (16 - hdr->cmpre);
5388610c7c6SAlexander Aring 	r = do_div(n, (16 - hdr->cmpri));
5398610c7c6SAlexander Aring 	/* checks if calculation was without remainder and n fits into
5408610c7c6SAlexander Aring 	 * unsigned char which is segments_left field. Should not be
5418610c7c6SAlexander Aring 	 * higher than that.
5428610c7c6SAlexander Aring 	 */
5438610c7c6SAlexander Aring 	if (r || (n + 1) > 255) {
5448610c7c6SAlexander Aring 		kfree_skb(skb);
5458610c7c6SAlexander Aring 		return -1;
5468610c7c6SAlexander Aring 	}
5478610c7c6SAlexander Aring 
5488610c7c6SAlexander Aring 	if (hdr->segments_left > n + 1) {
5498610c7c6SAlexander Aring 		__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
5508610c7c6SAlexander Aring 		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
5518610c7c6SAlexander Aring 				  ((&hdr->segments_left) -
5528610c7c6SAlexander Aring 				   skb_network_header(skb)));
5538610c7c6SAlexander Aring 		return -1;
5548610c7c6SAlexander Aring 	}
5558610c7c6SAlexander Aring 
5568610c7c6SAlexander Aring 	hdr->segments_left--;
5578610c7c6SAlexander Aring 	i = n - hdr->segments_left;
5588610c7c6SAlexander Aring 
5596f393457SGustavo A. R. Silva 	buf = kcalloc(struct_size(hdr, segments.addr, n + 2), 2, GFP_ATOMIC);
5608610c7c6SAlexander Aring 	if (unlikely(!buf)) {
5618610c7c6SAlexander Aring 		kfree_skb(skb);
5628610c7c6SAlexander Aring 		return -1;
5638610c7c6SAlexander Aring 	}
5648610c7c6SAlexander Aring 
5658610c7c6SAlexander Aring 	ohdr = (struct ipv6_rpl_sr_hdr *)buf;
5668610c7c6SAlexander Aring 	ipv6_rpl_srh_decompress(ohdr, hdr, &ipv6_hdr(skb)->daddr, n);
5678610c7c6SAlexander Aring 	chdr = (struct ipv6_rpl_sr_hdr *)(buf + ((ohdr->hdrlen + 1) << 3));
5688610c7c6SAlexander Aring 
5696facbca5SKuniyuki Iwashima 	if (ipv6_addr_is_multicast(&ohdr->rpl_segaddr[i])) {
5708610c7c6SAlexander Aring 		kfree_skb(skb);
5718610c7c6SAlexander Aring 		kfree(buf);
5728610c7c6SAlexander Aring 		return -1;
5738610c7c6SAlexander Aring 	}
5748610c7c6SAlexander Aring 
5758610c7c6SAlexander Aring 	err = ipv6_chk_rpl_srh_loop(net, ohdr->rpl_segaddr, n + 1);
5768610c7c6SAlexander Aring 	if (err) {
5778610c7c6SAlexander Aring 		icmpv6_send(skb, ICMPV6_PARAMPROB, 0, 0);
5788610c7c6SAlexander Aring 		kfree_skb(skb);
5798610c7c6SAlexander Aring 		kfree(buf);
5808610c7c6SAlexander Aring 		return -1;
5818610c7c6SAlexander Aring 	}
5828610c7c6SAlexander Aring 
5835ee6ad1dSGuo Zhengkui 	swap(ipv6_hdr(skb)->daddr, ohdr->rpl_segaddr[i]);
5848610c7c6SAlexander Aring 
5858610c7c6SAlexander Aring 	ipv6_rpl_srh_compress(chdr, ohdr, &ipv6_hdr(skb)->daddr, n);
5868610c7c6SAlexander Aring 
5878610c7c6SAlexander Aring 	oldhdr = ipv6_hdr(skb);
5888610c7c6SAlexander Aring 
5898610c7c6SAlexander Aring 	skb_pull(skb, ((hdr->hdrlen + 1) << 3));
5908610c7c6SAlexander Aring 	skb_postpull_rcsum(skb, oldhdr,
5918610c7c6SAlexander Aring 			   sizeof(struct ipv6hdr) + ((hdr->hdrlen + 1) << 3));
592a2f4c143SKuniyuki Iwashima 	if (unlikely(!hdr->segments_left)) {
593a2f4c143SKuniyuki Iwashima 		if (pskb_expand_head(skb, sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3), 0,
594a2f4c143SKuniyuki Iwashima 				     GFP_ATOMIC)) {
595a2f4c143SKuniyuki Iwashima 			__IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_OUTDISCARDS);
596a2f4c143SKuniyuki Iwashima 			kfree_skb(skb);
597a2f4c143SKuniyuki Iwashima 			kfree(buf);
598a2f4c143SKuniyuki Iwashima 			return -1;
599a2f4c143SKuniyuki Iwashima 		}
600a2f4c143SKuniyuki Iwashima 
601a2f4c143SKuniyuki Iwashima 		oldhdr = ipv6_hdr(skb);
602a2f4c143SKuniyuki Iwashima 	}
6038610c7c6SAlexander Aring 	skb_push(skb, ((chdr->hdrlen + 1) << 3) + sizeof(struct ipv6hdr));
6048610c7c6SAlexander Aring 	skb_reset_network_header(skb);
6058610c7c6SAlexander Aring 	skb_mac_header_rebuild(skb);
6068610c7c6SAlexander Aring 	skb_set_transport_header(skb, sizeof(struct ipv6hdr));
6078610c7c6SAlexander Aring 
6088610c7c6SAlexander Aring 	memmove(ipv6_hdr(skb), oldhdr, sizeof(struct ipv6hdr));
6098610c7c6SAlexander Aring 	memcpy(skb_transport_header(skb), chdr, (chdr->hdrlen + 1) << 3);
6108610c7c6SAlexander Aring 
6118610c7c6SAlexander Aring 	ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
6128610c7c6SAlexander Aring 	skb_postpush_rcsum(skb, ipv6_hdr(skb),
6138610c7c6SAlexander Aring 			   sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3));
6148610c7c6SAlexander Aring 
6158610c7c6SAlexander Aring 	kfree(buf);
6168610c7c6SAlexander Aring 
6178610c7c6SAlexander Aring 	ip6_route_input(skb);
6188610c7c6SAlexander Aring 
6198610c7c6SAlexander Aring 	if (skb_dst(skb)->error) {
6208610c7c6SAlexander Aring 		dst_input(skb);
6218610c7c6SAlexander Aring 		return -1;
6228610c7c6SAlexander Aring 	}
6238610c7c6SAlexander Aring 
6248610c7c6SAlexander Aring 	if (skb_dst(skb)->dev->flags & IFF_LOOPBACK) {
6258610c7c6SAlexander Aring 		if (ipv6_hdr(skb)->hop_limit <= 1) {
6268610c7c6SAlexander Aring 			__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
6278610c7c6SAlexander Aring 			icmpv6_send(skb, ICMPV6_TIME_EXCEED,
6288610c7c6SAlexander Aring 				    ICMPV6_EXC_HOPLIMIT, 0);
6298610c7c6SAlexander Aring 			kfree_skb(skb);
6308610c7c6SAlexander Aring 			return -1;
6318610c7c6SAlexander Aring 		}
6328610c7c6SAlexander Aring 		ipv6_hdr(skb)->hop_limit--;
6338610c7c6SAlexander Aring 
6348610c7c6SAlexander Aring 		skb_pull(skb, sizeof(struct ipv6hdr));
6358610c7c6SAlexander Aring 		goto looped_back;
6368610c7c6SAlexander Aring 	}
6378610c7c6SAlexander Aring 
6388610c7c6SAlexander Aring 	dst_input(skb);
6398610c7c6SAlexander Aring 
6408610c7c6SAlexander Aring 	return -1;
6418610c7c6SAlexander Aring }
6428610c7c6SAlexander Aring 
6431da177e4SLinus Torvalds /********************************
6441da177e4SLinus Torvalds   Routing header.
6451da177e4SLinus Torvalds  ********************************/
6461da177e4SLinus Torvalds 
647f6bc7d9eSEric Dumazet /* called with rcu_read_lock() */
ipv6_rthdr_rcv(struct sk_buff * skb)648e5bbef20SHerbert Xu static int ipv6_rthdr_rcv(struct sk_buff *skb)
6491da177e4SLinus Torvalds {
650bdb7cc64SStephen Suryaputra 	struct inet6_dev *idev = __in6_dev_get(skb->dev);
6511da177e4SLinus Torvalds 	struct inet6_skb_parm *opt = IP6CB(skb);
65265d4ed92SMasahide NAKAMURA 	struct in6_addr *addr = NULL;
6531da177e4SLinus Torvalds 	int n, i;
6541da177e4SLinus Torvalds 	struct ipv6_rt_hdr *hdr;
6551da177e4SLinus Torvalds 	struct rt0_hdr *rthdr;
656483a47d2SDenis V. Lunev 	struct net *net = dev_net(skb->dev);
657483a47d2SDenis V. Lunev 	int accept_source_route = net->ipv6.devconf_all->accept_source_route;
6580bcbc926SYOSHIFUJI Hideaki 
659f6bc7d9eSEric Dumazet 	if (idev && accept_source_route > idev->cnf.accept_source_route)
6600bcbc926SYOSHIFUJI Hideaki 		accept_source_route = idev->cnf.accept_source_route;
6611da177e4SLinus Torvalds 
662ea2ae17dSArnaldo Carvalho de Melo 	if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) ||
663ea2ae17dSArnaldo Carvalho de Melo 	    !pskb_may_pull(skb, (skb_transport_offset(skb) +
6649c70220bSArnaldo Carvalho de Melo 				 ((skb_transport_header(skb)[1] + 1) << 3)))) {
665bdb7cc64SStephen Suryaputra 		__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
6661da177e4SLinus Torvalds 		kfree_skb(skb);
6671da177e4SLinus Torvalds 		return -1;
6681da177e4SLinus Torvalds 	}
6691da177e4SLinus Torvalds 
6709c70220bSArnaldo Carvalho de Melo 	hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb);
6711da177e4SLinus Torvalds 
6720660e03fSArnaldo Carvalho de Melo 	if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) ||
6731da177e4SLinus Torvalds 	    skb->pkt_type != PACKET_HOST) {
674bdb7cc64SStephen Suryaputra 		__IP6_INC_STATS(net, idev, IPSTATS_MIB_INADDRERRORS);
6751da177e4SLinus Torvalds 		kfree_skb(skb);
6761da177e4SLinus Torvalds 		return -1;
6771da177e4SLinus Torvalds 	}
6781da177e4SLinus Torvalds 
6798610c7c6SAlexander Aring 	switch (hdr->type) {
6808610c7c6SAlexander Aring 	case IPV6_SRCRT_TYPE_4:
6811ababebaSDavid Lebrun 		/* segment routing */
6821ababebaSDavid Lebrun 		return ipv6_srh_rcv(skb);
6838610c7c6SAlexander Aring 	case IPV6_SRCRT_TYPE_3:
6848610c7c6SAlexander Aring 		/* rpl segment routing */
6858610c7c6SAlexander Aring 		return ipv6_rpl_srh_rcv(skb);
6868610c7c6SAlexander Aring 	default:
6878610c7c6SAlexander Aring 		break;
6888610c7c6SAlexander Aring 	}
6891ababebaSDavid Lebrun 
6901da177e4SLinus Torvalds looped_back:
6911da177e4SLinus Torvalds 	if (hdr->segments_left == 0) {
69265d4ed92SMasahide NAKAMURA 		switch (hdr->type) {
69307a93626SAmerigo Wang #if IS_ENABLED(CONFIG_IPV6_MIP6)
69465d4ed92SMasahide NAKAMURA 		case IPV6_SRCRT_TYPE_2:
69565d4ed92SMasahide NAKAMURA 			/* Silently discard type 2 header unless it was
69665d4ed92SMasahide NAKAMURA 			 * processed by own
69765d4ed92SMasahide NAKAMURA 			 */
69865d4ed92SMasahide NAKAMURA 			if (!addr) {
699bdb7cc64SStephen Suryaputra 				__IP6_INC_STATS(net, idev,
700a11d206dSYOSHIFUJI Hideaki 						IPSTATS_MIB_INADDRERRORS);
70165d4ed92SMasahide NAKAMURA 				kfree_skb(skb);
70265d4ed92SMasahide NAKAMURA 				return -1;
70365d4ed92SMasahide NAKAMURA 			}
70465d4ed92SMasahide NAKAMURA 			break;
70565d4ed92SMasahide NAKAMURA #endif
70665d4ed92SMasahide NAKAMURA 		default:
70765d4ed92SMasahide NAKAMURA 			break;
70865d4ed92SMasahide NAKAMURA 		}
70965d4ed92SMasahide NAKAMURA 
710cfe1fc77SArnaldo Carvalho de Melo 		opt->lastopt = opt->srcrt = skb_network_header_len(skb);
711b0e380b1SArnaldo Carvalho de Melo 		skb->transport_header += (hdr->hdrlen + 1) << 3;
7121da177e4SLinus Torvalds 		opt->dst0 = opt->dst1;
7131da177e4SLinus Torvalds 		opt->dst1 = 0;
714d56f90a7SArnaldo Carvalho de Melo 		opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb);
7151da177e4SLinus Torvalds 		return 1;
7161da177e4SLinus Torvalds 	}
7171da177e4SLinus Torvalds 
71865d4ed92SMasahide NAKAMURA 	switch (hdr->type) {
71907a93626SAmerigo Wang #if IS_ENABLED(CONFIG_IPV6_MIP6)
72065d4ed92SMasahide NAKAMURA 	case IPV6_SRCRT_TYPE_2:
721c382bb9dSYOSHIFUJI Hideaki 		if (accept_source_route < 0)
722c382bb9dSYOSHIFUJI Hideaki 			goto unknown_rh;
72365d4ed92SMasahide NAKAMURA 		/* Silently discard invalid RTH type 2 */
72465d4ed92SMasahide NAKAMURA 		if (hdr->hdrlen != 2 || hdr->segments_left != 1) {
725bdb7cc64SStephen Suryaputra 			__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
72665d4ed92SMasahide NAKAMURA 			kfree_skb(skb);
72765d4ed92SMasahide NAKAMURA 			return -1;
72865d4ed92SMasahide NAKAMURA 		}
72965d4ed92SMasahide NAKAMURA 		break;
73065d4ed92SMasahide NAKAMURA #endif
731c382bb9dSYOSHIFUJI Hideaki 	default:
732c382bb9dSYOSHIFUJI Hideaki 		goto unknown_rh;
73365d4ed92SMasahide NAKAMURA 	}
7341da177e4SLinus Torvalds 
7351da177e4SLinus Torvalds 	/*
7361da177e4SLinus Torvalds 	 *	This is the routing header forwarding algorithm from
7371da177e4SLinus Torvalds 	 *	RFC 2460, page 16.
7381da177e4SLinus Torvalds 	 */
7391da177e4SLinus Torvalds 
7401da177e4SLinus Torvalds 	n = hdr->hdrlen >> 1;
7411da177e4SLinus Torvalds 
7421da177e4SLinus Torvalds 	if (hdr->segments_left > n) {
743bdb7cc64SStephen Suryaputra 		__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
744d56f90a7SArnaldo Carvalho de Melo 		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
745d56f90a7SArnaldo Carvalho de Melo 				  ((&hdr->segments_left) -
746d56f90a7SArnaldo Carvalho de Melo 				   skb_network_header(skb)));
7471da177e4SLinus Torvalds 		return -1;
7481da177e4SLinus Torvalds 	}
7491da177e4SLinus Torvalds 
7501da177e4SLinus Torvalds 	/* We are about to mangle packet header. Be careful!
7511da177e4SLinus Torvalds 	   Do not damage packets queued somewhere.
7521da177e4SLinus Torvalds 	 */
7531da177e4SLinus Torvalds 	if (skb_cloned(skb)) {
7541da177e4SLinus Torvalds 		/* the copy is a forwarded packet */
75565c88466SHerbert Xu 		if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) {
7561d015503SEric Dumazet 			__IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
757a11d206dSYOSHIFUJI Hideaki 					IPSTATS_MIB_OUTDISCARDS);
758a11d206dSYOSHIFUJI Hideaki 			kfree_skb(skb);
7591da177e4SLinus Torvalds 			return -1;
7601da177e4SLinus Torvalds 		}
76165c88466SHerbert Xu 		hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb);
7621da177e4SLinus Torvalds 	}
7631da177e4SLinus Torvalds 
76484fa7933SPatrick McHardy 	if (skb->ip_summed == CHECKSUM_COMPLETE)
7651da177e4SLinus Torvalds 		skb->ip_summed = CHECKSUM_NONE;
7661da177e4SLinus Torvalds 
7671da177e4SLinus Torvalds 	i = n - --hdr->segments_left;
7681da177e4SLinus Torvalds 
7691da177e4SLinus Torvalds 	rthdr = (struct rt0_hdr *) hdr;
7701da177e4SLinus Torvalds 	addr = rthdr->addr;
7711da177e4SLinus Torvalds 	addr += i - 1;
7721da177e4SLinus Torvalds 
77365d4ed92SMasahide NAKAMURA 	switch (hdr->type) {
77407a93626SAmerigo Wang #if IS_ENABLED(CONFIG_IPV6_MIP6)
77565d4ed92SMasahide NAKAMURA 	case IPV6_SRCRT_TYPE_2:
77665d4ed92SMasahide NAKAMURA 		if (xfrm6_input_addr(skb, (xfrm_address_t *)addr,
7770660e03fSArnaldo Carvalho de Melo 				     (xfrm_address_t *)&ipv6_hdr(skb)->saddr,
77865d4ed92SMasahide NAKAMURA 				     IPPROTO_ROUTING) < 0) {
779bdb7cc64SStephen Suryaputra 			__IP6_INC_STATS(net, idev, IPSTATS_MIB_INADDRERRORS);
78065d4ed92SMasahide NAKAMURA 			kfree_skb(skb);
78165d4ed92SMasahide NAKAMURA 			return -1;
78265d4ed92SMasahide NAKAMURA 		}
783adf30907SEric Dumazet 		if (!ipv6_chk_home_addr(dev_net(skb_dst(skb)->dev), addr)) {
784bdb7cc64SStephen Suryaputra 			__IP6_INC_STATS(net, idev, IPSTATS_MIB_INADDRERRORS);
78565d4ed92SMasahide NAKAMURA 			kfree_skb(skb);
78665d4ed92SMasahide NAKAMURA 			return -1;
78765d4ed92SMasahide NAKAMURA 		}
78865d4ed92SMasahide NAKAMURA 		break;
78965d4ed92SMasahide NAKAMURA #endif
79065d4ed92SMasahide NAKAMURA 	default:
79165d4ed92SMasahide NAKAMURA 		break;
79265d4ed92SMasahide NAKAMURA 	}
79365d4ed92SMasahide NAKAMURA 
7941da177e4SLinus Torvalds 	if (ipv6_addr_is_multicast(addr)) {
795bdb7cc64SStephen Suryaputra 		__IP6_INC_STATS(net, idev, IPSTATS_MIB_INADDRERRORS);
7961da177e4SLinus Torvalds 		kfree_skb(skb);
7971da177e4SLinus Torvalds 		return -1;
7981da177e4SLinus Torvalds 	}
7991da177e4SLinus Torvalds 
800794529c4SZiyang Xuan 	swap(*addr, ipv6_hdr(skb)->daddr);
8011da177e4SLinus Torvalds 
8021da177e4SLinus Torvalds 	ip6_route_input(skb);
803adf30907SEric Dumazet 	if (skb_dst(skb)->error) {
804d56f90a7SArnaldo Carvalho de Melo 		skb_push(skb, skb->data - skb_network_header(skb));
8051da177e4SLinus Torvalds 		dst_input(skb);
8061da177e4SLinus Torvalds 		return -1;
8071da177e4SLinus Torvalds 	}
8081da177e4SLinus Torvalds 
809adf30907SEric Dumazet 	if (skb_dst(skb)->dev->flags&IFF_LOOPBACK) {
8100660e03fSArnaldo Carvalho de Melo 		if (ipv6_hdr(skb)->hop_limit <= 1) {
811bdb7cc64SStephen Suryaputra 			__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
8121da177e4SLinus Torvalds 			icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
8133ffe533cSAlexey Dobriyan 				    0);
8141da177e4SLinus Torvalds 			kfree_skb(skb);
8151da177e4SLinus Torvalds 			return -1;
8161da177e4SLinus Torvalds 		}
8170660e03fSArnaldo Carvalho de Melo 		ipv6_hdr(skb)->hop_limit--;
8181da177e4SLinus Torvalds 		goto looped_back;
8191da177e4SLinus Torvalds 	}
8201da177e4SLinus Torvalds 
821d56f90a7SArnaldo Carvalho de Melo 	skb_push(skb, skb->data - skb_network_header(skb));
8221da177e4SLinus Torvalds 	dst_input(skb);
8231da177e4SLinus Torvalds 	return -1;
824c382bb9dSYOSHIFUJI Hideaki 
825c382bb9dSYOSHIFUJI Hideaki unknown_rh:
826bdb7cc64SStephen Suryaputra 	__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
827c382bb9dSYOSHIFUJI Hideaki 	icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
828c382bb9dSYOSHIFUJI Hideaki 			  (&hdr->type) - skb_network_header(skb));
829c382bb9dSYOSHIFUJI Hideaki 	return -1;
8301da177e4SLinus Torvalds }
8311da177e4SLinus Torvalds 
83241135cc8SAlexey Dobriyan static const struct inet6_protocol rthdr_protocol = {
8331da177e4SLinus Torvalds 	.handler	=	ipv6_rthdr_rcv,
8342207afc8SVlad Yasevich 	.flags		=	INET6_PROTO_NOPOLICY,
8358ca896cfSVlad Yasevich };
8368ca896cfSVlad Yasevich 
83741135cc8SAlexey Dobriyan static const struct inet6_protocol destopt_protocol = {
838248b238dSDaniel Lezcano 	.handler	=	ipv6_destopt_rcv,
8392207afc8SVlad Yasevich 	.flags		=	INET6_PROTO_NOPOLICY,
8408ca896cfSVlad Yasevich };
8418ca896cfSVlad Yasevich 
84241135cc8SAlexey Dobriyan static const struct inet6_protocol nodata_protocol = {
843248b238dSDaniel Lezcano 	.handler	=	dst_discard,
844248b238dSDaniel Lezcano 	.flags		=	INET6_PROTO_NOPOLICY,
845248b238dSDaniel Lezcano };
846248b238dSDaniel Lezcano 
ipv6_exthdrs_init(void)847248b238dSDaniel Lezcano int __init ipv6_exthdrs_init(void)
848248b238dSDaniel Lezcano {
849248b238dSDaniel Lezcano 	int ret;
850248b238dSDaniel Lezcano 
8513336288aSVlad Yasevich 	ret = inet6_add_protocol(&rthdr_protocol, IPPROTO_ROUTING);
8523336288aSVlad Yasevich 	if (ret)
853c6b641a4SVlad Yasevich 		goto out;
8543336288aSVlad Yasevich 
855248b238dSDaniel Lezcano 	ret = inet6_add_protocol(&destopt_protocol, IPPROTO_DSTOPTS);
856248b238dSDaniel Lezcano 	if (ret)
857248b238dSDaniel Lezcano 		goto out_rthdr;
858248b238dSDaniel Lezcano 
859248b238dSDaniel Lezcano 	ret = inet6_add_protocol(&nodata_protocol, IPPROTO_NONE);
860248b238dSDaniel Lezcano 	if (ret)
861248b238dSDaniel Lezcano 		goto out_destopt;
862248b238dSDaniel Lezcano 
863248b238dSDaniel Lezcano out:
864248b238dSDaniel Lezcano 	return ret;
865248b238dSDaniel Lezcano out_destopt:
866248b238dSDaniel Lezcano 	inet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS);
8673336288aSVlad Yasevich out_rthdr:
8683336288aSVlad Yasevich 	inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING);
869248b238dSDaniel Lezcano 	goto out;
870248b238dSDaniel Lezcano };
871248b238dSDaniel Lezcano 
ipv6_exthdrs_exit(void)872248b238dSDaniel Lezcano void ipv6_exthdrs_exit(void)
873248b238dSDaniel Lezcano {
874248b238dSDaniel Lezcano 	inet6_del_protocol(&nodata_protocol, IPPROTO_NONE);
875248b238dSDaniel Lezcano 	inet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS);
876248b238dSDaniel Lezcano 	inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING);
877248b238dSDaniel Lezcano }
878248b238dSDaniel Lezcano 
8791da177e4SLinus Torvalds /**********************************
8801da177e4SLinus Torvalds   Hop-by-hop options.
8811da177e4SLinus Torvalds  **********************************/
8821da177e4SLinus Torvalds 
883e76b2b25SYOSHIFUJI Hideaki /*
884adf30907SEric Dumazet  * Note: we cannot rely on skb_dst(skb) before we assign it in ip6_route_input().
885e76b2b25SYOSHIFUJI Hideaki  */
ipv6_skb_net(struct sk_buff * skb)8862570a4f5SDavid S. Miller static inline struct net *ipv6_skb_net(struct sk_buff *skb)
8872570a4f5SDavid S. Miller {
8882570a4f5SDavid S. Miller 	return skb_dst(skb) ? dev_net(skb_dst(skb)->dev) : dev_net(skb->dev);
8892570a4f5SDavid S. Miller }
8902570a4f5SDavid S. Miller 
8911da177e4SLinus Torvalds /* Router Alert as of RFC 2711 */
8921da177e4SLinus Torvalds 
ipv6_hop_ra(struct sk_buff * skb,int optoff)893a50feda5SEric Dumazet static bool ipv6_hop_ra(struct sk_buff *skb, int optoff)
8941da177e4SLinus Torvalds {
895d56f90a7SArnaldo Carvalho de Melo 	const unsigned char *nh = skb_network_header(skb);
896a80ff03eSMasahide NAKAMURA 
897d56f90a7SArnaldo Carvalho de Melo 	if (nh[optoff + 1] == 2) {
898dd3332bfSYOSHIFUJI Hideaki / 吉藤英明 		IP6CB(skb)->flags |= IP6SKB_ROUTERALERT;
899dd3332bfSYOSHIFUJI Hideaki / 吉藤英明 		memcpy(&IP6CB(skb)->ra, nh + optoff + 2, sizeof(IP6CB(skb)->ra));
900a50feda5SEric Dumazet 		return true;
9011da177e4SLinus Torvalds 	}
902ba7a46f1SJoe Perches 	net_dbg_ratelimited("ipv6_hop_ra: wrong RA length %d\n",
903d56f90a7SArnaldo Carvalho de Melo 			    nh[optoff + 1]);
9047d9dbdfbSMenglong Dong 	kfree_skb_reason(skb, SKB_DROP_REASON_IP_INHDR);
905a50feda5SEric Dumazet 	return false;
9061da177e4SLinus Torvalds }
9071da177e4SLinus Torvalds 
9089ee11f0fSJustin Iurman /* IOAM */
9099ee11f0fSJustin Iurman 
ipv6_hop_ioam(struct sk_buff * skb,int optoff)9109ee11f0fSJustin Iurman static bool ipv6_hop_ioam(struct sk_buff *skb, int optoff)
9119ee11f0fSJustin Iurman {
9129ee11f0fSJustin Iurman 	struct ioam6_trace_hdr *trace;
9139ee11f0fSJustin Iurman 	struct ioam6_namespace *ns;
9149ee11f0fSJustin Iurman 	struct ioam6_hdr *hdr;
9159ee11f0fSJustin Iurman 
9169ee11f0fSJustin Iurman 	/* Bad alignment (must be 4n-aligned) */
9179ee11f0fSJustin Iurman 	if (optoff & 3)
9189ee11f0fSJustin Iurman 		goto drop;
9199ee11f0fSJustin Iurman 
9209ee11f0fSJustin Iurman 	/* Ignore if IOAM is not enabled on ingress */
9219ee11f0fSJustin Iurman 	if (!__in6_dev_get(skb->dev)->cnf.ioam6_enabled)
9229ee11f0fSJustin Iurman 		goto ignore;
9239ee11f0fSJustin Iurman 
9249ee11f0fSJustin Iurman 	/* Truncated Option header */
9259ee11f0fSJustin Iurman 	hdr = (struct ioam6_hdr *)(skb_network_header(skb) + optoff);
9269ee11f0fSJustin Iurman 	if (hdr->opt_len < 2)
9279ee11f0fSJustin Iurman 		goto drop;
9289ee11f0fSJustin Iurman 
9299ee11f0fSJustin Iurman 	switch (hdr->type) {
9309ee11f0fSJustin Iurman 	case IOAM6_TYPE_PREALLOC:
9319ee11f0fSJustin Iurman 		/* Truncated Pre-allocated Trace header */
9329ee11f0fSJustin Iurman 		if (hdr->opt_len < 2 + sizeof(*trace))
9339ee11f0fSJustin Iurman 			goto drop;
9349ee11f0fSJustin Iurman 
9359ee11f0fSJustin Iurman 		/* Malformed Pre-allocated Trace header */
9369ee11f0fSJustin Iurman 		trace = (struct ioam6_trace_hdr *)((u8 *)hdr + sizeof(*hdr));
9379ee11f0fSJustin Iurman 		if (hdr->opt_len < 2 + sizeof(*trace) + trace->remlen * 4)
9389ee11f0fSJustin Iurman 			goto drop;
9399ee11f0fSJustin Iurman 
9409ee11f0fSJustin Iurman 		/* Ignore if the IOAM namespace is unknown */
9419ee11f0fSJustin Iurman 		ns = ioam6_namespace(ipv6_skb_net(skb), trace->namespace_id);
9429ee11f0fSJustin Iurman 		if (!ns)
9439ee11f0fSJustin Iurman 			goto ignore;
9449ee11f0fSJustin Iurman 
9459ee11f0fSJustin Iurman 		if (!skb_valid_dst(skb))
9469ee11f0fSJustin Iurman 			ip6_route_input(skb);
9479ee11f0fSJustin Iurman 
948*8fbc1919SJustin Iurman 		/* About to mangle packet header */
949*8fbc1919SJustin Iurman 		if (skb_ensure_writable(skb, optoff + 2 + hdr->opt_len))
950*8fbc1919SJustin Iurman 			goto drop;
951*8fbc1919SJustin Iurman 
952*8fbc1919SJustin Iurman 		/* Trace pointer may have changed */
953*8fbc1919SJustin Iurman 		trace = (struct ioam6_trace_hdr *)(skb_network_header(skb)
954*8fbc1919SJustin Iurman 						   + optoff + sizeof(*hdr));
955*8fbc1919SJustin Iurman 
95652d03786SJustin Iurman 		ioam6_fill_trace_data(skb, ns, trace, true);
9579ee11f0fSJustin Iurman 		break;
9589ee11f0fSJustin Iurman 	default:
9599ee11f0fSJustin Iurman 		break;
9609ee11f0fSJustin Iurman 	}
9619ee11f0fSJustin Iurman 
9629ee11f0fSJustin Iurman ignore:
9639ee11f0fSJustin Iurman 	return true;
9649ee11f0fSJustin Iurman 
9659ee11f0fSJustin Iurman drop:
9667d9dbdfbSMenglong Dong 	kfree_skb_reason(skb, SKB_DROP_REASON_IP_INHDR);
9679ee11f0fSJustin Iurman 	return false;
9689ee11f0fSJustin Iurman }
9699ee11f0fSJustin Iurman 
9701da177e4SLinus Torvalds /* Jumbo payload */
9711da177e4SLinus Torvalds 
ipv6_hop_jumbo(struct sk_buff * skb,int optoff)972a50feda5SEric Dumazet static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
9731da177e4SLinus Torvalds {
974d56f90a7SArnaldo Carvalho de Melo 	const unsigned char *nh = skb_network_header(skb);
9757d9dbdfbSMenglong Dong 	SKB_DR(reason);
9761da177e4SLinus Torvalds 	u32 pkt_len;
9771da177e4SLinus Torvalds 
978d56f90a7SArnaldo Carvalho de Melo 	if (nh[optoff + 1] != 4 || (optoff & 3) != 2) {
979ba7a46f1SJoe Perches 		net_dbg_ratelimited("ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n",
980d56f90a7SArnaldo Carvalho de Melo 				    nh[optoff+1]);
9817d9dbdfbSMenglong Dong 		SKB_DR_SET(reason, IP_INHDR);
9821da177e4SLinus Torvalds 		goto drop;
9831da177e4SLinus Torvalds 	}
9841da177e4SLinus Torvalds 
985d56f90a7SArnaldo Carvalho de Melo 	pkt_len = ntohl(*(__be32 *)(nh + optoff + 2));
9861da177e4SLinus Torvalds 	if (pkt_len <= IPV6_MAXPLEN) {
9877d9dbdfbSMenglong Dong 		icmpv6_param_prob_reason(skb, ICMPV6_HDR_FIELD, optoff + 2,
9887d9dbdfbSMenglong Dong 					 SKB_DROP_REASON_IP_INHDR);
989a50feda5SEric Dumazet 		return false;
9901da177e4SLinus Torvalds 	}
9910660e03fSArnaldo Carvalho de Melo 	if (ipv6_hdr(skb)->payload_len) {
9927d9dbdfbSMenglong Dong 		icmpv6_param_prob_reason(skb, ICMPV6_HDR_FIELD, optoff,
9937d9dbdfbSMenglong Dong 					 SKB_DROP_REASON_IP_INHDR);
994a50feda5SEric Dumazet 		return false;
9951da177e4SLinus Torvalds 	}
9961da177e4SLinus Torvalds 
9977d9dbdfbSMenglong Dong 	if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
9987d9dbdfbSMenglong Dong 		SKB_DR_SET(reason, PKT_TOO_SMALL);
9991da177e4SLinus Torvalds 		goto drop;
10007d9dbdfbSMenglong Dong 	}
100142ca89c1SStephen Hemminger 
100242ca89c1SStephen Hemminger 	if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
100342ca89c1SStephen Hemminger 		goto drop;
100442ca89c1SStephen Hemminger 
1005cb891fa6SPaolo Abeni 	IP6CB(skb)->flags |= IP6SKB_JUMBOGRAM;
1006a50feda5SEric Dumazet 	return true;
10071da177e4SLinus Torvalds 
10081da177e4SLinus Torvalds drop:
10097d9dbdfbSMenglong Dong 	kfree_skb_reason(skb, reason);
1010a50feda5SEric Dumazet 	return false;
10111da177e4SLinus Torvalds }
10121da177e4SLinus Torvalds 
10132e532b70SHuw Davies /* CALIPSO RFC 5570 */
10142e532b70SHuw Davies 
ipv6_hop_calipso(struct sk_buff * skb,int optoff)10152e532b70SHuw Davies static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff)
10162e532b70SHuw Davies {
10172e532b70SHuw Davies 	const unsigned char *nh = skb_network_header(skb);
10182e532b70SHuw Davies 
10192e532b70SHuw Davies 	if (nh[optoff + 1] < 8)
10202e532b70SHuw Davies 		goto drop;
10212e532b70SHuw Davies 
10222e532b70SHuw Davies 	if (nh[optoff + 6] * 4 + 8 > nh[optoff + 1])
10232e532b70SHuw Davies 		goto drop;
10242e532b70SHuw Davies 
10252e532b70SHuw Davies 	if (!calipso_validate(skb, nh + optoff))
10262e532b70SHuw Davies 		goto drop;
10272e532b70SHuw Davies 
10282e532b70SHuw Davies 	return true;
10292e532b70SHuw Davies 
10302e532b70SHuw Davies drop:
10317d9dbdfbSMenglong Dong 	kfree_skb_reason(skb, SKB_DROP_REASON_IP_INHDR);
10322e532b70SHuw Davies 	return false;
10332e532b70SHuw Davies }
10342e532b70SHuw Davies 
ipv6_parse_hopopts(struct sk_buff * skb)1035e5bbef20SHerbert Xu int ipv6_parse_hopopts(struct sk_buff *skb)
10361da177e4SLinus Torvalds {
1037951dbc8aSPatrick McHardy 	struct inet6_skb_parm *opt = IP6CB(skb);
103847d3d7acSTom Herbert 	struct net *net = dev_net(skb->dev);
103947d3d7acSTom Herbert 	int extlen;
1040951dbc8aSPatrick McHardy 
1041ec670095SYOSHIFUJI Hideaki 	/*
1042d56f90a7SArnaldo Carvalho de Melo 	 * skb_network_header(skb) is equal to skb->data, and
1043cfe1fc77SArnaldo Carvalho de Melo 	 * skb_network_header_len(skb) is always equal to
1044ec670095SYOSHIFUJI Hideaki 	 * sizeof(struct ipv6hdr) by definition of
1045ec670095SYOSHIFUJI Hideaki 	 * hop-by-hop options.
1046ec670095SYOSHIFUJI Hideaki 	 */
1047ec670095SYOSHIFUJI Hideaki 	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + 8) ||
10489c70220bSArnaldo Carvalho de Melo 	    !pskb_may_pull(skb, (sizeof(struct ipv6hdr) +
10499c70220bSArnaldo Carvalho de Melo 				 ((skb_transport_header(skb)[1] + 1) << 3)))) {
105047d3d7acSTom Herbert fail_and_free:
1051ec670095SYOSHIFUJI Hideaki 		kfree_skb(skb);
1052ec670095SYOSHIFUJI Hideaki 		return -1;
1053ec670095SYOSHIFUJI Hideaki 	}
1054ec670095SYOSHIFUJI Hideaki 
105547d3d7acSTom Herbert 	extlen = (skb_transport_header(skb)[1] + 1) << 3;
105647d3d7acSTom Herbert 	if (extlen > net->ipv6.sysctl.max_hbh_opts_len)
105747d3d7acSTom Herbert 		goto fail_and_free;
105847d3d7acSTom Herbert 
10598b58a398SFlorian Westphal 	opt->flags |= IP6SKB_HOPBYHOP;
106051b8f812SEric Dumazet 	if (ip6_parse_tlv(true, skb, net->ipv6.sysctl.max_hbh_opts_cnt)) {
106147d3d7acSTom Herbert 		skb->transport_header += extlen;
1062dc435e6dSMasahide NAKAMURA 		opt = IP6CB(skb);
1063951dbc8aSPatrick McHardy 		opt->nhoff = sizeof(struct ipv6hdr);
1064b809739aSYOSHIFUJI Hideaki 		return 1;
1065951dbc8aSPatrick McHardy 	}
10661da177e4SLinus Torvalds 	return -1;
10671da177e4SLinus Torvalds }
10681da177e4SLinus Torvalds 
10691da177e4SLinus Torvalds /*
10701da177e4SLinus Torvalds  *	Creating outbound headers.
10711da177e4SLinus Torvalds  *
10721da177e4SLinus Torvalds  *	"build" functions work when skb is filled from head to tail (datagram)
10731da177e4SLinus Torvalds  *	"push"	functions work when headers are added from tail to head (tcp)
10741da177e4SLinus Torvalds  *
10751da177e4SLinus Torvalds  *	In both cases we assume, that caller reserved enough room
10761da177e4SLinus Torvalds  *	for headers.
10771da177e4SLinus Torvalds  */
10781da177e4SLinus Torvalds 
ipv6_push_rthdr0(struct sk_buff * skb,u8 * proto,struct ipv6_rt_hdr * opt,struct in6_addr ** addr_p,struct in6_addr * saddr)1079a149e7c7SDavid Lebrun static void ipv6_push_rthdr0(struct sk_buff *skb, u8 *proto,
10801da177e4SLinus Torvalds 			     struct ipv6_rt_hdr *opt,
1081613fa3caSDavid Lebrun 			     struct in6_addr **addr_p, struct in6_addr *saddr)
10821da177e4SLinus Torvalds {
10831da177e4SLinus Torvalds 	struct rt0_hdr *phdr, *ihdr;
10841da177e4SLinus Torvalds 	int hops;
10851da177e4SLinus Torvalds 
10861da177e4SLinus Torvalds 	ihdr = (struct rt0_hdr *) opt;
10871da177e4SLinus Torvalds 
1088d58ff351SJohannes Berg 	phdr = skb_push(skb, (ihdr->rt_hdr.hdrlen + 1) << 3);
10891da177e4SLinus Torvalds 	memcpy(phdr, ihdr, sizeof(struct rt0_hdr));
10901da177e4SLinus Torvalds 
10911da177e4SLinus Torvalds 	hops = ihdr->rt_hdr.hdrlen >> 1;
10921da177e4SLinus Torvalds 
10931da177e4SLinus Torvalds 	if (hops > 1)
10941da177e4SLinus Torvalds 		memcpy(phdr->addr, ihdr->addr + 1,
10951da177e4SLinus Torvalds 		       (hops - 1) * sizeof(struct in6_addr));
10961da177e4SLinus Torvalds 
10974e3fd7a0SAlexey Dobriyan 	phdr->addr[hops - 1] = **addr_p;
10981da177e4SLinus Torvalds 	*addr_p = ihdr->addr;
10991da177e4SLinus Torvalds 
11001da177e4SLinus Torvalds 	phdr->rt_hdr.nexthdr = *proto;
11011da177e4SLinus Torvalds 	*proto = NEXTHDR_ROUTING;
11021da177e4SLinus Torvalds }
11031da177e4SLinus Torvalds 
ipv6_push_rthdr4(struct sk_buff * skb,u8 * proto,struct ipv6_rt_hdr * opt,struct in6_addr ** addr_p,struct in6_addr * saddr)1104a149e7c7SDavid Lebrun static void ipv6_push_rthdr4(struct sk_buff *skb, u8 *proto,
1105a149e7c7SDavid Lebrun 			     struct ipv6_rt_hdr *opt,
1106a149e7c7SDavid Lebrun 			     struct in6_addr **addr_p, struct in6_addr *saddr)
1107a149e7c7SDavid Lebrun {
1108a149e7c7SDavid Lebrun 	struct ipv6_sr_hdr *sr_phdr, *sr_ihdr;
1109a149e7c7SDavid Lebrun 	int plen, hops;
1110a149e7c7SDavid Lebrun 
1111a149e7c7SDavid Lebrun 	sr_ihdr = (struct ipv6_sr_hdr *)opt;
1112a149e7c7SDavid Lebrun 	plen = (sr_ihdr->hdrlen + 1) << 3;
1113a149e7c7SDavid Lebrun 
1114d58ff351SJohannes Berg 	sr_phdr = skb_push(skb, plen);
1115a149e7c7SDavid Lebrun 	memcpy(sr_phdr, sr_ihdr, sizeof(struct ipv6_sr_hdr));
1116a149e7c7SDavid Lebrun 
1117a149e7c7SDavid Lebrun 	hops = sr_ihdr->first_segment + 1;
1118a149e7c7SDavid Lebrun 	memcpy(sr_phdr->segments + 1, sr_ihdr->segments + 1,
1119a149e7c7SDavid Lebrun 	       (hops - 1) * sizeof(struct in6_addr));
1120a149e7c7SDavid Lebrun 
1121a149e7c7SDavid Lebrun 	sr_phdr->segments[0] = **addr_p;
1122925615ceSDavid Lebrun 	*addr_p = &sr_ihdr->segments[sr_ihdr->segments_left];
1123a149e7c7SDavid Lebrun 
1124ccc12b11SMathieu Xhonneux 	if (sr_ihdr->hdrlen > hops * 2) {
1125ccc12b11SMathieu Xhonneux 		int tlvs_offset, tlvs_length;
1126ccc12b11SMathieu Xhonneux 
1127ccc12b11SMathieu Xhonneux 		tlvs_offset = (1 + hops * 2) << 3;
1128ccc12b11SMathieu Xhonneux 		tlvs_length = (sr_ihdr->hdrlen - hops * 2) << 3;
1129ccc12b11SMathieu Xhonneux 		memcpy((char *)sr_phdr + tlvs_offset,
1130ccc12b11SMathieu Xhonneux 		       (char *)sr_ihdr + tlvs_offset, tlvs_length);
1131ccc12b11SMathieu Xhonneux 	}
1132ccc12b11SMathieu Xhonneux 
1133a149e7c7SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC
1134a149e7c7SDavid Lebrun 	if (sr_has_hmac(sr_phdr)) {
1135a149e7c7SDavid Lebrun 		struct net *net = NULL;
1136a149e7c7SDavid Lebrun 
1137a149e7c7SDavid Lebrun 		if (skb->dev)
1138a149e7c7SDavid Lebrun 			net = dev_net(skb->dev);
1139a149e7c7SDavid Lebrun 		else if (skb->sk)
1140a149e7c7SDavid Lebrun 			net = sock_net(skb->sk);
1141a149e7c7SDavid Lebrun 
1142a149e7c7SDavid Lebrun 		WARN_ON(!net);
1143a149e7c7SDavid Lebrun 
1144a149e7c7SDavid Lebrun 		if (net)
1145a149e7c7SDavid Lebrun 			seg6_push_hmac(net, saddr, sr_phdr);
1146a149e7c7SDavid Lebrun 	}
1147a149e7c7SDavid Lebrun #endif
1148a149e7c7SDavid Lebrun 
1149a149e7c7SDavid Lebrun 	sr_phdr->nexthdr = *proto;
1150a149e7c7SDavid Lebrun 	*proto = NEXTHDR_ROUTING;
1151a149e7c7SDavid Lebrun }
1152a149e7c7SDavid Lebrun 
ipv6_push_rthdr(struct sk_buff * skb,u8 * proto,struct ipv6_rt_hdr * opt,struct in6_addr ** addr_p,struct in6_addr * saddr)1153a149e7c7SDavid Lebrun static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
1154a149e7c7SDavid Lebrun 			    struct ipv6_rt_hdr *opt,
1155a149e7c7SDavid Lebrun 			    struct in6_addr **addr_p, struct in6_addr *saddr)
1156a149e7c7SDavid Lebrun {
1157a149e7c7SDavid Lebrun 	switch (opt->type) {
1158a149e7c7SDavid Lebrun 	case IPV6_SRCRT_TYPE_0:
1159ec9c4215SSabrina Dubroca 	case IPV6_SRCRT_STRICT:
1160ec9c4215SSabrina Dubroca 	case IPV6_SRCRT_TYPE_2:
1161a149e7c7SDavid Lebrun 		ipv6_push_rthdr0(skb, proto, opt, addr_p, saddr);
1162a149e7c7SDavid Lebrun 		break;
1163a149e7c7SDavid Lebrun 	case IPV6_SRCRT_TYPE_4:
1164a149e7c7SDavid Lebrun 		ipv6_push_rthdr4(skb, proto, opt, addr_p, saddr);
1165a149e7c7SDavid Lebrun 		break;
1166a149e7c7SDavid Lebrun 	default:
1167a149e7c7SDavid Lebrun 		break;
1168a149e7c7SDavid Lebrun 	}
1169a149e7c7SDavid Lebrun }
1170a149e7c7SDavid Lebrun 
ipv6_push_exthdr(struct sk_buff * skb,u8 * proto,u8 type,struct ipv6_opt_hdr * opt)11711da177e4SLinus Torvalds static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt)
11721da177e4SLinus Torvalds {
1173d58ff351SJohannes Berg 	struct ipv6_opt_hdr *h = skb_push(skb, ipv6_optlen(opt));
11741da177e4SLinus Torvalds 
11751da177e4SLinus Torvalds 	memcpy(h, opt, ipv6_optlen(opt));
11761da177e4SLinus Torvalds 	h->nexthdr = *proto;
11771da177e4SLinus Torvalds 	*proto = type;
11781da177e4SLinus Torvalds }
11791da177e4SLinus Torvalds 
ipv6_push_nfrag_opts(struct sk_buff * skb,struct ipv6_txoptions * opt,u8 * proto,struct in6_addr ** daddr,struct in6_addr * saddr)11801da177e4SLinus Torvalds void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
11811da177e4SLinus Torvalds 			  u8 *proto,
1182613fa3caSDavid Lebrun 			  struct in6_addr **daddr, struct in6_addr *saddr)
11831da177e4SLinus Torvalds {
1184333fad53SYOSHIFUJI Hideaki 	if (opt->srcrt) {
1185613fa3caSDavid Lebrun 		ipv6_push_rthdr(skb, proto, opt->srcrt, daddr, saddr);
1186333fad53SYOSHIFUJI Hideaki 		/*
1187333fad53SYOSHIFUJI Hideaki 		 * IPV6_RTHDRDSTOPTS is ignored
1188333fad53SYOSHIFUJI Hideaki 		 * unless IPV6_RTHDR is set (RFC3542).
1189333fad53SYOSHIFUJI Hideaki 		 */
11901da177e4SLinus Torvalds 		if (opt->dst0opt)
11911da177e4SLinus Torvalds 			ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
1192333fad53SYOSHIFUJI Hideaki 	}
11931da177e4SLinus Torvalds 	if (opt->hopopt)
11941da177e4SLinus Torvalds 		ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
11951da177e4SLinus Torvalds }
11967159039aSYOSHIFUJI Hideaki 
ipv6_push_frag_opts(struct sk_buff * skb,struct ipv6_txoptions * opt,u8 * proto)11971da177e4SLinus Torvalds void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto)
11981da177e4SLinus Torvalds {
11991da177e4SLinus Torvalds 	if (opt->dst1opt)
12001da177e4SLinus Torvalds 		ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt);
12011da177e4SLinus Torvalds }
12025b8481faSDavid S. Miller EXPORT_SYMBOL(ipv6_push_frag_opts);
12031da177e4SLinus Torvalds 
12041da177e4SLinus Torvalds struct ipv6_txoptions *
ipv6_dup_options(struct sock * sk,struct ipv6_txoptions * opt)12051da177e4SLinus Torvalds ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
12061da177e4SLinus Torvalds {
12071da177e4SLinus Torvalds 	struct ipv6_txoptions *opt2;
12081da177e4SLinus Torvalds 
12091da177e4SLinus Torvalds 	opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC);
12101da177e4SLinus Torvalds 	if (opt2) {
12111da177e4SLinus Torvalds 		long dif = (char *)opt2 - (char *)opt;
12121da177e4SLinus Torvalds 		memcpy(opt2, opt, opt->tot_len);
12131da177e4SLinus Torvalds 		if (opt2->hopopt)
12141da177e4SLinus Torvalds 			*((char **)&opt2->hopopt) += dif;
12151da177e4SLinus Torvalds 		if (opt2->dst0opt)
12161da177e4SLinus Torvalds 			*((char **)&opt2->dst0opt) += dif;
12171da177e4SLinus Torvalds 		if (opt2->dst1opt)
12181da177e4SLinus Torvalds 			*((char **)&opt2->dst1opt) += dif;
12191da177e4SLinus Torvalds 		if (opt2->srcrt)
12201da177e4SLinus Torvalds 			*((char **)&opt2->srcrt) += dif;
12210aeea21aSReshetova, Elena 		refcount_set(&opt2->refcnt, 1);
12221da177e4SLinus Torvalds 	}
12231da177e4SLinus Torvalds 	return opt2;
12241da177e4SLinus Torvalds }
12253cf3dc6cSArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(ipv6_dup_options);
12263cf3dc6cSArnaldo Carvalho de Melo 
ipv6_renew_option(int renewtype,struct ipv6_opt_hdr ** dest,struct ipv6_opt_hdr * old,struct ipv6_opt_hdr * new,int newtype,char ** p)1227a9ba23d4SPaul Moore static void ipv6_renew_option(int renewtype,
1228a9ba23d4SPaul Moore 			      struct ipv6_opt_hdr **dest,
1229a9ba23d4SPaul Moore 			      struct ipv6_opt_hdr *old,
1230a9ba23d4SPaul Moore 			      struct ipv6_opt_hdr *new,
1231a9ba23d4SPaul Moore 			      int newtype, char **p)
1232333fad53SYOSHIFUJI Hideaki {
1233a9ba23d4SPaul Moore 	struct ipv6_opt_hdr *src;
1234a9ba23d4SPaul Moore 
1235a9ba23d4SPaul Moore 	src = (renewtype == newtype ? new : old);
1236a9ba23d4SPaul Moore 	if (!src)
1237a9ba23d4SPaul Moore 		return;
1238a9ba23d4SPaul Moore 
1239a9ba23d4SPaul Moore 	memcpy(*p, src, ipv6_optlen(src));
1240a9ba23d4SPaul Moore 	*dest = (struct ipv6_opt_hdr *)*p;
1241a9ba23d4SPaul Moore 	*p += CMSG_ALIGN(ipv6_optlen(*dest));
1242333fad53SYOSHIFUJI Hideaki }
1243333fad53SYOSHIFUJI Hideaki 
1244e67ae213SHuw Davies /**
1245e67ae213SHuw Davies  * ipv6_renew_options - replace a specific ext hdr with a new one.
1246e67ae213SHuw Davies  *
1247e67ae213SHuw Davies  * @sk: sock from which to allocate memory
1248e67ae213SHuw Davies  * @opt: original options
1249e67ae213SHuw Davies  * @newtype: option type to replace in @opt
1250e67ae213SHuw Davies  * @newopt: new option of type @newtype to replace (user-mem)
1251e67ae213SHuw Davies  *
1252e67ae213SHuw Davies  * Returns a new set of options which is a copy of @opt with the
1253e67ae213SHuw Davies  * option type @newtype replaced with @newopt.
1254e67ae213SHuw Davies  *
1255e67ae213SHuw Davies  * @opt may be NULL, in which case a new set of options is returned
1256e67ae213SHuw Davies  * containing just @newopt.
1257e67ae213SHuw Davies  *
1258e67ae213SHuw Davies  * @newopt may be NULL, in which case the specified option type is
1259e67ae213SHuw Davies  * not copied into the new set of options.
1260e67ae213SHuw Davies  *
1261e67ae213SHuw Davies  * The new set of options is allocated from the socket option memory
1262e67ae213SHuw Davies  * buffer of @sk.
1263e67ae213SHuw Davies  */
1264333fad53SYOSHIFUJI Hideaki struct ipv6_txoptions *
ipv6_renew_options(struct sock * sk,struct ipv6_txoptions * opt,int newtype,struct ipv6_opt_hdr * newopt)1265333fad53SYOSHIFUJI Hideaki ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
1266a9ba23d4SPaul Moore 		   int newtype, struct ipv6_opt_hdr *newopt)
1267333fad53SYOSHIFUJI Hideaki {
1268333fad53SYOSHIFUJI Hideaki 	int tot_len = 0;
1269333fad53SYOSHIFUJI Hideaki 	char *p;
1270333fad53SYOSHIFUJI Hideaki 	struct ipv6_txoptions *opt2;
1271333fad53SYOSHIFUJI Hideaki 
127299c7bc01SYOSHIFUJI Hideaki 	if (opt) {
1273333fad53SYOSHIFUJI Hideaki 		if (newtype != IPV6_HOPOPTS && opt->hopopt)
1274333fad53SYOSHIFUJI Hideaki 			tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt));
1275333fad53SYOSHIFUJI Hideaki 		if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt)
1276333fad53SYOSHIFUJI Hideaki 			tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt));
1277333fad53SYOSHIFUJI Hideaki 		if (newtype != IPV6_RTHDR && opt->srcrt)
1278333fad53SYOSHIFUJI Hideaki 			tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt));
1279333fad53SYOSHIFUJI Hideaki 		if (newtype != IPV6_DSTOPTS && opt->dst1opt)
1280333fad53SYOSHIFUJI Hideaki 			tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
128199c7bc01SYOSHIFUJI Hideaki 	}
128299c7bc01SYOSHIFUJI Hideaki 
1283a9ba23d4SPaul Moore 	if (newopt)
1284a9ba23d4SPaul Moore 		tot_len += CMSG_ALIGN(ipv6_optlen(newopt));
1285333fad53SYOSHIFUJI Hideaki 
1286333fad53SYOSHIFUJI Hideaki 	if (!tot_len)
1287333fad53SYOSHIFUJI Hideaki 		return NULL;
1288333fad53SYOSHIFUJI Hideaki 
12898b8aa4b5SYOSHIFUJI Hideaki 	tot_len += sizeof(*opt2);
1290333fad53SYOSHIFUJI Hideaki 	opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC);
1291333fad53SYOSHIFUJI Hideaki 	if (!opt2)
1292333fad53SYOSHIFUJI Hideaki 		return ERR_PTR(-ENOBUFS);
1293333fad53SYOSHIFUJI Hideaki 
1294333fad53SYOSHIFUJI Hideaki 	memset(opt2, 0, tot_len);
12950aeea21aSReshetova, Elena 	refcount_set(&opt2->refcnt, 1);
1296333fad53SYOSHIFUJI Hideaki 	opt2->tot_len = tot_len;
1297333fad53SYOSHIFUJI Hideaki 	p = (char *)(opt2 + 1);
1298333fad53SYOSHIFUJI Hideaki 
1299a9ba23d4SPaul Moore 	ipv6_renew_option(IPV6_HOPOPTS, &opt2->hopopt,
1300a9ba23d4SPaul Moore 			  (opt ? opt->hopopt : NULL),
1301a9ba23d4SPaul Moore 			  newopt, newtype, &p);
1302a9ba23d4SPaul Moore 	ipv6_renew_option(IPV6_RTHDRDSTOPTS, &opt2->dst0opt,
1303a9ba23d4SPaul Moore 			  (opt ? opt->dst0opt : NULL),
1304a9ba23d4SPaul Moore 			  newopt, newtype, &p);
1305a9ba23d4SPaul Moore 	ipv6_renew_option(IPV6_RTHDR,
1306a9ba23d4SPaul Moore 			  (struct ipv6_opt_hdr **)&opt2->srcrt,
1307a9ba23d4SPaul Moore 			  (opt ? (struct ipv6_opt_hdr *)opt->srcrt : NULL),
1308a9ba23d4SPaul Moore 			  newopt, newtype, &p);
1309a9ba23d4SPaul Moore 	ipv6_renew_option(IPV6_DSTOPTS, &opt2->dst1opt,
1310a9ba23d4SPaul Moore 			  (opt ? opt->dst1opt : NULL),
1311a9ba23d4SPaul Moore 			  newopt, newtype, &p);
1312333fad53SYOSHIFUJI Hideaki 
1313333fad53SYOSHIFUJI Hideaki 	opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) +
1314333fad53SYOSHIFUJI Hideaki 			  (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) +
1315333fad53SYOSHIFUJI Hideaki 			  (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0);
1316333fad53SYOSHIFUJI Hideaki 	opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0);
1317333fad53SYOSHIFUJI Hideaki 
1318333fad53SYOSHIFUJI Hideaki 	return opt2;
1319e67ae213SHuw Davies }
1320e67ae213SHuw Davies 
__ipv6_fixup_options(struct ipv6_txoptions * opt_space,struct ipv6_txoptions * opt)132131ed2261SPavel Begunkov struct ipv6_txoptions *__ipv6_fixup_options(struct ipv6_txoptions *opt_space,
1322df9890c3SYOSHIFUJI Hideaki 					    struct ipv6_txoptions *opt)
1323df9890c3SYOSHIFUJI Hideaki {
1324df9890c3SYOSHIFUJI Hideaki 	/*
1325df9890c3SYOSHIFUJI Hideaki 	 * ignore the dest before srcrt unless srcrt is being included.
1326df9890c3SYOSHIFUJI Hideaki 	 * --yoshfuji
1327df9890c3SYOSHIFUJI Hideaki 	 */
132831ed2261SPavel Begunkov 	if (opt->dst0opt && !opt->srcrt) {
1329df9890c3SYOSHIFUJI Hideaki 		if (opt_space != opt) {
1330df9890c3SYOSHIFUJI Hideaki 			memcpy(opt_space, opt, sizeof(*opt_space));
1331df9890c3SYOSHIFUJI Hideaki 			opt = opt_space;
1332df9890c3SYOSHIFUJI Hideaki 		}
1333df9890c3SYOSHIFUJI Hideaki 		opt->opt_nflen -= ipv6_optlen(opt->dst0opt);
1334df9890c3SYOSHIFUJI Hideaki 		opt->dst0opt = NULL;
1335df9890c3SYOSHIFUJI Hideaki 	}
1336df9890c3SYOSHIFUJI Hideaki 
1337df9890c3SYOSHIFUJI Hideaki 	return opt;
1338df9890c3SYOSHIFUJI Hideaki }
133931ed2261SPavel Begunkov EXPORT_SYMBOL_GPL(__ipv6_fixup_options);
1340df9890c3SYOSHIFUJI Hideaki 
134120c59de2SArnaud Ebalard /**
134220c59de2SArnaud Ebalard  * fl6_update_dst - update flowi destination address with info given
134320c59de2SArnaud Ebalard  *                  by srcrt option, if any.
134420c59de2SArnaud Ebalard  *
13454c9483b2SDavid S. Miller  * @fl6: flowi6 for which daddr is to be updated
134620c59de2SArnaud Ebalard  * @opt: struct ipv6_txoptions in which to look for srcrt opt
13474c9483b2SDavid S. Miller  * @orig: copy of original daddr address if modified
134820c59de2SArnaud Ebalard  *
134920c59de2SArnaud Ebalard  * Returns NULL if no txoptions or no srcrt, otherwise returns orig
13504c9483b2SDavid S. Miller  * and initial value of fl6->daddr set in orig
135120c59de2SArnaud Ebalard  */
fl6_update_dst(struct flowi6 * fl6,const struct ipv6_txoptions * opt,struct in6_addr * orig)13524c9483b2SDavid S. Miller struct in6_addr *fl6_update_dst(struct flowi6 *fl6,
135320c59de2SArnaud Ebalard 				const struct ipv6_txoptions *opt,
135420c59de2SArnaud Ebalard 				struct in6_addr *orig)
135520c59de2SArnaud Ebalard {
135620c59de2SArnaud Ebalard 	if (!opt || !opt->srcrt)
135720c59de2SArnaud Ebalard 		return NULL;
135820c59de2SArnaud Ebalard 
13594e3fd7a0SAlexey Dobriyan 	*orig = fl6->daddr;
1360a149e7c7SDavid Lebrun 
1361a149e7c7SDavid Lebrun 	switch (opt->srcrt->type) {
1362a149e7c7SDavid Lebrun 	case IPV6_SRCRT_TYPE_0:
1363ec9c4215SSabrina Dubroca 	case IPV6_SRCRT_STRICT:
1364ec9c4215SSabrina Dubroca 	case IPV6_SRCRT_TYPE_2:
13654e3fd7a0SAlexey Dobriyan 		fl6->daddr = *((struct rt0_hdr *)opt->srcrt)->addr;
1366a149e7c7SDavid Lebrun 		break;
1367a149e7c7SDavid Lebrun 	case IPV6_SRCRT_TYPE_4:
1368a149e7c7SDavid Lebrun 	{
1369a149e7c7SDavid Lebrun 		struct ipv6_sr_hdr *srh = (struct ipv6_sr_hdr *)opt->srcrt;
1370a149e7c7SDavid Lebrun 
1371925615ceSDavid Lebrun 		fl6->daddr = srh->segments[srh->segments_left];
1372a149e7c7SDavid Lebrun 		break;
1373a149e7c7SDavid Lebrun 	}
1374a149e7c7SDavid Lebrun 	default:
1375a149e7c7SDavid Lebrun 		return NULL;
1376a149e7c7SDavid Lebrun 	}
1377a149e7c7SDavid Lebrun 
137820c59de2SArnaud Ebalard 	return orig;
137920c59de2SArnaud Ebalard }
138020c59de2SArnaud Ebalard EXPORT_SYMBOL_GPL(fl6_update_dst);
1381