18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Internet Control Message Protocol (ICMPv6) 48c2ecf20Sopenharmony_ci * Linux INET6 implementation 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Authors: 78c2ecf20Sopenharmony_ci * Pedro Roque <roque@di.fc.ul.pt> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Based on net/ipv4/icmp.c 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * RFC 1885 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* 158c2ecf20Sopenharmony_ci * Changes: 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * Andi Kleen : exception handling 188c2ecf20Sopenharmony_ci * Andi Kleen add rate limits. never reply to a icmp. 198c2ecf20Sopenharmony_ci * add more length checks and other fixes. 208c2ecf20Sopenharmony_ci * yoshfuji : ensure to sent parameter problem for 218c2ecf20Sopenharmony_ci * fragments. 228c2ecf20Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI: added sysctl for icmp rate limit. 238c2ecf20Sopenharmony_ci * Randy Dunlap and 248c2ecf20Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI: Per-interface statistics support 258c2ecf20Sopenharmony_ci * Kazunori MIYAZAWA @USAGI: change output process to use ip6_append_data 268c2ecf20Sopenharmony_ci */ 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "IPv6: " fmt 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include <linux/module.h> 318c2ecf20Sopenharmony_ci#include <linux/errno.h> 328c2ecf20Sopenharmony_ci#include <linux/types.h> 338c2ecf20Sopenharmony_ci#include <linux/socket.h> 348c2ecf20Sopenharmony_ci#include <linux/in.h> 358c2ecf20Sopenharmony_ci#include <linux/kernel.h> 368c2ecf20Sopenharmony_ci#include <linux/sockios.h> 378c2ecf20Sopenharmony_ci#include <linux/net.h> 388c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 398c2ecf20Sopenharmony_ci#include <linux/init.h> 408c2ecf20Sopenharmony_ci#include <linux/netfilter.h> 418c2ecf20Sopenharmony_ci#include <linux/slab.h> 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL 448c2ecf20Sopenharmony_ci#include <linux/sysctl.h> 458c2ecf20Sopenharmony_ci#endif 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#include <linux/inet.h> 488c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 498c2ecf20Sopenharmony_ci#include <linux/icmpv6.h> 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#include <net/ip.h> 528c2ecf20Sopenharmony_ci#include <net/sock.h> 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#include <net/ipv6.h> 558c2ecf20Sopenharmony_ci#include <net/ip6_checksum.h> 568c2ecf20Sopenharmony_ci#include <net/ping.h> 578c2ecf20Sopenharmony_ci#include <net/protocol.h> 588c2ecf20Sopenharmony_ci#include <net/raw.h> 598c2ecf20Sopenharmony_ci#include <net/rawv6.h> 608c2ecf20Sopenharmony_ci#include <net/transp_v6.h> 618c2ecf20Sopenharmony_ci#include <net/ip6_route.h> 628c2ecf20Sopenharmony_ci#include <net/addrconf.h> 638c2ecf20Sopenharmony_ci#include <net/icmp.h> 648c2ecf20Sopenharmony_ci#include <net/xfrm.h> 658c2ecf20Sopenharmony_ci#include <net/inet_common.h> 668c2ecf20Sopenharmony_ci#include <net/dsfield.h> 678c2ecf20Sopenharmony_ci#include <net/l3mdev.h> 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* 728c2ecf20Sopenharmony_ci * The ICMP socket(s). This is the most convenient way to flow control 738c2ecf20Sopenharmony_ci * our ICMP output as well as maintain a clean interface throughout 748c2ecf20Sopenharmony_ci * all layers. All Socketless IP sends will soon be gone. 758c2ecf20Sopenharmony_ci * 768c2ecf20Sopenharmony_ci * On SMP we have one ICMP socket per-cpu. 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_cistatic struct sock *icmpv6_sk(struct net *net) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci return this_cpu_read(*net->ipv6.icmp_sk); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int icmpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 848c2ecf20Sopenharmony_ci u8 type, u8 code, int offset, __be32 info) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci /* icmpv6_notify checks 8 bytes can be pulled, icmp6hdr is 8 bytes */ 878c2ecf20Sopenharmony_ci struct icmp6hdr *icmp6 = (struct icmp6hdr *) (skb->data + offset); 888c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (type == ICMPV6_PKT_TOOBIG) 918c2ecf20Sopenharmony_ci ip6_update_pmtu(skb, net, info, skb->dev->ifindex, 0, sock_net_uid(net, NULL)); 928c2ecf20Sopenharmony_ci else if (type == NDISC_REDIRECT) 938c2ecf20Sopenharmony_ci ip6_redirect(skb, net, skb->dev->ifindex, 0, 948c2ecf20Sopenharmony_ci sock_net_uid(net, NULL)); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (!(type & ICMPV6_INFOMSG_MASK)) 978c2ecf20Sopenharmony_ci if (icmp6->icmp6_type == ICMPV6_ECHO_REQUEST) 988c2ecf20Sopenharmony_ci ping_err(skb, offset, ntohl(info)); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci return 0; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int icmpv6_rcv(struct sk_buff *skb); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic const struct inet6_protocol icmpv6_protocol = { 1068c2ecf20Sopenharmony_ci .handler = icmpv6_rcv, 1078c2ecf20Sopenharmony_ci .err_handler = icmpv6_err, 1088c2ecf20Sopenharmony_ci .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* Called with BH disabled */ 1128c2ecf20Sopenharmony_cistatic __inline__ struct sock *icmpv6_xmit_lock(struct net *net) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct sock *sk; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci sk = icmpv6_sk(net); 1178c2ecf20Sopenharmony_ci if (unlikely(!spin_trylock(&sk->sk_lock.slock))) { 1188c2ecf20Sopenharmony_ci /* This can happen if the output path (f.e. SIT or 1198c2ecf20Sopenharmony_ci * ip6ip6 tunnel) signals dst_link_failure() for an 1208c2ecf20Sopenharmony_ci * outgoing ICMP6 packet. 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_ci return NULL; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci return sk; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic __inline__ void icmpv6_xmit_unlock(struct sock *sk) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci spin_unlock(&sk->sk_lock.slock); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci/* 1338c2ecf20Sopenharmony_ci * Figure out, may we reply to this packet with icmp error. 1348c2ecf20Sopenharmony_ci * 1358c2ecf20Sopenharmony_ci * We do not reply, if: 1368c2ecf20Sopenharmony_ci * - it was icmp error message. 1378c2ecf20Sopenharmony_ci * - it is truncated, so that it is known, that protocol is ICMPV6 1388c2ecf20Sopenharmony_ci * (i.e. in the middle of some exthdr) 1398c2ecf20Sopenharmony_ci * 1408c2ecf20Sopenharmony_ci * --ANK (980726) 1418c2ecf20Sopenharmony_ci */ 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic bool is_ineligible(const struct sk_buff *skb) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci int ptr = (u8 *)(ipv6_hdr(skb) + 1) - skb->data; 1468c2ecf20Sopenharmony_ci int len = skb->len - ptr; 1478c2ecf20Sopenharmony_ci __u8 nexthdr = ipv6_hdr(skb)->nexthdr; 1488c2ecf20Sopenharmony_ci __be16 frag_off; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (len < 0) 1518c2ecf20Sopenharmony_ci return true; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci ptr = ipv6_skip_exthdr(skb, ptr, &nexthdr, &frag_off); 1548c2ecf20Sopenharmony_ci if (ptr < 0) 1558c2ecf20Sopenharmony_ci return false; 1568c2ecf20Sopenharmony_ci if (nexthdr == IPPROTO_ICMPV6) { 1578c2ecf20Sopenharmony_ci u8 _type, *tp; 1588c2ecf20Sopenharmony_ci tp = skb_header_pointer(skb, 1598c2ecf20Sopenharmony_ci ptr+offsetof(struct icmp6hdr, icmp6_type), 1608c2ecf20Sopenharmony_ci sizeof(_type), &_type); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* Based on RFC 8200, Section 4.5 Fragment Header, return 1638c2ecf20Sopenharmony_ci * false if this is a fragment packet with no icmp header info. 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_ci if (!tp && frag_off != 0) 1668c2ecf20Sopenharmony_ci return false; 1678c2ecf20Sopenharmony_ci else if (!tp || !(*tp & ICMPV6_INFOMSG_MASK)) 1688c2ecf20Sopenharmony_ci return true; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci return false; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic bool icmpv6_mask_allow(struct net *net, int type) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci if (type > ICMPV6_MSG_MAX) 1768c2ecf20Sopenharmony_ci return true; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* Limit if icmp type is set in ratemask. */ 1798c2ecf20Sopenharmony_ci if (!test_bit(type, net->ipv6.sysctl.icmpv6_ratemask)) 1808c2ecf20Sopenharmony_ci return true; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return false; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic bool icmpv6_global_allow(struct net *net, int type) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci if (icmpv6_mask_allow(net, type)) 1888c2ecf20Sopenharmony_ci return true; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (icmp_global_allow()) 1918c2ecf20Sopenharmony_ci return true; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return false; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/* 1978c2ecf20Sopenharmony_ci * Check the ICMP output rate limit 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_cistatic bool icmpv6_xrlim_allow(struct sock *sk, u8 type, 2008c2ecf20Sopenharmony_ci struct flowi6 *fl6) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 2038c2ecf20Sopenharmony_ci struct dst_entry *dst; 2048c2ecf20Sopenharmony_ci bool res = false; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (icmpv6_mask_allow(net, type)) 2078c2ecf20Sopenharmony_ci return true; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* 2108c2ecf20Sopenharmony_ci * Look up the output route. 2118c2ecf20Sopenharmony_ci * XXX: perhaps the expire for routing entries cloned by 2128c2ecf20Sopenharmony_ci * this lookup should be more aggressive (not longer than timeout). 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci dst = ip6_route_output(net, sk, fl6); 2158c2ecf20Sopenharmony_ci if (dst->error) { 2168c2ecf20Sopenharmony_ci IP6_INC_STATS(net, ip6_dst_idev(dst), 2178c2ecf20Sopenharmony_ci IPSTATS_MIB_OUTNOROUTES); 2188c2ecf20Sopenharmony_ci } else if (dst->dev && (dst->dev->flags&IFF_LOOPBACK)) { 2198c2ecf20Sopenharmony_ci res = true; 2208c2ecf20Sopenharmony_ci } else { 2218c2ecf20Sopenharmony_ci struct rt6_info *rt = (struct rt6_info *)dst; 2228c2ecf20Sopenharmony_ci int tmo = net->ipv6.sysctl.icmpv6_time; 2238c2ecf20Sopenharmony_ci struct inet_peer *peer; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* Give more bandwidth to wider prefixes. */ 2268c2ecf20Sopenharmony_ci if (rt->rt6i_dst.plen < 128) 2278c2ecf20Sopenharmony_ci tmo >>= ((128 - rt->rt6i_dst.plen)>>5); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci peer = inet_getpeer_v6(net->ipv6.peers, &fl6->daddr, 1); 2308c2ecf20Sopenharmony_ci res = inet_peer_xrlim_allow(peer, tmo); 2318c2ecf20Sopenharmony_ci if (peer) 2328c2ecf20Sopenharmony_ci inet_putpeer(peer); 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci dst_release(dst); 2358c2ecf20Sopenharmony_ci return res; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic bool icmpv6_rt_has_prefsrc(struct sock *sk, u8 type, 2398c2ecf20Sopenharmony_ci struct flowi6 *fl6) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 2428c2ecf20Sopenharmony_ci struct dst_entry *dst; 2438c2ecf20Sopenharmony_ci bool res = false; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci dst = ip6_route_output(net, sk, fl6); 2468c2ecf20Sopenharmony_ci if (!dst->error) { 2478c2ecf20Sopenharmony_ci struct rt6_info *rt = (struct rt6_info *)dst; 2488c2ecf20Sopenharmony_ci struct in6_addr prefsrc; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci rt6_get_prefsrc(rt, &prefsrc); 2518c2ecf20Sopenharmony_ci res = !ipv6_addr_any(&prefsrc); 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci dst_release(dst); 2548c2ecf20Sopenharmony_ci return res; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci/* 2588c2ecf20Sopenharmony_ci * an inline helper for the "simple" if statement below 2598c2ecf20Sopenharmony_ci * checks if parameter problem report is caused by an 2608c2ecf20Sopenharmony_ci * unrecognized IPv6 option that has the Option Type 2618c2ecf20Sopenharmony_ci * highest-order two bits set to 10 2628c2ecf20Sopenharmony_ci */ 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic bool opt_unrec(struct sk_buff *skb, __u32 offset) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci u8 _optval, *op; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci offset += skb_network_offset(skb); 2698c2ecf20Sopenharmony_ci op = skb_header_pointer(skb, offset, sizeof(_optval), &_optval); 2708c2ecf20Sopenharmony_ci if (!op) 2718c2ecf20Sopenharmony_ci return true; 2728c2ecf20Sopenharmony_ci return (*op & 0xC0) == 0x80; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_civoid icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, 2768c2ecf20Sopenharmony_ci struct icmp6hdr *thdr, int len) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci struct sk_buff *skb; 2798c2ecf20Sopenharmony_ci struct icmp6hdr *icmp6h; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci skb = skb_peek(&sk->sk_write_queue); 2828c2ecf20Sopenharmony_ci if (!skb) 2838c2ecf20Sopenharmony_ci return; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci icmp6h = icmp6_hdr(skb); 2868c2ecf20Sopenharmony_ci memcpy(icmp6h, thdr, sizeof(struct icmp6hdr)); 2878c2ecf20Sopenharmony_ci icmp6h->icmp6_cksum = 0; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (skb_queue_len(&sk->sk_write_queue) == 1) { 2908c2ecf20Sopenharmony_ci skb->csum = csum_partial(icmp6h, 2918c2ecf20Sopenharmony_ci sizeof(struct icmp6hdr), skb->csum); 2928c2ecf20Sopenharmony_ci icmp6h->icmp6_cksum = csum_ipv6_magic(&fl6->saddr, 2938c2ecf20Sopenharmony_ci &fl6->daddr, 2948c2ecf20Sopenharmony_ci len, fl6->flowi6_proto, 2958c2ecf20Sopenharmony_ci skb->csum); 2968c2ecf20Sopenharmony_ci } else { 2978c2ecf20Sopenharmony_ci __wsum tmp_csum = 0; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci skb_queue_walk(&sk->sk_write_queue, skb) { 3008c2ecf20Sopenharmony_ci tmp_csum = csum_add(tmp_csum, skb->csum); 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci tmp_csum = csum_partial(icmp6h, 3048c2ecf20Sopenharmony_ci sizeof(struct icmp6hdr), tmp_csum); 3058c2ecf20Sopenharmony_ci icmp6h->icmp6_cksum = csum_ipv6_magic(&fl6->saddr, 3068c2ecf20Sopenharmony_ci &fl6->daddr, 3078c2ecf20Sopenharmony_ci len, fl6->flowi6_proto, 3088c2ecf20Sopenharmony_ci tmp_csum); 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci ip6_push_pending_frames(sk); 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistruct icmpv6_msg { 3148c2ecf20Sopenharmony_ci struct sk_buff *skb; 3158c2ecf20Sopenharmony_ci int offset; 3168c2ecf20Sopenharmony_ci uint8_t type; 3178c2ecf20Sopenharmony_ci}; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic int icmpv6_getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct icmpv6_msg *msg = (struct icmpv6_msg *) from; 3228c2ecf20Sopenharmony_ci struct sk_buff *org_skb = msg->skb; 3238c2ecf20Sopenharmony_ci __wsum csum; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci csum = skb_copy_and_csum_bits(org_skb, msg->offset + offset, 3268c2ecf20Sopenharmony_ci to, len); 3278c2ecf20Sopenharmony_ci skb->csum = csum_block_add(skb->csum, csum, odd); 3288c2ecf20Sopenharmony_ci if (!(msg->type & ICMPV6_INFOMSG_MASK)) 3298c2ecf20Sopenharmony_ci nf_ct_attach(skb, org_skb); 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6) 3348c2ecf20Sopenharmony_cistatic void mip6_addr_swap(struct sk_buff *skb, const struct inet6_skb_parm *opt) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci struct ipv6hdr *iph = ipv6_hdr(skb); 3378c2ecf20Sopenharmony_ci struct ipv6_destopt_hao *hao; 3388c2ecf20Sopenharmony_ci struct in6_addr tmp; 3398c2ecf20Sopenharmony_ci int off; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (opt->dsthao) { 3428c2ecf20Sopenharmony_ci off = ipv6_find_tlv(skb, opt->dsthao, IPV6_TLV_HAO); 3438c2ecf20Sopenharmony_ci if (likely(off >= 0)) { 3448c2ecf20Sopenharmony_ci hao = (struct ipv6_destopt_hao *) 3458c2ecf20Sopenharmony_ci (skb_network_header(skb) + off); 3468c2ecf20Sopenharmony_ci tmp = iph->saddr; 3478c2ecf20Sopenharmony_ci iph->saddr = hao->addr; 3488c2ecf20Sopenharmony_ci hao->addr = tmp; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci#else 3538c2ecf20Sopenharmony_cistatic inline void mip6_addr_swap(struct sk_buff *skb, const struct inet6_skb_parm *opt) {} 3548c2ecf20Sopenharmony_ci#endif 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic struct dst_entry *icmpv6_route_lookup(struct net *net, 3578c2ecf20Sopenharmony_ci struct sk_buff *skb, 3588c2ecf20Sopenharmony_ci struct sock *sk, 3598c2ecf20Sopenharmony_ci struct flowi6 *fl6) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci struct dst_entry *dst, *dst2; 3628c2ecf20Sopenharmony_ci struct flowi6 fl2; 3638c2ecf20Sopenharmony_ci int err; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci err = ip6_dst_lookup(net, sk, &dst, fl6); 3668c2ecf20Sopenharmony_ci if (err) 3678c2ecf20Sopenharmony_ci return ERR_PTR(err); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* 3708c2ecf20Sopenharmony_ci * We won't send icmp if the destination is known 3718c2ecf20Sopenharmony_ci * anycast. 3728c2ecf20Sopenharmony_ci */ 3738c2ecf20Sopenharmony_ci if (ipv6_anycast_destination(dst, &fl6->daddr)) { 3748c2ecf20Sopenharmony_ci net_dbg_ratelimited("icmp6_send: acast source\n"); 3758c2ecf20Sopenharmony_ci dst_release(dst); 3768c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* No need to clone since we're just using its address. */ 3808c2ecf20Sopenharmony_ci dst2 = dst; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci dst = xfrm_lookup(net, dst, flowi6_to_flowi(fl6), sk, 0); 3838c2ecf20Sopenharmony_ci if (!IS_ERR(dst)) { 3848c2ecf20Sopenharmony_ci if (dst != dst2) 3858c2ecf20Sopenharmony_ci return dst; 3868c2ecf20Sopenharmony_ci } else { 3878c2ecf20Sopenharmony_ci if (PTR_ERR(dst) == -EPERM) 3888c2ecf20Sopenharmony_ci dst = NULL; 3898c2ecf20Sopenharmony_ci else 3908c2ecf20Sopenharmony_ci return dst; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci err = xfrm_decode_session_reverse(skb, flowi6_to_flowi(&fl2), AF_INET6); 3948c2ecf20Sopenharmony_ci if (err) 3958c2ecf20Sopenharmony_ci goto relookup_failed; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci err = ip6_dst_lookup(net, sk, &dst2, &fl2); 3988c2ecf20Sopenharmony_ci if (err) 3998c2ecf20Sopenharmony_ci goto relookup_failed; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci dst2 = xfrm_lookup(net, dst2, flowi6_to_flowi(&fl2), sk, XFRM_LOOKUP_ICMP); 4028c2ecf20Sopenharmony_ci if (!IS_ERR(dst2)) { 4038c2ecf20Sopenharmony_ci dst_release(dst); 4048c2ecf20Sopenharmony_ci dst = dst2; 4058c2ecf20Sopenharmony_ci } else { 4068c2ecf20Sopenharmony_ci err = PTR_ERR(dst2); 4078c2ecf20Sopenharmony_ci if (err == -EPERM) { 4088c2ecf20Sopenharmony_ci dst_release(dst); 4098c2ecf20Sopenharmony_ci return dst2; 4108c2ecf20Sopenharmony_ci } else 4118c2ecf20Sopenharmony_ci goto relookup_failed; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cirelookup_failed: 4158c2ecf20Sopenharmony_ci if (dst) 4168c2ecf20Sopenharmony_ci return dst; 4178c2ecf20Sopenharmony_ci return ERR_PTR(err); 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic struct net_device *icmp6_dev(const struct sk_buff *skb) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci struct net_device *dev = skb->dev; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* for local traffic to local address, skb dev is the loopback 4258c2ecf20Sopenharmony_ci * device. Check if there is a dst attached to the skb and if so 4268c2ecf20Sopenharmony_ci * get the real device index. Same is needed for replies to a link 4278c2ecf20Sopenharmony_ci * local address on a device enslaved to an L3 master device 4288c2ecf20Sopenharmony_ci */ 4298c2ecf20Sopenharmony_ci if (unlikely(dev->ifindex == LOOPBACK_IFINDEX || netif_is_l3_master(skb->dev))) { 4308c2ecf20Sopenharmony_ci const struct rt6_info *rt6 = skb_rt6_info(skb); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* The destination could be an external IP in Ext Hdr (SRv6, RPL, etc.), 4338c2ecf20Sopenharmony_ci * and ip6_null_entry could be set to skb if no route is found. 4348c2ecf20Sopenharmony_ci */ 4358c2ecf20Sopenharmony_ci if (rt6 && rt6->rt6i_idev) 4368c2ecf20Sopenharmony_ci dev = rt6->rt6i_idev->dev; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return dev; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic int icmp6_iif(const struct sk_buff *skb) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci return icmp6_dev(skb)->ifindex; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci/* 4488c2ecf20Sopenharmony_ci * Send an ICMP message in response to a packet in error 4498c2ecf20Sopenharmony_ci */ 4508c2ecf20Sopenharmony_civoid icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info, 4518c2ecf20Sopenharmony_ci const struct in6_addr *force_saddr, 4528c2ecf20Sopenharmony_ci const struct inet6_skb_parm *parm) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci struct inet6_dev *idev = NULL; 4558c2ecf20Sopenharmony_ci struct ipv6hdr *hdr = ipv6_hdr(skb); 4568c2ecf20Sopenharmony_ci struct sock *sk; 4578c2ecf20Sopenharmony_ci struct net *net; 4588c2ecf20Sopenharmony_ci struct ipv6_pinfo *np; 4598c2ecf20Sopenharmony_ci const struct in6_addr *saddr = NULL; 4608c2ecf20Sopenharmony_ci struct dst_entry *dst; 4618c2ecf20Sopenharmony_ci struct icmp6hdr tmp_hdr; 4628c2ecf20Sopenharmony_ci struct flowi6 fl6; 4638c2ecf20Sopenharmony_ci struct icmpv6_msg msg; 4648c2ecf20Sopenharmony_ci struct ipcm6_cookie ipc6; 4658c2ecf20Sopenharmony_ci int iif = 0; 4668c2ecf20Sopenharmony_ci int addr_type = 0; 4678c2ecf20Sopenharmony_ci int len; 4688c2ecf20Sopenharmony_ci u32 mark; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if ((u8 *)hdr < skb->head || 4718c2ecf20Sopenharmony_ci (skb_network_header(skb) + sizeof(*hdr)) > skb_tail_pointer(skb)) 4728c2ecf20Sopenharmony_ci return; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (!skb->dev) 4758c2ecf20Sopenharmony_ci return; 4768c2ecf20Sopenharmony_ci net = dev_net(skb->dev); 4778c2ecf20Sopenharmony_ci mark = IP6_REPLY_MARK(net, skb->mark); 4788c2ecf20Sopenharmony_ci /* 4798c2ecf20Sopenharmony_ci * Make sure we respect the rules 4808c2ecf20Sopenharmony_ci * i.e. RFC 1885 2.4(e) 4818c2ecf20Sopenharmony_ci * Rule (e.1) is enforced by not using icmp6_send 4828c2ecf20Sopenharmony_ci * in any code that processes icmp errors. 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_ci addr_type = ipv6_addr_type(&hdr->daddr); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (ipv6_chk_addr(net, &hdr->daddr, skb->dev, 0) || 4878c2ecf20Sopenharmony_ci ipv6_chk_acast_addr_src(net, skb->dev, &hdr->daddr)) 4888c2ecf20Sopenharmony_ci saddr = &hdr->daddr; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* 4918c2ecf20Sopenharmony_ci * Dest addr check 4928c2ecf20Sopenharmony_ci */ 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (addr_type & IPV6_ADDR_MULTICAST || skb->pkt_type != PACKET_HOST) { 4958c2ecf20Sopenharmony_ci if (type != ICMPV6_PKT_TOOBIG && 4968c2ecf20Sopenharmony_ci !(type == ICMPV6_PARAMPROB && 4978c2ecf20Sopenharmony_ci code == ICMPV6_UNK_OPTION && 4988c2ecf20Sopenharmony_ci (opt_unrec(skb, info)))) 4998c2ecf20Sopenharmony_ci return; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci saddr = NULL; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci addr_type = ipv6_addr_type(&hdr->saddr); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* 5078c2ecf20Sopenharmony_ci * Source addr check 5088c2ecf20Sopenharmony_ci */ 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (__ipv6_addr_needs_scope_id(addr_type)) { 5118c2ecf20Sopenharmony_ci iif = icmp6_iif(skb); 5128c2ecf20Sopenharmony_ci } else { 5138c2ecf20Sopenharmony_ci /* 5148c2ecf20Sopenharmony_ci * The source device is used for looking up which routing table 5158c2ecf20Sopenharmony_ci * to use for sending an ICMP error. 5168c2ecf20Sopenharmony_ci */ 5178c2ecf20Sopenharmony_ci iif = l3mdev_master_ifindex(skb->dev); 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci /* 5218c2ecf20Sopenharmony_ci * Must not send error if the source does not uniquely 5228c2ecf20Sopenharmony_ci * identify a single node (RFC2463 Section 2.4). 5238c2ecf20Sopenharmony_ci * We check unspecified / multicast addresses here, 5248c2ecf20Sopenharmony_ci * and anycast addresses will be checked later. 5258c2ecf20Sopenharmony_ci */ 5268c2ecf20Sopenharmony_ci if ((addr_type == IPV6_ADDR_ANY) || (addr_type & IPV6_ADDR_MULTICAST)) { 5278c2ecf20Sopenharmony_ci net_dbg_ratelimited("icmp6_send: addr_any/mcast source [%pI6c > %pI6c]\n", 5288c2ecf20Sopenharmony_ci &hdr->saddr, &hdr->daddr); 5298c2ecf20Sopenharmony_ci return; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci /* 5338c2ecf20Sopenharmony_ci * Never answer to a ICMP packet. 5348c2ecf20Sopenharmony_ci */ 5358c2ecf20Sopenharmony_ci if (is_ineligible(skb)) { 5368c2ecf20Sopenharmony_ci net_dbg_ratelimited("icmp6_send: no reply to icmp error [%pI6c > %pI6c]\n", 5378c2ecf20Sopenharmony_ci &hdr->saddr, &hdr->daddr); 5388c2ecf20Sopenharmony_ci return; 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci /* Needed by both icmp_global_allow and icmpv6_xmit_lock */ 5428c2ecf20Sopenharmony_ci local_bh_disable(); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci /* Check global sysctl_icmp_msgs_per_sec ratelimit */ 5458c2ecf20Sopenharmony_ci if (!(skb->dev->flags & IFF_LOOPBACK) && !icmpv6_global_allow(net, type)) 5468c2ecf20Sopenharmony_ci goto out_bh_enable; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci mip6_addr_swap(skb, parm); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci sk = icmpv6_xmit_lock(net); 5518c2ecf20Sopenharmony_ci if (!sk) 5528c2ecf20Sopenharmony_ci goto out_bh_enable; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci memset(&fl6, 0, sizeof(fl6)); 5558c2ecf20Sopenharmony_ci fl6.flowi6_proto = IPPROTO_ICMPV6; 5568c2ecf20Sopenharmony_ci fl6.daddr = hdr->saddr; 5578c2ecf20Sopenharmony_ci if (force_saddr) 5588c2ecf20Sopenharmony_ci saddr = force_saddr; 5598c2ecf20Sopenharmony_ci if (saddr) { 5608c2ecf20Sopenharmony_ci fl6.saddr = *saddr; 5618c2ecf20Sopenharmony_ci } else if (!icmpv6_rt_has_prefsrc(sk, type, &fl6)) { 5628c2ecf20Sopenharmony_ci /* select a more meaningful saddr from input if */ 5638c2ecf20Sopenharmony_ci struct net_device *in_netdev; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci in_netdev = dev_get_by_index(net, parm->iif); 5668c2ecf20Sopenharmony_ci if (in_netdev) { 5678c2ecf20Sopenharmony_ci ipv6_dev_get_saddr(net, in_netdev, &fl6.daddr, 5688c2ecf20Sopenharmony_ci inet6_sk(sk)->srcprefs, 5698c2ecf20Sopenharmony_ci &fl6.saddr); 5708c2ecf20Sopenharmony_ci dev_put(in_netdev); 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci fl6.flowi6_mark = mark; 5748c2ecf20Sopenharmony_ci fl6.flowi6_oif = iif; 5758c2ecf20Sopenharmony_ci fl6.fl6_icmp_type = type; 5768c2ecf20Sopenharmony_ci fl6.fl6_icmp_code = code; 5778c2ecf20Sopenharmony_ci fl6.flowi6_uid = sock_net_uid(net, NULL); 5788c2ecf20Sopenharmony_ci fl6.mp_hash = rt6_multipath_hash(net, &fl6, skb, NULL); 5798c2ecf20Sopenharmony_ci security_skb_classify_flow(skb, flowi6_to_flowi_common(&fl6)); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci np = inet6_sk(sk); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci if (!icmpv6_xrlim_allow(sk, type, &fl6)) 5848c2ecf20Sopenharmony_ci goto out; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci tmp_hdr.icmp6_type = type; 5878c2ecf20Sopenharmony_ci tmp_hdr.icmp6_code = code; 5888c2ecf20Sopenharmony_ci tmp_hdr.icmp6_cksum = 0; 5898c2ecf20Sopenharmony_ci tmp_hdr.icmp6_pointer = htonl(info); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) 5928c2ecf20Sopenharmony_ci fl6.flowi6_oif = np->mcast_oif; 5938c2ecf20Sopenharmony_ci else if (!fl6.flowi6_oif) 5948c2ecf20Sopenharmony_ci fl6.flowi6_oif = np->ucast_oif; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci ipcm6_init_sk(&ipc6, np); 5978c2ecf20Sopenharmony_ci ipc6.sockc.mark = mark; 5988c2ecf20Sopenharmony_ci fl6.flowlabel = ip6_make_flowinfo(ipc6.tclass, fl6.flowlabel); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci dst = icmpv6_route_lookup(net, skb, sk, &fl6); 6018c2ecf20Sopenharmony_ci if (IS_ERR(dst)) 6028c2ecf20Sopenharmony_ci goto out; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci ipc6.hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci msg.skb = skb; 6078c2ecf20Sopenharmony_ci msg.offset = skb_network_offset(skb); 6088c2ecf20Sopenharmony_ci msg.type = type; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci len = skb->len - msg.offset; 6118c2ecf20Sopenharmony_ci len = min_t(unsigned int, len, IPV6_MIN_MTU - sizeof(struct ipv6hdr) - sizeof(struct icmp6hdr)); 6128c2ecf20Sopenharmony_ci if (len < 0) { 6138c2ecf20Sopenharmony_ci net_dbg_ratelimited("icmp: len problem [%pI6c > %pI6c]\n", 6148c2ecf20Sopenharmony_ci &hdr->saddr, &hdr->daddr); 6158c2ecf20Sopenharmony_ci goto out_dst_release; 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci rcu_read_lock(); 6198c2ecf20Sopenharmony_ci idev = __in6_dev_get(skb->dev); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if (ip6_append_data(sk, icmpv6_getfrag, &msg, 6228c2ecf20Sopenharmony_ci len + sizeof(struct icmp6hdr), 6238c2ecf20Sopenharmony_ci sizeof(struct icmp6hdr), 6248c2ecf20Sopenharmony_ci &ipc6, &fl6, (struct rt6_info *)dst, 6258c2ecf20Sopenharmony_ci MSG_DONTWAIT)) { 6268c2ecf20Sopenharmony_ci ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTERRORS); 6278c2ecf20Sopenharmony_ci ip6_flush_pending_frames(sk); 6288c2ecf20Sopenharmony_ci } else { 6298c2ecf20Sopenharmony_ci icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr, 6308c2ecf20Sopenharmony_ci len + sizeof(struct icmp6hdr)); 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci rcu_read_unlock(); 6338c2ecf20Sopenharmony_ciout_dst_release: 6348c2ecf20Sopenharmony_ci dst_release(dst); 6358c2ecf20Sopenharmony_ciout: 6368c2ecf20Sopenharmony_ci icmpv6_xmit_unlock(sk); 6378c2ecf20Sopenharmony_ciout_bh_enable: 6388c2ecf20Sopenharmony_ci local_bh_enable(); 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ciEXPORT_SYMBOL(icmp6_send); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci/* Slightly more convenient version of icmp6_send. 6438c2ecf20Sopenharmony_ci */ 6448c2ecf20Sopenharmony_civoid icmpv6_param_prob(struct sk_buff *skb, u8 code, int pos) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci icmp6_send(skb, ICMPV6_PARAMPROB, code, pos, NULL, IP6CB(skb)); 6478c2ecf20Sopenharmony_ci kfree_skb(skb); 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci/* Generate icmpv6 with type/code ICMPV6_DEST_UNREACH/ICMPV6_ADDR_UNREACH 6518c2ecf20Sopenharmony_ci * if sufficient data bytes are available 6528c2ecf20Sopenharmony_ci * @nhs is the size of the tunnel header(s) : 6538c2ecf20Sopenharmony_ci * Either an IPv4 header for SIT encap 6548c2ecf20Sopenharmony_ci * an IPv4 header + GRE header for GRE encap 6558c2ecf20Sopenharmony_ci */ 6568c2ecf20Sopenharmony_ciint ip6_err_gen_icmpv6_unreach(struct sk_buff *skb, int nhs, int type, 6578c2ecf20Sopenharmony_ci unsigned int data_len) 6588c2ecf20Sopenharmony_ci{ 6598c2ecf20Sopenharmony_ci struct in6_addr temp_saddr; 6608c2ecf20Sopenharmony_ci struct rt6_info *rt; 6618c2ecf20Sopenharmony_ci struct sk_buff *skb2; 6628c2ecf20Sopenharmony_ci u32 info = 0; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, nhs + sizeof(struct ipv6hdr) + 8)) 6658c2ecf20Sopenharmony_ci return 1; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci /* RFC 4884 (partial) support for ICMP extensions */ 6688c2ecf20Sopenharmony_ci if (data_len < 128 || (data_len & 7) || skb->len < data_len) 6698c2ecf20Sopenharmony_ci data_len = 0; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci skb2 = data_len ? skb_copy(skb, GFP_ATOMIC) : skb_clone(skb, GFP_ATOMIC); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci if (!skb2) 6748c2ecf20Sopenharmony_ci return 1; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci skb_dst_drop(skb2); 6778c2ecf20Sopenharmony_ci skb_pull(skb2, nhs); 6788c2ecf20Sopenharmony_ci skb_reset_network_header(skb2); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci rt = rt6_lookup(dev_net(skb->dev), &ipv6_hdr(skb2)->saddr, NULL, 0, 6818c2ecf20Sopenharmony_ci skb, 0); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci if (rt && rt->dst.dev) 6848c2ecf20Sopenharmony_ci skb2->dev = rt->dst.dev; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci ipv6_addr_set_v4mapped(ip_hdr(skb)->saddr, &temp_saddr); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci if (data_len) { 6898c2ecf20Sopenharmony_ci /* RFC 4884 (partial) support : 6908c2ecf20Sopenharmony_ci * insert 0 padding at the end, before the extensions 6918c2ecf20Sopenharmony_ci */ 6928c2ecf20Sopenharmony_ci __skb_push(skb2, nhs); 6938c2ecf20Sopenharmony_ci skb_reset_network_header(skb2); 6948c2ecf20Sopenharmony_ci memmove(skb2->data, skb2->data + nhs, data_len - nhs); 6958c2ecf20Sopenharmony_ci memset(skb2->data + data_len - nhs, 0, nhs); 6968c2ecf20Sopenharmony_ci /* RFC 4884 4.5 : Length is measured in 64-bit words, 6978c2ecf20Sopenharmony_ci * and stored in reserved[0] 6988c2ecf20Sopenharmony_ci */ 6998c2ecf20Sopenharmony_ci info = (data_len/8) << 24; 7008c2ecf20Sopenharmony_ci } 7018c2ecf20Sopenharmony_ci if (type == ICMP_TIME_EXCEEDED) 7028c2ecf20Sopenharmony_ci icmp6_send(skb2, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT, 7038c2ecf20Sopenharmony_ci info, &temp_saddr, IP6CB(skb2)); 7048c2ecf20Sopenharmony_ci else 7058c2ecf20Sopenharmony_ci icmp6_send(skb2, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 7068c2ecf20Sopenharmony_ci info, &temp_saddr, IP6CB(skb2)); 7078c2ecf20Sopenharmony_ci if (rt) 7088c2ecf20Sopenharmony_ci ip6_rt_put(rt); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci kfree_skb(skb2); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci return 0; 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ip6_err_gen_icmpv6_unreach); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic void icmpv6_echo_reply(struct sk_buff *skb) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 7198c2ecf20Sopenharmony_ci struct sock *sk; 7208c2ecf20Sopenharmony_ci struct inet6_dev *idev; 7218c2ecf20Sopenharmony_ci struct ipv6_pinfo *np; 7228c2ecf20Sopenharmony_ci const struct in6_addr *saddr = NULL; 7238c2ecf20Sopenharmony_ci struct icmp6hdr *icmph = icmp6_hdr(skb); 7248c2ecf20Sopenharmony_ci struct icmp6hdr tmp_hdr; 7258c2ecf20Sopenharmony_ci struct flowi6 fl6; 7268c2ecf20Sopenharmony_ci struct icmpv6_msg msg; 7278c2ecf20Sopenharmony_ci struct dst_entry *dst; 7288c2ecf20Sopenharmony_ci struct ipcm6_cookie ipc6; 7298c2ecf20Sopenharmony_ci u32 mark = IP6_REPLY_MARK(net, skb->mark); 7308c2ecf20Sopenharmony_ci bool acast; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) && 7338c2ecf20Sopenharmony_ci net->ipv6.sysctl.icmpv6_echo_ignore_multicast) 7348c2ecf20Sopenharmony_ci return; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci saddr = &ipv6_hdr(skb)->daddr; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci acast = ipv6_anycast_destination(skb_dst(skb), saddr); 7398c2ecf20Sopenharmony_ci if (acast && net->ipv6.sysctl.icmpv6_echo_ignore_anycast) 7408c2ecf20Sopenharmony_ci return; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci if (!ipv6_unicast_destination(skb) && 7438c2ecf20Sopenharmony_ci !(net->ipv6.sysctl.anycast_src_echo_reply && acast)) 7448c2ecf20Sopenharmony_ci saddr = NULL; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci memcpy(&tmp_hdr, icmph, sizeof(tmp_hdr)); 7478c2ecf20Sopenharmony_ci tmp_hdr.icmp6_type = ICMPV6_ECHO_REPLY; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci memset(&fl6, 0, sizeof(fl6)); 7508c2ecf20Sopenharmony_ci if (net->ipv6.sysctl.flowlabel_reflect & FLOWLABEL_REFLECT_ICMPV6_ECHO_REPLIES) 7518c2ecf20Sopenharmony_ci fl6.flowlabel = ip6_flowlabel(ipv6_hdr(skb)); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci fl6.flowi6_proto = IPPROTO_ICMPV6; 7548c2ecf20Sopenharmony_ci fl6.daddr = ipv6_hdr(skb)->saddr; 7558c2ecf20Sopenharmony_ci if (saddr) 7568c2ecf20Sopenharmony_ci fl6.saddr = *saddr; 7578c2ecf20Sopenharmony_ci fl6.flowi6_oif = icmp6_iif(skb); 7588c2ecf20Sopenharmony_ci fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY; 7598c2ecf20Sopenharmony_ci fl6.flowi6_mark = mark; 7608c2ecf20Sopenharmony_ci fl6.flowi6_uid = sock_net_uid(net, NULL); 7618c2ecf20Sopenharmony_ci security_skb_classify_flow(skb, flowi6_to_flowi_common(&fl6)); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci local_bh_disable(); 7648c2ecf20Sopenharmony_ci sk = icmpv6_xmit_lock(net); 7658c2ecf20Sopenharmony_ci if (!sk) 7668c2ecf20Sopenharmony_ci goto out_bh_enable; 7678c2ecf20Sopenharmony_ci np = inet6_sk(sk); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) 7708c2ecf20Sopenharmony_ci fl6.flowi6_oif = np->mcast_oif; 7718c2ecf20Sopenharmony_ci else if (!fl6.flowi6_oif) 7728c2ecf20Sopenharmony_ci fl6.flowi6_oif = np->ucast_oif; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci if (ip6_dst_lookup(net, sk, &dst, &fl6)) 7758c2ecf20Sopenharmony_ci goto out; 7768c2ecf20Sopenharmony_ci dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), sk, 0); 7778c2ecf20Sopenharmony_ci if (IS_ERR(dst)) 7788c2ecf20Sopenharmony_ci goto out; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci /* Check the ratelimit */ 7818c2ecf20Sopenharmony_ci if ((!(skb->dev->flags & IFF_LOOPBACK) && !icmpv6_global_allow(net, ICMPV6_ECHO_REPLY)) || 7828c2ecf20Sopenharmony_ci !icmpv6_xrlim_allow(sk, ICMPV6_ECHO_REPLY, &fl6)) 7838c2ecf20Sopenharmony_ci goto out_dst_release; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci idev = __in6_dev_get(skb->dev); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci msg.skb = skb; 7888c2ecf20Sopenharmony_ci msg.offset = 0; 7898c2ecf20Sopenharmony_ci msg.type = ICMPV6_ECHO_REPLY; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci ipcm6_init_sk(&ipc6, np); 7928c2ecf20Sopenharmony_ci ipc6.hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst); 7938c2ecf20Sopenharmony_ci ipc6.tclass = ipv6_get_dsfield(ipv6_hdr(skb)); 7948c2ecf20Sopenharmony_ci ipc6.sockc.mark = mark; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci if (ip6_append_data(sk, icmpv6_getfrag, &msg, 7978c2ecf20Sopenharmony_ci skb->len + sizeof(struct icmp6hdr), 7988c2ecf20Sopenharmony_ci sizeof(struct icmp6hdr), &ipc6, &fl6, 7998c2ecf20Sopenharmony_ci (struct rt6_info *)dst, MSG_DONTWAIT)) { 8008c2ecf20Sopenharmony_ci __ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTERRORS); 8018c2ecf20Sopenharmony_ci ip6_flush_pending_frames(sk); 8028c2ecf20Sopenharmony_ci } else { 8038c2ecf20Sopenharmony_ci icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr, 8048c2ecf20Sopenharmony_ci skb->len + sizeof(struct icmp6hdr)); 8058c2ecf20Sopenharmony_ci } 8068c2ecf20Sopenharmony_ciout_dst_release: 8078c2ecf20Sopenharmony_ci dst_release(dst); 8088c2ecf20Sopenharmony_ciout: 8098c2ecf20Sopenharmony_ci icmpv6_xmit_unlock(sk); 8108c2ecf20Sopenharmony_ciout_bh_enable: 8118c2ecf20Sopenharmony_ci local_bh_enable(); 8128c2ecf20Sopenharmony_ci} 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_civoid icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info) 8158c2ecf20Sopenharmony_ci{ 8168c2ecf20Sopenharmony_ci const struct inet6_protocol *ipprot; 8178c2ecf20Sopenharmony_ci int inner_offset; 8188c2ecf20Sopenharmony_ci __be16 frag_off; 8198c2ecf20Sopenharmony_ci u8 nexthdr; 8208c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 8238c2ecf20Sopenharmony_ci goto out; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci nexthdr = ((struct ipv6hdr *)skb->data)->nexthdr; 8268c2ecf20Sopenharmony_ci if (ipv6_ext_hdr(nexthdr)) { 8278c2ecf20Sopenharmony_ci /* now skip over extension headers */ 8288c2ecf20Sopenharmony_ci inner_offset = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), 8298c2ecf20Sopenharmony_ci &nexthdr, &frag_off); 8308c2ecf20Sopenharmony_ci if (inner_offset < 0) 8318c2ecf20Sopenharmony_ci goto out; 8328c2ecf20Sopenharmony_ci } else { 8338c2ecf20Sopenharmony_ci inner_offset = sizeof(struct ipv6hdr); 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci /* Checkin header including 8 bytes of inner protocol header. */ 8378c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, inner_offset+8)) 8388c2ecf20Sopenharmony_ci goto out; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci /* BUGGG_FUTURE: we should try to parse exthdrs in this packet. 8418c2ecf20Sopenharmony_ci Without this we will not able f.e. to make source routed 8428c2ecf20Sopenharmony_ci pmtu discovery. 8438c2ecf20Sopenharmony_ci Corresponding argument (opt) to notifiers is already added. 8448c2ecf20Sopenharmony_ci --ANK (980726) 8458c2ecf20Sopenharmony_ci */ 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci ipprot = rcu_dereference(inet6_protos[nexthdr]); 8488c2ecf20Sopenharmony_ci if (ipprot && ipprot->err_handler) 8498c2ecf20Sopenharmony_ci ipprot->err_handler(skb, NULL, type, code, inner_offset, info); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci raw6_icmp_error(skb, nexthdr, type, code, inner_offset, info); 8528c2ecf20Sopenharmony_ci return; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ciout: 8558c2ecf20Sopenharmony_ci __ICMP6_INC_STATS(net, __in6_dev_get(skb->dev), ICMP6_MIB_INERRORS); 8568c2ecf20Sopenharmony_ci} 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci/* 8598c2ecf20Sopenharmony_ci * Handle icmp messages 8608c2ecf20Sopenharmony_ci */ 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_cistatic int icmpv6_rcv(struct sk_buff *skb) 8638c2ecf20Sopenharmony_ci{ 8648c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 8658c2ecf20Sopenharmony_ci struct net_device *dev = icmp6_dev(skb); 8668c2ecf20Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(dev); 8678c2ecf20Sopenharmony_ci const struct in6_addr *saddr, *daddr; 8688c2ecf20Sopenharmony_ci struct icmp6hdr *hdr; 8698c2ecf20Sopenharmony_ci u8 type; 8708c2ecf20Sopenharmony_ci bool success = false; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) { 8738c2ecf20Sopenharmony_ci struct sec_path *sp = skb_sec_path(skb); 8748c2ecf20Sopenharmony_ci int nh; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci if (!(sp && sp->xvec[sp->len - 1]->props.flags & 8778c2ecf20Sopenharmony_ci XFRM_STATE_ICMP)) 8788c2ecf20Sopenharmony_ci goto drop_no_count; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(*hdr) + sizeof(struct ipv6hdr))) 8818c2ecf20Sopenharmony_ci goto drop_no_count; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci nh = skb_network_offset(skb); 8848c2ecf20Sopenharmony_ci skb_set_network_header(skb, sizeof(*hdr)); 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci if (!xfrm6_policy_check_reverse(NULL, XFRM_POLICY_IN, skb)) 8878c2ecf20Sopenharmony_ci goto drop_no_count; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci skb_set_network_header(skb, nh); 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci __ICMP6_INC_STATS(dev_net(dev), idev, ICMP6_MIB_INMSGS); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci saddr = &ipv6_hdr(skb)->saddr; 8958c2ecf20Sopenharmony_ci daddr = &ipv6_hdr(skb)->daddr; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci if (skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo)) { 8988c2ecf20Sopenharmony_ci net_dbg_ratelimited("ICMPv6 checksum failed [%pI6c > %pI6c]\n", 8998c2ecf20Sopenharmony_ci saddr, daddr); 9008c2ecf20Sopenharmony_ci goto csum_error; 9018c2ecf20Sopenharmony_ci } 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci if (!pskb_pull(skb, sizeof(*hdr))) 9048c2ecf20Sopenharmony_ci goto discard_it; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci hdr = icmp6_hdr(skb); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci type = hdr->icmp6_type; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci ICMP6MSGIN_INC_STATS(dev_net(dev), idev, type); 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci switch (type) { 9138c2ecf20Sopenharmony_ci case ICMPV6_ECHO_REQUEST: 9148c2ecf20Sopenharmony_ci if (!net->ipv6.sysctl.icmpv6_echo_ignore_all) 9158c2ecf20Sopenharmony_ci icmpv6_echo_reply(skb); 9168c2ecf20Sopenharmony_ci break; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci case ICMPV6_ECHO_REPLY: 9198c2ecf20Sopenharmony_ci success = ping_rcv(skb); 9208c2ecf20Sopenharmony_ci break; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci case ICMPV6_PKT_TOOBIG: 9238c2ecf20Sopenharmony_ci /* BUGGG_FUTURE: if packet contains rthdr, we cannot update 9248c2ecf20Sopenharmony_ci standard destination cache. Seems, only "advanced" 9258c2ecf20Sopenharmony_ci destination cache will allow to solve this problem 9268c2ecf20Sopenharmony_ci --ANK (980726) 9278c2ecf20Sopenharmony_ci */ 9288c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 9298c2ecf20Sopenharmony_ci goto discard_it; 9308c2ecf20Sopenharmony_ci hdr = icmp6_hdr(skb); 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci /* to notify */ 9338c2ecf20Sopenharmony_ci fallthrough; 9348c2ecf20Sopenharmony_ci case ICMPV6_DEST_UNREACH: 9358c2ecf20Sopenharmony_ci case ICMPV6_TIME_EXCEED: 9368c2ecf20Sopenharmony_ci case ICMPV6_PARAMPROB: 9378c2ecf20Sopenharmony_ci icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu); 9388c2ecf20Sopenharmony_ci break; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci case NDISC_ROUTER_SOLICITATION: 9418c2ecf20Sopenharmony_ci case NDISC_ROUTER_ADVERTISEMENT: 9428c2ecf20Sopenharmony_ci case NDISC_NEIGHBOUR_SOLICITATION: 9438c2ecf20Sopenharmony_ci case NDISC_NEIGHBOUR_ADVERTISEMENT: 9448c2ecf20Sopenharmony_ci case NDISC_REDIRECT: 9458c2ecf20Sopenharmony_ci ndisc_rcv(skb); 9468c2ecf20Sopenharmony_ci break; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci case ICMPV6_MGM_QUERY: 9498c2ecf20Sopenharmony_ci igmp6_event_query(skb); 9508c2ecf20Sopenharmony_ci break; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci case ICMPV6_MGM_REPORT: 9538c2ecf20Sopenharmony_ci igmp6_event_report(skb); 9548c2ecf20Sopenharmony_ci break; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci case ICMPV6_MGM_REDUCTION: 9578c2ecf20Sopenharmony_ci case ICMPV6_NI_QUERY: 9588c2ecf20Sopenharmony_ci case ICMPV6_NI_REPLY: 9598c2ecf20Sopenharmony_ci case ICMPV6_MLD2_REPORT: 9608c2ecf20Sopenharmony_ci case ICMPV6_DHAAD_REQUEST: 9618c2ecf20Sopenharmony_ci case ICMPV6_DHAAD_REPLY: 9628c2ecf20Sopenharmony_ci case ICMPV6_MOBILE_PREFIX_SOL: 9638c2ecf20Sopenharmony_ci case ICMPV6_MOBILE_PREFIX_ADV: 9648c2ecf20Sopenharmony_ci break; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci default: 9678c2ecf20Sopenharmony_ci /* informational */ 9688c2ecf20Sopenharmony_ci if (type & ICMPV6_INFOMSG_MASK) 9698c2ecf20Sopenharmony_ci break; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci net_dbg_ratelimited("icmpv6: msg of unknown type [%pI6c > %pI6c]\n", 9728c2ecf20Sopenharmony_ci saddr, daddr); 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci /* 9758c2ecf20Sopenharmony_ci * error of unknown type. 9768c2ecf20Sopenharmony_ci * must pass to upper level 9778c2ecf20Sopenharmony_ci */ 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu); 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci /* until the v6 path can be better sorted assume failure and 9838c2ecf20Sopenharmony_ci * preserve the status quo behaviour for the rest of the paths to here 9848c2ecf20Sopenharmony_ci */ 9858c2ecf20Sopenharmony_ci if (success) 9868c2ecf20Sopenharmony_ci consume_skb(skb); 9878c2ecf20Sopenharmony_ci else 9888c2ecf20Sopenharmony_ci kfree_skb(skb); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci return 0; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_cicsum_error: 9938c2ecf20Sopenharmony_ci __ICMP6_INC_STATS(dev_net(dev), idev, ICMP6_MIB_CSUMERRORS); 9948c2ecf20Sopenharmony_cidiscard_it: 9958c2ecf20Sopenharmony_ci __ICMP6_INC_STATS(dev_net(dev), idev, ICMP6_MIB_INERRORS); 9968c2ecf20Sopenharmony_cidrop_no_count: 9978c2ecf20Sopenharmony_ci kfree_skb(skb); 9988c2ecf20Sopenharmony_ci return 0; 9998c2ecf20Sopenharmony_ci} 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_civoid icmpv6_flow_init(struct sock *sk, struct flowi6 *fl6, 10028c2ecf20Sopenharmony_ci u8 type, 10038c2ecf20Sopenharmony_ci const struct in6_addr *saddr, 10048c2ecf20Sopenharmony_ci const struct in6_addr *daddr, 10058c2ecf20Sopenharmony_ci int oif) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci memset(fl6, 0, sizeof(*fl6)); 10088c2ecf20Sopenharmony_ci fl6->saddr = *saddr; 10098c2ecf20Sopenharmony_ci fl6->daddr = *daddr; 10108c2ecf20Sopenharmony_ci fl6->flowi6_proto = IPPROTO_ICMPV6; 10118c2ecf20Sopenharmony_ci fl6->fl6_icmp_type = type; 10128c2ecf20Sopenharmony_ci fl6->fl6_icmp_code = 0; 10138c2ecf20Sopenharmony_ci fl6->flowi6_oif = oif; 10148c2ecf20Sopenharmony_ci security_sk_classify_flow(sk, flowi6_to_flowi_common(fl6)); 10158c2ecf20Sopenharmony_ci} 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_cistatic void __net_exit icmpv6_sk_exit(struct net *net) 10188c2ecf20Sopenharmony_ci{ 10198c2ecf20Sopenharmony_ci int i; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci for_each_possible_cpu(i) 10228c2ecf20Sopenharmony_ci inet_ctl_sock_destroy(*per_cpu_ptr(net->ipv6.icmp_sk, i)); 10238c2ecf20Sopenharmony_ci free_percpu(net->ipv6.icmp_sk); 10248c2ecf20Sopenharmony_ci} 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_cistatic int __net_init icmpv6_sk_init(struct net *net) 10278c2ecf20Sopenharmony_ci{ 10288c2ecf20Sopenharmony_ci struct sock *sk; 10298c2ecf20Sopenharmony_ci int err, i; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci net->ipv6.icmp_sk = alloc_percpu(struct sock *); 10328c2ecf20Sopenharmony_ci if (!net->ipv6.icmp_sk) 10338c2ecf20Sopenharmony_ci return -ENOMEM; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci for_each_possible_cpu(i) { 10368c2ecf20Sopenharmony_ci err = inet_ctl_sock_create(&sk, PF_INET6, 10378c2ecf20Sopenharmony_ci SOCK_RAW, IPPROTO_ICMPV6, net); 10388c2ecf20Sopenharmony_ci if (err < 0) { 10398c2ecf20Sopenharmony_ci pr_err("Failed to initialize the ICMP6 control socket (err %d)\n", 10408c2ecf20Sopenharmony_ci err); 10418c2ecf20Sopenharmony_ci goto fail; 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci *per_cpu_ptr(net->ipv6.icmp_sk, i) = sk; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci /* Enough space for 2 64K ICMP packets, including 10478c2ecf20Sopenharmony_ci * sk_buff struct overhead. 10488c2ecf20Sopenharmony_ci */ 10498c2ecf20Sopenharmony_ci sk->sk_sndbuf = 2 * SKB_TRUESIZE(64 * 1024); 10508c2ecf20Sopenharmony_ci } 10518c2ecf20Sopenharmony_ci return 0; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci fail: 10548c2ecf20Sopenharmony_ci icmpv6_sk_exit(net); 10558c2ecf20Sopenharmony_ci return err; 10568c2ecf20Sopenharmony_ci} 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_cistatic struct pernet_operations icmpv6_sk_ops = { 10598c2ecf20Sopenharmony_ci .init = icmpv6_sk_init, 10608c2ecf20Sopenharmony_ci .exit = icmpv6_sk_exit, 10618c2ecf20Sopenharmony_ci}; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ciint __init icmpv6_init(void) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci int err; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci err = register_pernet_subsys(&icmpv6_sk_ops); 10688c2ecf20Sopenharmony_ci if (err < 0) 10698c2ecf20Sopenharmony_ci return err; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci err = -EAGAIN; 10728c2ecf20Sopenharmony_ci if (inet6_add_protocol(&icmpv6_protocol, IPPROTO_ICMPV6) < 0) 10738c2ecf20Sopenharmony_ci goto fail; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci err = inet6_register_icmp_sender(icmp6_send); 10768c2ecf20Sopenharmony_ci if (err) 10778c2ecf20Sopenharmony_ci goto sender_reg_err; 10788c2ecf20Sopenharmony_ci return 0; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_cisender_reg_err: 10818c2ecf20Sopenharmony_ci inet6_del_protocol(&icmpv6_protocol, IPPROTO_ICMPV6); 10828c2ecf20Sopenharmony_cifail: 10838c2ecf20Sopenharmony_ci pr_err("Failed to register ICMP6 protocol\n"); 10848c2ecf20Sopenharmony_ci unregister_pernet_subsys(&icmpv6_sk_ops); 10858c2ecf20Sopenharmony_ci return err; 10868c2ecf20Sopenharmony_ci} 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_civoid icmpv6_cleanup(void) 10898c2ecf20Sopenharmony_ci{ 10908c2ecf20Sopenharmony_ci inet6_unregister_icmp_sender(icmp6_send); 10918c2ecf20Sopenharmony_ci unregister_pernet_subsys(&icmpv6_sk_ops); 10928c2ecf20Sopenharmony_ci inet6_del_protocol(&icmpv6_protocol, IPPROTO_ICMPV6); 10938c2ecf20Sopenharmony_ci} 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_cistatic const struct icmp6_err { 10978c2ecf20Sopenharmony_ci int err; 10988c2ecf20Sopenharmony_ci int fatal; 10998c2ecf20Sopenharmony_ci} tab_unreach[] = { 11008c2ecf20Sopenharmony_ci { /* NOROUTE */ 11018c2ecf20Sopenharmony_ci .err = ENETUNREACH, 11028c2ecf20Sopenharmony_ci .fatal = 0, 11038c2ecf20Sopenharmony_ci }, 11048c2ecf20Sopenharmony_ci { /* ADM_PROHIBITED */ 11058c2ecf20Sopenharmony_ci .err = EACCES, 11068c2ecf20Sopenharmony_ci .fatal = 1, 11078c2ecf20Sopenharmony_ci }, 11088c2ecf20Sopenharmony_ci { /* Was NOT_NEIGHBOUR, now reserved */ 11098c2ecf20Sopenharmony_ci .err = EHOSTUNREACH, 11108c2ecf20Sopenharmony_ci .fatal = 0, 11118c2ecf20Sopenharmony_ci }, 11128c2ecf20Sopenharmony_ci { /* ADDR_UNREACH */ 11138c2ecf20Sopenharmony_ci .err = EHOSTUNREACH, 11148c2ecf20Sopenharmony_ci .fatal = 0, 11158c2ecf20Sopenharmony_ci }, 11168c2ecf20Sopenharmony_ci { /* PORT_UNREACH */ 11178c2ecf20Sopenharmony_ci .err = ECONNREFUSED, 11188c2ecf20Sopenharmony_ci .fatal = 1, 11198c2ecf20Sopenharmony_ci }, 11208c2ecf20Sopenharmony_ci { /* POLICY_FAIL */ 11218c2ecf20Sopenharmony_ci .err = EACCES, 11228c2ecf20Sopenharmony_ci .fatal = 1, 11238c2ecf20Sopenharmony_ci }, 11248c2ecf20Sopenharmony_ci { /* REJECT_ROUTE */ 11258c2ecf20Sopenharmony_ci .err = EACCES, 11268c2ecf20Sopenharmony_ci .fatal = 1, 11278c2ecf20Sopenharmony_ci }, 11288c2ecf20Sopenharmony_ci}; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ciint icmpv6_err_convert(u8 type, u8 code, int *err) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci int fatal = 0; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci *err = EPROTO; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci switch (type) { 11378c2ecf20Sopenharmony_ci case ICMPV6_DEST_UNREACH: 11388c2ecf20Sopenharmony_ci fatal = 1; 11398c2ecf20Sopenharmony_ci if (code < ARRAY_SIZE(tab_unreach)) { 11408c2ecf20Sopenharmony_ci *err = tab_unreach[code].err; 11418c2ecf20Sopenharmony_ci fatal = tab_unreach[code].fatal; 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci break; 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci case ICMPV6_PKT_TOOBIG: 11468c2ecf20Sopenharmony_ci *err = EMSGSIZE; 11478c2ecf20Sopenharmony_ci break; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci case ICMPV6_PARAMPROB: 11508c2ecf20Sopenharmony_ci *err = EPROTO; 11518c2ecf20Sopenharmony_ci fatal = 1; 11528c2ecf20Sopenharmony_ci break; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci case ICMPV6_TIME_EXCEED: 11558c2ecf20Sopenharmony_ci *err = EHOSTUNREACH; 11568c2ecf20Sopenharmony_ci break; 11578c2ecf20Sopenharmony_ci } 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci return fatal; 11608c2ecf20Sopenharmony_ci} 11618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(icmpv6_err_convert); 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL 11648c2ecf20Sopenharmony_cistatic struct ctl_table ipv6_icmp_table_template[] = { 11658c2ecf20Sopenharmony_ci { 11668c2ecf20Sopenharmony_ci .procname = "ratelimit", 11678c2ecf20Sopenharmony_ci .data = &init_net.ipv6.sysctl.icmpv6_time, 11688c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 11698c2ecf20Sopenharmony_ci .mode = 0644, 11708c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_ms_jiffies, 11718c2ecf20Sopenharmony_ci }, 11728c2ecf20Sopenharmony_ci { 11738c2ecf20Sopenharmony_ci .procname = "echo_ignore_all", 11748c2ecf20Sopenharmony_ci .data = &init_net.ipv6.sysctl.icmpv6_echo_ignore_all, 11758c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 11768c2ecf20Sopenharmony_ci .mode = 0644, 11778c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 11788c2ecf20Sopenharmony_ci }, 11798c2ecf20Sopenharmony_ci { 11808c2ecf20Sopenharmony_ci .procname = "echo_ignore_multicast", 11818c2ecf20Sopenharmony_ci .data = &init_net.ipv6.sysctl.icmpv6_echo_ignore_multicast, 11828c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 11838c2ecf20Sopenharmony_ci .mode = 0644, 11848c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 11858c2ecf20Sopenharmony_ci }, 11868c2ecf20Sopenharmony_ci { 11878c2ecf20Sopenharmony_ci .procname = "echo_ignore_anycast", 11888c2ecf20Sopenharmony_ci .data = &init_net.ipv6.sysctl.icmpv6_echo_ignore_anycast, 11898c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 11908c2ecf20Sopenharmony_ci .mode = 0644, 11918c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 11928c2ecf20Sopenharmony_ci }, 11938c2ecf20Sopenharmony_ci { 11948c2ecf20Sopenharmony_ci .procname = "ratemask", 11958c2ecf20Sopenharmony_ci .data = &init_net.ipv6.sysctl.icmpv6_ratemask_ptr, 11968c2ecf20Sopenharmony_ci .maxlen = ICMPV6_MSG_MAX + 1, 11978c2ecf20Sopenharmony_ci .mode = 0644, 11988c2ecf20Sopenharmony_ci .proc_handler = proc_do_large_bitmap, 11998c2ecf20Sopenharmony_ci }, 12008c2ecf20Sopenharmony_ci { }, 12018c2ecf20Sopenharmony_ci}; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_cistruct ctl_table * __net_init ipv6_icmp_sysctl_init(struct net *net) 12048c2ecf20Sopenharmony_ci{ 12058c2ecf20Sopenharmony_ci struct ctl_table *table; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci table = kmemdup(ipv6_icmp_table_template, 12088c2ecf20Sopenharmony_ci sizeof(ipv6_icmp_table_template), 12098c2ecf20Sopenharmony_ci GFP_KERNEL); 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci if (table) { 12128c2ecf20Sopenharmony_ci table[0].data = &net->ipv6.sysctl.icmpv6_time; 12138c2ecf20Sopenharmony_ci table[1].data = &net->ipv6.sysctl.icmpv6_echo_ignore_all; 12148c2ecf20Sopenharmony_ci table[2].data = &net->ipv6.sysctl.icmpv6_echo_ignore_multicast; 12158c2ecf20Sopenharmony_ci table[3].data = &net->ipv6.sysctl.icmpv6_echo_ignore_anycast; 12168c2ecf20Sopenharmony_ci table[4].data = &net->ipv6.sysctl.icmpv6_ratemask_ptr; 12178c2ecf20Sopenharmony_ci } 12188c2ecf20Sopenharmony_ci return table; 12198c2ecf20Sopenharmony_ci} 12208c2ecf20Sopenharmony_ci#endif 1221