1781 lines · 44.4 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* *	NET3:	Implementation of the ICMP protocol layer. * *		Alan Cox, <alan@lxorguk.ukuu.org.uk> * *	Some of the function names and the icmp unreach table for this *	module were derived from [icmp.c 1.0.11 06/02/93] by *	Ross Biro, Fred N. van Kempen, Mark Evans, Alan Cox, Gerhard Koerting. *	Other than that this module is a complete rewrite. * *	Fixes: *	Clemens Fruhwirth	:	introduce global icmp rate limiting *					with icmp type masking ability instead *					of broken per type icmp timeouts. *		Mike Shaver	:	RFC1122 checks. *		Alan Cox	:	Multicast ping reply as self. *		Alan Cox	:	Fix atomicity lockup in ip_build_xmit *					call. *		Alan Cox	:	Added 216,128 byte paths to the MTU *					code. *		Martin Mares	:	RFC1812 checks. *		Martin Mares	:	Can be configured to follow redirects *					if acting as a router _without_ a *					routing protocol (RFC 1812). *		Martin Mares	:	Echo requests may be configured to *					be ignored (RFC 1812). *		Martin Mares	:	Limitation of ICMP error message *					transmit rate (RFC 1812). *		Martin Mares	:	TOS and Precedence set correctly *					(RFC 1812). *		Martin Mares	:	Now copying as much data from the *					original packet as we can without *					exceeding 576 bytes (RFC 1812). *	Willy Konynenberg	:	Transparent proxying support. *		Keith Owens	:	RFC1191 correction for 4.2BSD based *					path MTU bug. *		Thomas Quinot	:	ICMP Dest Unreach codes up to 15 are *					valid (RFC 1812). *		Andi Kleen	:	Check all packet lengths properly *					and moved all kfree_skb() up to *					icmp_rcv. *		Andi Kleen	:	Move the rate limit bookkeeping *					into the dest entry and use a token *					bucket filter (thanks to ANK). Make *					the rates sysctl configurable. *		Yu Tianli	:	Fixed two ugly bugs in icmp_send *					- IP option length was accounted wrongly *					- ICMP header length was not accounted *					  at all. *              Tristan Greaves :       Added sysctl option to ignore bogus *              			broadcast responses from broken routers. * * To Fix: * *	- Should use skb_pull() instead of all the manual checking. *	  This would also greatly simply some upper layer error handlers. --AK */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h>#include <linux/types.h>#include <linux/jiffies.h>#include <linux/kernel.h>#include <linux/fcntl.h>#include <linux/nospec.h>#include <linux/socket.h>#include <linux/in.h>#include <linux/inet.h>#include <linux/inetdevice.h>#include <linux/netdevice.h>#include <linux/string.h>#include <linux/netfilter_ipv4.h>#include <linux/slab.h>#include <net/flow.h>#include <net/snmp.h>#include <net/ip.h>#include <net/route.h>#include <net/protocol.h>#include <net/icmp.h>#include <net/tcp.h>#include <net/udp.h>#include <net/raw.h>#include <net/ping.h>#include <linux/skbuff.h>#include <net/sock.h>#include <linux/errno.h>#include <linux/timer.h>#include <linux/init.h>#include <linux/uaccess.h>#include <net/checksum.h>#include <net/xfrm.h>#include <net/inet_common.h>#include <net/ip_fib.h>#include <net/l3mdev.h>#include <net/addrconf.h>#include <net/inet_dscp.h>#define CREATE_TRACE_POINTS#include <trace/events/icmp.h> /* *	Build xmit assembly blocks */ struct icmp_bxm {	struct sk_buff *skb;	int offset;	int data_len; 	struct {		struct icmphdr icmph;		__be32	       times[3];	} data;	int head_len; 	/* Must be last as it ends in a flexible-array member. */	struct ip_options_rcu replyopts;}; /* An array of errno for error messages from dest unreach. *//* RFC 1122: 3.2.2.1 States that NET_UNREACH, HOST_UNREACH and SR_FAILED MUST be considered 'transient errs'. */ const struct icmp_err icmp_err_convert[] = {	{		.errno = ENETUNREACH,	/* ICMP_NET_UNREACH */		.fatal = 0,	},	{		.errno = EHOSTUNREACH,	/* ICMP_HOST_UNREACH */		.fatal = 0,	},	{		.errno = ENOPROTOOPT	/* ICMP_PROT_UNREACH */,		.fatal = 1,	},	{		.errno = ECONNREFUSED,	/* ICMP_PORT_UNREACH */		.fatal = 1,	},	{		.errno = EMSGSIZE,	/* ICMP_FRAG_NEEDED */		.fatal = 0,	},	{		.errno = EOPNOTSUPP,	/* ICMP_SR_FAILED */		.fatal = 0,	},	{		.errno = ENETUNREACH,	/* ICMP_NET_UNKNOWN */		.fatal = 1,	},	{		.errno = EHOSTDOWN,	/* ICMP_HOST_UNKNOWN */		.fatal = 1,	},	{		.errno = ENONET,	/* ICMP_HOST_ISOLATED */		.fatal = 1,	},	{		.errno = ENETUNREACH,	/* ICMP_NET_ANO	*/		.fatal = 1,	},	{		.errno = EHOSTUNREACH,	/* ICMP_HOST_ANO */		.fatal = 1,	},	{		.errno = ENETUNREACH,	/* ICMP_NET_UNR_TOS */		.fatal = 0,	},	{		.errno = EHOSTUNREACH,	/* ICMP_HOST_UNR_TOS */		.fatal = 0,	},	{		.errno = EHOSTUNREACH,	/* ICMP_PKT_FILTERED */		.fatal = 1,	},	{		.errno = EHOSTUNREACH,	/* ICMP_PREC_VIOLATION */		.fatal = 1,	},	{		.errno = EHOSTUNREACH,	/* ICMP_PREC_CUTOFF */		.fatal = 1,	},};EXPORT_SYMBOL(icmp_err_convert); /* *	ICMP control array. This specifies what to do with each ICMP. */ struct icmp_control {	enum skb_drop_reason (*handler)(struct sk_buff *skb);	short   error;		/* This ICMP is classed as an error message */}; static const struct icmp_control icmp_pointers[NR_ICMP_TYPES+1]; static DEFINE_PER_CPU(struct sock *, ipv4_icmp_sk); /* Called with BH disabled */static inline struct sock *icmp_xmit_lock(struct net *net){	struct sock *sk; 	sk = this_cpu_read(ipv4_icmp_sk); 	if (unlikely(!spin_trylock(&sk->sk_lock.slock))) {		/* This can happen if the output path signals a		 * dst_link_failure() for an outgoing ICMP packet.		 */		return NULL;	}	sock_net_set(sk, net);	return sk;} static inline void icmp_xmit_unlock(struct sock *sk){	sock_net_set(sk, &init_net);	spin_unlock(&sk->sk_lock.slock);} /** * icmp_global_allow - Are we allowed to send one more ICMP message ? * @net: network namespace * * Uses a token bucket to limit our ICMP messages to ~sysctl_icmp_msgs_per_sec. * Returns false if we reached the limit and can not send another packet. * Works in tandem with icmp_global_consume(). */bool icmp_global_allow(struct net *net){	u32 delta, now, oldstamp;	int incr, new, old; 	/* Note: many cpus could find this condition true.	 * Then later icmp_global_consume() could consume more credits,	 * this is an acceptable race.	 */	if (atomic_read(&net->ipv4.icmp_global_credit) > 0)		return true; 	now = jiffies;	oldstamp = READ_ONCE(net->ipv4.icmp_global_stamp);	delta = min_t(u32, now - oldstamp, HZ);	if (delta < HZ / 50)		return false; 	incr = READ_ONCE(net->ipv4.sysctl_icmp_msgs_per_sec);	incr = div_u64((u64)incr * delta, HZ);	if (!incr)		return false; 	if (cmpxchg(&net->ipv4.icmp_global_stamp, oldstamp, now) == oldstamp) {		old = atomic_read(&net->ipv4.icmp_global_credit);		do {			new = min(old + incr, READ_ONCE(net->ipv4.sysctl_icmp_msgs_burst));		} while (!atomic_try_cmpxchg(&net->ipv4.icmp_global_credit, &old, new));	}	return true;} void icmp_global_consume(struct net *net){	int credits = get_random_u32_below(3); 	/* Note: this might make icmp_global.credit negative. */	if (credits)		atomic_sub(credits, &net->ipv4.icmp_global_credit);} static bool icmpv4_mask_allow(struct net *net, int type, int code){	if (type > NR_ICMP_TYPES)		return true; 	/* Don't limit PMTU discovery. */	if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)		return true; 	/* Limit if icmp type is enabled in ratemask. */	if (!((1 << type) & READ_ONCE(net->ipv4.sysctl_icmp_ratemask)))		return true; 	return false;} static bool icmpv4_global_allow(struct net *net, int type, int code,				bool *apply_ratelimit){	if (icmpv4_mask_allow(net, type, code))		return true; 	if (icmp_global_allow(net)) {		*apply_ratelimit = true;		return true;	}	__ICMP_INC_STATS(net, ICMP_MIB_RATELIMITGLOBAL);	return false;} /* *	Send an ICMP frame. */ static bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt,			       struct flowi4 *fl4, int type, int code,			       bool apply_ratelimit){	struct dst_entry *dst = &rt->dst;	struct inet_peer *peer;	struct net_device *dev;	int peer_timeout;	bool rc = true; 	if (!apply_ratelimit)		return true; 	peer_timeout = READ_ONCE(net->ipv4.sysctl_icmp_ratelimit);	if (!peer_timeout)		goto out; 	/* No rate limit on loopback */	rcu_read_lock();	dev = dst_dev_rcu(dst);	if (dev && (dev->flags & IFF_LOOPBACK))		goto out_unlock; 	peer = inet_getpeer_v4(net->ipv4.peers, fl4->daddr,			       l3mdev_master_ifindex_rcu(dev));	rc = inet_peer_xrlim_allow(peer, peer_timeout); out_unlock:	rcu_read_unlock();out:	if (!rc)		__ICMP_INC_STATS(net, ICMP_MIB_RATELIMITHOST);	else		icmp_global_consume(net);	return rc;} /* *	Maintain the counters used in the SNMP statistics for outgoing ICMP */void icmp_out_count(struct net *net, unsigned char type){	ICMPMSGOUT_INC_STATS(net, type);	ICMP_INC_STATS(net, ICMP_MIB_OUTMSGS);} /* *	Checksum each fragment, and on the first include the headers and final *	checksum. */static int icmp_glue_bits(void *from, char *to, int offset, int len, int odd,			  struct sk_buff *skb){	DEFINE_RAW_FLEX(struct icmp_bxm, icmp_param, replyopts.opt.__data,			IP_OPTIONS_DATA_FIXED_SIZE);	__wsum csum; 	icmp_param = from; 	csum = skb_copy_and_csum_bits(icmp_param->skb,				      icmp_param->offset + offset,				      to, len); 	skb->csum = csum_block_add(skb->csum, csum, odd);	if (icmp_param->data.icmph.type <= NR_ICMP_TYPES &&	    icmp_pointers[array_index_nospec(icmp_param->data.icmph.type,					     NR_ICMP_TYPES + 1)].error)		nf_ct_attach(skb, icmp_param->skb);	return 0;} static void icmp_push_reply(struct sock *sk,			    struct icmp_bxm *icmp_param,			    struct flowi4 *fl4,			    struct ipcm_cookie *ipc, struct rtable **rt){	struct sk_buff *skb; 	if (ip_append_data(sk, fl4, icmp_glue_bits, icmp_param,			   icmp_param->data_len+icmp_param->head_len,			   icmp_param->head_len,			   ipc, rt, MSG_DONTWAIT) < 0) {		__ICMP_INC_STATS(sock_net(sk), ICMP_MIB_OUTERRORS);		ip_flush_pending_frames(sk);	} else if ((skb = skb_peek(&sk->sk_write_queue)) != NULL) {		struct icmphdr *icmph = icmp_hdr(skb);		__wsum csum;		struct sk_buff *skb1; 		csum = csum_partial_copy_nocheck((void *)&icmp_param->data,						 (char *)icmph,						 icmp_param->head_len);		skb_queue_walk(&sk->sk_write_queue, skb1) {			csum = csum_add(csum, skb1->csum);		}		icmph->checksum = csum_fold(csum);		skb->ip_summed = CHECKSUM_NONE;		ip_push_pending_frames(sk, fl4);	}} /* *	Driving logic for building and sending ICMP messages. */ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb){	struct rtable *rt = skb_rtable(skb);	struct net *net = dev_net_rcu(rt->dst.dev);	bool apply_ratelimit = false;	struct ipcm_cookie ipc;	struct flowi4 fl4;	struct sock *sk;	__be32 daddr, saddr;	u32 mark = IP4_REPLY_MARK(net, skb->mark);	int type = icmp_param->data.icmph.type;	int code = icmp_param->data.icmph.code; 	if (ip_options_echo(net, &icmp_param->replyopts.opt, skb))		return; 	/* Needed by both icmpv4_global_allow and icmp_xmit_lock */	local_bh_disable(); 	/* is global icmp_msgs_per_sec exhausted ? */	if (!icmpv4_global_allow(net, type, code, &apply_ratelimit))		goto out_bh_enable; 	sk = icmp_xmit_lock(net);	if (!sk)		goto out_bh_enable; 	icmp_param->data.icmph.checksum = 0; 	ipcm_init(&ipc);	ipc.tos = ip_hdr(skb)->tos;	ipc.sockc.mark = mark;	daddr = ipc.addr = ip_hdr(skb)->saddr;	saddr = fib_compute_spec_dst(skb); 	if (icmp_param->replyopts.opt.optlen) {		ipc.opt = &icmp_param->replyopts;		if (ipc.opt->opt.srr)			daddr = icmp_param->replyopts.opt.faddr;	}	memset(&fl4, 0, sizeof(fl4));	fl4.daddr = daddr;	fl4.saddr = saddr;	fl4.flowi4_mark = mark;	fl4.flowi4_uid = sock_net_uid(net, NULL);	fl4.flowi4_dscp = ip4h_dscp(ip_hdr(skb));	fl4.flowi4_proto = IPPROTO_ICMP;	fl4.flowi4_oif = l3mdev_master_ifindex(skb->dev);	security_skb_classify_flow(skb, flowi4_to_flowi_common(&fl4));	rt = ip_route_output_key(net, &fl4);	if (IS_ERR(rt))		goto out_unlock;	if (icmpv4_xrlim_allow(net, rt, &fl4, type, code, apply_ratelimit))		icmp_push_reply(sk, icmp_param, &fl4, &ipc, &rt);	ip_rt_put(rt);out_unlock:	icmp_xmit_unlock(sk);out_bh_enable:	local_bh_enable();} /* * The device used for looking up which routing table to use for sending an ICMP * error is preferably the source whenever it is set, which should ensure the * icmp error can be sent to the source host, else lookup using the routing * table of the destination device, else use the main routing table (index 0). */static struct net_device *icmp_get_route_lookup_dev(struct sk_buff *skb){	struct net_device *dev = skb->dev;	const struct dst_entry *dst; 	if (dev)		return dev;	dst = skb_dst(skb);	return dst ? dst_dev(dst) : NULL;} static struct rtable *icmp_route_lookup(struct net *net, struct flowi4 *fl4,					struct sk_buff *skb_in,					const struct iphdr *iph, __be32 saddr,					dscp_t dscp, u32 mark, int type,					int code, struct icmp_bxm *param){	struct net_device *route_lookup_dev;

Lines 1–500 of 1781.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t