nf_conntrack_proto_icmp.c 10.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/* (C) 1999-2001 Paul `Rusty' Russell
 * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/types.h>
#include <linux/timer.h>
#include <linux/netfilter.h>
#include <linux/in.h>
#include <linux/icmp.h>
#include <linux/seq_file.h>
#include <net/ip.h>
#include <net/checksum.h>
#include <linux/netfilter_ipv4.h>
#include <net/netfilter/nf_conntrack_tuple.h>
19
#include <net/netfilter/nf_conntrack_l4proto.h>
20
#include <net/netfilter/nf_conntrack_core.h>
21
#include <net/netfilter/nf_conntrack_zones.h>
22
#include <net/netfilter/nf_log.h>
23

24
static unsigned int nf_ct_icmp_timeout __read_mostly = 30*HZ;
25

26 27
static bool icmp_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff,
			      struct nf_conntrack_tuple *tuple)
28
{
29 30
	const struct icmphdr *hp;
	struct icmphdr _hdr;
31 32 33

	hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
	if (hp == NULL)
34
		return false;
35 36 37 38 39

	tuple->dst.u.icmp.type = hp->type;
	tuple->src.u.icmp.id = hp->un.echo.id;
	tuple->dst.u.icmp.code = hp->code;

40
	return true;
41 42
}

43 44 45 46 47 48 49 50 51 52 53 54
/* Add 1; spaces filled with 0. */
static const u_int8_t invmap[] = {
	[ICMP_ECHO] = ICMP_ECHOREPLY + 1,
	[ICMP_ECHOREPLY] = ICMP_ECHO + 1,
	[ICMP_TIMESTAMP] = ICMP_TIMESTAMPREPLY + 1,
	[ICMP_TIMESTAMPREPLY] = ICMP_TIMESTAMP + 1,
	[ICMP_INFO_REQUEST] = ICMP_INFO_REPLY + 1,
	[ICMP_INFO_REPLY] = ICMP_INFO_REQUEST + 1,
	[ICMP_ADDRESS] = ICMP_ADDRESSREPLY + 1,
	[ICMP_ADDRESSREPLY] = ICMP_ADDRESS + 1
};

55 56
static bool icmp_invert_tuple(struct nf_conntrack_tuple *tuple,
			      const struct nf_conntrack_tuple *orig)
57
{
58 59
	if (orig->dst.u.icmp.type >= sizeof(invmap) ||
	    !invmap[orig->dst.u.icmp.type])
60
		return false;
61 62 63 64

	tuple->src.u.icmp.id = orig->src.u.icmp.id;
	tuple->dst.u.icmp.type = invmap[orig->dst.u.icmp.type] - 1;
	tuple->dst.u.icmp.code = orig->dst.u.icmp.code;
65
	return true;
66 67 68 69 70 71 72 73 74 75 76 77
}

/* Print out the per-protocol part of the tuple. */
static int icmp_print_tuple(struct seq_file *s,
			    const struct nf_conntrack_tuple *tuple)
{
	return seq_printf(s, "type=%u code=%u id=%u ",
			  tuple->dst.u.icmp.type,
			  tuple->dst.u.icmp.code,
			  ntohs(tuple->src.u.icmp.id));
}

78 79 80 81 82
static unsigned int *icmp_get_timeouts(struct net *net)
{
	return &nf_ct_icmp_timeout;
}

83 84 85 86 87
/* Returns verdict for packet, or -1 for invalid. */
static int icmp_packet(struct nf_conn *ct,
		       const struct sk_buff *skb,
		       unsigned int dataoff,
		       enum ip_conntrack_info ctinfo,
88
		       u_int8_t pf,
89 90
		       unsigned int hooknum,
		       unsigned int *timeout)
91
{
92 93 94
	/* Do not immediately delete the connection after the first
	   successful reply to avoid excessive conntrackd traffic
	   and also to handle correctly ICMP echo reply duplicates. */
95
	nf_ct_refresh_acct(ct, ctinfo, skb, *timeout);
96 97 98 99 100

	return NF_ACCEPT;
}

