Skip to content
icmp.c 28.8 KiB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
	 */
	if (icmph->type > NR_ICMP_TYPES)
		goto error;


	/*
	 *	Parse the ICMP message
	 */

	if (rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) {
Linus Torvalds's avatar
Linus Torvalds committed
		/*
		 *	RFC 1122: 3.2.2.6 An ICMP_ECHO to broadcast MAY be
		 *	  silently ignored (we let user decide with a sysctl).
		 *	RFC 1122: 3.2.2.8 An ICMP_TIMESTAMP MAY be silently
		 *	  discarded if to broadcast/multicast.
		 */
		if ((icmph->type == ICMP_ECHO ||
		     icmph->type == ICMP_TIMESTAMP) &&
		    net->ipv4.sysctl_icmp_echo_ignore_broadcasts) {
Linus Torvalds's avatar
Linus Torvalds committed
			goto error;
		}
		if (icmph->type != ICMP_ECHO &&
		    icmph->type != ICMP_TIMESTAMP &&
		    icmph->type != ICMP_ADDRESS &&
		    icmph->type != ICMP_ADDRESSREPLY) {
			goto error;
	success = icmp_pointers[icmph->type].handler(skb);

	if (success)  {
		consume_skb(skb);
		return 0;
	}
Linus Torvalds's avatar
Linus Torvalds committed

drop:
	kfree_skb(skb);
	return 0;
csum_error:
	ICMP_INC_STATS_BH(net, ICMP_MIB_CSUMERRORS);
Linus Torvalds's avatar
Linus Torvalds committed
error:
	ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS);
Linus Torvalds's avatar
Linus Torvalds committed
	goto drop;
}

void icmp_err(struct sk_buff *skb, u32 info)
{
	struct iphdr *iph = (struct iphdr *)skb->data;
	int offset = iph->ihl<<2;
	struct icmphdr *icmph = (struct icmphdr *)(skb->data + offset);
	int type = icmp_hdr(skb)->type;
	int code = icmp_hdr(skb)->code;
	struct net *net = dev_net(skb->dev);

	/*
	 * Use ping_err to handle all icmp errors except those
	 * triggered by ICMP_ECHOREPLY which sent from kernel.
	 */
	if (icmph->type != ICMP_ECHOREPLY) {
		ping_err(skb, offset, info);
		return;
	}

	if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
		ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_ICMP, 0);
	else if (type == ICMP_REDIRECT)
		ipv4_redirect(skb, net, 0, 0, IPPROTO_ICMP, 0);
}

Linus Torvalds's avatar
Linus Torvalds committed
/*
 *	This table is the definition of how we handle ICMP.
 */
