nf_conntrack_proto_icmp.c 9.5 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 21
#include <net/netfilter/nf_conntrack_core.h>

22
static unsigned long nf_ct_icmp_timeout __read_mostly = 30*HZ;
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

static int icmp_pkt_to_tuple(const struct sk_buff *skb,
			     unsigned int dataoff,
			     struct nf_conntrack_tuple *tuple)
{
	struct icmphdr _hdr, *hp;

	hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
	if (hp == NULL)
		return 0;

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

	return 1;
}

41 42 43 44 45 46 47 48 49 50 51 52
/* 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
};

53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
static int icmp_invert_tuple(struct nf_conntrack_tuple *tuple,
			     const struct nf_conntrack_tuple *orig)
{
	if (orig->dst.u.icmp.type >= sizeof(invmap)
	    || !invmap[orig->dst.u.icmp.type])
		return 0;

	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;
	return 1;
}

/* 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));
}

/* Print out the private part of the conntrack. */
static int icmp_print_conntrack(struct seq_file *s,
				const struct nf_conn *conntrack)
{
	return 0;
}

/* 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,
		       int pf,
		       unsigned int hooknum)
{
	/* Try to delete connection immediately after all replies:
92 93 94
	   won't actually vanish as we still have skb, and del_timer
	   means this will only run once even if count hits zero twice
	   (theoretically possible with SMP) */
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
	if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) {
		if (atomic_dec_and_test(&ct->proto.icmp.count)
		    && del_timer(&ct->timeout))
			ct->timeout.function((unsigned long)ct);
	} else {
		atomic_inc(&ct->proto.icmp.count);
		nf_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, skb);
		nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_icmp_timeout);
	}

	return NF_ACCEPT;
}

/* Called when a new connection for this protocol found. */
static int icmp_new(struct nf_conn *conntrack,
		    const struct sk_buff *skb, unsigned int dataoff)
{
112 113 114 115 116 117
	static const u_int8_t valid_new[] = {
		[ICMP_ECHO] = 1,
		[ICMP_TIMESTAMP] = 1,
		[ICMP_INFO_REQUEST] = 1,
		[ICMP_ADDRESS] = 1
	};
118 119 120 121

	if (conntrack->tuplehash[0].tuple.dst.u.icmp.type >= sizeof(valid_new)
	    || !valid_new[conntrack->tuplehash[0].tuple.dst.u.icmp.type]) {
		/* Can't create a new ICMP `conn' with this. */
122 123
		pr_debug("icmp: can't create new conn with type %u\n",
			 conntrack->tuplehash[0].tuple.dst.u.icmp.type);
124 125 126 127 128 129 130 131 132 133 134
		NF_CT_DUMP_TUPLE(&conntrack->tuplehash[0].tuple);
		return 0;
	}
	atomic_set(&conntrack->proto.icmp.count, 0);
	return 1;
}

extern struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4;
/* Returns conntrack if it dealt with ICMP, and filled in skb fields */
static int
icmp_error_message(struct sk_buff *skb,
135 136
		 enum ip_conntrack_info *ctinfo,
		 unsigned int hooknum)
137 138
{
	struct nf_conntrack_tuple innertuple, origtuple;
139
	struct nf_conntrack_l4proto *innerproto;
140 141 142 143
	struct nf_conntrack_tuple_hash *h;

	NF_CT_ASSERT(skb->nfct == NULL);

144 145 146 147 148 149
	/* 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");
150 151 152
		return -NF_ACCEPT;
	}

153
	/* rcu_read_lock()ed by nf_hook_slow */
154
	innerproto = __nf_ct_l4proto_find(PF_INET, origtuple.dst.protonum);
155

156 157 158
	/* Ordinarily, we'd expect the inverted tupleproto, but it's
	   been preserved inside the ICMP. */
	if (!nf_ct_invert_tuple(&innertuple, &origtuple,
159
				&nf_conntrack_l3proto_ipv4, innerproto)) {
160
		pr_debug("icmp_error_message: no match\n");
161 162 163 164 165
		return -NF_ACCEPT;
	}

	*ctinfo = IP_CT_RELATED;

166
	h = nf_conntrack_find_get(&innertuple);
167
	if (!h) {
168 169
		pr_debug("icmp_error_message: no match\n");
		return -NF_ACCEPT;
170 171
	}

172 173 174
	if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY)
		*ctinfo += IP_CT_IS_REPLY;

175 176 177 178
	/* Update skb to refer to this connection */
	skb->nfct = &nf_ct_tuplehash_to_ctrack(h)->ct_general;
	skb->nfctinfo = *ctinfo;
	return -NF_ACCEPT;
179 180 181 182 183 184 185 186 187 188
}

/* Small and modified version of icmp_rcv */
static int
icmp_error(struct sk_buff *skb, unsigned int dataoff,
	   enum ip_conntrack_info *ctinfo, int pf, unsigned int hooknum)
{
	struct icmphdr _ih, *icmph;

	/* Not enough header? */
189
	icmph = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_ih), &_ih);
190 191 192 193 194 195 196 197
	if (icmph == NULL) {
		if (LOG_INVALID(IPPROTO_ICMP))
			nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
				      "nf_ct_icmp: short packet ");
		return -NF_ACCEPT;
	}

	/* See ip_conntrack_proto_tcp.c */