/* Called when a new connection for this protocol found. */
101
static bool icmp_new(struct nf_conn *ct, const struct sk_buff *skb,
102
		     unsigned int dataoff, unsigned int *timeouts)
103
{
104 105 106 107 108 109
	static const u_int8_t valid_new[] = {
		[ICMP_ECHO] = 1,
		[ICMP_TIMESTAMP] = 1,
		[ICMP_INFO_REQUEST] = 1,
		[ICMP_ADDRESS] = 1
	};
110

111 112
	if (ct->tuplehash[0].tuple.dst.u.icmp.type >= sizeof(valid_new) ||
	    !valid_new[ct->tuplehash[0].tuple.dst.u.icmp.type]) {
113
		/* Can't create a new ICMP `conn' with this. */
114
		pr_debug("icmp: can't create new conn with type %u\n",
115
			 ct->tuplehash[0].tuple.dst.u.icmp.type);
116
		nf_ct_dump_tuple_ip(&ct->tuplehash[0].tuple);
117
		return false;
118
	}
119
	return true;
120 121 122 123
}

/* Returns conntrack if it dealt with ICMP, and filled in skb fields */
static int
124
icmp_error_message(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb,
125 126
		 enum ip_conntrack_info *ctinfo,
		 unsigned int hooknum)
127 128
{
	struct nf_conntrack_tuple innertuple, origtuple;
129 130
	const struct nf_conntrack_l4proto *innerproto;
	const struct nf_conntrack_tuple_hash *h;
131
	u16 zone = tmpl ? nf_ct_zone(tmpl) : NF_CT_DEFAULT_ZONE;
132 133 134

	NF_CT_ASSERT(skb->nfct == NULL);

135 136 137 138 139 140
	/* Are they talking about one of our connections? */
	if (!nf_ct_get_tuplepr(skb,
			       skb_network_offset(skb) + ip_hdrlen(skb)
						       + sizeof(struct icmphdr),
			       PF_INET, &origtuple)) {
		pr_debug("icmp_error_message: failed to get tuple\n");
141 142 143
		return -NF_ACCEPT;
	}

144
	/* rcu_read_lock()ed by nf_hook_slow */
145
	innerproto = __nf_ct_l4proto_find(PF_INET, origtuple.dst.protonum);
146

147 148 149
	/* Ordinarily, we'd expect the inverted tupleproto, but it's
	   been preserved inside the ICMP. */
	if (!nf_ct_invert_tuple(&innertuple, &origtuple,
150
				&nf_conntrack_l3proto_ipv4, innerproto)) {
151
		pr_debug("icmp_error_message: no match\n");
152 153 154 155 156
		return -NF_ACCEPT;
	}

	*ctinfo = IP_CT_RELATED;

157
	h = nf_conntrack_find_get(net, zone, &innertuple);
158
	if (!h) {
159 160
		pr_debug("icmp_error_message: no match\n");
		return -NF_ACCEPT;
161 162
	}

163 164 165
	if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY)
		*ctinfo += IP_CT_IS_REPLY;

166 167 168
	/* Update skb to refer to this connection */
	skb->nfct = &nf_ct_tuplehash_to_ctrack(h)->ct_general;
	skb->nfctinfo = *ctinfo;
169
	return NF_ACCEPT;
170 171 172 173
}

/* Small and modified version of icmp_rcv */
static int
174 175
icmp_error(struct net *net, struct nf_conn *tmpl,
	   struct sk_buff *skb, unsigned int dataoff,
176
	   enum ip_conntrack_info *ctinfo, u_int8_t pf, unsigned int hooknum)
177
{
178 179
	const struct icmphdr *icmph;
	struct icmphdr _ih;
180 181

	/* Not enough header? */
182
	icmph = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_ih), &_ih);
