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