198
	if (nf_conntrack_checksum && hooknum == NF_IP_PRE_ROUTING &&
199
	    nf_ip_checksum(skb, hooknum, dataoff, 0)) {
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
		if (LOG_INVALID(IPPROTO_ICMP))
			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) {
		if (LOG_INVALID(IPPROTO_ICMP))
			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? */
	if (icmph->type != ICMP_DEST_UNREACH
	    && icmph->type != ICMP_SOURCE_QUENCH
	    && icmph->type != ICMP_TIME_EXCEEDED
	    && icmph->type != ICMP_PARAMETERPROB
	    && icmph->type != ICMP_REDIRECT)
		return NF_ACCEPT;

	return icmp_error_message(skb, ctinfo, hooknum);
}

230
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
231 232 233 234 235 236 237

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

static int icmp_tuple_to_nfattr(struct sk_buff *skb,
				const struct nf_conntrack_tuple *t)
{
238
	NLA_PUT(skb, CTA_PROTO_ICMP_ID, sizeof(u_int16_t),
239
		&t->src.u.icmp.id);
240
	NLA_PUT(skb, CTA_PROTO_ICMP_TYPE, sizeof(u_int8_t),
241
		&t->dst.u.icmp.type);
242
	NLA_PUT(skb, CTA_PROTO_ICMP_CODE, sizeof(u_int8_t),
243 244 245 246
		&t->dst.u.icmp.code);

	return 0;

247
nla_put_failure:
248 249 250
	return -1;
}

251 252 253 254
static const size_t cta_min_proto[CTA_PROTO_MAX+1] = {
	[CTA_PROTO_ICMP_TYPE]	= sizeof(u_int8_t),
	[CTA_PROTO_ICMP_CODE]	= sizeof(u_int8_t),
	[CTA_PROTO_ICMP_ID]	= sizeof(u_int16_t)
255 256
};

257
static int icmp_nfattr_to_tuple(struct nlattr *tb[],
258 259
				struct nf_conntrack_tuple *tuple)
{
260 261 262
	if (!tb[CTA_PROTO_ICMP_TYPE]
	    || !tb[CTA_PROTO_ICMP_CODE]
	    || !tb[CTA_PROTO_ICMP_ID])
263 264 265 266 267
		return -EINVAL;

	if (nfattr_bad_size(tb, CTA_PROTO_MAX, cta_min_proto))
		return -EINVAL;

268
	tuple->dst.u.icmp.type =
269
			*(u_int8_t *)nla_data(tb[CTA_PROTO_ICMP_TYPE]);
270
	tuple->dst.u.icmp.code =
271
			*(u_int8_t *)nla_data(tb[CTA_PROTO_ICMP_CODE]);
272
	tuple->src.u.icmp.id =
273
			*(__be16 *)nla_data(tb[CTA_PROTO_ICMP_ID]);
274 275 276 277 278 279 280 281 282

	if (tuple->dst.u.icmp.type >= sizeof(invmap)
	    || !invmap[tuple->dst.u.icmp.type])
		return -EINVAL;

	return 0;
}
#endif

283 284 285 286 287 288 289 290 291 292 293
#ifdef CONFIG_SYSCTL
static struct ctl_table_header *icmp_sysctl_header;
static struct ctl_table icmp_sysctl_table[] = {
	{
		.ctl_name	= NET_NF_CONNTRACK_ICMP_TIMEOUT,
		.procname	= "nf_conntrack_icmp_timeout",
		.data		= &nf_ct_icmp_timeout,
		.maxlen		= sizeof(unsigned int),
		.mode		= 0644,
		.proc_handler	= &proc_dointvec_jiffies,
	},
294
	{
295 296 297
		.ctl_name = 0
	}
};
298 299 300 301 302 303 304 305 306 307
#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
static struct ctl_table icmp_compat_sysctl_table[] = {
	{
		.ctl_name	= NET_IPV4_NF_CONNTRACK_ICMP_TIMEOUT,
		.procname	= "ip_conntrack_icmp_timeout",
		.data		= &nf_ct_icmp_timeout,
		.maxlen		= sizeof(unsigned int),
		.mode		= 0644,
		.proc_handler	= &proc_dointvec_jiffies,
	},
308
	{
309 310 311 312
		.ctl_name = 0
	}
};
#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
313 314
#endif /* CONFIG_SYSCTL */

315
struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp __read_mostly =
316 317
{
	.l3proto		= PF_INET,
318
	.l4proto		= IPPROTO_ICMP,
319 320 321 322 323 324 325 326 327
	.name			= "icmp",
	.pkt_to_tuple		= icmp_pkt_to_tuple,
	.invert_tuple		= icmp_invert_tuple,
	.print_tuple		= icmp_print_tuple,
	.print_conntrack	= icmp_print_conntrack,
	.packet			= icmp_packet,
	.new			= icmp_new,
	.error			= icmp_error,
	.destroy		= NULL,
328
	.me			= NULL,
329
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
330 331 332
	.tuple_to_nfattr	= icmp_tuple_to_nfattr,
	.nfattr_to_tuple	= icmp_nfattr_to_tuple,
#endif
333 334 335
#ifdef CONFIG_SYSCTL
	.ctl_table_header	= &icmp_sysctl_header,
	.ctl_table		= icmp_sysctl_table,
336 337 338
#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
	.ctl_compat_table	= icmp_compat_sysctl_table,
#endif
339
#endif
340
};