183
	if (icmph == NULL) {
184
		if (LOG_INVALID(net, IPPROTO_ICMP))
185 186 187 188 189 190
			nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
				      "nf_ct_icmp: short packet ");
		return -NF_ACCEPT;
	}

	/* See ip_conntrack_proto_tcp.c */
191
	if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING &&
192
	    nf_ip_checksum(skb, hooknum, dataoff, 0)) {
193
		if (LOG_INVALID(net, IPPROTO_ICMP))
194 195 196 197 198 199 200 201 202 203 204 205
			nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
				      "nf_ct_icmp: bad HW ICMP checksum ");
		return -NF_ACCEPT;
	}

	/*
	 *	18 is the highest 'known' ICMP type. Anything else is a mystery
	 *
	 *	RFC 1122: 3.2.2  Unknown ICMP messages types MUST be silently
	 *		  discarded.
	 */
	if (icmph->type > NR_ICMP_TYPES) {
206
		if (LOG_INVALID(net, IPPROTO_ICMP))
207 208 209 210 211 212
			nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
				      "nf_ct_icmp: invalid ICMP type ");
		return -NF_ACCEPT;
	}

	/* Need to track icmp error message? */
213 214 215 216 217
	if (icmph->type != ICMP_DEST_UNREACH &&
	    icmph->type != ICMP_SOURCE_QUENCH &&
	    icmph->type != ICMP_TIME_EXCEEDED &&
	    icmph->type != ICMP_PARAMETERPROB &&
	    icmph->type != ICMP_REDIRECT)
218 219
		return NF_ACCEPT;

220
	return icmp_error_message(net, tmpl, skb, ctinfo, hooknum);
221 222
}

223
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
224 225 226 227

#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nfnetlink_conntrack.h>

228
static int icmp_tuple_to_nlattr(struct sk_buff *skb,
229 230
				const struct nf_conntrack_tuple *t)
{
231 232 233
	NLA_PUT_BE16(skb, CTA_PROTO_ICMP_ID, t->src.u.icmp.id);
	NLA_PUT_U8(skb, CTA_PROTO_ICMP_TYPE, t->dst.u.icmp.type);
	NLA_PUT_U8(skb, CTA_PROTO_ICMP_CODE, t->dst.u.icmp.code);
234 235 236

	return 0;

237
nla_put_failure:
238 239 240
	return -1;
}

241 242 243 244
static const struct nla_policy icmp_nla_policy[CTA_PROTO_MAX+1] = {
	[CTA_PROTO_ICMP_TYPE]	= { .type = NLA_U8 },
	[CTA_PROTO_ICMP_CODE]	= { .type = NLA_U8 },
	[CTA_PROTO_ICMP_ID]	= { .type = NLA_U16 },
245 246
};

247
static int icmp_nlattr_to_tuple(struct nlattr *tb[],
248 249
				struct nf_conntrack_tuple *tuple)
{
250 251 252
	if (!tb[CTA_PROTO_ICMP_TYPE] ||
	    !tb[CTA_PROTO_ICMP_CODE] ||
	    !tb[CTA_PROTO_ICMP_ID])
253 254
		return -EINVAL;

255 256 257
	tuple->dst.u.icmp.type = nla_get_u8(tb[CTA_PROTO_ICMP_TYPE]);
	tuple->dst.u.icmp.code = nla_get_u8(tb[CTA_PROTO_ICMP_CODE]);
	tuple->src.u.icmp.id = nla_get_be16(tb[CTA_PROTO_ICMP_ID]);
258

259 260
	if (tuple->dst.u.icmp.type >= sizeof(invmap) ||
	    !invmap[tuple->dst.u.icmp.type])
261 262 263 264
		return -EINVAL;

	return 0;
}
265 266 267 268 269

static int icmp_nlattr_tuple_size(void)
{
	return nla_policy_len(icmp_nla_policy, CTA_PROTO_MAX + 1);
}
270 271
#endif

