1/*
2 * H.323 'brute force' extension for NAT alteration.
3 * Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
4 *
5 * Based on ip_masq_h323.c for 2.2 kernels from CoRiTel, Sofia project.
6 * (http://www.coritel.it/projects/sofia/nat.html)
7 * Uses Sampsa Ranta's excellent idea on using expectfn to 'bind'
8 * the unregistered helpers to the conntrack entries.
9 */
10
11
12#include <linux/module.h>
13#include <linux/netfilter.h>
14#include <linux/ip.h>
15#include <net/checksum.h>
16#include <net/tcp.h>
17
18#include <linux/netfilter_ipv4/lockhelp.h>
19#include <linux/netfilter_ipv4/ip_nat.h>
20#include <linux/netfilter_ipv4/ip_nat_helper.h>
21#include <linux/netfilter_ipv4/ip_nat_rule.h>
22#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
23#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
24#include <linux/netfilter_ipv4/ip_conntrack_h323.h>
25
26MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
27MODULE_DESCRIPTION("H.323 'brute force' connection tracking module");
28MODULE_LICENSE("GPL");
29
30DECLARE_LOCK_EXTERN(ip_h323_lock);
31struct module *ip_nat_h323 = THIS_MODULE;
32
33#define DEBUGP(format, args...)
34
35
36static unsigned int
37h225_nat_expected(struct sk_buff **pskb,
38		  unsigned int hooknum,
39		  struct ip_conntrack *ct,
40		  struct ip_nat_info *info);
41
42static unsigned int h225_nat_help(struct ip_conntrack *ct,
43				  struct ip_conntrack_expect *exp,
44			 	  struct ip_nat_info *info,
45			 	  enum ip_conntrack_info ctinfo,
46			 	  unsigned int hooknum,
47			 	  struct sk_buff **pskb);
48
49static struct ip_nat_helper h245 =
50	{ { NULL, NULL },
51          "H.245",				/* name */
52	  0,					/* flags */
53	  NULL,					/* module */
54	  { { 0, { 0 } },			/* tuple */
55	    { 0, { 0 }, IPPROTO_TCP } },
56	  { { 0, { 0xFFFF } },			/* mask */
57	    { 0, { 0 }, 0xFFFF } },
58	  h225_nat_help,			/* helper */
59	  h225_nat_expected			/* expectfn */
60	};
61
62static unsigned int
63h225_nat_expected(struct sk_buff **pskb,
64		  unsigned int hooknum,
65		  struct ip_conntrack *ct,
66		  struct ip_nat_info *info)
67{
68	struct ip_nat_multi_range mr;
69	u_int32_t newdstip, newsrcip, newip;
70	u_int16_t port;
71	struct ip_ct_h225_expect *exp_info;
72	struct ip_ct_h225_master *master_info;
73	struct ip_conntrack *master = master_ct(ct);
74	unsigned int is_h225, ret;
75
76	IP_NF_ASSERT(info);
77	IP_NF_ASSERT(master);
78
79	IP_NF_ASSERT(!(info->initialized & (1<<HOOK2MANIP(hooknum))));
80
81	DEBUGP("h225_nat_expected: We have a connection!\n");
82	master_info = &ct->master->expectant->help.ct_h225_info;
83	exp_info = &ct->master->help.exp_h225_info;
84
85	LOCK_BH(&ip_h323_lock);
86
87	DEBUGP("master: ");
88	DUMP_TUPLE(&master->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
89	DUMP_TUPLE(&master->tuplehash[IP_CT_DIR_REPLY].tuple);
90	DEBUGP("conntrack: ");
91	DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
92	if (exp_info->dir == IP_CT_DIR_ORIGINAL) {
93		/* Make connection go to the client. */
94		newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
95		newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
96		DEBUGP("h225_nat_expected: %u.%u.%u.%u->%u.%u.%u.%u (to client)\n",
97		       NIPQUAD(newsrcip), NIPQUAD(newdstip));
98	} else {
99		/* Make the connection go to the server */
100		newdstip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
101		newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
102		DEBUGP("h225_nat_expected: %u.%u.%u.%u->%u.%u.%u.%u (to server)\n",
103		       NIPQUAD(newsrcip), NIPQUAD(newdstip));
104	}
105	port = exp_info->port;
106	is_h225 = master_info->is_h225 == H225_PORT;
107	UNLOCK_BH(&ip_h323_lock);
108
109	if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)
110		newip = newsrcip;
111	else
112		newip = newdstip;
113
114	DEBUGP("h225_nat_expected: IP to %u.%u.%u.%u\n", NIPQUAD(newip));
115
116	mr.rangesize = 1;
117	/* We don't want to manip the per-protocol, just the IPs... */
118	mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
119	mr.range[0].min_ip = mr.range[0].max_ip = newip;
120
121	/* ... unless we're doing a MANIP_DST, in which case, make
122	   sure we map to the correct port */
123	if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) {
124		mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
125		mr.range[0].min = mr.range[0].max
126			= ((union ip_conntrack_manip_proto)
127				{ port });
128	}
129
130	ret = ip_nat_setup_info(ct, &mr, hooknum);
131
132	if (is_h225) {
133		DEBUGP("h225_nat_expected: H.225, setting NAT helper for %p\n", ct);
134		/* NAT expectfn called with ip_nat_lock write-locked */
135		info->helper = &h245;
136	}
137	return ret;
138}
139
140static int h323_signal_address_fixup(struct ip_conntrack *ct,
141			      	     struct sk_buff **pskb,
142			      	     enum ip_conntrack_info ctinfo)
143{
144	struct iphdr *iph = (*pskb)->nh.iph;
145	struct tcphdr *tcph = (void *)iph + iph->ihl*4;
146	unsigned char *data;
147	u_int32_t tcplen = (*pskb)->len - iph->ihl*4;
148	u_int32_t datalen = tcplen - tcph->doff*4;
149	struct ip_ct_h225_master *info = &ct->help.ct_h225_info;
150	u_int32_t newip;
151	u_int16_t port;
152	u_int8_t buffer[6];
153	int i;
154
155	MUST_BE_LOCKED(&ip_h323_lock);
156
157	DEBUGP("h323_signal_address_fixup: %s %s\n",
158		between(info->seq[IP_CT_DIR_ORIGINAL], ntohl(tcph->seq), ntohl(tcph->seq) + datalen)
159			? "yes" : "no",
160		between(info->seq[IP_CT_DIR_REPLY], ntohl(tcph->seq), ntohl(tcph->seq) + datalen)
161			? "yes" : "no");
162	if (!(between(info->seq[IP_CT_DIR_ORIGINAL], ntohl(tcph->seq), ntohl(tcph->seq) + datalen)
163	      || between(info->seq[IP_CT_DIR_REPLY], ntohl(tcph->seq), ntohl(tcph->seq) + datalen)))
164		return 1;
165
166	DEBUGP("h323_signal_address_fixup: offsets %u + 6  and %u + 6 in %u\n",
167		info->offset[IP_CT_DIR_ORIGINAL],
168		info->offset[IP_CT_DIR_REPLY],
169		tcplen);
170	DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
171	DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
172
173	for (i = 0; i < IP_CT_DIR_MAX; i++) {
174		DEBUGP("h323_signal_address_fixup: %s %s\n",
175			info->dir == IP_CT_DIR_ORIGINAL ? "original" : "reply",
176			i == IP_CT_DIR_ORIGINAL ? "caller" : "callee");
177		if (!between(info->seq[i], ntohl(tcph->seq),
178			     ntohl(tcph->seq) + datalen))
179			continue;
180		if (!between(info->seq[i] + 6, ntohl(tcph->seq),
181			     ntohl(tcph->seq) + datalen)) {
182			/* Partial retransmisison. It's a cracker being funky. */
183			if (net_ratelimit()) {
184				printk("H.323_NAT: partial packet %u/6 in %u/%u\n",
185				     info->seq[i],
186				     ntohl(tcph->seq),
187				     ntohl(tcph->seq) + datalen);
188			}
189			return 0;
190		}
191
192		/* Change address inside packet to match way we're mapping
193		   this connection. */
194		if (i == IP_CT_DIR_ORIGINAL) {
195			newip = ct->tuplehash[!info->dir].tuple.dst.ip;
196			port = ct->tuplehash[!info->dir].tuple.dst.u.tcp.port;
197		} else {
198			newip = ct->tuplehash[!info->dir].tuple.src.ip;
199			port = ct->tuplehash[!info->dir].tuple.src.u.tcp.port;
200		}
201
202		data = (char *) tcph + tcph->doff * 4 + info->offset[i];
203
204		DEBUGP("h323_signal_address_fixup: orig %s IP:port %u.%u.%u.%u:%u\n",
205			i == IP_CT_DIR_ORIGINAL ? "source" : "dest  ",
206		        data[0], data[1], data[2], data[3],
207		        (data[4] << 8 | data[5]));
208
209		/* Modify the packet */
210		memcpy(buffer, &newip, 4);
211		memcpy(buffer + 4, &port, 2);
212		if (!ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, info->offset[i],
213					      6, buffer, 6))
214			return 0;
215
216		DEBUGP("h323_signal_address_fixup:  new %s IP:port %u.%u.%u.%u:%u\n",
217			i == IP_CT_DIR_ORIGINAL ? "source" : "dest  ",
218		        data[0], data[1], data[2], data[3],
219		        (data[4] << 8 | data[5]));
220	}
221
222	return 1;
223}
224
225static int h323_data_fixup(struct ip_ct_h225_expect *info,
226			   struct ip_conntrack *ct,
227			   struct sk_buff **pskb,
228			   enum ip_conntrack_info ctinfo,
229			   struct ip_conntrack_expect *expect)
230{
231	u_int32_t newip;
232	u_int16_t port;
233	u_int8_t buffer[6];
234	struct ip_conntrack_tuple newtuple;
235	struct iphdr *iph = (*pskb)->nh.iph;
236	struct tcphdr *tcph = (void *)iph + iph->ihl*4;
237	unsigned char *data;
238	u_int32_t tcplen = (*pskb)->len - iph->ihl*4;
239	struct ip_ct_h225_master *master_info = &ct->help.ct_h225_info;
240	int is_h225;
241
242	MUST_BE_LOCKED(&ip_h323_lock);
243	DEBUGP("h323_data_fixup: offset %u + 6 in %u\n", info->offset, tcplen);
244	DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
245	DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
246
247	if (!between(expect->seq + 6, ntohl(tcph->seq),
248		    ntohl(tcph->seq) + tcplen - tcph->doff * 4)) {
249		/* Partial retransmisison. It's a cracker being funky. */
250		if (net_ratelimit()) {
251			printk("H.323_NAT: partial packet %u/6 in %u/%u\n",
252			     expect->seq,
253			     ntohl(tcph->seq),
254			     ntohl(tcph->seq) + tcplen - tcph->doff * 4);
255		}
256		return 0;
257	}
258
259	/* Change address inside packet to match way we're mapping
260	   this connection. */
261	if (info->dir == IP_CT_DIR_REPLY) {
262		/* Must be where client thinks server is */
263		newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
264		/* Expect something from client->server */
265		newtuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
266		newtuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
267	} else {
268		/* Must be where server thinks client is */
269		newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
270		/* Expect something from server->client */
271		newtuple.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
272		newtuple.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
273	}
274
275	is_h225 = (master_info->is_h225 == H225_PORT);
276
277	if (is_h225) {
278		newtuple.dst.protonum = IPPROTO_TCP;
279		newtuple.src.u.tcp.port = expect->tuple.src.u.tcp.port;
280	} else {
281		newtuple.dst.protonum = IPPROTO_UDP;
282		newtuple.src.u.udp.port = expect->tuple.src.u.udp.port;
283	}
284
285	/* Try to get same port: if not, try to change it. */
286	for (port = ntohs(info->port); port != 0; port++) {
287		if (is_h225)
288			newtuple.dst.u.tcp.port = htons(port);
289		else
290			newtuple.dst.u.udp.port = htons(port);
291
292		if (ip_conntrack_change_expect(expect, &newtuple) == 0)
293			break;
294	}
295	if (port == 0) {
296		DEBUGP("h323_data_fixup: no free port found!\n");
297		return 0;
298	}
299
300	port = htons(port);
301
302	data = (char *) tcph + tcph->doff * 4 + info->offset;
303
304	DEBUGP("h323_data_fixup: orig IP:port %u.%u.%u.%u:%u\n",
305	        data[0], data[1], data[2], data[3],
306	        (data[4] << 8 | data[5]));
307
308	/* Modify the packet */
309	memcpy(buffer, &newip, 4);
310	memcpy(buffer + 4, &port, 2);
311	if (!ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, info->offset,
312				      6, buffer, 6))
313		return 0;
314
315	DEBUGP("h323_data_fixup: new IP:port %u.%u.%u.%u:%u\n",
316	        data[0], data[1], data[2], data[3],
317	        (data[4] << 8 | data[5]));
318
319	return 1;
320}
321
322static unsigned int h225_nat_help(struct ip_conntrack *ct,
323				  struct ip_conntrack_expect *exp,
324			 	  struct ip_nat_info *info,
325			 	  enum ip_conntrack_info ctinfo,
326			 	  unsigned int hooknum,
327			 	  struct sk_buff **pskb)
328{
329	int dir;
330	struct ip_ct_h225_expect *exp_info;
331
332	/* Only mangle things once: original direction in POST_ROUTING
333	   and reply direction on PRE_ROUTING. */
334	dir = CTINFO2DIR(ctinfo);
335	DEBUGP("nat_h323: dir %s at hook %s\n",
336	       dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
337	       hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
338	       : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
339	       : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???");
340	if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL)
341	      || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) {
342		DEBUGP("nat_h323: Not touching dir %s at hook %s\n",
343		       dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
344		       hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
345		       : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
346		       : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???");
347		return NF_ACCEPT;
348	}
349
350	if (!exp) {
351		LOCK_BH(&ip_h323_lock);
352		if (!h323_signal_address_fixup(ct, pskb, ctinfo)) {
353			UNLOCK_BH(&ip_h323_lock);
354			return NF_DROP;
355		}
356		UNLOCK_BH(&ip_h323_lock);
357		return NF_ACCEPT;
358	}
359
360	exp_info = &exp->help.exp_h225_info;
361
362	LOCK_BH(&ip_h323_lock);
363	if (!h323_data_fixup(exp_info, ct, pskb, ctinfo, exp)) {
364		UNLOCK_BH(&ip_h323_lock);
365		return NF_DROP;
366	}
367	UNLOCK_BH(&ip_h323_lock);
368
369	return NF_ACCEPT;
370}
371
372static struct ip_nat_helper h225 =
373	{ { NULL, NULL },
374	  "H.225",					/* name */
375	  IP_NAT_HELPER_F_ALWAYS, 			/* flags */
376	  THIS_MODULE,					/* module */
377	  { { 0, { __constant_htons(H225_PORT) } },	/* tuple */
378	    { 0, { 0 }, IPPROTO_TCP } },
379	  { { 0, { 0xFFFF } },				/* mask */
380	    { 0, { 0 }, 0xFFFF } },
381	  h225_nat_help,				/* helper */
382	  h225_nat_expected				/* expectfn */
383	};
384
385static int __init init(void)
386{
387	int ret;
388
389	ret = ip_nat_helper_register(&h225);
390
391	if (ret != 0)
392		printk("ip_nat_h323: cannot initialize the module!\n");
393
394	return ret;
395}
396
397static void __exit fini(void)
398{
399	ip_nat_helper_unregister(&h225);
400}
401
402module_init(init);
403module_exit(fini);
404