[NETFILTER]: Remove changelogs and CVS IDs
[linux-2.6.git] / net / ipv4 / netfilter / nf_conntrack_proto_icmp.c
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
22 static unsigned long nf_ct_icmp_timeout __read_mostly = 30*HZ;
23
24 #if 0
25 #define DEBUGP printk
26 #else
27 #define DEBUGP(format, args...)
28 #endif
29
30 static int icmp_pkt_to_tuple(const struct sk_buff *skb,
31                              unsigned int dataoff,
32                              struct nf_conntrack_tuple *tuple)
33 {
34         struct icmphdr _hdr, *hp;
35
36         hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
37         if (hp == NULL)
38                 return 0;
39
40         tuple->dst.u.icmp.type = hp->type;
41         tuple->src.u.icmp.id = hp->un.echo.id;
42         tuple->dst.u.icmp.code = hp->code;
43
44         return 1;
45 }
46
47 /* Add 1; spaces filled with 0. */
48 static const u_int8_t invmap[] = {
49         [ICMP_ECHO] = ICMP_ECHOREPLY + 1,
50         [ICMP_ECHOREPLY] = ICMP_ECHO + 1,
51         [ICMP_TIMESTAMP] = ICMP_TIMESTAMPREPLY + 1,
52         [ICMP_TIMESTAMPREPLY] = ICMP_TIMESTAMP + 1,
53         [ICMP_INFO_REQUEST] = ICMP_INFO_REPLY + 1,
54         [ICMP_INFO_REPLY] = ICMP_INFO_REQUEST + 1,
55         [ICMP_ADDRESS] = ICMP_ADDRESSREPLY + 1,
56         [ICMP_ADDRESSREPLY] = ICMP_ADDRESS + 1
57 };
58
59 static int icmp_invert_tuple(struct nf_conntrack_tuple *tuple,
60                              const struct nf_conntrack_tuple *orig)
61 {
62         if (orig->dst.u.icmp.type >= sizeof(invmap)
63             || !invmap[orig->dst.u.icmp.type])
64                 return 0;
65
66         tuple->src.u.icmp.id = orig->src.u.icmp.id;
67         tuple->dst.u.icmp.type = invmap[orig->dst.u.icmp.type] - 1;
68         tuple->dst.u.icmp.code = orig->dst.u.icmp.code;
69         return 1;
70 }
71
72 /* Print out the per-protocol part of the tuple. */
73 static int icmp_print_tuple(struct seq_file *s,
74                             const struct nf_conntrack_tuple *tuple)
75 {
76         return seq_printf(s, "type=%u code=%u id=%u ",
77                           tuple->dst.u.icmp.type,
78                           tuple->dst.u.icmp.code,
79                           ntohs(tuple->src.u.icmp.id));
80 }
81
82 /* Print out the private part of the conntrack. */
83 static int icmp_print_conntrack(struct seq_file *s,
84                                 const struct nf_conn *conntrack)
85 {
86         return 0;
87 }
88
89 /* Returns verdict for packet, or -1 for invalid. */
90 static int icmp_packet(struct nf_conn *ct,
91                        const struct sk_buff *skb,
92                        unsigned int dataoff,
93                        enum ip_conntrack_info ctinfo,
94                        int pf,
95                        unsigned int hooknum)
96 {
97         /* Try to delete connection immediately after all replies:
98            won't actually vanish as we still have skb, and del_timer
99            means this will only run once even if count hits zero twice
100            (theoretically possible with SMP) */
101         if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) {
102                 if (atomic_dec_and_test(&ct->proto.icmp.count)
103                     && del_timer(&ct->timeout))
104                         ct->timeout.function((unsigned long)ct);
105         } else {
106                 atomic_inc(&ct->proto.icmp.count);
107                 nf_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, skb);
108                 nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_icmp_timeout);
109         }
110
111         return NF_ACCEPT;
112 }
113
114 /* Called when a new connection for this protocol found. */
115 static int icmp_new(struct nf_conn *conntrack,
116                     const struct sk_buff *skb, unsigned int dataoff)
117 {
118         static const u_int8_t valid_new[] = {
119                 [ICMP_ECHO] = 1,
120                 [ICMP_TIMESTAMP] = 1,
121                 [ICMP_INFO_REQUEST] = 1,
122                 [ICMP_ADDRESS] = 1
123         };
124
125         if (conntrack->tuplehash[0].tuple.dst.u.icmp.type >= sizeof(valid_new)
126             || !valid_new[conntrack->tuplehash[0].tuple.dst.u.icmp.type]) {
127                 /* Can't create a new ICMP `conn' with this. */
128                 DEBUGP("icmp: can't create new conn with type %u\n",
129                        conntrack->tuplehash[0].tuple.dst.u.icmp.type);
130                 NF_CT_DUMP_TUPLE(&conntrack->tuplehash[0].tuple);
131                 return 0;
132         }
133         atomic_set(&conntrack->proto.icmp.count, 0);
134         return 1;
135 }
136
137 extern struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4;
138 /* Returns conntrack if it dealt with ICMP, and filled in skb fields */
139 static int
140 icmp_error_message(struct sk_buff *skb,
141                  enum ip_conntrack_info *ctinfo,
142                  unsigned int hooknum)
143 {
144         struct nf_conntrack_tuple innertuple, origtuple;
145         struct {
146                 struct icmphdr icmp;
147                 struct iphdr ip;
148         } _in, *inside;
149         struct nf_conntrack_l4proto *innerproto;
150         struct nf_conntrack_tuple_hash *h;
151         int dataoff;
152
153         NF_CT_ASSERT(skb->nfct == NULL);
154
155         /* Not enough header? */
156         inside = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_in), &_in);
157         if (inside == NULL)
158                 return -NF_ACCEPT;
159
160         /* Ignore ICMP's containing fragments (shouldn't happen) */
161         if (inside->ip.frag_off & htons(IP_OFFSET)) {
162                 DEBUGP("icmp_error_message: fragment of proto %u\n",
163                        inside->ip.protocol);
164                 return -NF_ACCEPT;
165         }
166
167         /* rcu_read_lock()ed by nf_hook_slow */
168         innerproto = __nf_ct_l4proto_find(PF_INET, inside->ip.protocol);
169
170         dataoff = ip_hdrlen(skb) + sizeof(inside->icmp);
171         /* Are they talking about one of our connections? */
172         if (!nf_ct_get_tuple(skb, dataoff, dataoff + inside->ip.ihl*4, PF_INET,
173                              inside->ip.protocol, &origtuple,
174                              &nf_conntrack_l3proto_ipv4, innerproto)) {
175                 DEBUGP("icmp_error_message: ! get_tuple p=%u",
176                        inside->ip.protocol);
177                 return -NF_ACCEPT;
178         }
179
180         /* Ordinarily, we'd expect the inverted tupleproto, but it's
181            been preserved inside the ICMP. */
182         if (!nf_ct_invert_tuple(&innertuple, &origtuple,
183                                 &nf_conntrack_l3proto_ipv4, innerproto)) {
184                 DEBUGP("icmp_error_message: no match\n");
185                 return -NF_ACCEPT;
186         }
187
188         *ctinfo = IP_CT_RELATED;
189
190         h = nf_conntrack_find_get(&innertuple, NULL);
191         if (!h) {
192                 /* Locally generated ICMPs will match inverted if they
193                    haven't been SNAT'ed yet */
194                 /* FIXME: NAT code has to handle half-done double NAT --RR */
195                 if (hooknum == NF_IP_LOCAL_OUT)
196                         h = nf_conntrack_find_get(&origtuple, NULL);
197
198                 if (!h) {
199                         DEBUGP("icmp_error_message: no match\n");
200                         return -NF_ACCEPT;
201                 }
202
203                 /* Reverse direction from that found */
204                 if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY)
205                         *ctinfo += IP_CT_IS_REPLY;
206         } else {
207                 if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY)
208                         *ctinfo += IP_CT_IS_REPLY;
209         }
210
211         /* Update skb to refer to this connection */
212         skb->nfct = &nf_ct_tuplehash_to_ctrack(h)->ct_general;
213         skb->nfctinfo = *ctinfo;
214         return -NF_ACCEPT;
215 }
216
217 /* Small and modified version of icmp_rcv */
218 static int
219 icmp_error(struct sk_buff *skb, unsigned int dataoff,
220            enum ip_conntrack_info *ctinfo, int pf, unsigned int hooknum)
221 {
222         struct icmphdr _ih, *icmph;
223
224         /* Not enough header? */
225         icmph = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_ih), &_ih);
226         if (icmph == NULL) {
227                 if (LOG_INVALID(IPPROTO_ICMP))
228                         nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
229                                       "nf_ct_icmp: short packet ");
230                 return -NF_ACCEPT;
231         }
232
233         /* See ip_conntrack_proto_tcp.c */
234         if (nf_conntrack_checksum && hooknum == NF_IP_PRE_ROUTING &&
235             nf_ip_checksum(skb, hooknum, dataoff, 0)) {
236                 if (LOG_INVALID(IPPROTO_ICMP))
237                         nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
238                                       "nf_ct_icmp: bad HW ICMP checksum ");
239                 return -NF_ACCEPT;
240         }
241
242         /*
243          *      18 is the highest 'known' ICMP type. Anything else is a mystery
244          *
245          *      RFC 1122: 3.2.2  Unknown ICMP messages types MUST be silently
246          *                discarded.
247          */
248         if (icmph->type > NR_ICMP_TYPES) {
249                 if (LOG_INVALID(IPPROTO_ICMP))
250                         nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
251                                       "nf_ct_icmp: invalid ICMP type ");
252                 return -NF_ACCEPT;
253         }
254
255         /* Need to track icmp error message? */
256         if (icmph->type != ICMP_DEST_UNREACH
257             && icmph->type != ICMP_SOURCE_QUENCH
258             && icmph->type != ICMP_TIME_EXCEEDED
259             && icmph->type != ICMP_PARAMETERPROB
260             && icmph->type != ICMP_REDIRECT)
261                 return NF_ACCEPT;
262
263         return icmp_error_message(skb, ctinfo, hooknum);
264 }
265
266 #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
267
268 #include <linux/netfilter/nfnetlink.h>
269 #include <linux/netfilter/nfnetlink_conntrack.h>
270
271 static int icmp_tuple_to_nfattr(struct sk_buff *skb,
272                                 const struct nf_conntrack_tuple *t)
273 {
274         NFA_PUT(skb, CTA_PROTO_ICMP_ID, sizeof(u_int16_t),
275                 &t->src.u.icmp.id);
276         NFA_PUT(skb, CTA_PROTO_ICMP_TYPE, sizeof(u_int8_t),
277                 &t->dst.u.icmp.type);
278         NFA_PUT(skb, CTA_PROTO_ICMP_CODE, sizeof(u_int8_t),
279                 &t->dst.u.icmp.code);
280
281         return 0;
282
283 nfattr_failure:
284         return -1;
285 }
286
287 static const size_t cta_min_proto[CTA_PROTO_MAX] = {
288         [CTA_PROTO_ICMP_TYPE-1] = sizeof(u_int8_t),
289         [CTA_PROTO_ICMP_CODE-1] = sizeof(u_int8_t),
290         [CTA_PROTO_ICMP_ID-1]   = sizeof(u_int16_t)
291 };
292
293 static int icmp_nfattr_to_tuple(struct nfattr *tb[],
294                                 struct nf_conntrack_tuple *tuple)
295 {
296         if (!tb[CTA_PROTO_ICMP_TYPE-1]
297             || !tb[CTA_PROTO_ICMP_CODE-1]
298             || !tb[CTA_PROTO_ICMP_ID-1])
299                 return -EINVAL;
300
301         if (nfattr_bad_size(tb, CTA_PROTO_MAX, cta_min_proto))
302                 return -EINVAL;
303
304         tuple->dst.u.icmp.type =
305                         *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_TYPE-1]);
306         tuple->dst.u.icmp.code =
307                         *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_CODE-1]);
308         tuple->src.u.icmp.id =
309                         *(__be16 *)NFA_DATA(tb[CTA_PROTO_ICMP_ID-1]);
310
311         if (tuple->dst.u.icmp.type >= sizeof(invmap)
312             || !invmap[tuple->dst.u.icmp.type])
313                 return -EINVAL;
314
315         return 0;
316 }
317 #endif
318
319 #ifdef CONFIG_SYSCTL
320 static struct ctl_table_header *icmp_sysctl_header;
321 static struct ctl_table icmp_sysctl_table[] = {
322         {
323                 .ctl_name       = NET_NF_CONNTRACK_ICMP_TIMEOUT,
324                 .procname       = "nf_conntrack_icmp_timeout",
325                 .data           = &nf_ct_icmp_timeout,
326                 .maxlen         = sizeof(unsigned int),
327                 .mode           = 0644,
328                 .proc_handler   = &proc_dointvec_jiffies,
329         },
330         {
331                 .ctl_name = 0
332         }
333 };
334 #ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
335 static struct ctl_table icmp_compat_sysctl_table[] = {
336         {
337                 .ctl_name       = NET_IPV4_NF_CONNTRACK_ICMP_TIMEOUT,
338                 .procname       = "ip_conntrack_icmp_timeout",
339                 .data           = &nf_ct_icmp_timeout,
340                 .maxlen         = sizeof(unsigned int),
341                 .mode           = 0644,
342                 .proc_handler   = &proc_dointvec_jiffies,
343         },
344         {
345                 .ctl_name = 0
346         }
347 };
348 #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
349 #endif /* CONFIG_SYSCTL */
350
351 struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp =
352 {
353         .l3proto                = PF_INET,
354         .l4proto                = IPPROTO_ICMP,
355         .name                   = "icmp",
356         .pkt_to_tuple           = icmp_pkt_to_tuple,
357         .invert_tuple           = icmp_invert_tuple,
358         .print_tuple            = icmp_print_tuple,
359         .print_conntrack        = icmp_print_conntrack,
360         .packet                 = icmp_packet,
361         .new                    = icmp_new,
362         .error                  = icmp_error,
363         .destroy                = NULL,
364         .me                     = NULL,
365 #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
366         .tuple_to_nfattr        = icmp_tuple_to_nfattr,
367         .nfattr_to_tuple        = icmp_nfattr_to_tuple,
368 #endif
369 #ifdef CONFIG_SYSCTL
370         .ctl_table_header       = &icmp_sysctl_header,
371         .ctl_table              = icmp_sysctl_table,
372 #ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
373         .ctl_compat_table       = icmp_compat_sysctl_table,
374 #endif
375 #endif
376 };
377 EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_icmp);