1/* IRC extension for IP connection tracking, Version 1.21
2 * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
3 * based on RR's ip_conntrack_ftp.c
4 *
5 * ip_conntrack_irc.c,v 1.21 2002/02/05 14:49:26 laforge Exp
6 *
7 *      This program is free software; you can redistribute it and/or
8 *      modify it under the terms of the GNU General Public License
9 *      as published by the Free Software Foundation; either version
10 *      2 of the License, or (at your option) any later version.
11 **
12 *	Module load syntax:
13 * 	insmod ip_conntrack_irc.o ports=port1,port2,...port<MAX_PORTS>
14 *			    max_dcc_channels=n dcc_timeout=secs
15 *
16 * 	please give the ports of all IRC servers You wish to connect to.
17 *	If You don't specify ports, the default will be port 6667.
18 *	With max_dcc_channels you can define the maximum number of not
19 *	yet answered DCC channels per IRC session (default 8).
20 *	With dcc_timeout you can specify how long the system waits for
21 *	an expected DCC channel (default 300 seconds).
22 *
23 */
24
25#include <linux/config.h>
26#include <linux/module.h>
27#include <linux/netfilter.h>
28#include <linux/ip.h>
29#include <net/checksum.h>
30#include <net/tcp.h>
31
32#include <linux/netfilter_ipv4/lockhelp.h>
33#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
34#include <linux/netfilter_ipv4/ip_conntrack_irc.h>
35
36#define MAX_PORTS 8
37static int ports[MAX_PORTS];
38static int ports_c = 0;
39static int max_dcc_channels = 8;
40static unsigned int dcc_timeout = 300;
41
42MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
43MODULE_DESCRIPTION("IRC (DCC) connection tracking module");
44MODULE_LICENSE("GPL");
45#ifdef MODULE_PARM
46MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
47MODULE_PARM_DESC(ports, "port numbers of IRC servers");
48MODULE_PARM(max_dcc_channels, "i");
49MODULE_PARM_DESC(max_dcc_channels, "max number of expected DCC channels per IRC session");
50MODULE_PARM(dcc_timeout, "i");
51MODULE_PARM_DESC(dcc_timeout, "timeout on for unestablished DCC channels");
52#endif
53
54#define NUM_DCCPROTO 	5
55struct dccproto dccprotos[NUM_DCCPROTO] = {
56	{"SEND ", 5},
57	{"CHAT ", 5},
58	{"MOVE ", 5},
59	{"TSEND ", 6},
60	{"SCHAT ", 6}
61};
62#define MAXMATCHLEN	6
63
64DECLARE_LOCK(ip_irc_lock);
65struct module *ip_conntrack_irc = THIS_MODULE;
66
67#define DEBUGP(format, args...)
68
69int parse_dcc(char *data, char *data_end, u_int32_t * ip, u_int16_t * port,
70	      char **ad_beg_p, char **ad_end_p)
71/* tries to get the ip_addr and port out of a dcc command
72   return value: -1 on failure, 0 on success
73	data		pointer to first byte of DCC command data
74	data_end	pointer to last byte of dcc command data
75	ip		returns parsed ip of dcc command
76	port		returns parsed port of dcc command
77	ad_beg_p	returns pointer to first byte of addr data
78	ad_end_p	returns pointer to last byte of addr data */
79{
80
81	/* at least 12: "AAAAAAAA P\1\n" */
82	while (*data++ != ' ')
83		if (data > data_end - 12)
84			return -1;
85
86	*ad_beg_p = data;
87	*ip = simple_strtoul(data, &data, 10);
88
89	/* skip blanks between ip and port */
90	while (*data == ' ')
91		data++;
92
93
94	*port = simple_strtoul(data, &data, 10);
95	*ad_end_p = data;
96
97	return 0;
98}
99
100
101static int help(const struct iphdr *iph, size_t len,
102		struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
103{
104	/* tcplen not negative guarenteed by ip_conntrack_tcp.c */
105	struct tcphdr *tcph = (void *) iph + iph->ihl * 4;
106	const char *data = (const char *) tcph + tcph->doff * 4;
107	const char *_data = data;
108	char *data_limit;
109	u_int32_t tcplen = len - iph->ihl * 4;
110	u_int32_t datalen = tcplen - tcph->doff * 4;
111	int dir = CTINFO2DIR(ctinfo);
112	struct ip_conntrack_expect expect, *exp = &expect;
113	struct ip_ct_irc_expect *exp_irc_info = &exp->help.exp_irc_info;
114
115	u_int32_t dcc_ip;
116	u_int16_t dcc_port;
117	int i;
118	char *addr_beg_p, *addr_end_p;
119
120	DEBUGP("entered\n");
121
122	/* If packet is coming from IRC server */
123	if (dir == IP_CT_DIR_REPLY)
124		return NF_ACCEPT;
125
126	/* Until there's been traffic both ways, don't look in packets. */
127	if (ctinfo != IP_CT_ESTABLISHED
128	    && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) {
129		DEBUGP("Conntrackinfo = %u\n", ctinfo);
130		return NF_ACCEPT;
131	}
132
133	/* Not whole TCP header? */
134	if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4) {
135		DEBUGP("tcplen = %u\n", (unsigned) tcplen);
136		return NF_ACCEPT;
137	}
138
139	/* Checksum invalid?  Ignore. */
140	if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
141			 csum_partial((char *) tcph, tcplen, 0))) {
142		DEBUGP("bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
143		     tcph, tcplen, NIPQUAD(iph->saddr),
144		     NIPQUAD(iph->daddr));
145		return NF_ACCEPT;
146	}
147
148	data_limit = (char *) data + datalen;
149	while (data < (data_limit - (22 + MAXMATCHLEN))) {
150		if (memcmp(data, "\1DCC ", 5)) {
151			data++;
152			continue;
153		}
154
155		data += 5;
156
157		DEBUGP("DCC found in master %u.%u.%u.%u:%u %u.%u.%u.%u:%u...\n",
158			NIPQUAD(iph->saddr), ntohs(tcph->source),
159			NIPQUAD(iph->daddr), ntohs(tcph->dest));
160
161		for (i = 0; i < NUM_DCCPROTO; i++) {
162			if (memcmp(data, dccprotos[i].match,
163				   dccprotos[i].matchlen)) {
164				/* no match */
165				continue;
166			}
167
168			DEBUGP("DCC %s detected\n", dccprotos[i].match);
169			data += dccprotos[i].matchlen;
170			if (parse_dcc((char *) data, data_limit, &dcc_ip,
171				       &dcc_port, &addr_beg_p, &addr_end_p)) {
172				/* unable to parse */
173				DEBUGP("unable to parse dcc command\n");
174				continue;
175			}
176			DEBUGP("DCC bound ip/port: %u.%u.%u.%u:%u\n",
177				HIPQUAD(dcc_ip), dcc_port);
178
179			if (ct->tuplehash[dir].tuple.src.ip != htonl(dcc_ip)) {
180				if (net_ratelimit())
181					printk(KERN_WARNING
182						"Forged DCC command from "
183						"%u.%u.%u.%u: %u.%u.%u.%u:%u\n",
184				NIPQUAD(ct->tuplehash[dir].tuple.src.ip),
185						HIPQUAD(dcc_ip), dcc_port);
186
187				continue;
188			}
189
190			memset(&expect, 0, sizeof(expect));
191
192			LOCK_BH(&ip_irc_lock);
193
194			/* save position of address in dcc string,
195			 * neccessary for NAT */
196			DEBUGP("tcph->seq = %u\n", tcph->seq);
197			exp->seq = ntohl(tcph->seq) + (addr_beg_p - _data);
198			exp_irc_info->len = (addr_end_p - addr_beg_p);
199			exp_irc_info->port = dcc_port;
200			DEBUGP("wrote info seq=%u (ofs=%u), len=%d\n",
201				exp->seq, (addr_end_p - _data), exp_irc_info->len);
202
203			exp->tuple = ((struct ip_conntrack_tuple)
204				{ { 0, { 0 } },
205				  { htonl(dcc_ip), { htons(dcc_port) },
206				    IPPROTO_TCP }});
207			exp->mask = ((struct ip_conntrack_tuple)
208				{ { 0, { 0 } },
209				  { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }});
210
211			exp->expectfn = NULL;
212
213			DEBUGP("expect_related %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n",
214				NIPQUAD(exp->tuple.src.ip),
215				ntohs(exp->tuple.src.u.tcp.port),
216				NIPQUAD(exp->tuple.dst.ip),
217				ntohs(exp->tuple.dst.u.tcp.port));
218
219			ip_conntrack_expect_related(ct, &expect);
220			UNLOCK_BH(&ip_irc_lock);
221
222			return NF_ACCEPT;
223		} /* for .. NUM_DCCPROTO */
224	} /* while data < ... */
225
226	return NF_ACCEPT;
227}
228
229static struct ip_conntrack_helper irc_helpers[MAX_PORTS];
230static char irc_names[MAX_PORTS][10];
231
232static void fini(void);
233
234static int __init init(void)
235{
236	int i, ret;
237	struct ip_conntrack_helper *hlpr;
238	char *tmpname;
239
240	if (max_dcc_channels < 1) {
241		printk("ip_conntrack_irc: max_dcc_channels must be a positive integer\n");
242		return -EBUSY;
243	}
244	if (dcc_timeout < 0) {
245		printk("ip_conntrack_irc: dcc_timeout must be a positive integer\n");
246		return -EBUSY;
247	}
248
249	/* If no port given, default to standard irc port */
250	if (ports[0] == 0)
251		ports[0] = IRC_PORT;
252
253	for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
254		hlpr = &irc_helpers[i];
255		memset(hlpr, 0,
256		       sizeof(struct ip_conntrack_helper));
257		hlpr->tuple.src.u.tcp.port = htons(ports[i]);
258		hlpr->tuple.dst.protonum = IPPROTO_TCP;
259		hlpr->mask.src.u.tcp.port = 0xFFFF;
260		hlpr->mask.dst.protonum = 0xFFFF;
261		hlpr->max_expected = max_dcc_channels;
262		hlpr->timeout = dcc_timeout;
263		hlpr->flags = IP_CT_HELPER_F_REUSE_EXPECT;
264		hlpr->me = ip_conntrack_irc;
265		hlpr->help = help;
266
267		tmpname = &irc_names[i][0];
268		if (ports[i] == IRC_PORT)
269			sprintf(tmpname, "irc");
270		else
271			sprintf(tmpname, "irc-%d", i);
272		hlpr->name = tmpname;
273
274		DEBUGP("port #%d: %d\n", i, ports[i]);
275
276		ret = ip_conntrack_helper_register(hlpr);
277
278		if (ret) {
279			printk("ip_conntrack_irc: ERROR registering port %d\n",
280				ports[i]);
281			fini();
282			return -EBUSY;
283		}
284		ports_c++;
285	}
286	return 0;
287}
288
289/* This function is intentionally _NOT_ defined as __exit, because
290 * it is needed by the init function */
291static void fini(void)
292{
293	int i;
294	for (i = 0; i < ports_c; i++) {
295		DEBUGP("unregistering port %d\n",
296		       ports[i]);
297		ip_conntrack_helper_unregister(&irc_helpers[i]);
298	}
299}
300
301#ifdef CONFIG_IP_NF_NAT_NEEDED
302EXPORT_SYMBOL(ip_irc_lock);
303#endif
304
305module_init(init);
306module_exit(fini);
307