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