162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Internet Control Message Protocol (ICMPv6) 462306a36Sopenharmony_ci * Linux INET6 implementation 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Authors: 762306a36Sopenharmony_ci * Pedro Roque <roque@di.fc.ul.pt> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Based on net/ipv4/icmp.c 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * RFC 1885 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* 1562306a36Sopenharmony_ci * Changes: 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * Andi Kleen : exception handling 1862306a36Sopenharmony_ci * Andi Kleen add rate limits. never reply to a icmp. 1962306a36Sopenharmony_ci * add more length checks and other fixes. 2062306a36Sopenharmony_ci * yoshfuji : ensure to sent parameter problem for 2162306a36Sopenharmony_ci * fragments. 2262306a36Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI: added sysctl for icmp rate limit. 2362306a36Sopenharmony_ci * Randy Dunlap and 2462306a36Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI: Per-interface statistics support 2562306a36Sopenharmony_ci * Kazunori MIYAZAWA @USAGI: change output process to use ip6_append_data 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define pr_fmt(fmt) "IPv6: " fmt 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <linux/module.h> 3162306a36Sopenharmony_ci#include <linux/errno.h> 3262306a36Sopenharmony_ci#include <linux/types.h> 3362306a36Sopenharmony_ci#include <linux/socket.h> 3462306a36Sopenharmony_ci#include <linux/in.h> 3562306a36Sopenharmony_ci#include <linux/kernel.h> 3662306a36Sopenharmony_ci#include <linux/sockios.h> 3762306a36Sopenharmony_ci#include <linux/net.h> 3862306a36Sopenharmony_ci#include <linux/skbuff.h> 3962306a36Sopenharmony_ci#include <linux/init.h> 4062306a36Sopenharmony_ci#include <linux/netfilter.h> 4162306a36Sopenharmony_ci#include <linux/slab.h> 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 4462306a36Sopenharmony_ci#include <linux/sysctl.h> 4562306a36Sopenharmony_ci#endif 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#include <linux/inet.h> 4862306a36Sopenharmony_ci#include <linux/netdevice.h> 4962306a36Sopenharmony_ci#include <linux/icmpv6.h> 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#include <net/ip.h> 5262306a36Sopenharmony_ci#include <net/sock.h> 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#include <net/ipv6.h> 5562306a36Sopenharmony_ci#include <net/ip6_checksum.h> 5662306a36Sopenharmony_ci#include <net/ping.h> 5762306a36Sopenharmony_ci#include <net/protocol.h> 5862306a36Sopenharmony_ci#include <net/raw.h> 5962306a36Sopenharmony_ci#include <net/rawv6.h> 6062306a36Sopenharmony_ci#include <net/seg6.h> 6162306a36Sopenharmony_ci#include <net/transp_v6.h> 6262306a36Sopenharmony_ci#include <net/ip6_route.h> 6362306a36Sopenharmony_ci#include <net/addrconf.h> 6462306a36Sopenharmony_ci#include <net/icmp.h> 6562306a36Sopenharmony_ci#include <net/xfrm.h> 6662306a36Sopenharmony_ci#include <net/inet_common.h> 6762306a36Sopenharmony_ci#include <net/dsfield.h> 6862306a36Sopenharmony_ci#include <net/l3mdev.h> 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#include <linux/uaccess.h> 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct sock *, ipv6_icmp_sk); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic int icmpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 7562306a36Sopenharmony_ci u8 type, u8 code, int offset, __be32 info) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci /* icmpv6_notify checks 8 bytes can be pulled, icmp6hdr is 8 bytes */ 7862306a36Sopenharmony_ci struct icmp6hdr *icmp6 = (struct icmp6hdr *) (skb->data + offset); 7962306a36Sopenharmony_ci struct net *net = dev_net(skb->dev); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (type == ICMPV6_PKT_TOOBIG) 8262306a36Sopenharmony_ci ip6_update_pmtu(skb, net, info, skb->dev->ifindex, 0, sock_net_uid(net, NULL)); 8362306a36Sopenharmony_ci else if (type == NDISC_REDIRECT) 8462306a36Sopenharmony_ci ip6_redirect(skb, net, skb->dev->ifindex, 0, 8562306a36Sopenharmony_ci sock_net_uid(net, NULL)); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (!(type & ICMPV6_INFOMSG_MASK)) 8862306a36Sopenharmony_ci if (icmp6->icmp6_type == ICMPV6_ECHO_REQUEST) 8962306a36Sopenharmony_ci ping_err(skb, offset, ntohl(info)); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci return 0; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic int icmpv6_rcv(struct sk_buff *skb); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic const struct inet6_protocol icmpv6_protocol = { 9762306a36Sopenharmony_ci .handler = icmpv6_rcv, 9862306a36Sopenharmony_ci .err_handler = icmpv6_err, 9962306a36Sopenharmony_ci .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* Called with BH disabled */ 10362306a36Sopenharmony_cistatic struct sock *icmpv6_xmit_lock(struct net *net) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct sock *sk; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci sk = this_cpu_read(ipv6_icmp_sk); 10862306a36Sopenharmony_ci if (unlikely(!spin_trylock(&sk->sk_lock.slock))) { 10962306a36Sopenharmony_ci /* This can happen if the output path (f.e. SIT or 11062306a36Sopenharmony_ci * ip6ip6 tunnel) signals dst_link_failure() for an 11162306a36Sopenharmony_ci * outgoing ICMP6 packet. 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_ci return NULL; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci sock_net_set(sk, net); 11662306a36Sopenharmony_ci return sk; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic void icmpv6_xmit_unlock(struct sock *sk) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci sock_net_set(sk, &init_net); 12262306a36Sopenharmony_ci spin_unlock(&sk->sk_lock.slock); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/* 12662306a36Sopenharmony_ci * Figure out, may we reply to this packet with icmp error. 12762306a36Sopenharmony_ci * 12862306a36Sopenharmony_ci * We do not reply, if: 12962306a36Sopenharmony_ci * - it was icmp error message. 13062306a36Sopenharmony_ci * - it is truncated, so that it is known, that protocol is ICMPV6 13162306a36Sopenharmony_ci * (i.e. in the middle of some exthdr) 13262306a36Sopenharmony_ci * 13362306a36Sopenharmony_ci * --ANK (980726) 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic bool is_ineligible(const struct sk_buff *skb) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci int ptr = (u8 *)(ipv6_hdr(skb) + 1) - skb->data; 13962306a36Sopenharmony_ci int len = skb->len - ptr; 14062306a36Sopenharmony_ci __u8 nexthdr = ipv6_hdr(skb)->nexthdr; 14162306a36Sopenharmony_ci __be16 frag_off; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (len < 0) 14462306a36Sopenharmony_ci return true; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci ptr = ipv6_skip_exthdr(skb, ptr, &nexthdr, &frag_off); 14762306a36Sopenharmony_ci if (ptr < 0) 14862306a36Sopenharmony_ci return false; 14962306a36Sopenharmony_ci if (nexthdr == IPPROTO_ICMPV6) { 15062306a36Sopenharmony_ci u8 _type, *tp; 15162306a36Sopenharmony_ci tp = skb_header_pointer(skb, 15262306a36Sopenharmony_ci ptr+offsetof(struct icmp6hdr, icmp6_type), 15362306a36Sopenharmony_ci sizeof(_type), &_type); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* Based on RFC 8200, Section 4.5 Fragment Header, return 15662306a36Sopenharmony_ci * false if this is a fragment packet with no icmp header info. 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_ci if (!tp && frag_off != 0) 15962306a36Sopenharmony_ci return false; 16062306a36Sopenharmony_ci else if (!tp || !(*tp & ICMPV6_INFOMSG_MASK)) 16162306a36Sopenharmony_ci return true; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci return false; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic bool icmpv6_mask_allow(struct net *net, int type) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci if (type > ICMPV6_MSG_MAX) 16962306a36Sopenharmony_ci return true; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* Limit if icmp type is set in ratemask. */ 17262306a36Sopenharmony_ci if (!test_bit(type, net->ipv6.sysctl.icmpv6_ratemask)) 17362306a36Sopenharmony_ci return true; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return false; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic bool icmpv6_global_allow(struct net *net, int type) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci if (icmpv6_mask_allow(net, type)) 18162306a36Sopenharmony_ci return true; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (icmp_global_allow()) 18462306a36Sopenharmony_ci return true; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci __ICMP_INC_STATS(net, ICMP_MIB_RATELIMITGLOBAL); 18762306a36Sopenharmony_ci return false; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/* 19162306a36Sopenharmony_ci * Check the ICMP output rate limit 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_cistatic bool icmpv6_xrlim_allow(struct sock *sk, u8 type, 19462306a36Sopenharmony_ci struct flowi6 *fl6) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct net *net = sock_net(sk); 19762306a36Sopenharmony_ci struct dst_entry *dst; 19862306a36Sopenharmony_ci bool res = false; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (icmpv6_mask_allow(net, type)) 20162306a36Sopenharmony_ci return true; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* 20462306a36Sopenharmony_ci * Look up the output route. 20562306a36Sopenharmony_ci * XXX: perhaps the expire for routing entries cloned by 20662306a36Sopenharmony_ci * this lookup should be more aggressive (not longer than timeout). 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_ci dst = ip6_route_output(net, sk, fl6); 20962306a36Sopenharmony_ci if (dst->error) { 21062306a36Sopenharmony_ci IP6_INC_STATS(net, ip6_dst_idev(dst), 21162306a36Sopenharmony_ci IPSTATS_MIB_OUTNOROUTES); 21262306a36Sopenharmony_ci } else if (dst->dev && (dst->dev->flags&IFF_LOOPBACK)) { 21362306a36Sopenharmony_ci res = true; 21462306a36Sopenharmony_ci } else { 21562306a36Sopenharmony_ci struct rt6_info *rt = (struct rt6_info *)dst; 21662306a36Sopenharmony_ci int tmo = net->ipv6.sysctl.icmpv6_time; 21762306a36Sopenharmony_ci struct inet_peer *peer; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* Give more bandwidth to wider prefixes. */ 22062306a36Sopenharmony_ci if (rt->rt6i_dst.plen < 128) 22162306a36Sopenharmony_ci tmo >>= ((128 - rt->rt6i_dst.plen)>>5); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci peer = inet_getpeer_v6(net->ipv6.peers, &fl6->daddr, 1); 22462306a36Sopenharmony_ci res = inet_peer_xrlim_allow(peer, tmo); 22562306a36Sopenharmony_ci if (peer) 22662306a36Sopenharmony_ci inet_putpeer(peer); 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci if (!res) 22962306a36Sopenharmony_ci __ICMP6_INC_STATS(net, ip6_dst_idev(dst), 23062306a36Sopenharmony_ci ICMP6_MIB_RATELIMITHOST); 23162306a36Sopenharmony_ci dst_release(dst); 23262306a36Sopenharmony_ci return res; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic bool icmpv6_rt_has_prefsrc(struct sock *sk, u8 type, 23662306a36Sopenharmony_ci struct flowi6 *fl6) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct net *net = sock_net(sk); 23962306a36Sopenharmony_ci struct dst_entry *dst; 24062306a36Sopenharmony_ci bool res = false; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci dst = ip6_route_output(net, sk, fl6); 24362306a36Sopenharmony_ci if (!dst->error) { 24462306a36Sopenharmony_ci struct rt6_info *rt = (struct rt6_info *)dst; 24562306a36Sopenharmony_ci struct in6_addr prefsrc; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci rt6_get_prefsrc(rt, &prefsrc); 24862306a36Sopenharmony_ci res = !ipv6_addr_any(&prefsrc); 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci dst_release(dst); 25162306a36Sopenharmony_ci return res; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci/* 25562306a36Sopenharmony_ci * an inline helper for the "simple" if statement below 25662306a36Sopenharmony_ci * checks if parameter problem report is caused by an 25762306a36Sopenharmony_ci * unrecognized IPv6 option that has the Option Type 25862306a36Sopenharmony_ci * highest-order two bits set to 10 25962306a36Sopenharmony_ci */ 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic bool opt_unrec(struct sk_buff *skb, __u32 offset) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci u8 _optval, *op; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci offset += skb_network_offset(skb); 26662306a36Sopenharmony_ci op = skb_header_pointer(skb, offset, sizeof(_optval), &_optval); 26762306a36Sopenharmony_ci if (!op) 26862306a36Sopenharmony_ci return true; 26962306a36Sopenharmony_ci return (*op & 0xC0) == 0x80; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_civoid icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, 27362306a36Sopenharmony_ci struct icmp6hdr *thdr, int len) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci struct sk_buff *skb; 27662306a36Sopenharmony_ci struct icmp6hdr *icmp6h; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci skb = skb_peek(&sk->sk_write_queue); 27962306a36Sopenharmony_ci if (!skb) 28062306a36Sopenharmony_ci return; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci icmp6h = icmp6_hdr(skb); 28362306a36Sopenharmony_ci memcpy(icmp6h, thdr, sizeof(struct icmp6hdr)); 28462306a36Sopenharmony_ci icmp6h->icmp6_cksum = 0; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (skb_queue_len(&sk->sk_write_queue) == 1) { 28762306a36Sopenharmony_ci skb->csum = csum_partial(icmp6h, 28862306a36Sopenharmony_ci sizeof(struct icmp6hdr), skb->csum); 28962306a36Sopenharmony_ci icmp6h->icmp6_cksum = csum_ipv6_magic(&fl6->saddr, 29062306a36Sopenharmony_ci &fl6->daddr, 29162306a36Sopenharmony_ci len, fl6->flowi6_proto, 29262306a36Sopenharmony_ci skb->csum); 29362306a36Sopenharmony_ci } else { 29462306a36Sopenharmony_ci __wsum tmp_csum = 0; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci skb_queue_walk(&sk->sk_write_queue, skb) { 29762306a36Sopenharmony_ci tmp_csum = csum_add(tmp_csum, skb->csum); 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci tmp_csum = csum_partial(icmp6h, 30162306a36Sopenharmony_ci sizeof(struct icmp6hdr), tmp_csum); 30262306a36Sopenharmony_ci icmp6h->icmp6_cksum = csum_ipv6_magic(&fl6->saddr, 30362306a36Sopenharmony_ci &fl6->daddr, 30462306a36Sopenharmony_ci len, fl6->flowi6_proto, 30562306a36Sopenharmony_ci tmp_csum); 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci ip6_push_pending_frames(sk); 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistruct icmpv6_msg { 31162306a36Sopenharmony_ci struct sk_buff *skb; 31262306a36Sopenharmony_ci int offset; 31362306a36Sopenharmony_ci uint8_t type; 31462306a36Sopenharmony_ci}; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic int icmpv6_getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci struct icmpv6_msg *msg = (struct icmpv6_msg *) from; 31962306a36Sopenharmony_ci struct sk_buff *org_skb = msg->skb; 32062306a36Sopenharmony_ci __wsum csum; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci csum = skb_copy_and_csum_bits(org_skb, msg->offset + offset, 32362306a36Sopenharmony_ci to, len); 32462306a36Sopenharmony_ci skb->csum = csum_block_add(skb->csum, csum, odd); 32562306a36Sopenharmony_ci if (!(msg->type & ICMPV6_INFOMSG_MASK)) 32662306a36Sopenharmony_ci nf_ct_attach(skb, org_skb); 32762306a36Sopenharmony_ci return 0; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6) 33162306a36Sopenharmony_cistatic void mip6_addr_swap(struct sk_buff *skb, const struct inet6_skb_parm *opt) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci struct ipv6hdr *iph = ipv6_hdr(skb); 33462306a36Sopenharmony_ci struct ipv6_destopt_hao *hao; 33562306a36Sopenharmony_ci int off; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (opt->dsthao) { 33862306a36Sopenharmony_ci off = ipv6_find_tlv(skb, opt->dsthao, IPV6_TLV_HAO); 33962306a36Sopenharmony_ci if (likely(off >= 0)) { 34062306a36Sopenharmony_ci hao = (struct ipv6_destopt_hao *) 34162306a36Sopenharmony_ci (skb_network_header(skb) + off); 34262306a36Sopenharmony_ci swap(iph->saddr, hao->addr); 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci#else 34762306a36Sopenharmony_cistatic inline void mip6_addr_swap(struct sk_buff *skb, const struct inet6_skb_parm *opt) {} 34862306a36Sopenharmony_ci#endif 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic struct dst_entry *icmpv6_route_lookup(struct net *net, 35162306a36Sopenharmony_ci struct sk_buff *skb, 35262306a36Sopenharmony_ci struct sock *sk, 35362306a36Sopenharmony_ci struct flowi6 *fl6) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci struct dst_entry *dst, *dst2; 35662306a36Sopenharmony_ci struct flowi6 fl2; 35762306a36Sopenharmony_ci int err; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci err = ip6_dst_lookup(net, sk, &dst, fl6); 36062306a36Sopenharmony_ci if (err) 36162306a36Sopenharmony_ci return ERR_PTR(err); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* 36462306a36Sopenharmony_ci * We won't send icmp if the destination is known 36562306a36Sopenharmony_ci * anycast unless we need to treat anycast as unicast. 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_ci if (!READ_ONCE(net->ipv6.sysctl.icmpv6_error_anycast_as_unicast) && 36862306a36Sopenharmony_ci ipv6_anycast_destination(dst, &fl6->daddr)) { 36962306a36Sopenharmony_ci net_dbg_ratelimited("icmp6_send: acast source\n"); 37062306a36Sopenharmony_ci dst_release(dst); 37162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* No need to clone since we're just using its address. */ 37562306a36Sopenharmony_ci dst2 = dst; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci dst = xfrm_lookup(net, dst, flowi6_to_flowi(fl6), sk, 0); 37862306a36Sopenharmony_ci if (!IS_ERR(dst)) { 37962306a36Sopenharmony_ci if (dst != dst2) 38062306a36Sopenharmony_ci return dst; 38162306a36Sopenharmony_ci } else { 38262306a36Sopenharmony_ci if (PTR_ERR(dst) == -EPERM) 38362306a36Sopenharmony_ci dst = NULL; 38462306a36Sopenharmony_ci else 38562306a36Sopenharmony_ci return dst; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci err = xfrm_decode_session_reverse(skb, flowi6_to_flowi(&fl2), AF_INET6); 38962306a36Sopenharmony_ci if (err) 39062306a36Sopenharmony_ci goto relookup_failed; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci err = ip6_dst_lookup(net, sk, &dst2, &fl2); 39362306a36Sopenharmony_ci if (err) 39462306a36Sopenharmony_ci goto relookup_failed; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci dst2 = xfrm_lookup(net, dst2, flowi6_to_flowi(&fl2), sk, XFRM_LOOKUP_ICMP); 39762306a36Sopenharmony_ci if (!IS_ERR(dst2)) { 39862306a36Sopenharmony_ci dst_release(dst); 39962306a36Sopenharmony_ci dst = dst2; 40062306a36Sopenharmony_ci } else { 40162306a36Sopenharmony_ci err = PTR_ERR(dst2); 40262306a36Sopenharmony_ci if (err == -EPERM) { 40362306a36Sopenharmony_ci dst_release(dst); 40462306a36Sopenharmony_ci return dst2; 40562306a36Sopenharmony_ci } else 40662306a36Sopenharmony_ci goto relookup_failed; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cirelookup_failed: 41062306a36Sopenharmony_ci if (dst) 41162306a36Sopenharmony_ci return dst; 41262306a36Sopenharmony_ci return ERR_PTR(err); 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic struct net_device *icmp6_dev(const struct sk_buff *skb) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct net_device *dev = skb->dev; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* for local traffic to local address, skb dev is the loopback 42062306a36Sopenharmony_ci * device. Check if there is a dst attached to the skb and if so 42162306a36Sopenharmony_ci * get the real device index. Same is needed for replies to a link 42262306a36Sopenharmony_ci * local address on a device enslaved to an L3 master device 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_ci if (unlikely(dev->ifindex == LOOPBACK_IFINDEX || netif_is_l3_master(skb->dev))) { 42562306a36Sopenharmony_ci const struct rt6_info *rt6 = skb_rt6_info(skb); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* The destination could be an external IP in Ext Hdr (SRv6, RPL, etc.), 42862306a36Sopenharmony_ci * and ip6_null_entry could be set to skb if no route is found. 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_ci if (rt6 && rt6->rt6i_idev) 43162306a36Sopenharmony_ci dev = rt6->rt6i_idev->dev; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci return dev; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic int icmp6_iif(const struct sk_buff *skb) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci return icmp6_dev(skb)->ifindex; 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci/* 44362306a36Sopenharmony_ci * Send an ICMP message in response to a packet in error 44462306a36Sopenharmony_ci */ 44562306a36Sopenharmony_civoid icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info, 44662306a36Sopenharmony_ci const struct in6_addr *force_saddr, 44762306a36Sopenharmony_ci const struct inet6_skb_parm *parm) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci struct inet6_dev *idev = NULL; 45062306a36Sopenharmony_ci struct ipv6hdr *hdr = ipv6_hdr(skb); 45162306a36Sopenharmony_ci struct sock *sk; 45262306a36Sopenharmony_ci struct net *net; 45362306a36Sopenharmony_ci struct ipv6_pinfo *np; 45462306a36Sopenharmony_ci const struct in6_addr *saddr = NULL; 45562306a36Sopenharmony_ci struct dst_entry *dst; 45662306a36Sopenharmony_ci struct icmp6hdr tmp_hdr; 45762306a36Sopenharmony_ci struct flowi6 fl6; 45862306a36Sopenharmony_ci struct icmpv6_msg msg; 45962306a36Sopenharmony_ci struct ipcm6_cookie ipc6; 46062306a36Sopenharmony_ci int iif = 0; 46162306a36Sopenharmony_ci int addr_type = 0; 46262306a36Sopenharmony_ci int len; 46362306a36Sopenharmony_ci u32 mark; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if ((u8 *)hdr < skb->head || 46662306a36Sopenharmony_ci (skb_network_header(skb) + sizeof(*hdr)) > skb_tail_pointer(skb)) 46762306a36Sopenharmony_ci return; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (!skb->dev) 47062306a36Sopenharmony_ci return; 47162306a36Sopenharmony_ci net = dev_net(skb->dev); 47262306a36Sopenharmony_ci mark = IP6_REPLY_MARK(net, skb->mark); 47362306a36Sopenharmony_ci /* 47462306a36Sopenharmony_ci * Make sure we respect the rules 47562306a36Sopenharmony_ci * i.e. RFC 1885 2.4(e) 47662306a36Sopenharmony_ci * Rule (e.1) is enforced by not using icmp6_send 47762306a36Sopenharmony_ci * in any code that processes icmp errors. 47862306a36Sopenharmony_ci */ 47962306a36Sopenharmony_ci addr_type = ipv6_addr_type(&hdr->daddr); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (ipv6_chk_addr(net, &hdr->daddr, skb->dev, 0) || 48262306a36Sopenharmony_ci ipv6_chk_acast_addr_src(net, skb->dev, &hdr->daddr)) 48362306a36Sopenharmony_ci saddr = &hdr->daddr; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* 48662306a36Sopenharmony_ci * Dest addr check 48762306a36Sopenharmony_ci */ 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (addr_type & IPV6_ADDR_MULTICAST || skb->pkt_type != PACKET_HOST) { 49062306a36Sopenharmony_ci if (type != ICMPV6_PKT_TOOBIG && 49162306a36Sopenharmony_ci !(type == ICMPV6_PARAMPROB && 49262306a36Sopenharmony_ci code == ICMPV6_UNK_OPTION && 49362306a36Sopenharmony_ci (opt_unrec(skb, info)))) 49462306a36Sopenharmony_ci return; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci saddr = NULL; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci addr_type = ipv6_addr_type(&hdr->saddr); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci /* 50262306a36Sopenharmony_ci * Source addr check 50362306a36Sopenharmony_ci */ 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (__ipv6_addr_needs_scope_id(addr_type)) { 50662306a36Sopenharmony_ci iif = icmp6_iif(skb); 50762306a36Sopenharmony_ci } else { 50862306a36Sopenharmony_ci /* 50962306a36Sopenharmony_ci * The source device is used for looking up which routing table 51062306a36Sopenharmony_ci * to use for sending an ICMP error. 51162306a36Sopenharmony_ci */ 51262306a36Sopenharmony_ci iif = l3mdev_master_ifindex(skb->dev); 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* 51662306a36Sopenharmony_ci * Must not send error if the source does not uniquely 51762306a36Sopenharmony_ci * identify a single node (RFC2463 Section 2.4). 51862306a36Sopenharmony_ci * We check unspecified / multicast addresses here, 51962306a36Sopenharmony_ci * and anycast addresses will be checked later. 52062306a36Sopenharmony_ci */ 52162306a36Sopenharmony_ci if ((addr_type == IPV6_ADDR_ANY) || (addr_type & IPV6_ADDR_MULTICAST)) { 52262306a36Sopenharmony_ci net_dbg_ratelimited("icmp6_send: addr_any/mcast source [%pI6c > %pI6c]\n", 52362306a36Sopenharmony_ci &hdr->saddr, &hdr->daddr); 52462306a36Sopenharmony_ci return; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* 52862306a36Sopenharmony_ci * Never answer to a ICMP packet. 52962306a36Sopenharmony_ci */ 53062306a36Sopenharmony_ci if (is_ineligible(skb)) { 53162306a36Sopenharmony_ci net_dbg_ratelimited("icmp6_send: no reply to icmp error [%pI6c > %pI6c]\n", 53262306a36Sopenharmony_ci &hdr->saddr, &hdr->daddr); 53362306a36Sopenharmony_ci return; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* Needed by both icmp_global_allow and icmpv6_xmit_lock */ 53762306a36Sopenharmony_ci local_bh_disable(); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* Check global sysctl_icmp_msgs_per_sec ratelimit */ 54062306a36Sopenharmony_ci if (!(skb->dev->flags & IFF_LOOPBACK) && !icmpv6_global_allow(net, type)) 54162306a36Sopenharmony_ci goto out_bh_enable; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci mip6_addr_swap(skb, parm); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci sk = icmpv6_xmit_lock(net); 54662306a36Sopenharmony_ci if (!sk) 54762306a36Sopenharmony_ci goto out_bh_enable; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci memset(&fl6, 0, sizeof(fl6)); 55062306a36Sopenharmony_ci fl6.flowi6_proto = IPPROTO_ICMPV6; 55162306a36Sopenharmony_ci fl6.daddr = hdr->saddr; 55262306a36Sopenharmony_ci if (force_saddr) 55362306a36Sopenharmony_ci saddr = force_saddr; 55462306a36Sopenharmony_ci if (saddr) { 55562306a36Sopenharmony_ci fl6.saddr = *saddr; 55662306a36Sopenharmony_ci } else if (!icmpv6_rt_has_prefsrc(sk, type, &fl6)) { 55762306a36Sopenharmony_ci /* select a more meaningful saddr from input if */ 55862306a36Sopenharmony_ci struct net_device *in_netdev; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci in_netdev = dev_get_by_index(net, parm->iif); 56162306a36Sopenharmony_ci if (in_netdev) { 56262306a36Sopenharmony_ci ipv6_dev_get_saddr(net, in_netdev, &fl6.daddr, 56362306a36Sopenharmony_ci inet6_sk(sk)->srcprefs, 56462306a36Sopenharmony_ci &fl6.saddr); 56562306a36Sopenharmony_ci dev_put(in_netdev); 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci fl6.flowi6_mark = mark; 56962306a36Sopenharmony_ci fl6.flowi6_oif = iif; 57062306a36Sopenharmony_ci fl6.fl6_icmp_type = type; 57162306a36Sopenharmony_ci fl6.fl6_icmp_code = code; 57262306a36Sopenharmony_ci fl6.flowi6_uid = sock_net_uid(net, NULL); 57362306a36Sopenharmony_ci fl6.mp_hash = rt6_multipath_hash(net, &fl6, skb, NULL); 57462306a36Sopenharmony_ci security_skb_classify_flow(skb, flowi6_to_flowi_common(&fl6)); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci np = inet6_sk(sk); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (!icmpv6_xrlim_allow(sk, type, &fl6)) 57962306a36Sopenharmony_ci goto out; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci tmp_hdr.icmp6_type = type; 58262306a36Sopenharmony_ci tmp_hdr.icmp6_code = code; 58362306a36Sopenharmony_ci tmp_hdr.icmp6_cksum = 0; 58462306a36Sopenharmony_ci tmp_hdr.icmp6_pointer = htonl(info); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) 58762306a36Sopenharmony_ci fl6.flowi6_oif = np->mcast_oif; 58862306a36Sopenharmony_ci else if (!fl6.flowi6_oif) 58962306a36Sopenharmony_ci fl6.flowi6_oif = np->ucast_oif; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci ipcm6_init_sk(&ipc6, np); 59262306a36Sopenharmony_ci ipc6.sockc.mark = mark; 59362306a36Sopenharmony_ci fl6.flowlabel = ip6_make_flowinfo(ipc6.tclass, fl6.flowlabel); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci dst = icmpv6_route_lookup(net, skb, sk, &fl6); 59662306a36Sopenharmony_ci if (IS_ERR(dst)) 59762306a36Sopenharmony_ci goto out; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci ipc6.hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci msg.skb = skb; 60262306a36Sopenharmony_ci msg.offset = skb_network_offset(skb); 60362306a36Sopenharmony_ci msg.type = type; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci len = skb->len - msg.offset; 60662306a36Sopenharmony_ci len = min_t(unsigned int, len, IPV6_MIN_MTU - sizeof(struct ipv6hdr) - sizeof(struct icmp6hdr)); 60762306a36Sopenharmony_ci if (len < 0) { 60862306a36Sopenharmony_ci net_dbg_ratelimited("icmp: len problem [%pI6c > %pI6c]\n", 60962306a36Sopenharmony_ci &hdr->saddr, &hdr->daddr); 61062306a36Sopenharmony_ci goto out_dst_release; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci rcu_read_lock(); 61462306a36Sopenharmony_ci idev = __in6_dev_get(skb->dev); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci if (ip6_append_data(sk, icmpv6_getfrag, &msg, 61762306a36Sopenharmony_ci len + sizeof(struct icmp6hdr), 61862306a36Sopenharmony_ci sizeof(struct icmp6hdr), 61962306a36Sopenharmony_ci &ipc6, &fl6, (struct rt6_info *)dst, 62062306a36Sopenharmony_ci MSG_DONTWAIT)) { 62162306a36Sopenharmony_ci ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTERRORS); 62262306a36Sopenharmony_ci ip6_flush_pending_frames(sk); 62362306a36Sopenharmony_ci } else { 62462306a36Sopenharmony_ci icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr, 62562306a36Sopenharmony_ci len + sizeof(struct icmp6hdr)); 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci rcu_read_unlock(); 62862306a36Sopenharmony_ciout_dst_release: 62962306a36Sopenharmony_ci dst_release(dst); 63062306a36Sopenharmony_ciout: 63162306a36Sopenharmony_ci icmpv6_xmit_unlock(sk); 63262306a36Sopenharmony_ciout_bh_enable: 63362306a36Sopenharmony_ci local_bh_enable(); 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ciEXPORT_SYMBOL(icmp6_send); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci/* Slightly more convenient version of icmp6_send with drop reasons. 63862306a36Sopenharmony_ci */ 63962306a36Sopenharmony_civoid icmpv6_param_prob_reason(struct sk_buff *skb, u8 code, int pos, 64062306a36Sopenharmony_ci enum skb_drop_reason reason) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci icmp6_send(skb, ICMPV6_PARAMPROB, code, pos, NULL, IP6CB(skb)); 64362306a36Sopenharmony_ci kfree_skb_reason(skb, reason); 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci/* Generate icmpv6 with type/code ICMPV6_DEST_UNREACH/ICMPV6_ADDR_UNREACH 64762306a36Sopenharmony_ci * if sufficient data bytes are available 64862306a36Sopenharmony_ci * @nhs is the size of the tunnel header(s) : 64962306a36Sopenharmony_ci * Either an IPv4 header for SIT encap 65062306a36Sopenharmony_ci * an IPv4 header + GRE header for GRE encap 65162306a36Sopenharmony_ci */ 65262306a36Sopenharmony_ciint ip6_err_gen_icmpv6_unreach(struct sk_buff *skb, int nhs, int type, 65362306a36Sopenharmony_ci unsigned int data_len) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci struct in6_addr temp_saddr; 65662306a36Sopenharmony_ci struct rt6_info *rt; 65762306a36Sopenharmony_ci struct sk_buff *skb2; 65862306a36Sopenharmony_ci u32 info = 0; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (!pskb_may_pull(skb, nhs + sizeof(struct ipv6hdr) + 8)) 66162306a36Sopenharmony_ci return 1; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci /* RFC 4884 (partial) support for ICMP extensions */ 66462306a36Sopenharmony_ci if (data_len < 128 || (data_len & 7) || skb->len < data_len) 66562306a36Sopenharmony_ci data_len = 0; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci skb2 = data_len ? skb_copy(skb, GFP_ATOMIC) : skb_clone(skb, GFP_ATOMIC); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci if (!skb2) 67062306a36Sopenharmony_ci return 1; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci skb_dst_drop(skb2); 67362306a36Sopenharmony_ci skb_pull(skb2, nhs); 67462306a36Sopenharmony_ci skb_reset_network_header(skb2); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci rt = rt6_lookup(dev_net(skb->dev), &ipv6_hdr(skb2)->saddr, NULL, 0, 67762306a36Sopenharmony_ci skb, 0); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci if (rt && rt->dst.dev) 68062306a36Sopenharmony_ci skb2->dev = rt->dst.dev; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci ipv6_addr_set_v4mapped(ip_hdr(skb)->saddr, &temp_saddr); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if (data_len) { 68562306a36Sopenharmony_ci /* RFC 4884 (partial) support : 68662306a36Sopenharmony_ci * insert 0 padding at the end, before the extensions 68762306a36Sopenharmony_ci */ 68862306a36Sopenharmony_ci __skb_push(skb2, nhs); 68962306a36Sopenharmony_ci skb_reset_network_header(skb2); 69062306a36Sopenharmony_ci memmove(skb2->data, skb2->data + nhs, data_len - nhs); 69162306a36Sopenharmony_ci memset(skb2->data + data_len - nhs, 0, nhs); 69262306a36Sopenharmony_ci /* RFC 4884 4.5 : Length is measured in 64-bit words, 69362306a36Sopenharmony_ci * and stored in reserved[0] 69462306a36Sopenharmony_ci */ 69562306a36Sopenharmony_ci info = (data_len/8) << 24; 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci if (type == ICMP_TIME_EXCEEDED) 69862306a36Sopenharmony_ci icmp6_send(skb2, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT, 69962306a36Sopenharmony_ci info, &temp_saddr, IP6CB(skb2)); 70062306a36Sopenharmony_ci else 70162306a36Sopenharmony_ci icmp6_send(skb2, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 70262306a36Sopenharmony_ci info, &temp_saddr, IP6CB(skb2)); 70362306a36Sopenharmony_ci if (rt) 70462306a36Sopenharmony_ci ip6_rt_put(rt); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci kfree_skb(skb2); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci return 0; 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ciEXPORT_SYMBOL(ip6_err_gen_icmpv6_unreach); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic enum skb_drop_reason icmpv6_echo_reply(struct sk_buff *skb) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci struct net *net = dev_net(skb->dev); 71562306a36Sopenharmony_ci struct sock *sk; 71662306a36Sopenharmony_ci struct inet6_dev *idev; 71762306a36Sopenharmony_ci struct ipv6_pinfo *np; 71862306a36Sopenharmony_ci const struct in6_addr *saddr = NULL; 71962306a36Sopenharmony_ci struct icmp6hdr *icmph = icmp6_hdr(skb); 72062306a36Sopenharmony_ci struct icmp6hdr tmp_hdr; 72162306a36Sopenharmony_ci struct flowi6 fl6; 72262306a36Sopenharmony_ci struct icmpv6_msg msg; 72362306a36Sopenharmony_ci struct dst_entry *dst; 72462306a36Sopenharmony_ci struct ipcm6_cookie ipc6; 72562306a36Sopenharmony_ci u32 mark = IP6_REPLY_MARK(net, skb->mark); 72662306a36Sopenharmony_ci SKB_DR(reason); 72762306a36Sopenharmony_ci bool acast; 72862306a36Sopenharmony_ci u8 type; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) && 73162306a36Sopenharmony_ci net->ipv6.sysctl.icmpv6_echo_ignore_multicast) 73262306a36Sopenharmony_ci return reason; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci saddr = &ipv6_hdr(skb)->daddr; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci acast = ipv6_anycast_destination(skb_dst(skb), saddr); 73762306a36Sopenharmony_ci if (acast && net->ipv6.sysctl.icmpv6_echo_ignore_anycast) 73862306a36Sopenharmony_ci return reason; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if (!ipv6_unicast_destination(skb) && 74162306a36Sopenharmony_ci !(net->ipv6.sysctl.anycast_src_echo_reply && acast)) 74262306a36Sopenharmony_ci saddr = NULL; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci if (icmph->icmp6_type == ICMPV6_EXT_ECHO_REQUEST) 74562306a36Sopenharmony_ci type = ICMPV6_EXT_ECHO_REPLY; 74662306a36Sopenharmony_ci else 74762306a36Sopenharmony_ci type = ICMPV6_ECHO_REPLY; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci memcpy(&tmp_hdr, icmph, sizeof(tmp_hdr)); 75062306a36Sopenharmony_ci tmp_hdr.icmp6_type = type; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci memset(&fl6, 0, sizeof(fl6)); 75362306a36Sopenharmony_ci if (net->ipv6.sysctl.flowlabel_reflect & FLOWLABEL_REFLECT_ICMPV6_ECHO_REPLIES) 75462306a36Sopenharmony_ci fl6.flowlabel = ip6_flowlabel(ipv6_hdr(skb)); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci fl6.flowi6_proto = IPPROTO_ICMPV6; 75762306a36Sopenharmony_ci fl6.daddr = ipv6_hdr(skb)->saddr; 75862306a36Sopenharmony_ci if (saddr) 75962306a36Sopenharmony_ci fl6.saddr = *saddr; 76062306a36Sopenharmony_ci fl6.flowi6_oif = icmp6_iif(skb); 76162306a36Sopenharmony_ci fl6.fl6_icmp_type = type; 76262306a36Sopenharmony_ci fl6.flowi6_mark = mark; 76362306a36Sopenharmony_ci fl6.flowi6_uid = sock_net_uid(net, NULL); 76462306a36Sopenharmony_ci security_skb_classify_flow(skb, flowi6_to_flowi_common(&fl6)); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci local_bh_disable(); 76762306a36Sopenharmony_ci sk = icmpv6_xmit_lock(net); 76862306a36Sopenharmony_ci if (!sk) 76962306a36Sopenharmony_ci goto out_bh_enable; 77062306a36Sopenharmony_ci np = inet6_sk(sk); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) 77362306a36Sopenharmony_ci fl6.flowi6_oif = np->mcast_oif; 77462306a36Sopenharmony_ci else if (!fl6.flowi6_oif) 77562306a36Sopenharmony_ci fl6.flowi6_oif = np->ucast_oif; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (ip6_dst_lookup(net, sk, &dst, &fl6)) 77862306a36Sopenharmony_ci goto out; 77962306a36Sopenharmony_ci dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), sk, 0); 78062306a36Sopenharmony_ci if (IS_ERR(dst)) 78162306a36Sopenharmony_ci goto out; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci /* Check the ratelimit */ 78462306a36Sopenharmony_ci if ((!(skb->dev->flags & IFF_LOOPBACK) && !icmpv6_global_allow(net, ICMPV6_ECHO_REPLY)) || 78562306a36Sopenharmony_ci !icmpv6_xrlim_allow(sk, ICMPV6_ECHO_REPLY, &fl6)) 78662306a36Sopenharmony_ci goto out_dst_release; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci idev = __in6_dev_get(skb->dev); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci msg.skb = skb; 79162306a36Sopenharmony_ci msg.offset = 0; 79262306a36Sopenharmony_ci msg.type = type; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci ipcm6_init_sk(&ipc6, np); 79562306a36Sopenharmony_ci ipc6.hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst); 79662306a36Sopenharmony_ci ipc6.tclass = ipv6_get_dsfield(ipv6_hdr(skb)); 79762306a36Sopenharmony_ci ipc6.sockc.mark = mark; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci if (icmph->icmp6_type == ICMPV6_EXT_ECHO_REQUEST) 80062306a36Sopenharmony_ci if (!icmp_build_probe(skb, (struct icmphdr *)&tmp_hdr)) 80162306a36Sopenharmony_ci goto out_dst_release; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci if (ip6_append_data(sk, icmpv6_getfrag, &msg, 80462306a36Sopenharmony_ci skb->len + sizeof(struct icmp6hdr), 80562306a36Sopenharmony_ci sizeof(struct icmp6hdr), &ipc6, &fl6, 80662306a36Sopenharmony_ci (struct rt6_info *)dst, MSG_DONTWAIT)) { 80762306a36Sopenharmony_ci __ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTERRORS); 80862306a36Sopenharmony_ci ip6_flush_pending_frames(sk); 80962306a36Sopenharmony_ci } else { 81062306a36Sopenharmony_ci icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr, 81162306a36Sopenharmony_ci skb->len + sizeof(struct icmp6hdr)); 81262306a36Sopenharmony_ci reason = SKB_CONSUMED; 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ciout_dst_release: 81562306a36Sopenharmony_ci dst_release(dst); 81662306a36Sopenharmony_ciout: 81762306a36Sopenharmony_ci icmpv6_xmit_unlock(sk); 81862306a36Sopenharmony_ciout_bh_enable: 81962306a36Sopenharmony_ci local_bh_enable(); 82062306a36Sopenharmony_ci return reason; 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cienum skb_drop_reason icmpv6_notify(struct sk_buff *skb, u8 type, 82462306a36Sopenharmony_ci u8 code, __be32 info) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci struct inet6_skb_parm *opt = IP6CB(skb); 82762306a36Sopenharmony_ci struct net *net = dev_net(skb->dev); 82862306a36Sopenharmony_ci const struct inet6_protocol *ipprot; 82962306a36Sopenharmony_ci enum skb_drop_reason reason; 83062306a36Sopenharmony_ci int inner_offset; 83162306a36Sopenharmony_ci __be16 frag_off; 83262306a36Sopenharmony_ci u8 nexthdr; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci reason = pskb_may_pull_reason(skb, sizeof(struct ipv6hdr)); 83562306a36Sopenharmony_ci if (reason != SKB_NOT_DROPPED_YET) 83662306a36Sopenharmony_ci goto out; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci seg6_icmp_srh(skb, opt); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci nexthdr = ((struct ipv6hdr *)skb->data)->nexthdr; 84162306a36Sopenharmony_ci if (ipv6_ext_hdr(nexthdr)) { 84262306a36Sopenharmony_ci /* now skip over extension headers */ 84362306a36Sopenharmony_ci inner_offset = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), 84462306a36Sopenharmony_ci &nexthdr, &frag_off); 84562306a36Sopenharmony_ci if (inner_offset < 0) { 84662306a36Sopenharmony_ci SKB_DR_SET(reason, IPV6_BAD_EXTHDR); 84762306a36Sopenharmony_ci goto out; 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci } else { 85062306a36Sopenharmony_ci inner_offset = sizeof(struct ipv6hdr); 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci /* Checkin header including 8 bytes of inner protocol header. */ 85462306a36Sopenharmony_ci reason = pskb_may_pull_reason(skb, inner_offset + 8); 85562306a36Sopenharmony_ci if (reason != SKB_NOT_DROPPED_YET) 85662306a36Sopenharmony_ci goto out; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci /* BUGGG_FUTURE: we should try to parse exthdrs in this packet. 85962306a36Sopenharmony_ci Without this we will not able f.e. to make source routed 86062306a36Sopenharmony_ci pmtu discovery. 86162306a36Sopenharmony_ci Corresponding argument (opt) to notifiers is already added. 86262306a36Sopenharmony_ci --ANK (980726) 86362306a36Sopenharmony_ci */ 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci ipprot = rcu_dereference(inet6_protos[nexthdr]); 86662306a36Sopenharmony_ci if (ipprot && ipprot->err_handler) 86762306a36Sopenharmony_ci ipprot->err_handler(skb, opt, type, code, inner_offset, info); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci raw6_icmp_error(skb, nexthdr, type, code, inner_offset, info); 87062306a36Sopenharmony_ci return SKB_CONSUMED; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ciout: 87362306a36Sopenharmony_ci __ICMP6_INC_STATS(net, __in6_dev_get(skb->dev), ICMP6_MIB_INERRORS); 87462306a36Sopenharmony_ci return reason; 87562306a36Sopenharmony_ci} 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci/* 87862306a36Sopenharmony_ci * Handle icmp messages 87962306a36Sopenharmony_ci */ 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cistatic int icmpv6_rcv(struct sk_buff *skb) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED; 88462306a36Sopenharmony_ci struct net *net = dev_net(skb->dev); 88562306a36Sopenharmony_ci struct net_device *dev = icmp6_dev(skb); 88662306a36Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(dev); 88762306a36Sopenharmony_ci const struct in6_addr *saddr, *daddr; 88862306a36Sopenharmony_ci struct icmp6hdr *hdr; 88962306a36Sopenharmony_ci u8 type; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) { 89262306a36Sopenharmony_ci struct sec_path *sp = skb_sec_path(skb); 89362306a36Sopenharmony_ci int nh; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci if (!(sp && sp->xvec[sp->len - 1]->props.flags & 89662306a36Sopenharmony_ci XFRM_STATE_ICMP)) { 89762306a36Sopenharmony_ci reason = SKB_DROP_REASON_XFRM_POLICY; 89862306a36Sopenharmony_ci goto drop_no_count; 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(*hdr) + sizeof(struct ipv6hdr))) 90262306a36Sopenharmony_ci goto drop_no_count; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci nh = skb_network_offset(skb); 90562306a36Sopenharmony_ci skb_set_network_header(skb, sizeof(*hdr)); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci if (!xfrm6_policy_check_reverse(NULL, XFRM_POLICY_IN, 90862306a36Sopenharmony_ci skb)) { 90962306a36Sopenharmony_ci reason = SKB_DROP_REASON_XFRM_POLICY; 91062306a36Sopenharmony_ci goto drop_no_count; 91162306a36Sopenharmony_ci } 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci skb_set_network_header(skb, nh); 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci __ICMP6_INC_STATS(dev_net(dev), idev, ICMP6_MIB_INMSGS); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci saddr = &ipv6_hdr(skb)->saddr; 91962306a36Sopenharmony_ci daddr = &ipv6_hdr(skb)->daddr; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci if (skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo)) { 92262306a36Sopenharmony_ci net_dbg_ratelimited("ICMPv6 checksum failed [%pI6c > %pI6c]\n", 92362306a36Sopenharmony_ci saddr, daddr); 92462306a36Sopenharmony_ci goto csum_error; 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci if (!pskb_pull(skb, sizeof(*hdr))) 92862306a36Sopenharmony_ci goto discard_it; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci hdr = icmp6_hdr(skb); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci type = hdr->icmp6_type; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci ICMP6MSGIN_INC_STATS(dev_net(dev), idev, type); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci switch (type) { 93762306a36Sopenharmony_ci case ICMPV6_ECHO_REQUEST: 93862306a36Sopenharmony_ci if (!net->ipv6.sysctl.icmpv6_echo_ignore_all) 93962306a36Sopenharmony_ci reason = icmpv6_echo_reply(skb); 94062306a36Sopenharmony_ci break; 94162306a36Sopenharmony_ci case ICMPV6_EXT_ECHO_REQUEST: 94262306a36Sopenharmony_ci if (!net->ipv6.sysctl.icmpv6_echo_ignore_all && 94362306a36Sopenharmony_ci READ_ONCE(net->ipv4.sysctl_icmp_echo_enable_probe)) 94462306a36Sopenharmony_ci reason = icmpv6_echo_reply(skb); 94562306a36Sopenharmony_ci break; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci case ICMPV6_ECHO_REPLY: 94862306a36Sopenharmony_ci reason = ping_rcv(skb); 94962306a36Sopenharmony_ci break; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci case ICMPV6_EXT_ECHO_REPLY: 95262306a36Sopenharmony_ci reason = ping_rcv(skb); 95362306a36Sopenharmony_ci break; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci case ICMPV6_PKT_TOOBIG: 95662306a36Sopenharmony_ci /* BUGGG_FUTURE: if packet contains rthdr, we cannot update 95762306a36Sopenharmony_ci standard destination cache. Seems, only "advanced" 95862306a36Sopenharmony_ci destination cache will allow to solve this problem 95962306a36Sopenharmony_ci --ANK (980726) 96062306a36Sopenharmony_ci */ 96162306a36Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 96262306a36Sopenharmony_ci goto discard_it; 96362306a36Sopenharmony_ci hdr = icmp6_hdr(skb); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci /* to notify */ 96662306a36Sopenharmony_ci fallthrough; 96762306a36Sopenharmony_ci case ICMPV6_DEST_UNREACH: 96862306a36Sopenharmony_ci case ICMPV6_TIME_EXCEED: 96962306a36Sopenharmony_ci case ICMPV6_PARAMPROB: 97062306a36Sopenharmony_ci reason = icmpv6_notify(skb, type, hdr->icmp6_code, 97162306a36Sopenharmony_ci hdr->icmp6_mtu); 97262306a36Sopenharmony_ci break; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci case NDISC_ROUTER_SOLICITATION: 97562306a36Sopenharmony_ci case NDISC_ROUTER_ADVERTISEMENT: 97662306a36Sopenharmony_ci case NDISC_NEIGHBOUR_SOLICITATION: 97762306a36Sopenharmony_ci case NDISC_NEIGHBOUR_ADVERTISEMENT: 97862306a36Sopenharmony_ci case NDISC_REDIRECT: 97962306a36Sopenharmony_ci reason = ndisc_rcv(skb); 98062306a36Sopenharmony_ci break; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci case ICMPV6_MGM_QUERY: 98362306a36Sopenharmony_ci igmp6_event_query(skb); 98462306a36Sopenharmony_ci return 0; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci case ICMPV6_MGM_REPORT: 98762306a36Sopenharmony_ci igmp6_event_report(skb); 98862306a36Sopenharmony_ci return 0; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci case ICMPV6_MGM_REDUCTION: 99162306a36Sopenharmony_ci case ICMPV6_NI_QUERY: 99262306a36Sopenharmony_ci case ICMPV6_NI_REPLY: 99362306a36Sopenharmony_ci case ICMPV6_MLD2_REPORT: 99462306a36Sopenharmony_ci case ICMPV6_DHAAD_REQUEST: 99562306a36Sopenharmony_ci case ICMPV6_DHAAD_REPLY: 99662306a36Sopenharmony_ci case ICMPV6_MOBILE_PREFIX_SOL: 99762306a36Sopenharmony_ci case ICMPV6_MOBILE_PREFIX_ADV: 99862306a36Sopenharmony_ci break; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci default: 100162306a36Sopenharmony_ci /* informational */ 100262306a36Sopenharmony_ci if (type & ICMPV6_INFOMSG_MASK) 100362306a36Sopenharmony_ci break; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci net_dbg_ratelimited("icmpv6: msg of unknown type [%pI6c > %pI6c]\n", 100662306a36Sopenharmony_ci saddr, daddr); 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci /* 100962306a36Sopenharmony_ci * error of unknown type. 101062306a36Sopenharmony_ci * must pass to upper level 101162306a36Sopenharmony_ci */ 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci reason = icmpv6_notify(skb, type, hdr->icmp6_code, 101462306a36Sopenharmony_ci hdr->icmp6_mtu); 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci /* until the v6 path can be better sorted assume failure and 101862306a36Sopenharmony_ci * preserve the status quo behaviour for the rest of the paths to here 101962306a36Sopenharmony_ci */ 102062306a36Sopenharmony_ci if (reason) 102162306a36Sopenharmony_ci kfree_skb_reason(skb, reason); 102262306a36Sopenharmony_ci else 102362306a36Sopenharmony_ci consume_skb(skb); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci return 0; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_cicsum_error: 102862306a36Sopenharmony_ci reason = SKB_DROP_REASON_ICMP_CSUM; 102962306a36Sopenharmony_ci __ICMP6_INC_STATS(dev_net(dev), idev, ICMP6_MIB_CSUMERRORS); 103062306a36Sopenharmony_cidiscard_it: 103162306a36Sopenharmony_ci __ICMP6_INC_STATS(dev_net(dev), idev, ICMP6_MIB_INERRORS); 103262306a36Sopenharmony_cidrop_no_count: 103362306a36Sopenharmony_ci kfree_skb_reason(skb, reason); 103462306a36Sopenharmony_ci return 0; 103562306a36Sopenharmony_ci} 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_civoid icmpv6_flow_init(const struct sock *sk, struct flowi6 *fl6, u8 type, 103862306a36Sopenharmony_ci const struct in6_addr *saddr, 103962306a36Sopenharmony_ci const struct in6_addr *daddr, int oif) 104062306a36Sopenharmony_ci{ 104162306a36Sopenharmony_ci memset(fl6, 0, sizeof(*fl6)); 104262306a36Sopenharmony_ci fl6->saddr = *saddr; 104362306a36Sopenharmony_ci fl6->daddr = *daddr; 104462306a36Sopenharmony_ci fl6->flowi6_proto = IPPROTO_ICMPV6; 104562306a36Sopenharmony_ci fl6->fl6_icmp_type = type; 104662306a36Sopenharmony_ci fl6->fl6_icmp_code = 0; 104762306a36Sopenharmony_ci fl6->flowi6_oif = oif; 104862306a36Sopenharmony_ci security_sk_classify_flow(sk, flowi6_to_flowi_common(fl6)); 104962306a36Sopenharmony_ci} 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ciint __init icmpv6_init(void) 105262306a36Sopenharmony_ci{ 105362306a36Sopenharmony_ci struct sock *sk; 105462306a36Sopenharmony_ci int err, i; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci for_each_possible_cpu(i) { 105762306a36Sopenharmony_ci err = inet_ctl_sock_create(&sk, PF_INET6, 105862306a36Sopenharmony_ci SOCK_RAW, IPPROTO_ICMPV6, &init_net); 105962306a36Sopenharmony_ci if (err < 0) { 106062306a36Sopenharmony_ci pr_err("Failed to initialize the ICMP6 control socket (err %d)\n", 106162306a36Sopenharmony_ci err); 106262306a36Sopenharmony_ci return err; 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci per_cpu(ipv6_icmp_sk, i) = sk; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci /* Enough space for 2 64K ICMP packets, including 106862306a36Sopenharmony_ci * sk_buff struct overhead. 106962306a36Sopenharmony_ci */ 107062306a36Sopenharmony_ci sk->sk_sndbuf = 2 * SKB_TRUESIZE(64 * 1024); 107162306a36Sopenharmony_ci } 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci err = -EAGAIN; 107462306a36Sopenharmony_ci if (inet6_add_protocol(&icmpv6_protocol, IPPROTO_ICMPV6) < 0) 107562306a36Sopenharmony_ci goto fail; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci err = inet6_register_icmp_sender(icmp6_send); 107862306a36Sopenharmony_ci if (err) 107962306a36Sopenharmony_ci goto sender_reg_err; 108062306a36Sopenharmony_ci return 0; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_cisender_reg_err: 108362306a36Sopenharmony_ci inet6_del_protocol(&icmpv6_protocol, IPPROTO_ICMPV6); 108462306a36Sopenharmony_cifail: 108562306a36Sopenharmony_ci pr_err("Failed to register ICMP6 protocol\n"); 108662306a36Sopenharmony_ci return err; 108762306a36Sopenharmony_ci} 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_civoid icmpv6_cleanup(void) 109062306a36Sopenharmony_ci{ 109162306a36Sopenharmony_ci inet6_unregister_icmp_sender(icmp6_send); 109262306a36Sopenharmony_ci inet6_del_protocol(&icmpv6_protocol, IPPROTO_ICMPV6); 109362306a36Sopenharmony_ci} 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_cistatic const struct icmp6_err { 109762306a36Sopenharmony_ci int err; 109862306a36Sopenharmony_ci int fatal; 109962306a36Sopenharmony_ci} tab_unreach[] = { 110062306a36Sopenharmony_ci { /* NOROUTE */ 110162306a36Sopenharmony_ci .err = ENETUNREACH, 110262306a36Sopenharmony_ci .fatal = 0, 110362306a36Sopenharmony_ci }, 110462306a36Sopenharmony_ci { /* ADM_PROHIBITED */ 110562306a36Sopenharmony_ci .err = EACCES, 110662306a36Sopenharmony_ci .fatal = 1, 110762306a36Sopenharmony_ci }, 110862306a36Sopenharmony_ci { /* Was NOT_NEIGHBOUR, now reserved */ 110962306a36Sopenharmony_ci .err = EHOSTUNREACH, 111062306a36Sopenharmony_ci .fatal = 0, 111162306a36Sopenharmony_ci }, 111262306a36Sopenharmony_ci { /* ADDR_UNREACH */ 111362306a36Sopenharmony_ci .err = EHOSTUNREACH, 111462306a36Sopenharmony_ci .fatal = 0, 111562306a36Sopenharmony_ci }, 111662306a36Sopenharmony_ci { /* PORT_UNREACH */ 111762306a36Sopenharmony_ci .err = ECONNREFUSED, 111862306a36Sopenharmony_ci .fatal = 1, 111962306a36Sopenharmony_ci }, 112062306a36Sopenharmony_ci { /* POLICY_FAIL */ 112162306a36Sopenharmony_ci .err = EACCES, 112262306a36Sopenharmony_ci .fatal = 1, 112362306a36Sopenharmony_ci }, 112462306a36Sopenharmony_ci { /* REJECT_ROUTE */ 112562306a36Sopenharmony_ci .err = EACCES, 112662306a36Sopenharmony_ci .fatal = 1, 112762306a36Sopenharmony_ci }, 112862306a36Sopenharmony_ci}; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ciint icmpv6_err_convert(u8 type, u8 code, int *err) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci int fatal = 0; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci *err = EPROTO; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci switch (type) { 113762306a36Sopenharmony_ci case ICMPV6_DEST_UNREACH: 113862306a36Sopenharmony_ci fatal = 1; 113962306a36Sopenharmony_ci if (code < ARRAY_SIZE(tab_unreach)) { 114062306a36Sopenharmony_ci *err = tab_unreach[code].err; 114162306a36Sopenharmony_ci fatal = tab_unreach[code].fatal; 114262306a36Sopenharmony_ci } 114362306a36Sopenharmony_ci break; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci case ICMPV6_PKT_TOOBIG: 114662306a36Sopenharmony_ci *err = EMSGSIZE; 114762306a36Sopenharmony_ci break; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci case ICMPV6_PARAMPROB: 115062306a36Sopenharmony_ci *err = EPROTO; 115162306a36Sopenharmony_ci fatal = 1; 115262306a36Sopenharmony_ci break; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci case ICMPV6_TIME_EXCEED: 115562306a36Sopenharmony_ci *err = EHOSTUNREACH; 115662306a36Sopenharmony_ci break; 115762306a36Sopenharmony_ci } 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci return fatal; 116062306a36Sopenharmony_ci} 116162306a36Sopenharmony_ciEXPORT_SYMBOL(icmpv6_err_convert); 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 116462306a36Sopenharmony_cistatic struct ctl_table ipv6_icmp_table_template[] = { 116562306a36Sopenharmony_ci { 116662306a36Sopenharmony_ci .procname = "ratelimit", 116762306a36Sopenharmony_ci .data = &init_net.ipv6.sysctl.icmpv6_time, 116862306a36Sopenharmony_ci .maxlen = sizeof(int), 116962306a36Sopenharmony_ci .mode = 0644, 117062306a36Sopenharmony_ci .proc_handler = proc_dointvec_ms_jiffies, 117162306a36Sopenharmony_ci }, 117262306a36Sopenharmony_ci { 117362306a36Sopenharmony_ci .procname = "echo_ignore_all", 117462306a36Sopenharmony_ci .data = &init_net.ipv6.sysctl.icmpv6_echo_ignore_all, 117562306a36Sopenharmony_ci .maxlen = sizeof(u8), 117662306a36Sopenharmony_ci .mode = 0644, 117762306a36Sopenharmony_ci .proc_handler = proc_dou8vec_minmax, 117862306a36Sopenharmony_ci }, 117962306a36Sopenharmony_ci { 118062306a36Sopenharmony_ci .procname = "echo_ignore_multicast", 118162306a36Sopenharmony_ci .data = &init_net.ipv6.sysctl.icmpv6_echo_ignore_multicast, 118262306a36Sopenharmony_ci .maxlen = sizeof(u8), 118362306a36Sopenharmony_ci .mode = 0644, 118462306a36Sopenharmony_ci .proc_handler = proc_dou8vec_minmax, 118562306a36Sopenharmony_ci }, 118662306a36Sopenharmony_ci { 118762306a36Sopenharmony_ci .procname = "echo_ignore_anycast", 118862306a36Sopenharmony_ci .data = &init_net.ipv6.sysctl.icmpv6_echo_ignore_anycast, 118962306a36Sopenharmony_ci .maxlen = sizeof(u8), 119062306a36Sopenharmony_ci .mode = 0644, 119162306a36Sopenharmony_ci .proc_handler = proc_dou8vec_minmax, 119262306a36Sopenharmony_ci }, 119362306a36Sopenharmony_ci { 119462306a36Sopenharmony_ci .procname = "ratemask", 119562306a36Sopenharmony_ci .data = &init_net.ipv6.sysctl.icmpv6_ratemask_ptr, 119662306a36Sopenharmony_ci .maxlen = ICMPV6_MSG_MAX + 1, 119762306a36Sopenharmony_ci .mode = 0644, 119862306a36Sopenharmony_ci .proc_handler = proc_do_large_bitmap, 119962306a36Sopenharmony_ci }, 120062306a36Sopenharmony_ci { 120162306a36Sopenharmony_ci .procname = "error_anycast_as_unicast", 120262306a36Sopenharmony_ci .data = &init_net.ipv6.sysctl.icmpv6_error_anycast_as_unicast, 120362306a36Sopenharmony_ci .maxlen = sizeof(u8), 120462306a36Sopenharmony_ci .mode = 0644, 120562306a36Sopenharmony_ci .proc_handler = proc_dou8vec_minmax, 120662306a36Sopenharmony_ci .extra1 = SYSCTL_ZERO, 120762306a36Sopenharmony_ci .extra2 = SYSCTL_ONE, 120862306a36Sopenharmony_ci }, 120962306a36Sopenharmony_ci { }, 121062306a36Sopenharmony_ci}; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_cistruct ctl_table * __net_init ipv6_icmp_sysctl_init(struct net *net) 121362306a36Sopenharmony_ci{ 121462306a36Sopenharmony_ci struct ctl_table *table; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci table = kmemdup(ipv6_icmp_table_template, 121762306a36Sopenharmony_ci sizeof(ipv6_icmp_table_template), 121862306a36Sopenharmony_ci GFP_KERNEL); 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci if (table) { 122162306a36Sopenharmony_ci table[0].data = &net->ipv6.sysctl.icmpv6_time; 122262306a36Sopenharmony_ci table[1].data = &net->ipv6.sysctl.icmpv6_echo_ignore_all; 122362306a36Sopenharmony_ci table[2].data = &net->ipv6.sysctl.icmpv6_echo_ignore_multicast; 122462306a36Sopenharmony_ci table[3].data = &net->ipv6.sysctl.icmpv6_echo_ignore_anycast; 122562306a36Sopenharmony_ci table[4].data = &net->ipv6.sysctl.icmpv6_ratemask_ptr; 122662306a36Sopenharmony_ci table[5].data = &net->ipv6.sysctl.icmpv6_error_anycast_as_unicast; 122762306a36Sopenharmony_ci } 122862306a36Sopenharmony_ci return table; 122962306a36Sopenharmony_ci} 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_cisize_t ipv6_icmp_sysctl_table_size(void) 123262306a36Sopenharmony_ci{ 123362306a36Sopenharmony_ci return ARRAY_SIZE(ipv6_icmp_table_template); 123462306a36Sopenharmony_ci} 123562306a36Sopenharmony_ci#endif 1236