1 /* (C) 1999-2001 Paul `Rusty' Russell
2  * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  */
8 
9 #include <linux/types.h>
10 #include <linux/timer.h>
11 #include <linux/netfilter.h>
12 #include <linux/in.h>
13 #include <linux/icmp.h>
14 #include <linux/seq_file.h>
15 #include <net/ip.h>
16 #include <net/checksum.h>
17 #include <linux/netfilter_ipv4.h>
18 #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 
icmp_pkt_to_tuple(const struct sk_buff * skb,unsigned int dataoff,struct nf_conntrack_tuple * tuple)26 static bool icmp_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff,
27 			      struct nf_conntrack_tuple *tuple)
28 {
29 	const struct icmphdr *hp;
30 	struct icmphdr _hdr;
31 
32 	hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
33 	if (hp == NULL)
34 		return false;
35 
36 	tuple->dst.u.icmp.type = hp->type;
37 	tuple->src.u.icmp.id = hp->un.echo.id;
38 	tuple->dst.u.icmp.code = hp->code;
39 
40 	return true;
41 }
42 
43 /* Add 1; spaces filled with 0. */
44 static const u_int8_t invmap[] = {
45 	[ICMP_ECHO] = ICMP_ECHOREPLY + 1,
46 	[ICMP_ECHOREPLY] = ICMP_ECHO + 1,
47 	[ICMP_TIMESTAMP] = ICMP_TIMESTAMPREPLY + 1,
48 	[ICMP_TIMESTAMPREPLY] = ICMP_TIMESTAMP + 1,
49 	[ICMP_INFO_REQUEST] = ICMP_INFO_REPLY + 1,
50 	[ICMP_INFO_REPLY] = ICMP_INFO_REQUEST + 1,
51 	[ICMP_ADDRESS] = ICMP_ADDRESSREPLY + 1,
52 	[ICMP_ADDRESSREPLY] = ICMP_ADDRESS + 1
53 };
54 
icmp_invert_tuple(struct nf_conntrack_tuple * tuple,const struct nf_conntrack_tuple * orig)55 static bool icmp_invert_tuple(struct nf_conntrack_tuple *tuple,
56 			      const struct nf_conntrack_tuple *orig)
57 {
58 	if (orig->dst.u.icmp.type >= sizeof(invmap) ||
59 	    !invmap[orig->dst.u.icmp.type])
60 		return false;
61 
62 	tuple->src.u.icmp.id = orig->src.u.icmp.id;
63 	tuple->dst.u.icmp.type = invmap[orig->dst.u.icmp.type] - 1;
64 	tuple->dst.u.icmp.code = orig->dst.u.icmp.code;
65 	return true;
66 }
67 
68 /* Print out the per-protocol part of the tuple. */
icmp_print_tuple(struct seq_file * s,const struct nf_conntrack_tuple * tuple)69 static int icmp_print_tuple(struct seq_file *s,
70 			    const struct nf_conntrack_tuple *tuple)
71 {
72 	return seq_printf(s, "type=%u code=%u id=%u ",
73 			  tuple->dst.u.icmp.type,
74 			  tuple->dst.u.icmp.code,
75 			  ntohs(tuple->src.u.icmp.id));
76 }
77 
78 /* Returns verdict for packet, or -1 for invalid. */
icmp_packet(struct nf_conn * ct,const struct sk_buff * skb,unsigned int dataoff,enum ip_conntrack_info ctinfo,u_int8_t pf,unsigned int hooknum)79 static int icmp_packet(struct nf_conn *ct,
80 		       const struct sk_buff *skb,
81 		       unsigned int dataoff,
82 		       enum ip_conntrack_info ctinfo,
83 		       u_int8_t pf,
84 		       unsigned int hooknum)
85 {
86 	/* Do not immediately delete the connection after the first
87 	   successful reply to avoid excessive conntrackd traffic
88 	   and also to handle correctly ICMP echo reply duplicates. */
89 	nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_icmp_timeout);
90 
91 	return NF_ACCEPT;
92 }
93 
94 /* Called when a new connection for this protocol found. */
icmp_new(struct nf_conn * ct,const struct sk_buff * skb,unsigned int dataoff)95 static bool icmp_new(struct nf_conn *ct, const struct sk_buff *skb,
96 		     unsigned int dataoff)
97 {
98 	static const u_int8_t valid_new[] = {
99 		[ICMP_ECHO] = 1,
100 		[ICMP_TIMESTAMP] = 1,
101 		[ICMP_INFO_REQUEST] = 1,
102 		[ICMP_ADDRESS] = 1
103 	};
104 
105 	if (ct->tuplehash[0].tuple.dst.u.icmp.type >= sizeof(valid_new) ||
106 	    !valid_new[ct->tuplehash[0].tuple.dst.u.icmp.type]) {
107 		/* Can't create a new ICMP `conn' with this. */
108 		pr_debug("icmp: can't create new conn with type %u\n",
109 			 ct->tuplehash[0].tuple.dst.u.icmp.type);
110 		nf_ct_dump_tuple_ip(&ct->tuplehash[0].tuple);
111 		return false;
112 	}
113 	return true;
114 }
115 
116 /* Returns conntrack if it dealt with ICMP, and filled in skb fields */
117 static int
icmp_error_message(struct net * net,struct nf_conn * tmpl,struct sk_buff * skb,enum ip_conntrack_info * ctinfo,unsigned int hooknum)118 icmp_error_message(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb,
119 		 enum ip_conntrack_info *ctinfo,
120 		 unsigned int hooknum)
121 {
122 	struct nf_conntrack_tuple innertuple, origtuple;
123 	const struct nf_conntrack_l4proto *innerproto;
124 	const struct nf_conntrack_tuple_hash *h;
125 	u16 zone = tmpl ? nf_ct_zone(tmpl) : NF_CT_DEFAULT_ZONE;
126 
127 	NF_CT_ASSERT(skb->nfct == NULL);
128 
129 	/* Are they talking about one of our connections? */
130 	if (!nf_ct_get_tuplepr(skb,
131 			       skb_network_offset(skb) + ip_hdrlen(skb)
132 						       + sizeof(struct icmphdr),
133 			       PF_INET, &origtuple)) {
134 		pr_debug("icmp_error_message: failed to get tuple\n");
135 		return -NF_ACCEPT;
136 	}
137 
138 	/* rcu_read_lock()ed by nf_hook_slow */
139 	innerproto = __nf_ct_l4proto_find(PF_INET, origtuple.dst.protonum);
140 
141 	/* Ordinarily, we'd expect the inverted tupleproto, but it's
142 	   been preserved inside the ICMP. */
143 	if (!nf_ct_invert_tuple(&innertuple, &origtuple,
144 				&nf_conntrack_l3proto_ipv4, innerproto)) {
145 		pr_debug("icmp_error_message: no match\n");
146 		return -NF_ACCEPT;
147 	}
148 
149 	*ctinfo = IP_CT_RELATED;
150 
151 	h = nf_conntrack_find_get(net, zone, &innertuple);
152 	if (!h) {
153 		pr_debug("icmp_error_message: no match\n");
154 		return -NF_ACCEPT;
155 	}
156 
157 	if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY)
158 		*ctinfo += IP_CT_IS_REPLY;
159 
160 	/* Update skb to refer to this connection */
161 	skb->nfct = &nf_ct_tuplehash_to_ctrack(h)->ct_general;
162 	skb->nfctinfo = *ctinfo;
163 	return NF_ACCEPT;
164 }
165 
166 /* Small and modified version of icmp_rcv */
167 static int
icmp_error(struct net * net,struct nf_conn * tmpl,struct sk_buff * skb,unsigned int dataoff,enum ip_conntrack_info * ctinfo,u_int8_t pf,unsigned int hooknum)168 icmp_error(struct net *net, struct nf_conn *tmpl,
169 	   struct sk_buff *skb, unsigned int dataoff,
170 	   enum ip_conntrack_info *ctinfo, u_int8_t pf, unsigned int hooknum)
171 {
172 	const struct icmphdr *icmph;
173 	struct icmphdr _ih;
174 
175 	/* Not enough header? */
176 	icmph = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_ih), &_ih);
177 	if (icmph == NULL) {
178 		if (LOG_INVALID(net, IPPROTO_ICMP))
179 			nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
180 				      "nf_ct_icmp: short packet ");
181 		return -NF_ACCEPT;
182 	}
183 
184 	/* See ip_conntrack_proto_tcp.c */
185 	if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING &&
186 	    nf_ip_checksum(skb, hooknum, dataoff, 0)) {
187 		if (LOG_INVALID(net, IPPROTO_ICMP))
188 			nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
189 				      "nf_ct_icmp: bad HW ICMP checksum ");
190 		return -NF_ACCEPT;
191 	}
192 
193 	/*
194 	 *	18 is the highest 'known' ICMP type. Anything else is a mystery
195 	 *
196 	 *	RFC 1122: 3.2.2  Unknown ICMP messages types MUST be silently
197 	 *		  discarded.
198 	 */
199 	if (icmph->type > NR_ICMP_TYPES) {
200 		if (LOG_INVALID(net, IPPROTO_ICMP))
201 			nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
202 				      "nf_ct_icmp: invalid ICMP type ");
203 		return -NF_ACCEPT;
204 	}
205 
206 	/* Need to track icmp error message? */
207 	if (icmph->type != ICMP_DEST_UNREACH &&
208 	    icmph->type != ICMP_SOURCE_QUENCH &&
209 	    icmph->type != ICMP_TIME_EXCEEDED &&
210 	    icmph->type != ICMP_PARAMETERPROB &&
211 	    icmph->type != ICMP_REDIRECT)
212 		return NF_ACCEPT;
213 
214 	return icmp_error_message(net, tmpl, skb, ctinfo, hooknum);
215 }
216 
217 #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
218 
219 #include <linux/netfilter/nfnetlink.h>
220 #include <linux/netfilter/nfnetlink_conntrack.h>
221 
icmp_tuple_to_nlattr(struct sk_buff * skb,const struct nf_conntrack_tuple * t)222 static int icmp_tuple_to_nlattr(struct sk_buff *skb,
223 				const struct nf_conntrack_tuple *t)
224 {
225 	NLA_PUT_BE16(skb, CTA_PROTO_ICMP_ID, t->src.u.icmp.id);
226 	NLA_PUT_U8(skb, CTA_PROTO_ICMP_TYPE, t->dst.u.icmp.type);
227 	NLA_PUT_U8(skb, CTA_PROTO_ICMP_CODE, t->dst.u.icmp.code);
228 
229 	return 0;
230 
231 nla_put_failure:
232 	return -1;
233 }
234 
235 static const struct nla_policy icmp_nla_policy[CTA_PROTO_MAX+1] = {
236 	[CTA_PROTO_ICMP_TYPE]	= { .type = NLA_U8 },
237 	[CTA_PROTO_ICMP_CODE]	= { .type = NLA_U8 },
238 	[CTA_PROTO_ICMP_ID]	= { .type = NLA_U16 },
239 };
240 
icmp_nlattr_to_tuple(struct nlattr * tb[],struct nf_conntrack_tuple * tuple)241 static int icmp_nlattr_to_tuple(struct nlattr *tb[],
242 				struct nf_conntrack_tuple *tuple)
243 {
244 	if (!tb[CTA_PROTO_ICMP_TYPE] ||
245 	    !tb[CTA_PROTO_ICMP_CODE] ||
246 	    !tb[CTA_PROTO_ICMP_ID])
247 		return -EINVAL;
248 
249 	tuple->dst.u.icmp.type = nla_get_u8(tb[CTA_PROTO_ICMP_TYPE]);
250 	tuple->dst.u.icmp.code = nla_get_u8(tb[CTA_PROTO_ICMP_CODE]);
251 	tuple->src.u.icmp.id = nla_get_be16(tb[CTA_PROTO_ICMP_ID]);
252 
253 	if (tuple->dst.u.icmp.type >= sizeof(invmap) ||
254 	    !invmap[tuple->dst.u.icmp.type])
255 		return -EINVAL;
256 
257 	return 0;
258 }
259 
icmp_nlattr_tuple_size(void)260 static int icmp_nlattr_tuple_size(void)
261 {
262 	return nla_policy_len(icmp_nla_policy, CTA_PROTO_MAX + 1);
263 }
264 #endif
265 
266 #ifdef CONFIG_SYSCTL
267 static struct ctl_table_header *icmp_sysctl_header;
268 static struct ctl_table icmp_sysctl_table[] = {
269 	{
270 		.procname	= "nf_conntrack_icmp_timeout",
271 		.data		= &nf_ct_icmp_timeout,
272 		.maxlen		= sizeof(unsigned int),
273 		.mode		= 0644,
274 		.proc_handler	= proc_dointvec_jiffies,
275 	},
276 	{ }
277 };
278 #ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
279 static struct ctl_table icmp_compat_sysctl_table[] = {
280 	{
281 		.procname	= "ip_conntrack_icmp_timeout",
282 		.data		= &nf_ct_icmp_timeout,
283 		.maxlen		= sizeof(unsigned int),
284 		.mode		= 0644,
285 		.proc_handler	= proc_dointvec_jiffies,
286 	},
287 	{ }
288 };
289 #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
290 #endif /* CONFIG_SYSCTL */
291 
292 struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp __read_mostly =
293 {
294 	.l3proto		= PF_INET,
295 	.l4proto		= IPPROTO_ICMP,
296 	.name			= "icmp",
297 	.pkt_to_tuple		= icmp_pkt_to_tuple,
298 	.invert_tuple		= icmp_invert_tuple,
299 	.print_tuple		= icmp_print_tuple,
300 	.packet			= icmp_packet,
301 	.new			= icmp_new,
302 	.error			= icmp_error,
303 	.destroy		= NULL,
304 	.me			= NULL,
305 #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
306 	.tuple_to_nlattr	= icmp_tuple_to_nlattr,
307 	.nlattr_tuple_size	= icmp_nlattr_tuple_size,
308 	.nlattr_to_tuple	= icmp_nlattr_to_tuple,
309 	.nla_policy		= icmp_nla_policy,
310 #endif
311 #ifdef CONFIG_SYSCTL
312 	.ctl_table_header	= &icmp_sysctl_header,
313 	.ctl_table		= icmp_sysctl_table,
314 #ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
315 	.ctl_compat_table	= icmp_compat_sysctl_table,
316 #endif
317 #endif
318 };
319