Tomato 1.25
[tomato.git] / release / src / linux / linux / net / ipv4 / netfilter / ip_nat_mms.c
blob4d42e6cb54e753213563e1fa7ecaaff339aa723f
1 /* MMS extension for TCP NAT alteration.
2 * (C) 2002 by Filip Sneppe <filip.sneppe@cronos.be>
3 * based on ip_nat_ftp.c and ip_nat_irc.c
5 * ip_nat_mms.c v0.3 2002-09-22
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.
12 * Module load syntax:
13 * insmod ip_nat_mms.o ports=port1,port2,...port<MAX_PORTS>
15 * Please give the ports of all MMS servers You wish to connect to.
16 * If you don't specify ports, the default will be TCP port 1755.
18 * More info on MMS protocol, firewalls and NAT:
19 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/MMSFirewall.asp
20 * http://www.microsoft.com/windows/windowsmedia/serve/firewall.asp
22 * The SDP project people are reverse-engineering MMS:
23 * http://get.to/sdp
27 #include <linux/module.h>
28 #include <linux/netfilter_ipv4.h>
29 #include <linux/ip.h>
30 #include <linux/tcp.h>
31 #include <net/tcp.h>
32 #include <linux/netfilter_ipv4/ip_nat.h>
33 #include <linux/netfilter_ipv4/ip_nat_helper.h>
34 #include <linux/netfilter_ipv4/ip_nat_rule.h>
35 #include <linux/netfilter_ipv4/ip_conntrack_mms.h>
36 #include <linux/netfilter_ipv4/ip_conntrack_helper.h>
38 #define DEBUGP(format, args...)
39 #define DUMP_BYTES(address, counter)
41 #define MAX_PORTS 8
42 static int ports[MAX_PORTS];
43 static int ports_c = 0;
45 #ifdef MODULE_PARM
46 MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
47 #endif
49 MODULE_AUTHOR("Filip Sneppe <filip.sneppe@cronos.be>");
50 MODULE_DESCRIPTION("Microsoft Windows Media Services (MMS) NAT module");
51 MODULE_LICENSE("GPL");
53 DECLARE_LOCK_EXTERN(ip_mms_lock);
56 static int mms_data_fixup(const struct ip_ct_mms_expect *ct_mms_info,
57 struct ip_conntrack *ct,
58 struct sk_buff **pskb,
59 enum ip_conntrack_info ctinfo,
60 struct ip_conntrack_expect *expect)
62 u_int32_t newip;
63 struct ip_conntrack_tuple t;
64 struct iphdr *iph = (*pskb)->nh.iph;
65 struct tcphdr *tcph = (void *) iph + iph->ihl * 4;
66 char *data = (char *)tcph + tcph->doff * 4;
67 int i, j, k, port;
68 u_int16_t mms_proto;
70 u_int32_t *mms_chunkLenLV = (u_int32_t *)(data + MMS_SRV_CHUNKLENLV_OFFSET);
71 u_int32_t *mms_chunkLenLM = (u_int32_t *)(data + MMS_SRV_CHUNKLENLM_OFFSET);
72 u_int32_t *mms_messageLength = (u_int32_t *)(data + MMS_SRV_MESSAGELENGTH_OFFSET);
74 int zero_padding;
76 char buffer[28]; /* "\\255.255.255.255\UDP\65635" * 2 (for unicode) */
77 char unicode_buffer[75]; /* 27*2 (unicode) + 20 + 1 */
78 char proto_string[6];
80 MUST_BE_LOCKED(&ip_mms_lock);
82 /* what was the protocol again ? */
83 mms_proto = expect->tuple.dst.protonum;
84 sprintf(proto_string, "%u", mms_proto);
86 DEBUGP("ip_nat_mms: mms_data_fixup: info (seq %u + %u) in %u, proto %s\n",
87 expect->seq, ct_mms_info->len, ntohl(tcph->seq),
88 mms_proto == IPPROTO_UDP ? "UDP"
89 : mms_proto == IPPROTO_TCP ? "TCP":proto_string);
91 newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
93 /* Alter conntrack's expectations. */
94 t = expect->tuple;
95 t.dst.ip = newip;
96 for (port = ct_mms_info->port; port != 0; port++) {
97 t.dst.u.tcp.port = htons(port);
98 if (ip_conntrack_change_expect(expect, &t) == 0) {
99 DEBUGP("ip_nat_mms: mms_data_fixup: using port %d\n", port);
100 break;
104 if(port == 0)
105 return 0;
107 sprintf(buffer, "\\\\%u.%u.%u.%u\\%s\\%u",
108 NIPQUAD(newip),
109 expect->tuple.dst.protonum == IPPROTO_UDP ? "UDP"
110 : expect->tuple.dst.protonum == IPPROTO_TCP ? "TCP":proto_string,
111 port);
112 DEBUGP("ip_nat_mms: new unicode string=%s\n", buffer);
114 memset(unicode_buffer, 0, sizeof(char)*75);
116 for (i=0; i<strlen(buffer); ++i)
117 *(unicode_buffer+i*2)=*(buffer+i);
119 DEBUGP("ip_nat_mms: mms_data_fixup: padding: %u len: %u\n", ct_mms_info->padding, ct_mms_info->len);
120 DEBUGP("ip_nat_mms: mms_data_fixup: offset: %u\n", MMS_SRV_UNICODE_STRING_OFFSET+ct_mms_info->len);
121 DUMP_BYTES(data+MMS_SRV_UNICODE_STRING_OFFSET, 60);
123 /* add end of packet to it */
124 for (j=0; j<ct_mms_info->padding; ++j) {
125 DEBUGP("ip_nat_mms: mms_data_fixup: i=%u j=%u byte=%u\n",
126 i, j, (u8)*(data+MMS_SRV_UNICODE_STRING_OFFSET+ct_mms_info->len+j));
127 *(unicode_buffer+i*2+j) = *(data+MMS_SRV_UNICODE_STRING_OFFSET+ct_mms_info->len+j);
130 /* pad with zeroes at the end ? see explanation of weird math below */
131 zero_padding = (8-(strlen(buffer)*2 + ct_mms_info->padding + 4)%8)%8;
132 for (k=0; k<zero_padding; ++k)
133 *(unicode_buffer+i*2+j+k)= (char)0;
135 DEBUGP("ip_nat_mms: mms_data_fixup: zero_padding = %u\n", zero_padding);
136 DEBUGP("ip_nat_mms: original=> chunkLenLV=%u chunkLenLM=%u messageLength=%u\n",
137 *mms_chunkLenLV, *mms_chunkLenLM, *mms_messageLength);
139 /* explanation, before I forget what I did:
140 strlen(buffer)*2 + ct_mms_info->padding + 4 must be divisable by 8;
141 divide by 8 and add 3 to compute the mms_chunkLenLM field,
142 but note that things may have to be padded with zeroes to align by 8
143 bytes, hence we add 7 and divide by 8 to get the correct length */
144 *mms_chunkLenLM = (u_int32_t) (3+(strlen(buffer)*2+ct_mms_info->padding+11)/8);
145 *mms_chunkLenLV = *mms_chunkLenLM+2;
146 *mms_messageLength = *mms_chunkLenLV*8;
148 DEBUGP("ip_nat_mms: modified=> chunkLenLV=%u chunkLenLM=%u messageLength=%u\n",
149 *mms_chunkLenLV, *mms_chunkLenLM, *mms_messageLength);
151 ip_nat_mangle_tcp_packet(pskb, ct, ctinfo,
152 expect->seq - ntohl(tcph->seq),
153 ct_mms_info->len + ct_mms_info->padding, unicode_buffer,
154 strlen(buffer)*2 + ct_mms_info->padding + zero_padding);
155 DUMP_BYTES(unicode_buffer, 60);
157 return 1;
160 static unsigned int
161 mms_nat_expected(struct sk_buff **pskb,
162 unsigned int hooknum,
163 struct ip_conntrack *ct,
164 struct ip_nat_info *info)
166 struct ip_nat_multi_range mr;
167 u_int32_t newdstip, newsrcip, newip;
169 struct ip_conntrack *master = master_ct(ct);
171 IP_NF_ASSERT(info);
172 IP_NF_ASSERT(master);
174 IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum))));
176 DEBUGP("ip_nat_mms: mms_nat_expected: We have a connection!\n");
178 newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
179 newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
180 DEBUGP("ip_nat_mms: mms_nat_expected: hook %s: newsrc->newdst %u.%u.%u.%u->%u.%u.%u.%u\n",
181 hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
182 : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
183 : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???",
184 NIPQUAD(newsrcip), NIPQUAD(newdstip));
186 if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)
187 newip = newsrcip;
188 else
189 newip = newdstip;
191 DEBUGP("ip_nat_mms: mms_nat_expected: IP to %u.%u.%u.%u\n", NIPQUAD(newip));
193 mr.rangesize = 1;
194 /* We don't want to manip the per-protocol, just the IPs. */
195 mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
196 mr.range[0].min_ip = mr.range[0].max_ip = newip;
198 return ip_nat_setup_info(ct, &mr, hooknum);
202 static unsigned int mms_nat_help(struct ip_conntrack *ct,
203 struct ip_conntrack_expect *exp,
204 struct ip_nat_info *info,
205 enum ip_conntrack_info ctinfo,
206 unsigned int hooknum,
207 struct sk_buff **pskb)
209 struct iphdr *iph = (*pskb)->nh.iph;
210 struct tcphdr *tcph = (void *) iph + iph->ihl * 4;
211 unsigned int datalen;
212 int dir;
213 struct ip_ct_mms_expect *ct_mms_info;
215 if (!exp)
216 DEBUGP("ip_nat_mms: no exp!!");
218 ct_mms_info = &exp->help.exp_mms_info;
220 /* Only mangle things once: original direction in POST_ROUTING
221 and reply direction on PRE_ROUTING. */
222 dir = CTINFO2DIR(ctinfo);
223 if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL)
224 ||(hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) {
225 DEBUGP("ip_nat_mms: mms_nat_help: not touching dir %s at hook %s\n",
226 dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
227 hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
228 : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
229 : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???");
230 return NF_ACCEPT;
232 DEBUGP("ip_nat_mms: mms_nat_help: beyond not touching (dir %s at hook %s)\n",
233 dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
234 hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
235 : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
236 : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???");
238 datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4;
240 DEBUGP("ip_nat_mms: mms_nat_help: %u+%u=%u %u %u\n", exp->seq, ct_mms_info->len,
241 exp->seq + ct_mms_info->len,
242 ntohl(tcph->seq),
243 ntohl(tcph->seq) + datalen);
245 LOCK_BH(&ip_mms_lock);
246 /* Check wether the whole IP/proto/port pattern is carried in the payload */
247 if (between(exp->seq + ct_mms_info->len,
248 ntohl(tcph->seq),
249 ntohl(tcph->seq) + datalen)) {
250 if (!mms_data_fixup(ct_mms_info, ct, pskb, ctinfo, exp)) {
251 UNLOCK_BH(&ip_mms_lock);
252 return NF_DROP;
254 } else {
255 /* Half a match? This means a partial retransmisison.
256 It's a cracker being funky. */
257 if (net_ratelimit()) {
258 printk("ip_nat_mms: partial packet %u/%u in %u/%u\n",
259 exp->seq, ct_mms_info->len,
260 ntohl(tcph->seq),
261 ntohl(tcph->seq) + datalen);
263 UNLOCK_BH(&ip_mms_lock);
264 return NF_DROP;
266 UNLOCK_BH(&ip_mms_lock);
268 return NF_ACCEPT;
271 static struct ip_nat_helper mms[MAX_PORTS];
272 static char mms_names[MAX_PORTS][10];
274 /* Not __exit: called from init() */
275 static void fini(void)
277 int i;
279 for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
280 DEBUGP("ip_nat_mms: unregistering helper for port %d\n", ports[i]);
281 ip_nat_helper_unregister(&mms[i]);
285 static int __init init(void)
287 int i, ret = 0;
288 char *tmpname;
290 if (ports[0] == 0)
291 ports[0] = MMS_PORT;
293 for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
295 memset(&mms[i], 0, sizeof(struct ip_nat_helper));
297 mms[i].tuple.dst.protonum = IPPROTO_TCP;
298 mms[i].tuple.src.u.tcp.port = htons(ports[i]);
299 mms[i].mask.dst.protonum = 0xFFFF;
300 mms[i].mask.src.u.tcp.port = 0xFFFF;
301 mms[i].help = mms_nat_help;
302 mms[i].me = THIS_MODULE;
303 mms[i].flags = 0;
304 mms[i].expect = mms_nat_expected;
306 tmpname = &mms_names[i][0];
307 if (ports[i] == MMS_PORT)
308 sprintf(tmpname, "mms");
309 else
310 sprintf(tmpname, "mms-%d", i);
311 mms[i].name = tmpname;
313 DEBUGP("ip_nat_mms: register helper for port %d\n",
314 ports[i]);
315 ret = ip_nat_helper_register(&mms[i]);
317 if (ret) {
318 printk("ip_nat_mms: error registering "
319 "helper for port %d\n", ports[i]);
320 fini();
321 return ret;
323 ports_c++;
326 return ret;
329 module_init(init);
330 module_exit(fini);