nf_conntrack_proto_icmp.c 9.0 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_log.h>
22

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

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

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

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

/* 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:
86 87 88
	   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) */
89 90 91 92 93 94 95 96 97 98 99 100 101 102
	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. */
103
static int icmp_new(struct nf_conn *ct,
104 105
		    const struct sk_buff *skb, unsigned int dataoff)
{
106 107 108 109 110 111
	static const u_int8_t valid_new[] = {
		[ICMP_ECHO] = 1,
		[ICMP_TIMESTAMP] = 1,
		[ICMP_INFO_REQUEST] = 1,
		[ICMP_ADDRESS] = 1
	};
112

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

/* Returns conntrack if it dealt with ICMP, and filled in skb fields */
static int
icmp_error_message(struct sk_buff *skb,
128 129
		 enum ip_conntrack_info *ctinfo,
		 unsigned int hooknum)
130 131
{
	struct nf_conntrack_tuple innertuple, origtuple;
132
	struct nf_conntrack_l4proto *innerproto;
133 134 135 136
	struct nf_conntrack_tuple_hash *h;

	NF_CT_ASSERT(skb->nfct == NULL);

137 138 139 140 141 142
	/* 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");
143 144 145
		return -NF_ACCEPT;
	}

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

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

	*ctinfo = IP_CT_RELATED;

159
	h = nf_conntrack_find_get(&innertuple);
160
	if (!h) {
161 162
		pr_debug("icmp_error_message: no match\n");
		return -NF_ACCEPT;
163 164
	}

165 166 167
	if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY)
		*ctinfo += IP_CT_IS_REPLY;

168 169 170 171
	/* Update skb to refer to this connection */
	skb->nfct = &nf_ct_tuplehash_to_ctrack(h)->ct_general;
	skb->nfctinfo = *ctinfo;
	return -NF_ACCEPT;
172 173 174 175 176 177 178 179 180 181
}

/* 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? */
182
	icmph = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_ih), &_ih);
183 184 185 186 187 188 189 190
	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 */
191
	if (nf_conntrack_checksum && hooknum == NF_INET_PRE_ROUTING &&
192
	    nf_ip_checksum(skb, hooknum, dataoff, 0)) {
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
		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);
}

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 261 262 263 264 265 266

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

	return 0;
}
#endif

267 268 269 270 271 272 273 274 275 276
#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,
		.proc_handler	= &proc_dointvec_jiffies,
	},
277
	{
278 279 280
		.ctl_name = 0
	}
};
281 282 283 284 285 286 287 288 289
#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,
		.proc_handler	= &proc_dointvec_jiffies,
	},
290
	{
291 292 293 294
		.ctl_name = 0
	}
};
#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
295 296
#endif /* CONFIG_SYSCTL */

297
struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp __read_mostly =
298 299
{
	.l3proto		= PF_INET,
300
	.l4proto		= IPPROTO_ICMP,
301 302 303 304 305 306 307 308
	.name			= "icmp",
	.pkt_to_tuple		= icmp_pkt_to_tuple,
	.invert_tuple		= icmp_invert_tuple,
	.print_tuple		= icmp_print_tuple,
	.packet			= icmp_packet,
	.new			= icmp_new,
	.error			= icmp_error,
	.destroy		= NULL,
309
	.me			= NULL,
310
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
311 312
	.tuple_to_nlattr	= icmp_tuple_to_nlattr,
	.nlattr_to_tuple	= icmp_nlattr_to_tuple,
313
	.nla_policy		= icmp_nla_policy,
314
#endif
315 316 317
#ifdef CONFIG_SYSCTL
	.ctl_table_header	= &icmp_sysctl_header,
	.ctl_table		= icmp_sysctl_table,
318 319 320
#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
	.ctl_compat_table	= icmp_compat_sysctl_table,
#endif
321
#endif
322
};