18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Extension Header handling for IPv6 48c2ecf20Sopenharmony_ci * Linux INET6 implementation 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Authors: 78c2ecf20Sopenharmony_ci * Pedro Roque <roque@di.fc.ul.pt> 88c2ecf20Sopenharmony_ci * Andi Kleen <ak@muc.de> 98c2ecf20Sopenharmony_ci * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci/* Changes: 138c2ecf20Sopenharmony_ci * yoshfuji : ensure not to overrun while parsing 148c2ecf20Sopenharmony_ci * tlv options. 158c2ecf20Sopenharmony_ci * Mitsuru KANDA @USAGI and: Remove ipv6_parse_exthdrs(). 168c2ecf20Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI Register inbound extension header 178c2ecf20Sopenharmony_ci * handlers as inet6_protocol{}. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/errno.h> 218c2ecf20Sopenharmony_ci#include <linux/types.h> 228c2ecf20Sopenharmony_ci#include <linux/socket.h> 238c2ecf20Sopenharmony_ci#include <linux/sockios.h> 248c2ecf20Sopenharmony_ci#include <linux/net.h> 258c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 268c2ecf20Sopenharmony_ci#include <linux/in6.h> 278c2ecf20Sopenharmony_ci#include <linux/icmpv6.h> 288c2ecf20Sopenharmony_ci#include <linux/slab.h> 298c2ecf20Sopenharmony_ci#include <linux/export.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <net/dst.h> 328c2ecf20Sopenharmony_ci#include <net/sock.h> 338c2ecf20Sopenharmony_ci#include <net/snmp.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include <net/ipv6.h> 368c2ecf20Sopenharmony_ci#include <net/protocol.h> 378c2ecf20Sopenharmony_ci#include <net/transp_v6.h> 388c2ecf20Sopenharmony_ci#include <net/rawv6.h> 398c2ecf20Sopenharmony_ci#include <net/ndisc.h> 408c2ecf20Sopenharmony_ci#include <net/ip6_route.h> 418c2ecf20Sopenharmony_ci#include <net/addrconf.h> 428c2ecf20Sopenharmony_ci#include <net/calipso.h> 438c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6) 448c2ecf20Sopenharmony_ci#include <net/xfrm.h> 458c2ecf20Sopenharmony_ci#endif 468c2ecf20Sopenharmony_ci#include <linux/seg6.h> 478c2ecf20Sopenharmony_ci#include <net/seg6.h> 488c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 498c2ecf20Sopenharmony_ci#include <net/seg6_hmac.h> 508c2ecf20Sopenharmony_ci#endif 518c2ecf20Sopenharmony_ci#include <net/rpl.h> 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* 568c2ecf20Sopenharmony_ci * Parsing tlv encoded headers. 578c2ecf20Sopenharmony_ci * 588c2ecf20Sopenharmony_ci * Parsing function "func" returns true, if parsing succeed 598c2ecf20Sopenharmony_ci * and false, if it failed. 608c2ecf20Sopenharmony_ci * It MUST NOT touch skb->h. 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistruct tlvtype_proc { 648c2ecf20Sopenharmony_ci int type; 658c2ecf20Sopenharmony_ci bool (*func)(struct sk_buff *skb, int offset); 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/********************* 698c2ecf20Sopenharmony_ci Generic functions 708c2ecf20Sopenharmony_ci *********************/ 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* An unknown option is detected, decide what to do */ 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic bool ip6_tlvopt_unknown(struct sk_buff *skb, int optoff, 758c2ecf20Sopenharmony_ci bool disallow_unknowns) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci if (disallow_unknowns) { 788c2ecf20Sopenharmony_ci /* If unknown TLVs are disallowed by configuration 798c2ecf20Sopenharmony_ci * then always silently drop packet. Note this also 808c2ecf20Sopenharmony_ci * means no ICMP parameter problem is sent which 818c2ecf20Sopenharmony_ci * could be a good property to mitigate a reflection DOS 828c2ecf20Sopenharmony_ci * attack. 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci goto drop; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci switch ((skb_network_header(skb)[optoff] & 0xC0) >> 6) { 898c2ecf20Sopenharmony_ci case 0: /* ignore */ 908c2ecf20Sopenharmony_ci return true; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci case 1: /* drop packet */ 938c2ecf20Sopenharmony_ci break; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci case 3: /* Send ICMP if not a multicast address and drop packet */ 968c2ecf20Sopenharmony_ci /* Actually, it is redundant check. icmp_send 978c2ecf20Sopenharmony_ci will recheck in any case. 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_ci if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr)) 1008c2ecf20Sopenharmony_ci break; 1018c2ecf20Sopenharmony_ci fallthrough; 1028c2ecf20Sopenharmony_ci case 2: /* send ICMP PARM PROB regardless and drop packet */ 1038c2ecf20Sopenharmony_ci icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, optoff); 1048c2ecf20Sopenharmony_ci return false; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cidrop: 1088c2ecf20Sopenharmony_ci kfree_skb(skb); 1098c2ecf20Sopenharmony_ci return false; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* Parse tlv encoded option header (hop-by-hop or destination) */ 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic bool ip6_parse_tlv(const struct tlvtype_proc *procs, 1158c2ecf20Sopenharmony_ci struct sk_buff *skb, 1168c2ecf20Sopenharmony_ci int max_count) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci int len = (skb_transport_header(skb)[1] + 1) << 3; 1198c2ecf20Sopenharmony_ci const unsigned char *nh = skb_network_header(skb); 1208c2ecf20Sopenharmony_ci int off = skb_network_header_len(skb); 1218c2ecf20Sopenharmony_ci const struct tlvtype_proc *curr; 1228c2ecf20Sopenharmony_ci bool disallow_unknowns = false; 1238c2ecf20Sopenharmony_ci int tlv_count = 0; 1248c2ecf20Sopenharmony_ci int padlen = 0; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (unlikely(max_count < 0)) { 1278c2ecf20Sopenharmony_ci disallow_unknowns = true; 1288c2ecf20Sopenharmony_ci max_count = -max_count; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (skb_transport_offset(skb) + len > skb_headlen(skb)) 1328c2ecf20Sopenharmony_ci goto bad; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci off += 2; 1358c2ecf20Sopenharmony_ci len -= 2; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci while (len > 0) { 1388c2ecf20Sopenharmony_ci int optlen, i; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (nh[off] == IPV6_TLV_PAD1) { 1418c2ecf20Sopenharmony_ci padlen++; 1428c2ecf20Sopenharmony_ci if (padlen > 7) 1438c2ecf20Sopenharmony_ci goto bad; 1448c2ecf20Sopenharmony_ci off++; 1458c2ecf20Sopenharmony_ci len--; 1468c2ecf20Sopenharmony_ci continue; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci if (len < 2) 1498c2ecf20Sopenharmony_ci goto bad; 1508c2ecf20Sopenharmony_ci optlen = nh[off + 1] + 2; 1518c2ecf20Sopenharmony_ci if (optlen > len) 1528c2ecf20Sopenharmony_ci goto bad; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (nh[off] == IPV6_TLV_PADN) { 1558c2ecf20Sopenharmony_ci /* RFC 2460 states that the purpose of PadN is 1568c2ecf20Sopenharmony_ci * to align the containing header to multiples 1578c2ecf20Sopenharmony_ci * of 8. 7 is therefore the highest valid value. 1588c2ecf20Sopenharmony_ci * See also RFC 4942, Section 2.1.9.5. 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_ci padlen += optlen; 1618c2ecf20Sopenharmony_ci if (padlen > 7) 1628c2ecf20Sopenharmony_ci goto bad; 1638c2ecf20Sopenharmony_ci /* RFC 4942 recommends receiving hosts to 1648c2ecf20Sopenharmony_ci * actively check PadN payload to contain 1658c2ecf20Sopenharmony_ci * only zeroes. 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_ci for (i = 2; i < optlen; i++) { 1688c2ecf20Sopenharmony_ci if (nh[off + i] != 0) 1698c2ecf20Sopenharmony_ci goto bad; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci } else { 1728c2ecf20Sopenharmony_ci tlv_count++; 1738c2ecf20Sopenharmony_ci if (tlv_count > max_count) 1748c2ecf20Sopenharmony_ci goto bad; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci for (curr = procs; curr->type >= 0; curr++) { 1778c2ecf20Sopenharmony_ci if (curr->type == nh[off]) { 1788c2ecf20Sopenharmony_ci /* type specific length/alignment 1798c2ecf20Sopenharmony_ci checks will be performed in the 1808c2ecf20Sopenharmony_ci func(). */ 1818c2ecf20Sopenharmony_ci if (curr->func(skb, off) == false) 1828c2ecf20Sopenharmony_ci return false; 1838c2ecf20Sopenharmony_ci break; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci if (curr->type < 0 && 1878c2ecf20Sopenharmony_ci !ip6_tlvopt_unknown(skb, off, disallow_unknowns)) 1888c2ecf20Sopenharmony_ci return false; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci padlen = 0; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci off += optlen; 1938c2ecf20Sopenharmony_ci len -= optlen; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci if (len == 0) 1978c2ecf20Sopenharmony_ci return true; 1988c2ecf20Sopenharmony_cibad: 1998c2ecf20Sopenharmony_ci kfree_skb(skb); 2008c2ecf20Sopenharmony_ci return false; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci/***************************** 2048c2ecf20Sopenharmony_ci Destination options header. 2058c2ecf20Sopenharmony_ci *****************************/ 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6) 2088c2ecf20Sopenharmony_cistatic bool ipv6_dest_hao(struct sk_buff *skb, int optoff) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct ipv6_destopt_hao *hao; 2118c2ecf20Sopenharmony_ci struct inet6_skb_parm *opt = IP6CB(skb); 2128c2ecf20Sopenharmony_ci struct ipv6hdr *ipv6h = ipv6_hdr(skb); 2138c2ecf20Sopenharmony_ci int ret; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (opt->dsthao) { 2168c2ecf20Sopenharmony_ci net_dbg_ratelimited("hao duplicated\n"); 2178c2ecf20Sopenharmony_ci goto discard; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci opt->dsthao = opt->dst1; 2208c2ecf20Sopenharmony_ci opt->dst1 = 0; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + optoff); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (hao->length != 16) { 2258c2ecf20Sopenharmony_ci net_dbg_ratelimited("hao invalid option length = %d\n", 2268c2ecf20Sopenharmony_ci hao->length); 2278c2ecf20Sopenharmony_ci goto discard; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) { 2318c2ecf20Sopenharmony_ci net_dbg_ratelimited("hao is not an unicast addr: %pI6\n", 2328c2ecf20Sopenharmony_ci &hao->addr); 2338c2ecf20Sopenharmony_ci goto discard; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci ret = xfrm6_input_addr(skb, (xfrm_address_t *)&ipv6h->daddr, 2378c2ecf20Sopenharmony_ci (xfrm_address_t *)&hao->addr, IPPROTO_DSTOPTS); 2388c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) 2398c2ecf20Sopenharmony_ci goto discard; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (skb_cloned(skb)) { 2428c2ecf20Sopenharmony_ci if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) 2438c2ecf20Sopenharmony_ci goto discard; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* update all variable using below by copied skbuff */ 2468c2ecf20Sopenharmony_ci hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + 2478c2ecf20Sopenharmony_ci optoff); 2488c2ecf20Sopenharmony_ci ipv6h = ipv6_hdr(skb); 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_COMPLETE) 2528c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci swap(ipv6h->saddr, hao->addr); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (skb->tstamp == 0) 2578c2ecf20Sopenharmony_ci __net_timestamp(skb); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return true; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci discard: 2628c2ecf20Sopenharmony_ci kfree_skb(skb); 2638c2ecf20Sopenharmony_ci return false; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci#endif 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic const struct tlvtype_proc tlvprocdestopt_lst[] = { 2688c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6) 2698c2ecf20Sopenharmony_ci { 2708c2ecf20Sopenharmony_ci .type = IPV6_TLV_HAO, 2718c2ecf20Sopenharmony_ci .func = ipv6_dest_hao, 2728c2ecf20Sopenharmony_ci }, 2738c2ecf20Sopenharmony_ci#endif 2748c2ecf20Sopenharmony_ci {-1, NULL} 2758c2ecf20Sopenharmony_ci}; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic int ipv6_destopt_rcv(struct sk_buff *skb) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(skb->dev); 2808c2ecf20Sopenharmony_ci struct inet6_skb_parm *opt = IP6CB(skb); 2818c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6) 2828c2ecf20Sopenharmony_ci __u16 dstbuf; 2838c2ecf20Sopenharmony_ci#endif 2848c2ecf20Sopenharmony_ci struct dst_entry *dst = skb_dst(skb); 2858c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 2868c2ecf20Sopenharmony_ci int extlen; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) || 2898c2ecf20Sopenharmony_ci !pskb_may_pull(skb, (skb_transport_offset(skb) + 2908c2ecf20Sopenharmony_ci ((skb_transport_header(skb)[1] + 1) << 3)))) { 2918c2ecf20Sopenharmony_ci __IP6_INC_STATS(dev_net(dst->dev), idev, 2928c2ecf20Sopenharmony_ci IPSTATS_MIB_INHDRERRORS); 2938c2ecf20Sopenharmony_cifail_and_free: 2948c2ecf20Sopenharmony_ci kfree_skb(skb); 2958c2ecf20Sopenharmony_ci return -1; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci extlen = (skb_transport_header(skb)[1] + 1) << 3; 2998c2ecf20Sopenharmony_ci if (extlen > net->ipv6.sysctl.max_dst_opts_len) 3008c2ecf20Sopenharmony_ci goto fail_and_free; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci opt->lastopt = opt->dst1 = skb_network_header_len(skb); 3038c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6) 3048c2ecf20Sopenharmony_ci dstbuf = opt->dst1; 3058c2ecf20Sopenharmony_ci#endif 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (ip6_parse_tlv(tlvprocdestopt_lst, skb, 3088c2ecf20Sopenharmony_ci net->ipv6.sysctl.max_dst_opts_cnt)) { 3098c2ecf20Sopenharmony_ci skb->transport_header += extlen; 3108c2ecf20Sopenharmony_ci opt = IP6CB(skb); 3118c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6) 3128c2ecf20Sopenharmony_ci opt->nhoff = dstbuf; 3138c2ecf20Sopenharmony_ci#else 3148c2ecf20Sopenharmony_ci opt->nhoff = opt->dst1; 3158c2ecf20Sopenharmony_ci#endif 3168c2ecf20Sopenharmony_ci return 1; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); 3208c2ecf20Sopenharmony_ci return -1; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic void seg6_update_csum(struct sk_buff *skb) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci struct ipv6_sr_hdr *hdr; 3268c2ecf20Sopenharmony_ci struct in6_addr *addr; 3278c2ecf20Sopenharmony_ci __be32 from, to; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* srh is at transport offset and seg_left is already decremented 3308c2ecf20Sopenharmony_ci * but daddr is not yet updated with next segment 3318c2ecf20Sopenharmony_ci */ 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci hdr = (struct ipv6_sr_hdr *)skb_transport_header(skb); 3348c2ecf20Sopenharmony_ci addr = hdr->segments + hdr->segments_left; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci hdr->segments_left++; 3378c2ecf20Sopenharmony_ci from = *(__be32 *)hdr; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci hdr->segments_left--; 3408c2ecf20Sopenharmony_ci to = *(__be32 *)hdr; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* update skb csum with diff resulting from seg_left decrement */ 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci update_csum_diff4(skb, from, to); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* compute csum diff between current and next segment and update */ 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci update_csum_diff16(skb, (__be32 *)(&ipv6_hdr(skb)->daddr), 3498c2ecf20Sopenharmony_ci (__be32 *)addr); 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic int ipv6_srh_rcv(struct sk_buff *skb) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci struct inet6_skb_parm *opt = IP6CB(skb); 3558c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 3568c2ecf20Sopenharmony_ci struct ipv6_sr_hdr *hdr; 3578c2ecf20Sopenharmony_ci struct inet6_dev *idev; 3588c2ecf20Sopenharmony_ci struct in6_addr *addr; 3598c2ecf20Sopenharmony_ci int accept_seg6; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci hdr = (struct ipv6_sr_hdr *)skb_transport_header(skb); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci idev = __in6_dev_get(skb->dev); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci accept_seg6 = net->ipv6.devconf_all->seg6_enabled; 3668c2ecf20Sopenharmony_ci if (accept_seg6 > idev->cnf.seg6_enabled) 3678c2ecf20Sopenharmony_ci accept_seg6 = idev->cnf.seg6_enabled; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci if (!accept_seg6) { 3708c2ecf20Sopenharmony_ci kfree_skb(skb); 3718c2ecf20Sopenharmony_ci return -1; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 3758c2ecf20Sopenharmony_ci if (!seg6_hmac_validate_skb(skb)) { 3768c2ecf20Sopenharmony_ci kfree_skb(skb); 3778c2ecf20Sopenharmony_ci return -1; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci#endif 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cilooped_back: 3828c2ecf20Sopenharmony_ci if (hdr->segments_left == 0) { 3838c2ecf20Sopenharmony_ci if (hdr->nexthdr == NEXTHDR_IPV6) { 3848c2ecf20Sopenharmony_ci int offset = (hdr->hdrlen + 1) << 3; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci skb_postpull_rcsum(skb, skb_network_header(skb), 3878c2ecf20Sopenharmony_ci skb_network_header_len(skb)); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (!pskb_pull(skb, offset)) { 3908c2ecf20Sopenharmony_ci kfree_skb(skb); 3918c2ecf20Sopenharmony_ci return -1; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci skb_postpull_rcsum(skb, skb_transport_header(skb), 3948c2ecf20Sopenharmony_ci offset); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 3978c2ecf20Sopenharmony_ci skb_reset_transport_header(skb); 3988c2ecf20Sopenharmony_ci skb->encapsulation = 0; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci __skb_tunnel_rx(skb, skb->dev, net); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci netif_rx(skb); 4038c2ecf20Sopenharmony_ci return -1; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci opt->srcrt = skb_network_header_len(skb); 4078c2ecf20Sopenharmony_ci opt->lastopt = opt->srcrt; 4088c2ecf20Sopenharmony_ci skb->transport_header += (hdr->hdrlen + 1) << 3; 4098c2ecf20Sopenharmony_ci opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci return 1; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (hdr->segments_left >= (hdr->hdrlen >> 1)) { 4158c2ecf20Sopenharmony_ci __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); 4168c2ecf20Sopenharmony_ci icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, 4178c2ecf20Sopenharmony_ci ((&hdr->segments_left) - 4188c2ecf20Sopenharmony_ci skb_network_header(skb))); 4198c2ecf20Sopenharmony_ci return -1; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci if (skb_cloned(skb)) { 4238c2ecf20Sopenharmony_ci if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) { 4248c2ecf20Sopenharmony_ci __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), 4258c2ecf20Sopenharmony_ci IPSTATS_MIB_OUTDISCARDS); 4268c2ecf20Sopenharmony_ci kfree_skb(skb); 4278c2ecf20Sopenharmony_ci return -1; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci hdr = (struct ipv6_sr_hdr *)skb_transport_header(skb); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci hdr->segments_left--; 4348c2ecf20Sopenharmony_ci addr = hdr->segments + hdr->segments_left; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci skb_push(skb, sizeof(struct ipv6hdr)); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_COMPLETE) 4398c2ecf20Sopenharmony_ci seg6_update_csum(skb); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci ipv6_hdr(skb)->daddr = *addr; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci skb_dst_drop(skb); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci ip6_route_input(skb); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (skb_dst(skb)->error) { 4488c2ecf20Sopenharmony_ci dst_input(skb); 4498c2ecf20Sopenharmony_ci return -1; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (skb_dst(skb)->dev->flags & IFF_LOOPBACK) { 4538c2ecf20Sopenharmony_ci if (ipv6_hdr(skb)->hop_limit <= 1) { 4548c2ecf20Sopenharmony_ci __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); 4558c2ecf20Sopenharmony_ci icmpv6_send(skb, ICMPV6_TIME_EXCEED, 4568c2ecf20Sopenharmony_ci ICMPV6_EXC_HOPLIMIT, 0); 4578c2ecf20Sopenharmony_ci kfree_skb(skb); 4588c2ecf20Sopenharmony_ci return -1; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci ipv6_hdr(skb)->hop_limit--; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(struct ipv6hdr)); 4638c2ecf20Sopenharmony_ci goto looped_back; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci dst_input(skb); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci return -1; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic int ipv6_rpl_srh_rcv(struct sk_buff *skb) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci struct ipv6_rpl_sr_hdr *hdr, *ohdr, *chdr; 4748c2ecf20Sopenharmony_ci struct inet6_skb_parm *opt = IP6CB(skb); 4758c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 4768c2ecf20Sopenharmony_ci struct inet6_dev *idev; 4778c2ecf20Sopenharmony_ci struct ipv6hdr *oldhdr; 4788c2ecf20Sopenharmony_ci struct in6_addr addr; 4798c2ecf20Sopenharmony_ci unsigned char *buf; 4808c2ecf20Sopenharmony_ci int accept_rpl_seg; 4818c2ecf20Sopenharmony_ci int i, err; 4828c2ecf20Sopenharmony_ci u64 n = 0; 4838c2ecf20Sopenharmony_ci u32 r; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci idev = __in6_dev_get(skb->dev); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci accept_rpl_seg = net->ipv6.devconf_all->rpl_seg_enabled; 4888c2ecf20Sopenharmony_ci if (accept_rpl_seg > idev->cnf.rpl_seg_enabled) 4898c2ecf20Sopenharmony_ci accept_rpl_seg = idev->cnf.rpl_seg_enabled; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if (!accept_rpl_seg) { 4928c2ecf20Sopenharmony_ci kfree_skb(skb); 4938c2ecf20Sopenharmony_ci return -1; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cilooped_back: 4978c2ecf20Sopenharmony_ci hdr = (struct ipv6_rpl_sr_hdr *)skb_transport_header(skb); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (hdr->segments_left == 0) { 5008c2ecf20Sopenharmony_ci if (hdr->nexthdr == NEXTHDR_IPV6) { 5018c2ecf20Sopenharmony_ci int offset = (hdr->hdrlen + 1) << 3; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci skb_postpull_rcsum(skb, skb_network_header(skb), 5048c2ecf20Sopenharmony_ci skb_network_header_len(skb)); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci if (!pskb_pull(skb, offset)) { 5078c2ecf20Sopenharmony_ci kfree_skb(skb); 5088c2ecf20Sopenharmony_ci return -1; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci skb_postpull_rcsum(skb, skb_transport_header(skb), 5118c2ecf20Sopenharmony_ci offset); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 5148c2ecf20Sopenharmony_ci skb_reset_transport_header(skb); 5158c2ecf20Sopenharmony_ci skb->encapsulation = 0; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci __skb_tunnel_rx(skb, skb->dev, net); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci netif_rx(skb); 5208c2ecf20Sopenharmony_ci return -1; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci opt->srcrt = skb_network_header_len(skb); 5248c2ecf20Sopenharmony_ci opt->lastopt = opt->srcrt; 5258c2ecf20Sopenharmony_ci skb->transport_header += (hdr->hdrlen + 1) << 3; 5268c2ecf20Sopenharmony_ci opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci return 1; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(*hdr))) { 5328c2ecf20Sopenharmony_ci kfree_skb(skb); 5338c2ecf20Sopenharmony_ci return -1; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci n = (hdr->hdrlen << 3) - hdr->pad - (16 - hdr->cmpre); 5378c2ecf20Sopenharmony_ci r = do_div(n, (16 - hdr->cmpri)); 5388c2ecf20Sopenharmony_ci /* checks if calculation was without remainder and n fits into 5398c2ecf20Sopenharmony_ci * unsigned char which is segments_left field. Should not be 5408c2ecf20Sopenharmony_ci * higher than that. 5418c2ecf20Sopenharmony_ci */ 5428c2ecf20Sopenharmony_ci if (r || (n + 1) > 255) { 5438c2ecf20Sopenharmony_ci kfree_skb(skb); 5448c2ecf20Sopenharmony_ci return -1; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci if (hdr->segments_left > n + 1) { 5488c2ecf20Sopenharmony_ci __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); 5498c2ecf20Sopenharmony_ci icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, 5508c2ecf20Sopenharmony_ci ((&hdr->segments_left) - 5518c2ecf20Sopenharmony_ci skb_network_header(skb))); 5528c2ecf20Sopenharmony_ci return -1; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, ipv6_rpl_srh_size(n, hdr->cmpri, 5568c2ecf20Sopenharmony_ci hdr->cmpre))) { 5578c2ecf20Sopenharmony_ci kfree_skb(skb); 5588c2ecf20Sopenharmony_ci return -1; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci hdr->segments_left--; 5628c2ecf20Sopenharmony_ci i = n - hdr->segments_left; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci buf = kcalloc(struct_size(hdr, segments.addr, n + 2), 2, GFP_ATOMIC); 5658c2ecf20Sopenharmony_ci if (unlikely(!buf)) { 5668c2ecf20Sopenharmony_ci kfree_skb(skb); 5678c2ecf20Sopenharmony_ci return -1; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci ohdr = (struct ipv6_rpl_sr_hdr *)buf; 5718c2ecf20Sopenharmony_ci ipv6_rpl_srh_decompress(ohdr, hdr, &ipv6_hdr(skb)->daddr, n); 5728c2ecf20Sopenharmony_ci chdr = (struct ipv6_rpl_sr_hdr *)(buf + ((ohdr->hdrlen + 1) << 3)); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if ((ipv6_addr_type(&ipv6_hdr(skb)->daddr) & IPV6_ADDR_MULTICAST) || 5758c2ecf20Sopenharmony_ci (ipv6_addr_type(&ohdr->rpl_segaddr[i]) & IPV6_ADDR_MULTICAST)) { 5768c2ecf20Sopenharmony_ci kfree_skb(skb); 5778c2ecf20Sopenharmony_ci kfree(buf); 5788c2ecf20Sopenharmony_ci return -1; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci err = ipv6_chk_rpl_srh_loop(net, ohdr->rpl_segaddr, n + 1); 5828c2ecf20Sopenharmony_ci if (err) { 5838c2ecf20Sopenharmony_ci icmpv6_send(skb, ICMPV6_PARAMPROB, 0, 0); 5848c2ecf20Sopenharmony_ci kfree_skb(skb); 5858c2ecf20Sopenharmony_ci kfree(buf); 5868c2ecf20Sopenharmony_ci return -1; 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci addr = ipv6_hdr(skb)->daddr; 5908c2ecf20Sopenharmony_ci ipv6_hdr(skb)->daddr = ohdr->rpl_segaddr[i]; 5918c2ecf20Sopenharmony_ci ohdr->rpl_segaddr[i] = addr; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci ipv6_rpl_srh_compress(chdr, ohdr, &ipv6_hdr(skb)->daddr, n); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci oldhdr = ipv6_hdr(skb); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci skb_pull(skb, ((hdr->hdrlen + 1) << 3)); 5988c2ecf20Sopenharmony_ci skb_postpull_rcsum(skb, oldhdr, 5998c2ecf20Sopenharmony_ci sizeof(struct ipv6hdr) + ((hdr->hdrlen + 1) << 3)); 6008c2ecf20Sopenharmony_ci if (unlikely(!hdr->segments_left)) { 6018c2ecf20Sopenharmony_ci if (pskb_expand_head(skb, sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3), 0, 6028c2ecf20Sopenharmony_ci GFP_ATOMIC)) { 6038c2ecf20Sopenharmony_ci __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_OUTDISCARDS); 6048c2ecf20Sopenharmony_ci kfree_skb(skb); 6058c2ecf20Sopenharmony_ci kfree(buf); 6068c2ecf20Sopenharmony_ci return -1; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci oldhdr = ipv6_hdr(skb); 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci skb_push(skb, ((chdr->hdrlen + 1) << 3) + sizeof(struct ipv6hdr)); 6128c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 6138c2ecf20Sopenharmony_ci skb_mac_header_rebuild(skb); 6148c2ecf20Sopenharmony_ci skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci memmove(ipv6_hdr(skb), oldhdr, sizeof(struct ipv6hdr)); 6178c2ecf20Sopenharmony_ci memcpy(skb_transport_header(skb), chdr, (chdr->hdrlen + 1) << 3); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 6208c2ecf20Sopenharmony_ci skb_postpush_rcsum(skb, ipv6_hdr(skb), 6218c2ecf20Sopenharmony_ci sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3)); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci kfree(buf); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci skb_dst_drop(skb); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci ip6_route_input(skb); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (skb_dst(skb)->error) { 6308c2ecf20Sopenharmony_ci dst_input(skb); 6318c2ecf20Sopenharmony_ci return -1; 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci if (skb_dst(skb)->dev->flags & IFF_LOOPBACK) { 6358c2ecf20Sopenharmony_ci if (ipv6_hdr(skb)->hop_limit <= 1) { 6368c2ecf20Sopenharmony_ci __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); 6378c2ecf20Sopenharmony_ci icmpv6_send(skb, ICMPV6_TIME_EXCEED, 6388c2ecf20Sopenharmony_ci ICMPV6_EXC_HOPLIMIT, 0); 6398c2ecf20Sopenharmony_ci kfree_skb(skb); 6408c2ecf20Sopenharmony_ci return -1; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci ipv6_hdr(skb)->hop_limit--; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(struct ipv6hdr)); 6458c2ecf20Sopenharmony_ci goto looped_back; 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci dst_input(skb); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci return -1; 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci/******************************** 6548c2ecf20Sopenharmony_ci Routing header. 6558c2ecf20Sopenharmony_ci ********************************/ 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci/* called with rcu_read_lock() */ 6588c2ecf20Sopenharmony_cistatic int ipv6_rthdr_rcv(struct sk_buff *skb) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(skb->dev); 6618c2ecf20Sopenharmony_ci struct inet6_skb_parm *opt = IP6CB(skb); 6628c2ecf20Sopenharmony_ci struct in6_addr *addr = NULL; 6638c2ecf20Sopenharmony_ci struct in6_addr daddr; 6648c2ecf20Sopenharmony_ci int n, i; 6658c2ecf20Sopenharmony_ci struct ipv6_rt_hdr *hdr; 6668c2ecf20Sopenharmony_ci struct rt0_hdr *rthdr; 6678c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 6688c2ecf20Sopenharmony_ci int accept_source_route = net->ipv6.devconf_all->accept_source_route; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci idev = __in6_dev_get(skb->dev); 6718c2ecf20Sopenharmony_ci if (idev && accept_source_route > idev->cnf.accept_source_route) 6728c2ecf20Sopenharmony_ci accept_source_route = idev->cnf.accept_source_route; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) || 6758c2ecf20Sopenharmony_ci !pskb_may_pull(skb, (skb_transport_offset(skb) + 6768c2ecf20Sopenharmony_ci ((skb_transport_header(skb)[1] + 1) << 3)))) { 6778c2ecf20Sopenharmony_ci __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); 6788c2ecf20Sopenharmony_ci kfree_skb(skb); 6798c2ecf20Sopenharmony_ci return -1; 6808c2ecf20Sopenharmony_ci } 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) || 6858c2ecf20Sopenharmony_ci skb->pkt_type != PACKET_HOST) { 6868c2ecf20Sopenharmony_ci __IP6_INC_STATS(net, idev, IPSTATS_MIB_INADDRERRORS); 6878c2ecf20Sopenharmony_ci kfree_skb(skb); 6888c2ecf20Sopenharmony_ci return -1; 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci switch (hdr->type) { 6928c2ecf20Sopenharmony_ci case IPV6_SRCRT_TYPE_4: 6938c2ecf20Sopenharmony_ci /* segment routing */ 6948c2ecf20Sopenharmony_ci return ipv6_srh_rcv(skb); 6958c2ecf20Sopenharmony_ci case IPV6_SRCRT_TYPE_3: 6968c2ecf20Sopenharmony_ci /* rpl segment routing */ 6978c2ecf20Sopenharmony_ci return ipv6_rpl_srh_rcv(skb); 6988c2ecf20Sopenharmony_ci default: 6998c2ecf20Sopenharmony_ci break; 7008c2ecf20Sopenharmony_ci } 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_cilooped_back: 7038c2ecf20Sopenharmony_ci if (hdr->segments_left == 0) { 7048c2ecf20Sopenharmony_ci switch (hdr->type) { 7058c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6) 7068c2ecf20Sopenharmony_ci case IPV6_SRCRT_TYPE_2: 7078c2ecf20Sopenharmony_ci /* Silently discard type 2 header unless it was 7088c2ecf20Sopenharmony_ci * processed by own 7098c2ecf20Sopenharmony_ci */ 7108c2ecf20Sopenharmony_ci if (!addr) { 7118c2ecf20Sopenharmony_ci __IP6_INC_STATS(net, idev, 7128c2ecf20Sopenharmony_ci IPSTATS_MIB_INADDRERRORS); 7138c2ecf20Sopenharmony_ci kfree_skb(skb); 7148c2ecf20Sopenharmony_ci return -1; 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci break; 7178c2ecf20Sopenharmony_ci#endif 7188c2ecf20Sopenharmony_ci default: 7198c2ecf20Sopenharmony_ci break; 7208c2ecf20Sopenharmony_ci } 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci opt->lastopt = opt->srcrt = skb_network_header_len(skb); 7238c2ecf20Sopenharmony_ci skb->transport_header += (hdr->hdrlen + 1) << 3; 7248c2ecf20Sopenharmony_ci opt->dst0 = opt->dst1; 7258c2ecf20Sopenharmony_ci opt->dst1 = 0; 7268c2ecf20Sopenharmony_ci opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb); 7278c2ecf20Sopenharmony_ci return 1; 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci switch (hdr->type) { 7318c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6) 7328c2ecf20Sopenharmony_ci case IPV6_SRCRT_TYPE_2: 7338c2ecf20Sopenharmony_ci if (accept_source_route < 0) 7348c2ecf20Sopenharmony_ci goto unknown_rh; 7358c2ecf20Sopenharmony_ci /* Silently discard invalid RTH type 2 */ 7368c2ecf20Sopenharmony_ci if (hdr->hdrlen != 2 || hdr->segments_left != 1) { 7378c2ecf20Sopenharmony_ci __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); 7388c2ecf20Sopenharmony_ci kfree_skb(skb); 7398c2ecf20Sopenharmony_ci return -1; 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci break; 7428c2ecf20Sopenharmony_ci#endif 7438c2ecf20Sopenharmony_ci default: 7448c2ecf20Sopenharmony_ci goto unknown_rh; 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci /* 7488c2ecf20Sopenharmony_ci * This is the routing header forwarding algorithm from 7498c2ecf20Sopenharmony_ci * RFC 2460, page 16. 7508c2ecf20Sopenharmony_ci */ 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci n = hdr->hdrlen >> 1; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci if (hdr->segments_left > n) { 7558c2ecf20Sopenharmony_ci __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); 7568c2ecf20Sopenharmony_ci icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, 7578c2ecf20Sopenharmony_ci ((&hdr->segments_left) - 7588c2ecf20Sopenharmony_ci skb_network_header(skb))); 7598c2ecf20Sopenharmony_ci return -1; 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci /* We are about to mangle packet header. Be careful! 7638c2ecf20Sopenharmony_ci Do not damage packets queued somewhere. 7648c2ecf20Sopenharmony_ci */ 7658c2ecf20Sopenharmony_ci if (skb_cloned(skb)) { 7668c2ecf20Sopenharmony_ci /* the copy is a forwarded packet */ 7678c2ecf20Sopenharmony_ci if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) { 7688c2ecf20Sopenharmony_ci __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), 7698c2ecf20Sopenharmony_ci IPSTATS_MIB_OUTDISCARDS); 7708c2ecf20Sopenharmony_ci kfree_skb(skb); 7718c2ecf20Sopenharmony_ci return -1; 7728c2ecf20Sopenharmony_ci } 7738c2ecf20Sopenharmony_ci hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb); 7748c2ecf20Sopenharmony_ci } 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_COMPLETE) 7778c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci i = n - --hdr->segments_left; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci rthdr = (struct rt0_hdr *) hdr; 7828c2ecf20Sopenharmony_ci addr = rthdr->addr; 7838c2ecf20Sopenharmony_ci addr += i - 1; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci switch (hdr->type) { 7868c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6) 7878c2ecf20Sopenharmony_ci case IPV6_SRCRT_TYPE_2: 7888c2ecf20Sopenharmony_ci if (xfrm6_input_addr(skb, (xfrm_address_t *)addr, 7898c2ecf20Sopenharmony_ci (xfrm_address_t *)&ipv6_hdr(skb)->saddr, 7908c2ecf20Sopenharmony_ci IPPROTO_ROUTING) < 0) { 7918c2ecf20Sopenharmony_ci __IP6_INC_STATS(net, idev, IPSTATS_MIB_INADDRERRORS); 7928c2ecf20Sopenharmony_ci kfree_skb(skb); 7938c2ecf20Sopenharmony_ci return -1; 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci if (!ipv6_chk_home_addr(dev_net(skb_dst(skb)->dev), addr)) { 7968c2ecf20Sopenharmony_ci __IP6_INC_STATS(net, idev, IPSTATS_MIB_INADDRERRORS); 7978c2ecf20Sopenharmony_ci kfree_skb(skb); 7988c2ecf20Sopenharmony_ci return -1; 7998c2ecf20Sopenharmony_ci } 8008c2ecf20Sopenharmony_ci break; 8018c2ecf20Sopenharmony_ci#endif 8028c2ecf20Sopenharmony_ci default: 8038c2ecf20Sopenharmony_ci break; 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci if (ipv6_addr_is_multicast(addr)) { 8078c2ecf20Sopenharmony_ci __IP6_INC_STATS(net, idev, IPSTATS_MIB_INADDRERRORS); 8088c2ecf20Sopenharmony_ci kfree_skb(skb); 8098c2ecf20Sopenharmony_ci return -1; 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci daddr = *addr; 8138c2ecf20Sopenharmony_ci *addr = ipv6_hdr(skb)->daddr; 8148c2ecf20Sopenharmony_ci ipv6_hdr(skb)->daddr = daddr; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci skb_dst_drop(skb); 8178c2ecf20Sopenharmony_ci ip6_route_input(skb); 8188c2ecf20Sopenharmony_ci if (skb_dst(skb)->error) { 8198c2ecf20Sopenharmony_ci skb_push(skb, skb->data - skb_network_header(skb)); 8208c2ecf20Sopenharmony_ci dst_input(skb); 8218c2ecf20Sopenharmony_ci return -1; 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci if (skb_dst(skb)->dev->flags&IFF_LOOPBACK) { 8258c2ecf20Sopenharmony_ci if (ipv6_hdr(skb)->hop_limit <= 1) { 8268c2ecf20Sopenharmony_ci __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); 8278c2ecf20Sopenharmony_ci icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT, 8288c2ecf20Sopenharmony_ci 0); 8298c2ecf20Sopenharmony_ci kfree_skb(skb); 8308c2ecf20Sopenharmony_ci return -1; 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci ipv6_hdr(skb)->hop_limit--; 8338c2ecf20Sopenharmony_ci goto looped_back; 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci skb_push(skb, skb->data - skb_network_header(skb)); 8378c2ecf20Sopenharmony_ci dst_input(skb); 8388c2ecf20Sopenharmony_ci return -1; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ciunknown_rh: 8418c2ecf20Sopenharmony_ci __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); 8428c2ecf20Sopenharmony_ci icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, 8438c2ecf20Sopenharmony_ci (&hdr->type) - skb_network_header(skb)); 8448c2ecf20Sopenharmony_ci return -1; 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_cistatic const struct inet6_protocol rthdr_protocol = { 8488c2ecf20Sopenharmony_ci .handler = ipv6_rthdr_rcv, 8498c2ecf20Sopenharmony_ci .flags = INET6_PROTO_NOPOLICY, 8508c2ecf20Sopenharmony_ci}; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_cistatic const struct inet6_protocol destopt_protocol = { 8538c2ecf20Sopenharmony_ci .handler = ipv6_destopt_rcv, 8548c2ecf20Sopenharmony_ci .flags = INET6_PROTO_NOPOLICY, 8558c2ecf20Sopenharmony_ci}; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_cistatic const struct inet6_protocol nodata_protocol = { 8588c2ecf20Sopenharmony_ci .handler = dst_discard, 8598c2ecf20Sopenharmony_ci .flags = INET6_PROTO_NOPOLICY, 8608c2ecf20Sopenharmony_ci}; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ciint __init ipv6_exthdrs_init(void) 8638c2ecf20Sopenharmony_ci{ 8648c2ecf20Sopenharmony_ci int ret; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci ret = inet6_add_protocol(&rthdr_protocol, IPPROTO_ROUTING); 8678c2ecf20Sopenharmony_ci if (ret) 8688c2ecf20Sopenharmony_ci goto out; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci ret = inet6_add_protocol(&destopt_protocol, IPPROTO_DSTOPTS); 8718c2ecf20Sopenharmony_ci if (ret) 8728c2ecf20Sopenharmony_ci goto out_rthdr; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci ret = inet6_add_protocol(&nodata_protocol, IPPROTO_NONE); 8758c2ecf20Sopenharmony_ci if (ret) 8768c2ecf20Sopenharmony_ci goto out_destopt; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ciout: 8798c2ecf20Sopenharmony_ci return ret; 8808c2ecf20Sopenharmony_ciout_destopt: 8818c2ecf20Sopenharmony_ci inet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS); 8828c2ecf20Sopenharmony_ciout_rthdr: 8838c2ecf20Sopenharmony_ci inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING); 8848c2ecf20Sopenharmony_ci goto out; 8858c2ecf20Sopenharmony_ci}; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_civoid ipv6_exthdrs_exit(void) 8888c2ecf20Sopenharmony_ci{ 8898c2ecf20Sopenharmony_ci inet6_del_protocol(&nodata_protocol, IPPROTO_NONE); 8908c2ecf20Sopenharmony_ci inet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS); 8918c2ecf20Sopenharmony_ci inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING); 8928c2ecf20Sopenharmony_ci} 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci/********************************** 8958c2ecf20Sopenharmony_ci Hop-by-hop options. 8968c2ecf20Sopenharmony_ci **********************************/ 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci/* 8998c2ecf20Sopenharmony_ci * Note: we cannot rely on skb_dst(skb) before we assign it in ip6_route_input(). 9008c2ecf20Sopenharmony_ci */ 9018c2ecf20Sopenharmony_cistatic inline struct inet6_dev *ipv6_skb_idev(struct sk_buff *skb) 9028c2ecf20Sopenharmony_ci{ 9038c2ecf20Sopenharmony_ci return skb_dst(skb) ? ip6_dst_idev(skb_dst(skb)) : __in6_dev_get(skb->dev); 9048c2ecf20Sopenharmony_ci} 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_cistatic inline struct net *ipv6_skb_net(struct sk_buff *skb) 9078c2ecf20Sopenharmony_ci{ 9088c2ecf20Sopenharmony_ci return skb_dst(skb) ? dev_net(skb_dst(skb)->dev) : dev_net(skb->dev); 9098c2ecf20Sopenharmony_ci} 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci/* Router Alert as of RFC 2711 */ 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_cistatic bool ipv6_hop_ra(struct sk_buff *skb, int optoff) 9148c2ecf20Sopenharmony_ci{ 9158c2ecf20Sopenharmony_ci const unsigned char *nh = skb_network_header(skb); 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci if (nh[optoff + 1] == 2) { 9188c2ecf20Sopenharmony_ci IP6CB(skb)->flags |= IP6SKB_ROUTERALERT; 9198c2ecf20Sopenharmony_ci memcpy(&IP6CB(skb)->ra, nh + optoff + 2, sizeof(IP6CB(skb)->ra)); 9208c2ecf20Sopenharmony_ci return true; 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci net_dbg_ratelimited("ipv6_hop_ra: wrong RA length %d\n", 9238c2ecf20Sopenharmony_ci nh[optoff + 1]); 9248c2ecf20Sopenharmony_ci kfree_skb(skb); 9258c2ecf20Sopenharmony_ci return false; 9268c2ecf20Sopenharmony_ci} 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci/* Jumbo payload */ 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_cistatic bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff) 9318c2ecf20Sopenharmony_ci{ 9328c2ecf20Sopenharmony_ci const unsigned char *nh = skb_network_header(skb); 9338c2ecf20Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get_safely(skb->dev); 9348c2ecf20Sopenharmony_ci struct net *net = ipv6_skb_net(skb); 9358c2ecf20Sopenharmony_ci u32 pkt_len; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci if (nh[optoff + 1] != 4 || (optoff & 3) != 2) { 9388c2ecf20Sopenharmony_ci net_dbg_ratelimited("ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n", 9398c2ecf20Sopenharmony_ci nh[optoff+1]); 9408c2ecf20Sopenharmony_ci __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); 9418c2ecf20Sopenharmony_ci goto drop; 9428c2ecf20Sopenharmony_ci } 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci pkt_len = ntohl(*(__be32 *)(nh + optoff + 2)); 9458c2ecf20Sopenharmony_ci if (pkt_len <= IPV6_MAXPLEN) { 9468c2ecf20Sopenharmony_ci __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); 9478c2ecf20Sopenharmony_ci icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2); 9488c2ecf20Sopenharmony_ci return false; 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci if (ipv6_hdr(skb)->payload_len) { 9518c2ecf20Sopenharmony_ci __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); 9528c2ecf20Sopenharmony_ci icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff); 9538c2ecf20Sopenharmony_ci return false; 9548c2ecf20Sopenharmony_ci } 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci if (pkt_len > skb->len - sizeof(struct ipv6hdr)) { 9578c2ecf20Sopenharmony_ci __IP6_INC_STATS(net, idev, IPSTATS_MIB_INTRUNCATEDPKTS); 9588c2ecf20Sopenharmony_ci goto drop; 9598c2ecf20Sopenharmony_ci } 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr))) 9628c2ecf20Sopenharmony_ci goto drop; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci IP6CB(skb)->flags |= IP6SKB_JUMBOGRAM; 9658c2ecf20Sopenharmony_ci return true; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_cidrop: 9688c2ecf20Sopenharmony_ci kfree_skb(skb); 9698c2ecf20Sopenharmony_ci return false; 9708c2ecf20Sopenharmony_ci} 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci/* CALIPSO RFC 5570 */ 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_cistatic bool ipv6_hop_calipso(struct sk_buff *skb, int optoff) 9758c2ecf20Sopenharmony_ci{ 9768c2ecf20Sopenharmony_ci const unsigned char *nh = skb_network_header(skb); 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci if (nh[optoff + 1] < 8) 9798c2ecf20Sopenharmony_ci goto drop; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci if (nh[optoff + 6] * 4 + 8 > nh[optoff + 1]) 9828c2ecf20Sopenharmony_ci goto drop; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci if (!calipso_validate(skb, nh + optoff)) 9858c2ecf20Sopenharmony_ci goto drop; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci return true; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_cidrop: 9908c2ecf20Sopenharmony_ci kfree_skb(skb); 9918c2ecf20Sopenharmony_ci return false; 9928c2ecf20Sopenharmony_ci} 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_cistatic const struct tlvtype_proc tlvprochopopt_lst[] = { 9958c2ecf20Sopenharmony_ci { 9968c2ecf20Sopenharmony_ci .type = IPV6_TLV_ROUTERALERT, 9978c2ecf20Sopenharmony_ci .func = ipv6_hop_ra, 9988c2ecf20Sopenharmony_ci }, 9998c2ecf20Sopenharmony_ci { 10008c2ecf20Sopenharmony_ci .type = IPV6_TLV_JUMBO, 10018c2ecf20Sopenharmony_ci .func = ipv6_hop_jumbo, 10028c2ecf20Sopenharmony_ci }, 10038c2ecf20Sopenharmony_ci { 10048c2ecf20Sopenharmony_ci .type = IPV6_TLV_CALIPSO, 10058c2ecf20Sopenharmony_ci .func = ipv6_hop_calipso, 10068c2ecf20Sopenharmony_ci }, 10078c2ecf20Sopenharmony_ci { -1, } 10088c2ecf20Sopenharmony_ci}; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ciint ipv6_parse_hopopts(struct sk_buff *skb) 10118c2ecf20Sopenharmony_ci{ 10128c2ecf20Sopenharmony_ci struct inet6_skb_parm *opt = IP6CB(skb); 10138c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 10148c2ecf20Sopenharmony_ci int extlen; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci /* 10178c2ecf20Sopenharmony_ci * skb_network_header(skb) is equal to skb->data, and 10188c2ecf20Sopenharmony_ci * skb_network_header_len(skb) is always equal to 10198c2ecf20Sopenharmony_ci * sizeof(struct ipv6hdr) by definition of 10208c2ecf20Sopenharmony_ci * hop-by-hop options. 10218c2ecf20Sopenharmony_ci */ 10228c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + 8) || 10238c2ecf20Sopenharmony_ci !pskb_may_pull(skb, (sizeof(struct ipv6hdr) + 10248c2ecf20Sopenharmony_ci ((skb_transport_header(skb)[1] + 1) << 3)))) { 10258c2ecf20Sopenharmony_cifail_and_free: 10268c2ecf20Sopenharmony_ci kfree_skb(skb); 10278c2ecf20Sopenharmony_ci return -1; 10288c2ecf20Sopenharmony_ci } 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci extlen = (skb_transport_header(skb)[1] + 1) << 3; 10318c2ecf20Sopenharmony_ci if (extlen > net->ipv6.sysctl.max_hbh_opts_len) 10328c2ecf20Sopenharmony_ci goto fail_and_free; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci opt->flags |= IP6SKB_HOPBYHOP; 10358c2ecf20Sopenharmony_ci if (ip6_parse_tlv(tlvprochopopt_lst, skb, 10368c2ecf20Sopenharmony_ci net->ipv6.sysctl.max_hbh_opts_cnt)) { 10378c2ecf20Sopenharmony_ci skb->transport_header += extlen; 10388c2ecf20Sopenharmony_ci opt = IP6CB(skb); 10398c2ecf20Sopenharmony_ci opt->nhoff = sizeof(struct ipv6hdr); 10408c2ecf20Sopenharmony_ci return 1; 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci return -1; 10438c2ecf20Sopenharmony_ci} 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci/* 10468c2ecf20Sopenharmony_ci * Creating outbound headers. 10478c2ecf20Sopenharmony_ci * 10488c2ecf20Sopenharmony_ci * "build" functions work when skb is filled from head to tail (datagram) 10498c2ecf20Sopenharmony_ci * "push" functions work when headers are added from tail to head (tcp) 10508c2ecf20Sopenharmony_ci * 10518c2ecf20Sopenharmony_ci * In both cases we assume, that caller reserved enough room 10528c2ecf20Sopenharmony_ci * for headers. 10538c2ecf20Sopenharmony_ci */ 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_cistatic void ipv6_push_rthdr0(struct sk_buff *skb, u8 *proto, 10568c2ecf20Sopenharmony_ci struct ipv6_rt_hdr *opt, 10578c2ecf20Sopenharmony_ci struct in6_addr **addr_p, struct in6_addr *saddr) 10588c2ecf20Sopenharmony_ci{ 10598c2ecf20Sopenharmony_ci struct rt0_hdr *phdr, *ihdr; 10608c2ecf20Sopenharmony_ci int hops; 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci ihdr = (struct rt0_hdr *) opt; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci phdr = skb_push(skb, (ihdr->rt_hdr.hdrlen + 1) << 3); 10658c2ecf20Sopenharmony_ci memcpy(phdr, ihdr, sizeof(struct rt0_hdr)); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci hops = ihdr->rt_hdr.hdrlen >> 1; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci if (hops > 1) 10708c2ecf20Sopenharmony_ci memcpy(phdr->addr, ihdr->addr + 1, 10718c2ecf20Sopenharmony_ci (hops - 1) * sizeof(struct in6_addr)); 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci phdr->addr[hops - 1] = **addr_p; 10748c2ecf20Sopenharmony_ci *addr_p = ihdr->addr; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci phdr->rt_hdr.nexthdr = *proto; 10778c2ecf20Sopenharmony_ci *proto = NEXTHDR_ROUTING; 10788c2ecf20Sopenharmony_ci} 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_cistatic void ipv6_push_rthdr4(struct sk_buff *skb, u8 *proto, 10818c2ecf20Sopenharmony_ci struct ipv6_rt_hdr *opt, 10828c2ecf20Sopenharmony_ci struct in6_addr **addr_p, struct in6_addr *saddr) 10838c2ecf20Sopenharmony_ci{ 10848c2ecf20Sopenharmony_ci struct ipv6_sr_hdr *sr_phdr, *sr_ihdr; 10858c2ecf20Sopenharmony_ci int plen, hops; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci sr_ihdr = (struct ipv6_sr_hdr *)opt; 10888c2ecf20Sopenharmony_ci plen = (sr_ihdr->hdrlen + 1) << 3; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci sr_phdr = skb_push(skb, plen); 10918c2ecf20Sopenharmony_ci memcpy(sr_phdr, sr_ihdr, sizeof(struct ipv6_sr_hdr)); 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci hops = sr_ihdr->first_segment + 1; 10948c2ecf20Sopenharmony_ci memcpy(sr_phdr->segments + 1, sr_ihdr->segments + 1, 10958c2ecf20Sopenharmony_ci (hops - 1) * sizeof(struct in6_addr)); 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci sr_phdr->segments[0] = **addr_p; 10988c2ecf20Sopenharmony_ci *addr_p = &sr_ihdr->segments[sr_ihdr->segments_left]; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci if (sr_ihdr->hdrlen > hops * 2) { 11018c2ecf20Sopenharmony_ci int tlvs_offset, tlvs_length; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci tlvs_offset = (1 + hops * 2) << 3; 11048c2ecf20Sopenharmony_ci tlvs_length = (sr_ihdr->hdrlen - hops * 2) << 3; 11058c2ecf20Sopenharmony_ci memcpy((char *)sr_phdr + tlvs_offset, 11068c2ecf20Sopenharmony_ci (char *)sr_ihdr + tlvs_offset, tlvs_length); 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 11108c2ecf20Sopenharmony_ci if (sr_has_hmac(sr_phdr)) { 11118c2ecf20Sopenharmony_ci struct net *net = NULL; 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci if (skb->dev) 11148c2ecf20Sopenharmony_ci net = dev_net(skb->dev); 11158c2ecf20Sopenharmony_ci else if (skb->sk) 11168c2ecf20Sopenharmony_ci net = sock_net(skb->sk); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci WARN_ON(!net); 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci if (net) 11218c2ecf20Sopenharmony_ci seg6_push_hmac(net, saddr, sr_phdr); 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci#endif 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci sr_phdr->nexthdr = *proto; 11268c2ecf20Sopenharmony_ci *proto = NEXTHDR_ROUTING; 11278c2ecf20Sopenharmony_ci} 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_cistatic void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto, 11308c2ecf20Sopenharmony_ci struct ipv6_rt_hdr *opt, 11318c2ecf20Sopenharmony_ci struct in6_addr **addr_p, struct in6_addr *saddr) 11328c2ecf20Sopenharmony_ci{ 11338c2ecf20Sopenharmony_ci switch (opt->type) { 11348c2ecf20Sopenharmony_ci case IPV6_SRCRT_TYPE_0: 11358c2ecf20Sopenharmony_ci case IPV6_SRCRT_STRICT: 11368c2ecf20Sopenharmony_ci case IPV6_SRCRT_TYPE_2: 11378c2ecf20Sopenharmony_ci ipv6_push_rthdr0(skb, proto, opt, addr_p, saddr); 11388c2ecf20Sopenharmony_ci break; 11398c2ecf20Sopenharmony_ci case IPV6_SRCRT_TYPE_4: 11408c2ecf20Sopenharmony_ci ipv6_push_rthdr4(skb, proto, opt, addr_p, saddr); 11418c2ecf20Sopenharmony_ci break; 11428c2ecf20Sopenharmony_ci default: 11438c2ecf20Sopenharmony_ci break; 11448c2ecf20Sopenharmony_ci } 11458c2ecf20Sopenharmony_ci} 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_cistatic void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci struct ipv6_opt_hdr *h = skb_push(skb, ipv6_optlen(opt)); 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci memcpy(h, opt, ipv6_optlen(opt)); 11528c2ecf20Sopenharmony_ci h->nexthdr = *proto; 11538c2ecf20Sopenharmony_ci *proto = type; 11548c2ecf20Sopenharmony_ci} 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_civoid ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, 11578c2ecf20Sopenharmony_ci u8 *proto, 11588c2ecf20Sopenharmony_ci struct in6_addr **daddr, struct in6_addr *saddr) 11598c2ecf20Sopenharmony_ci{ 11608c2ecf20Sopenharmony_ci if (opt->srcrt) { 11618c2ecf20Sopenharmony_ci ipv6_push_rthdr(skb, proto, opt->srcrt, daddr, saddr); 11628c2ecf20Sopenharmony_ci /* 11638c2ecf20Sopenharmony_ci * IPV6_RTHDRDSTOPTS is ignored 11648c2ecf20Sopenharmony_ci * unless IPV6_RTHDR is set (RFC3542). 11658c2ecf20Sopenharmony_ci */ 11668c2ecf20Sopenharmony_ci if (opt->dst0opt) 11678c2ecf20Sopenharmony_ci ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt); 11688c2ecf20Sopenharmony_ci } 11698c2ecf20Sopenharmony_ci if (opt->hopopt) 11708c2ecf20Sopenharmony_ci ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt); 11718c2ecf20Sopenharmony_ci} 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_civoid ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto) 11748c2ecf20Sopenharmony_ci{ 11758c2ecf20Sopenharmony_ci if (opt->dst1opt) 11768c2ecf20Sopenharmony_ci ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt); 11778c2ecf20Sopenharmony_ci} 11788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipv6_push_frag_opts); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_cistruct ipv6_txoptions * 11818c2ecf20Sopenharmony_ciipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt) 11828c2ecf20Sopenharmony_ci{ 11838c2ecf20Sopenharmony_ci struct ipv6_txoptions *opt2; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC); 11868c2ecf20Sopenharmony_ci if (opt2) { 11878c2ecf20Sopenharmony_ci long dif = (char *)opt2 - (char *)opt; 11888c2ecf20Sopenharmony_ci memcpy(opt2, opt, opt->tot_len); 11898c2ecf20Sopenharmony_ci if (opt2->hopopt) 11908c2ecf20Sopenharmony_ci *((char **)&opt2->hopopt) += dif; 11918c2ecf20Sopenharmony_ci if (opt2->dst0opt) 11928c2ecf20Sopenharmony_ci *((char **)&opt2->dst0opt) += dif; 11938c2ecf20Sopenharmony_ci if (opt2->dst1opt) 11948c2ecf20Sopenharmony_ci *((char **)&opt2->dst1opt) += dif; 11958c2ecf20Sopenharmony_ci if (opt2->srcrt) 11968c2ecf20Sopenharmony_ci *((char **)&opt2->srcrt) += dif; 11978c2ecf20Sopenharmony_ci refcount_set(&opt2->refcnt, 1); 11988c2ecf20Sopenharmony_ci } 11998c2ecf20Sopenharmony_ci return opt2; 12008c2ecf20Sopenharmony_ci} 12018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipv6_dup_options); 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_cistatic void ipv6_renew_option(int renewtype, 12048c2ecf20Sopenharmony_ci struct ipv6_opt_hdr **dest, 12058c2ecf20Sopenharmony_ci struct ipv6_opt_hdr *old, 12068c2ecf20Sopenharmony_ci struct ipv6_opt_hdr *new, 12078c2ecf20Sopenharmony_ci int newtype, char **p) 12088c2ecf20Sopenharmony_ci{ 12098c2ecf20Sopenharmony_ci struct ipv6_opt_hdr *src; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci src = (renewtype == newtype ? new : old); 12128c2ecf20Sopenharmony_ci if (!src) 12138c2ecf20Sopenharmony_ci return; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci memcpy(*p, src, ipv6_optlen(src)); 12168c2ecf20Sopenharmony_ci *dest = (struct ipv6_opt_hdr *)*p; 12178c2ecf20Sopenharmony_ci *p += CMSG_ALIGN(ipv6_optlen(*dest)); 12188c2ecf20Sopenharmony_ci} 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci/** 12218c2ecf20Sopenharmony_ci * ipv6_renew_options - replace a specific ext hdr with a new one. 12228c2ecf20Sopenharmony_ci * 12238c2ecf20Sopenharmony_ci * @sk: sock from which to allocate memory 12248c2ecf20Sopenharmony_ci * @opt: original options 12258c2ecf20Sopenharmony_ci * @newtype: option type to replace in @opt 12268c2ecf20Sopenharmony_ci * @newopt: new option of type @newtype to replace (user-mem) 12278c2ecf20Sopenharmony_ci * 12288c2ecf20Sopenharmony_ci * Returns a new set of options which is a copy of @opt with the 12298c2ecf20Sopenharmony_ci * option type @newtype replaced with @newopt. 12308c2ecf20Sopenharmony_ci * 12318c2ecf20Sopenharmony_ci * @opt may be NULL, in which case a new set of options is returned 12328c2ecf20Sopenharmony_ci * containing just @newopt. 12338c2ecf20Sopenharmony_ci * 12348c2ecf20Sopenharmony_ci * @newopt may be NULL, in which case the specified option type is 12358c2ecf20Sopenharmony_ci * not copied into the new set of options. 12368c2ecf20Sopenharmony_ci * 12378c2ecf20Sopenharmony_ci * The new set of options is allocated from the socket option memory 12388c2ecf20Sopenharmony_ci * buffer of @sk. 12398c2ecf20Sopenharmony_ci */ 12408c2ecf20Sopenharmony_cistruct ipv6_txoptions * 12418c2ecf20Sopenharmony_ciipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt, 12428c2ecf20Sopenharmony_ci int newtype, struct ipv6_opt_hdr *newopt) 12438c2ecf20Sopenharmony_ci{ 12448c2ecf20Sopenharmony_ci int tot_len = 0; 12458c2ecf20Sopenharmony_ci char *p; 12468c2ecf20Sopenharmony_ci struct ipv6_txoptions *opt2; 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci if (opt) { 12498c2ecf20Sopenharmony_ci if (newtype != IPV6_HOPOPTS && opt->hopopt) 12508c2ecf20Sopenharmony_ci tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt)); 12518c2ecf20Sopenharmony_ci if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt) 12528c2ecf20Sopenharmony_ci tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt)); 12538c2ecf20Sopenharmony_ci if (newtype != IPV6_RTHDR && opt->srcrt) 12548c2ecf20Sopenharmony_ci tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt)); 12558c2ecf20Sopenharmony_ci if (newtype != IPV6_DSTOPTS && opt->dst1opt) 12568c2ecf20Sopenharmony_ci tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt)); 12578c2ecf20Sopenharmony_ci } 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci if (newopt) 12608c2ecf20Sopenharmony_ci tot_len += CMSG_ALIGN(ipv6_optlen(newopt)); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci if (!tot_len) 12638c2ecf20Sopenharmony_ci return NULL; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci tot_len += sizeof(*opt2); 12668c2ecf20Sopenharmony_ci opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC); 12678c2ecf20Sopenharmony_ci if (!opt2) 12688c2ecf20Sopenharmony_ci return ERR_PTR(-ENOBUFS); 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci memset(opt2, 0, tot_len); 12718c2ecf20Sopenharmony_ci refcount_set(&opt2->refcnt, 1); 12728c2ecf20Sopenharmony_ci opt2->tot_len = tot_len; 12738c2ecf20Sopenharmony_ci p = (char *)(opt2 + 1); 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci ipv6_renew_option(IPV6_HOPOPTS, &opt2->hopopt, 12768c2ecf20Sopenharmony_ci (opt ? opt->hopopt : NULL), 12778c2ecf20Sopenharmony_ci newopt, newtype, &p); 12788c2ecf20Sopenharmony_ci ipv6_renew_option(IPV6_RTHDRDSTOPTS, &opt2->dst0opt, 12798c2ecf20Sopenharmony_ci (opt ? opt->dst0opt : NULL), 12808c2ecf20Sopenharmony_ci newopt, newtype, &p); 12818c2ecf20Sopenharmony_ci ipv6_renew_option(IPV6_RTHDR, 12828c2ecf20Sopenharmony_ci (struct ipv6_opt_hdr **)&opt2->srcrt, 12838c2ecf20Sopenharmony_ci (opt ? (struct ipv6_opt_hdr *)opt->srcrt : NULL), 12848c2ecf20Sopenharmony_ci newopt, newtype, &p); 12858c2ecf20Sopenharmony_ci ipv6_renew_option(IPV6_DSTOPTS, &opt2->dst1opt, 12868c2ecf20Sopenharmony_ci (opt ? opt->dst1opt : NULL), 12878c2ecf20Sopenharmony_ci newopt, newtype, &p); 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) + 12908c2ecf20Sopenharmony_ci (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) + 12918c2ecf20Sopenharmony_ci (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0); 12928c2ecf20Sopenharmony_ci opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0); 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci return opt2; 12958c2ecf20Sopenharmony_ci} 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_cistruct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space, 12988c2ecf20Sopenharmony_ci struct ipv6_txoptions *opt) 12998c2ecf20Sopenharmony_ci{ 13008c2ecf20Sopenharmony_ci /* 13018c2ecf20Sopenharmony_ci * ignore the dest before srcrt unless srcrt is being included. 13028c2ecf20Sopenharmony_ci * --yoshfuji 13038c2ecf20Sopenharmony_ci */ 13048c2ecf20Sopenharmony_ci if (opt && opt->dst0opt && !opt->srcrt) { 13058c2ecf20Sopenharmony_ci if (opt_space != opt) { 13068c2ecf20Sopenharmony_ci memcpy(opt_space, opt, sizeof(*opt_space)); 13078c2ecf20Sopenharmony_ci opt = opt_space; 13088c2ecf20Sopenharmony_ci } 13098c2ecf20Sopenharmony_ci opt->opt_nflen -= ipv6_optlen(opt->dst0opt); 13108c2ecf20Sopenharmony_ci opt->dst0opt = NULL; 13118c2ecf20Sopenharmony_ci } 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci return opt; 13148c2ecf20Sopenharmony_ci} 13158c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipv6_fixup_options); 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci/** 13188c2ecf20Sopenharmony_ci * fl6_update_dst - update flowi destination address with info given 13198c2ecf20Sopenharmony_ci * by srcrt option, if any. 13208c2ecf20Sopenharmony_ci * 13218c2ecf20Sopenharmony_ci * @fl6: flowi6 for which daddr is to be updated 13228c2ecf20Sopenharmony_ci * @opt: struct ipv6_txoptions in which to look for srcrt opt 13238c2ecf20Sopenharmony_ci * @orig: copy of original daddr address if modified 13248c2ecf20Sopenharmony_ci * 13258c2ecf20Sopenharmony_ci * Returns NULL if no txoptions or no srcrt, otherwise returns orig 13268c2ecf20Sopenharmony_ci * and initial value of fl6->daddr set in orig 13278c2ecf20Sopenharmony_ci */ 13288c2ecf20Sopenharmony_cistruct in6_addr *fl6_update_dst(struct flowi6 *fl6, 13298c2ecf20Sopenharmony_ci const struct ipv6_txoptions *opt, 13308c2ecf20Sopenharmony_ci struct in6_addr *orig) 13318c2ecf20Sopenharmony_ci{ 13328c2ecf20Sopenharmony_ci if (!opt || !opt->srcrt) 13338c2ecf20Sopenharmony_ci return NULL; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci *orig = fl6->daddr; 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci switch (opt->srcrt->type) { 13388c2ecf20Sopenharmony_ci case IPV6_SRCRT_TYPE_0: 13398c2ecf20Sopenharmony_ci case IPV6_SRCRT_STRICT: 13408c2ecf20Sopenharmony_ci case IPV6_SRCRT_TYPE_2: 13418c2ecf20Sopenharmony_ci fl6->daddr = *((struct rt0_hdr *)opt->srcrt)->addr; 13428c2ecf20Sopenharmony_ci break; 13438c2ecf20Sopenharmony_ci case IPV6_SRCRT_TYPE_4: 13448c2ecf20Sopenharmony_ci { 13458c2ecf20Sopenharmony_ci struct ipv6_sr_hdr *srh = (struct ipv6_sr_hdr *)opt->srcrt; 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci fl6->daddr = srh->segments[srh->segments_left]; 13488c2ecf20Sopenharmony_ci break; 13498c2ecf20Sopenharmony_ci } 13508c2ecf20Sopenharmony_ci default: 13518c2ecf20Sopenharmony_ci return NULL; 13528c2ecf20Sopenharmony_ci } 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci return orig; 13558c2ecf20Sopenharmony_ci} 13568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fl6_update_dst); 1357