1#include <linux/types.h>
2#include <linux/sched.h>
3#include <linux/timer.h>
4#include <linux/netfilter.h>
5#include <linux/in.h>
6#include <linux/icmp.h>
7#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
8
9#define ICMP_TIMEOUT (30*HZ)
10
11#define DEBUGP(format, args...)
12
13static int icmp_pkt_to_tuple(const void *datah, size_t datalen,
14			     struct ip_conntrack_tuple *tuple)
15{
16	const struct icmphdr *hdr = datah;
17
18	tuple->dst.u.icmp.type = hdr->type;
19	tuple->src.u.icmp.id = hdr->un.echo.id;
20	tuple->dst.u.icmp.code = hdr->code;
21
22	return 1;
23}
24
25static int icmp_invert_tuple(struct ip_conntrack_tuple *tuple,
26			     const struct ip_conntrack_tuple *orig)
27{
28	/* Add 1; spaces filled with 0. */
29	static u_int8_t invmap[]
30		= { [ICMP_ECHO] = ICMP_ECHOREPLY + 1,
31		    [ICMP_ECHOREPLY] = ICMP_ECHO + 1,
32		    [ICMP_TIMESTAMP] = ICMP_TIMESTAMPREPLY + 1,
33		    [ICMP_TIMESTAMPREPLY] = ICMP_TIMESTAMP + 1,
34		    [ICMP_INFO_REQUEST] = ICMP_INFO_REPLY + 1,
35		    [ICMP_INFO_REPLY] = ICMP_INFO_REQUEST + 1,
36		    [ICMP_ADDRESS] = ICMP_ADDRESSREPLY + 1,
37		    [ICMP_ADDRESSREPLY] = ICMP_ADDRESS + 1};
38
39	if (orig->dst.u.icmp.type >= sizeof(invmap)
40	    || !invmap[orig->dst.u.icmp.type])
41		return 0;
42
43	tuple->src.u.icmp.id = orig->src.u.icmp.id;
44	tuple->dst.u.icmp.type = invmap[orig->dst.u.icmp.type] - 1;
45	tuple->dst.u.icmp.code = orig->dst.u.icmp.code;
46	return 1;
47}
48
49/* Print out the per-protocol part of the tuple. */
50static unsigned int icmp_print_tuple(char *buffer,
51				     const struct ip_conntrack_tuple *tuple)
52{
53	return sprintf(buffer, "type=%u code=%u id=%u ",
54		       tuple->dst.u.icmp.type,
55		       tuple->dst.u.icmp.code,
56		       ntohs(tuple->src.u.icmp.id));
57}
58
59/* Print out the private part of the conntrack. */
60static unsigned int icmp_print_conntrack(char *buffer,
61				     const struct ip_conntrack *conntrack)
62{
63	return 0;
64}
65
66/* Returns verdict for packet, or -1 for invalid. */
67static int icmp_packet(struct ip_conntrack *ct,
68		       struct iphdr *iph, size_t len,
69		       enum ip_conntrack_info ctinfo)
70{
71	/* Try to delete connection immediately after all replies:
72           won't actually vanish as we still have skb, and del_timer
73           means this will only run once even if count hits zero twice
74           (theoretically possible with SMP) */
75	if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) {
76		if (atomic_dec_and_test(&ct->proto.icmp.count)
77		    && del_timer(&ct->timeout))
78			ct->timeout.function((unsigned long)ct);
79	} else {
80		atomic_inc(&ct->proto.icmp.count);
81		ip_ct_refresh(ct, ICMP_TIMEOUT);
82	}
83
84	return NF_ACCEPT;
85}
86
87/* Called when a new connection for this protocol found. */
88static int icmp_new(struct ip_conntrack *conntrack,
89		    struct iphdr *iph, size_t len)
90{
91	static u_int8_t valid_new[]
92		= { [ICMP_ECHO] = 1,
93		    [ICMP_TIMESTAMP] = 1,
94		    [ICMP_INFO_REQUEST] = 1,
95		    [ICMP_ADDRESS] = 1 };
96
97	if (conntrack->tuplehash[0].tuple.dst.u.icmp.type >= sizeof(valid_new)
98	    || !valid_new[conntrack->tuplehash[0].tuple.dst.u.icmp.type]) {
99		/* Can't create a new ICMP `conn' with this. */
100		DEBUGP("icmp: can't create new conn with type %u\n",
101		       conntrack->tuplehash[0].tuple.dst.u.icmp.type);
102		DUMP_TUPLE(&conntrack->tuplehash[0].tuple);
103		return 0;
104	}
105	atomic_set(&conntrack->proto.icmp.count, 0);
106	return 1;
107}
108
109struct ip_conntrack_protocol ip_conntrack_protocol_icmp
110= { { NULL, NULL }, IPPROTO_ICMP, "icmp",
111    icmp_pkt_to_tuple, icmp_invert_tuple, icmp_print_tuple,
112    icmp_print_conntrack, icmp_packet, icmp_new, NULL, NULL, NULL };
113