ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / net / ipv4 / netfilter / ip_nat_irc.c
1 /* IRC extension for TCP NAT alteration.
2  * (C) 2000-2001 by Harald Welte <laforge@gnumonks.org>
3  * based on a copy of RR's ip_nat_ftp.c
4  *
5  * ip_nat_irc.c,v 1.16 2001/12/06 07:42:10 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_nat_irc.o ports=port1,port2,...port<MAX_PORTS>
14  *      
15  *      please give the ports of all IRC servers You wish to connect to.
16  *      If You don't specify ports, the default will be port 6667
17  */
18
19 #include <linux/module.h>
20 #include <linux/netfilter_ipv4.h>
21 #include <linux/ip.h>
22 #include <linux/tcp.h>
23 #include <linux/kernel.h>
24 #include <net/tcp.h>
25 #include <linux/netfilter_ipv4/ip_nat.h>
26 #include <linux/netfilter_ipv4/ip_nat_helper.h>
27 #include <linux/netfilter_ipv4/ip_nat_rule.h>
28 #include <linux/netfilter_ipv4/ip_conntrack_irc.h>
29 #include <linux/netfilter_ipv4/ip_conntrack_helper.h>
30
31 #if 0
32 #define DEBUGP printk
33 #else
34 #define DEBUGP(format, args...)
35 #endif
36
37 #define MAX_PORTS 8
38 static int ports[MAX_PORTS];
39 static int ports_c;
40
41 MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
42 MODULE_DESCRIPTION("IRC (DCC) NAT helper");
43 MODULE_LICENSE("GPL");
44 #ifdef MODULE_PARM
45 MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
46 MODULE_PARM_DESC(ports, "port numbers of IRC servers");
47 #endif
48
49 /* protects irc part of conntracks */
50 DECLARE_LOCK_EXTERN(ip_irc_lock);
51
52 /* FIXME: Time out? --RR */
53
54 static unsigned int
55 irc_nat_expected(struct sk_buff **pskb,
56                  unsigned int hooknum,
57                  struct ip_conntrack *ct,
58                  struct ip_nat_info *info)
59 {
60         struct ip_nat_multi_range mr;
61         u_int32_t newdstip, newsrcip, newip;
62
63         struct ip_conntrack *master = master_ct(ct);
64
65         IP_NF_ASSERT(info);
66         IP_NF_ASSERT(master);
67
68         IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum))));
69
70         DEBUGP("nat_expected: We have a connection!\n");
71
72         newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
73         newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
74         DEBUGP("nat_expected: DCC cmd. %u.%u.%u.%u->%u.%u.%u.%u\n",
75                NIPQUAD(newsrcip), NIPQUAD(newdstip));
76
77         if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)
78                 newip = newsrcip;
79         else
80                 newip = newdstip;
81
82         DEBUGP("nat_expected: IP to %u.%u.%u.%u\n", NIPQUAD(newip));
83
84         mr.rangesize = 1;
85         /* We don't want to manip the per-protocol, just the IPs. */
86         mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
87         mr.range[0].min_ip = mr.range[0].max_ip = newip;
88
89         return ip_nat_setup_info(ct, &mr, hooknum);
90 }
91
92 static int irc_data_fixup(const struct ip_ct_irc_expect *ct_irc_info,
93                           struct ip_conntrack *ct,
94                           struct sk_buff **pskb,
95                           enum ip_conntrack_info ctinfo,
96                           struct ip_conntrack_expect *expect)
97 {
98         u_int32_t newip;
99         struct ip_conntrack_tuple t;
100         struct iphdr *iph = (*pskb)->nh.iph;
101         struct tcphdr *tcph = (void *) iph + iph->ihl * 4;
102         u_int16_t port;
103
104         /* "4294967296 65635 " */
105         char buffer[18];
106
107         MUST_BE_LOCKED(&ip_irc_lock);
108
109         DEBUGP("IRC_NAT: info (seq %u + %u) in %u\n",
110                expect->seq, ct_irc_info->len,
111                ntohl(tcph->seq));
112
113         newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
114
115         /* Alter conntrack's expectations. */
116
117         /* We can read expect here without conntrack lock, since it's
118            only set in ip_conntrack_irc, with ip_irc_lock held
119            writable */
120
121         t = expect->tuple;
122         t.dst.ip = newip;
123         for (port = ct_irc_info->port; port != 0; port++) {
124                 t.dst.u.tcp.port = htons(port);
125                 if (ip_conntrack_change_expect(expect, &t) == 0) {
126                         DEBUGP("using port %d", port);
127                         break;
128                 }
129
130         }
131         if (port == 0)
132                 return 0;
133
134         /*      strlen("\1DCC CHAT chat AAAAAAAA P\1\n")=27
135          *      strlen("\1DCC SCHAT chat AAAAAAAA P\1\n")=28
136          *      strlen("\1DCC SEND F AAAAAAAA P S\1\n")=26
137          *      strlen("\1DCC MOVE F AAAAAAAA P S\1\n")=26
138          *      strlen("\1DCC TSEND F AAAAAAAA P S\1\n")=27
139          *              AAAAAAAAA: bound addr (1.0.0.0==16777216, min 8 digits,
140          *                      255.255.255.255==4294967296, 10 digits)
141          *              P:         bound port (min 1 d, max 5d (65635))
142          *              F:         filename   (min 1 d )
143          *              S:         size       (min 1 d )
144          *              0x01, \n:  terminators
145          */
146
147         sprintf(buffer, "%u %u", ntohl(newip), port);
148         DEBUGP("ip_nat_irc: Inserting '%s' == %u.%u.%u.%u, port %u\n",
149                buffer, NIPQUAD(newip), port);
150
151         return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, 
152                                         expect->seq - ntohl(tcph->seq),
153                                         ct_irc_info->len, buffer, 
154                                         strlen(buffer));
155 }
156
157 static unsigned int help(struct ip_conntrack *ct,
158                          struct ip_conntrack_expect *exp,
159                          struct ip_nat_info *info,
160                          enum ip_conntrack_info ctinfo,
161                          unsigned int hooknum, 
162                          struct sk_buff **pskb)
163 {
164         struct iphdr *iph = (*pskb)->nh.iph;
165         struct tcphdr *tcph = (void *) iph + iph->ihl * 4;
166         unsigned int datalen;
167         int dir;
168         struct ip_ct_irc_expect *ct_irc_info;
169
170         if (!exp)
171                 DEBUGP("ip_nat_irc: no exp!!");
172                 
173         ct_irc_info = &exp->help.exp_irc_info;
174
175         /* Only mangle things once: original direction in POST_ROUTING
176            and reply direction on PRE_ROUTING. */
177         dir = CTINFO2DIR(ctinfo);
178         if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL)
179               || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) {
180                 DEBUGP("nat_irc: Not touching dir %s at hook %s\n",
181                        dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
182                        hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
183                        : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
184                        : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???");
185                 return NF_ACCEPT;
186         }
187         DEBUGP("got beyond not touching\n");
188
189         datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4;
190         LOCK_BH(&ip_irc_lock);
191         /* Check whether the whole IP/address pattern is carried in the payload */
192         if (between(exp->seq + ct_irc_info->len,
193                     ntohl(tcph->seq),
194                     ntohl(tcph->seq) + datalen)) {
195                 if (!irc_data_fixup(ct_irc_info, ct, pskb, ctinfo, exp)) {
196                         UNLOCK_BH(&ip_irc_lock);
197                         return NF_DROP;
198                 }
199         } else { 
200                 /* Half a match?  This means a partial retransmisison.
201                    It's a cracker being funky. */
202                 if (net_ratelimit()) {
203                         printk
204                             ("IRC_NAT: partial packet %u/%u in %u/%u\n",
205                              exp->seq, ct_irc_info->len,
206                              ntohl(tcph->seq),
207                              ntohl(tcph->seq) + datalen);
208                 }
209                 UNLOCK_BH(&ip_irc_lock);
210                 return NF_DROP;
211         }
212         UNLOCK_BH(&ip_irc_lock);
213
214         return NF_ACCEPT;
215 }
216
217 static struct ip_nat_helper ip_nat_irc_helpers[MAX_PORTS];
218 static char irc_names[MAX_PORTS][10];
219
220 /* This function is intentionally _NOT_ defined as  __exit, because
221  * it is needed by init() */
222 static void fini(void)
223 {
224         int i;
225
226         for (i = 0; i < ports_c; i++) {
227                 DEBUGP("ip_nat_irc: unregistering helper for port %d\n",
228                        ports[i]);
229                 ip_nat_helper_unregister(&ip_nat_irc_helpers[i]);
230         } 
231 }
232
233 static int __init init(void)
234 {
235         int ret = 0;
236         int i;
237         struct ip_nat_helper *hlpr;
238         char *tmpname;
239
240         if (ports[0] == 0) {
241                 ports[0] = IRC_PORT;
242         }
243
244         for (i = 0; (i < MAX_PORTS) && ports[i] != 0; i++) {
245                 hlpr = &ip_nat_irc_helpers[i];
246                 hlpr->tuple.dst.protonum = IPPROTO_TCP;
247                 hlpr->tuple.src.u.tcp.port = htons(ports[i]);
248                 hlpr->mask.src.u.tcp.port = 0xFFFF;
249                 hlpr->mask.dst.protonum = 0xFFFF;
250                 hlpr->help = help;
251                 hlpr->flags = 0;
252                 hlpr->me = THIS_MODULE;
253                 hlpr->expect = irc_nat_expected;
254
255                 tmpname = &irc_names[i][0];
256                 if (ports[i] == IRC_PORT)
257                         sprintf(tmpname, "irc");
258                 else
259                         sprintf(tmpname, "irc-%d", i);
260                 hlpr->name = tmpname;
261
262                 DEBUGP
263                     ("ip_nat_irc: Trying to register helper for port %d: name %s\n",
264                      ports[i], hlpr->name);
265                 ret = ip_nat_helper_register(hlpr);
266
267                 if (ret) {
268                         printk
269                             ("ip_nat_irc: error registering helper for port %d\n",
270                              ports[i]);
271                         fini();
272                         return 1;
273                 }
274                 ports_c++;
275         }
276         return ret;
277 }
278
279 NEEDS_CONNTRACK(irc);
280
281 module_init(init);
282 module_exit(fini);