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