static const struct icmp_control icmp_pointers[NR_ICMP_TYPES + 1] = {
Linus Torvalds's avatar
Linus Torvalds committed
	[ICMP_ECHOREPLY] = {
		.handler = ping_rcv,
Linus Torvalds's avatar
Linus Torvalds committed
	},
	[1] = {
		.handler = icmp_discard,
		.error = 1,
	},
	[2] = {
		.handler = icmp_discard,
		.error = 1,
	},
	[ICMP_DEST_UNREACH] = {
		.handler = icmp_unreach,
		.error = 1,
	},
	[ICMP_SOURCE_QUENCH] = {
		.handler = icmp_unreach,
		.error = 1,
	},
	[ICMP_REDIRECT] = {
		.handler = icmp_redirect,
		.error = 1,
	},
	[6] = {
		.handler = icmp_discard,
		.error = 1,
	},
	[7] = {
		.handler = icmp_discard,
		.error = 1,
	},
	[ICMP_ECHO] = {
		.handler = icmp_echo,
	},
	[9] = {
		.handler = icmp_discard,
		.error = 1,
	},
	[10] = {
		.handler = icmp_discard,
		.error = 1,
	},
	[ICMP_TIME_EXCEEDED] = {
		.handler = icmp_unreach,
		.error = 1,
	},
	[ICMP_PARAMETERPROB] = {
		.handler = icmp_unreach,
		.error = 1,
	},
	[ICMP_TIMESTAMP] = {
		.handler = icmp_timestamp,
	},
	[ICMP_TIMESTAMPREPLY] = {
		.handler = icmp_discard,
	},
	[ICMP_INFO_REQUEST] = {
		.handler = icmp_discard,
	},
	[ICMP_INFO_REPLY] = {
Linus Torvalds's avatar
Linus Torvalds committed
		.handler = icmp_discard,
	},
	[ICMP_ADDRESS] = {
		.handler = icmp_discard,
Linus Torvalds's avatar
Linus Torvalds committed
	},
	[ICMP_ADDRESSREPLY] = {
		.handler = icmp_discard,
static void __net_exit icmp_sk_exit(struct net *net)
Linus Torvalds's avatar
Linus Torvalds committed
{
	int i;

	for_each_possible_cpu(i)
		inet_ctl_sock_destroy(*per_cpu_ptr(net->ipv4.icmp_sk, i));
	free_percpu(net->ipv4.icmp_sk);
	net->ipv4.icmp_sk = NULL;
Linus Torvalds's avatar
Linus Torvalds committed

static int __net_init icmp_sk_init(struct net *net)
	net->ipv4.icmp_sk = alloc_percpu(struct sock *);
	if (!net->ipv4.icmp_sk)
	for_each_possible_cpu(i) {
		struct sock *sk;
Linus Torvalds's avatar
Linus Torvalds committed

		err = inet_ctl_sock_create(&sk, PF_INET,
					   SOCK_RAW, IPPROTO_ICMP, net);
Linus Torvalds's avatar
Linus Torvalds committed
		if (err < 0)
Linus Torvalds's avatar
Linus Torvalds committed

		*per_cpu_ptr(net->ipv4.icmp_sk, i) = sk;
Linus Torvalds's avatar
Linus Torvalds committed

		/* Enough space for 2 64K ICMP packets, including
		 * sk_buff/skb_shared_info struct overhead.
Linus Torvalds's avatar
Linus Torvalds committed
		 */
		sk->sk_sndbuf =	2 * SKB_TRUESIZE(64 * 1024);
Linus Torvalds's avatar
Linus Torvalds committed

		/*
		 * Speedup sock_wfree()
		 */
		sock_set_flag(sk, SOCK_USE_WRITE_QUEUE);
		inet_sk(sk)->pmtudisc = IP_PMTUDISC_DONT;
Linus Torvalds's avatar
Linus Torvalds committed
	}

	/* Control parameters for ECHO replies. */
	net->ipv4.sysctl_icmp_echo_ignore_all = 0;
	net->ipv4.sysctl_icmp_echo_ignore_broadcasts = 1;

	/* Control parameter - ignore bogus broadcast responses? */
	net->ipv4.sysctl_icmp_ignore_bogus_error_responses = 1;

	/*
	 * 	Configurable global rate limit.
	 *
	 *	ratelimit defines tokens/packet consumed for dst->rate_token
	 *	bucket ratemask defines which icmp types are ratelimited by
	 *	setting	it's bit position.
	 *
	 *	default:
	 *	dest unreachable (3), source quench (4),
	 *	time exceeded (11), parameter problem (12)
	 */

	net->ipv4.sysctl_icmp_ratelimit = 1 * HZ;
	net->ipv4.sysctl_icmp_ratemask = 0x1818;
	net->ipv4.sysctl_icmp_errors_use_inbound_ifaddr = 0;

		inet_ctl_sock_destroy(*per_cpu_ptr(net->ipv4.icmp_sk, i));
	free_percpu(net->ipv4.icmp_sk);
static struct pernet_operations __net_initdata icmp_sk_ops = {
       .init = icmp_sk_init,
       .exit = icmp_sk_exit,
};

int __init icmp_init(void)
{
	return register_pernet_subsys(&icmp_sk_ops);