272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)

#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nfnetlink_cttimeout.h>

static int icmp_timeout_nlattr_to_obj(struct nlattr *tb[], void *data)
{
	unsigned int *timeout = data;

	if (tb[CTA_TIMEOUT_ICMP_TIMEOUT]) {
		*timeout =
			ntohl(nla_get_be32(tb[CTA_TIMEOUT_ICMP_TIMEOUT])) * HZ;
	} else {
		/* Set default ICMP timeout. */
		*timeout = nf_ct_icmp_timeout;
	}
	return 0;
}

static int
icmp_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data)
{
	const unsigned int *timeout = data;

	NLA_PUT_BE32(skb, CTA_TIMEOUT_ICMP_TIMEOUT, htonl(*timeout / HZ));

	return 0;

nla_put_failure:
	return -ENOSPC;
}

static const struct nla_policy
icmp_timeout_nla_policy[CTA_TIMEOUT_ICMP_MAX+1] = {
	[CTA_TIMEOUT_ICMP_TIMEOUT]	= { .type = NLA_U32 },
};
#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */

310 311 312 313 314 315 316 317
#ifdef CONFIG_SYSCTL
static struct ctl_table_header *icmp_sysctl_header;
static struct ctl_table icmp_sysctl_table[] = {
	{
		.procname	= "nf_conntrack_icmp_timeout",
		.data		= &nf_ct_icmp_timeout,
		.maxlen		= sizeof(unsigned int),
		.mode		= 0644,
A
Alexey Dobriyan 已提交
318
		.proc_handler	= proc_dointvec_jiffies,
319
	},
320
	{ }
321
};
322 323 324 325 326 327 328
#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
static struct ctl_table icmp_compat_sysctl_table[] = {
	{
		.procname	= "ip_conntrack_icmp_timeout",
		.data		= &nf_ct_icmp_timeout,
		.maxlen		= sizeof(unsigned int),
		.mode		= 0644,
A
Alexey Dobriyan 已提交
329
		.proc_handler	= proc_dointvec_jiffies,
330
	},
331
	{ }
332 333
};
#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
334 335
#endif /* CONFIG_SYSCTL */

336
struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp __read_mostly =
337 338
{
	.l3proto		= PF_INET,
339
	.l4proto		= IPPROTO_ICMP,
340 341 342 343 344
	.name			= "icmp",
	.pkt_to_tuple		= icmp_pkt_to_tuple,
	.invert_tuple		= icmp_invert_tuple,
	.print_tuple		= icmp_print_tuple,
	.packet			= icmp_packet,
345
	.get_timeouts		= icmp_get_timeouts,
346 347 348
	.new			= icmp_new,
	.error			= icmp_error,
	.destroy		= NULL,
349
	.me			= NULL,
350
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
351
	.tuple_to_nlattr	= icmp_tuple_to_nlattr,
352
	.nlattr_tuple_size	= icmp_nlattr_tuple_size,
353
	.nlattr_to_tuple	= icmp_nlattr_to_tuple,
354
	.nla_policy		= icmp_nla_policy,
355
#endif
356 357 358 359 360 361 362 363 364
#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
	.ctnl_timeout		= {
		.nlattr_to_obj	= icmp_timeout_nlattr_to_obj,
		.obj_to_nlattr	= icmp_timeout_obj_to_nlattr,
		.nlattr_max	= CTA_TIMEOUT_ICMP_MAX,
		.obj_size	= sizeof(unsigned int),
		.nla_policy	= icmp_timeout_nla_policy,
	},
#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
365 366 367
#ifdef CONFIG_SYSCTL
	.ctl_table_header	= &icmp_sysctl_header,
	.ctl_table		= icmp_sysctl_table,
368 369 370
#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
	.ctl_compat_table	= icmp_compat_sysctl_table,
#endif
371
#endif
372
};