1/*
2 * H.323 'brute force' extension for H.323 connection tracking.
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/)
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_conntrack.h>
20#include <linux/netfilter_ipv4/ip_conntrack_core.h>
21#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
22#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
23#include <linux/netfilter_ipv4/ip_conntrack_h323.h>
24
25MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
26MODULE_DESCRIPTION("H.323 'brute force' connection tracking module");
27MODULE_LICENSE("GPL");
28
29DECLARE_LOCK(ip_h323_lock);
30struct module *ip_conntrack_h323 = THIS_MODULE;
31
32#define DEBUGP(format, args...)
33
34static int h245_help(const struct iphdr *iph, size_t len,
35		     struct ip_conntrack *ct,
36		     enum ip_conntrack_info ctinfo)
37{
38	struct tcphdr *tcph = (void *)iph + iph->ihl * 4;
39	unsigned char *data = (unsigned char *) tcph + tcph->doff * 4;
40	unsigned char *data_limit;
41	u_int32_t tcplen = len - iph->ihl * 4;
42	u_int32_t datalen = tcplen - tcph->doff * 4;
43	int dir = CTINFO2DIR(ctinfo);
44	struct ip_ct_h225_master *info = &ct->help.ct_h225_info;
45	struct ip_conntrack_expect expect, *exp = &expect;
46	struct ip_ct_h225_expect *exp_info = &exp->help.exp_h225_info;
47	u_int16_t data_port;
48	u_int32_t data_ip;
49	unsigned int i;
50
51	DEBUGP("ct_h245_help: help entered %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n",
52		NIPQUAD(iph->saddr), ntohs(tcph->source),
53		NIPQUAD(iph->daddr), ntohs(tcph->dest));
54
55	/* Can't track connections formed before we registered */
56	if (!info)
57		return NF_ACCEPT;
58
59	/* Until there's been traffic both ways, don't look in packets. */
60	if (ctinfo != IP_CT_ESTABLISHED
61	    && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) {
62		DEBUGP("ct_h245_help: Conntrackinfo = %u\n", ctinfo);
63		return NF_ACCEPT;
64	}
65
66	/* Not whole TCP header or too short packet? */
67	if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4 + 5) {
68		DEBUGP("ct_h245_help: tcplen = %u\n", (unsigned)tcplen);
69		return NF_ACCEPT;
70	}
71
72	/* Checksum invalid?  Ignore. */
73	if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
74			      csum_partial((char *)tcph, tcplen, 0))) {
75		DEBUGP("ct_h245_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
76		       tcph, tcplen, NIPQUAD(iph->saddr),
77		       NIPQUAD(iph->daddr));
78		return NF_ACCEPT;
79	}
80
81	data_limit = (unsigned char *) data + datalen;
82	/* bytes: 0123   45
83	          ipadrr port */
84	for (i = 0; data < (data_limit - 5); data++, i++) {
85		memcpy(&data_ip, data, sizeof(u_int32_t));
86		if (data_ip == iph->saddr) {
87			memcpy(&data_port, data + 4, sizeof(u_int16_t));
88			memset(&expect, 0, sizeof(expect));
89			/* update the H.225 info */
90			DEBUGP("ct_h245_help: new RTCP/RTP requested %u.%u.%u.%u:->%u.%u.%u.%u:%u\n",
91				NIPQUAD(ct->tuplehash[!dir].tuple.src.ip),
92				NIPQUAD(iph->saddr), ntohs(data_port));
93			LOCK_BH(&ip_h323_lock);
94			info->is_h225 = H225_PORT + 1;
95			exp_info->port = data_port;
96			exp_info->dir = dir;
97			exp_info->offset = i;
98
99			exp->seq = ntohl(tcph->seq) + i;
100
101			exp->tuple = ((struct ip_conntrack_tuple)
102				{ { ct->tuplehash[!dir].tuple.src.ip,
103				    { 0 } },
104				  { data_ip,
105				    { data_port },
106				    IPPROTO_UDP }});
107			exp->mask = ((struct ip_conntrack_tuple)
108				{ { 0xFFFFFFFF, { 0 } },
109				  { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }});
110
111			exp->expectfn = NULL;
112
113			/* Ignore failure; should only happen with NAT */
114			ip_conntrack_expect_related(ct, exp);
115
116			UNLOCK_BH(&ip_h323_lock);
117		}
118	}
119
120	return NF_ACCEPT;
121
122}
123
124/* H.245 helper is not registered! */
125static struct ip_conntrack_helper h245 =
126	{ { NULL, NULL },
127          "H.245",				/* name */
128          IP_CT_HELPER_F_REUSE_EXPECT,		/* flags */
129          NULL,					/* module */
130          8,					/* max_ expected */
131          240,					/* timeout */
132          { { 0, { 0 } },			/* tuple */
133            { 0, { 0 }, IPPROTO_TCP } },
134          { { 0, { 0xFFFF } },			/* mask */
135            { 0, { 0 }, 0xFFFF } },
136          h245_help				/* helper */
137	};
138
139static int h225_expect(struct ip_conntrack *ct)
140{
141	WRITE_LOCK(&ip_conntrack_lock);
142	ct->helper = &h245;
143	DEBUGP("h225_expect: helper for %p added\n", ct);
144	WRITE_UNLOCK(&ip_conntrack_lock);
145
146	return NF_ACCEPT;	/* unused */
147}
148
149static int h225_help(const struct iphdr *iph, size_t len,
150		     struct ip_conntrack *ct,
151		     enum ip_conntrack_info ctinfo)
152{
153	struct tcphdr *tcph = (void *)iph + iph->ihl * 4;
154	unsigned char *data = (unsigned char *) tcph + tcph->doff * 4;
155	unsigned char *data_limit;
156	u_int32_t tcplen = len - iph->ihl * 4;
157	u_int32_t datalen = tcplen - tcph->doff * 4;
158	int dir = CTINFO2DIR(ctinfo);
159	struct ip_ct_h225_master *info = &ct->help.ct_h225_info;
160	struct ip_conntrack_expect expect, *exp = &expect;
161	struct ip_ct_h225_expect *exp_info = &exp->help.exp_h225_info;
162	u_int16_t data_port;
163	u_int32_t data_ip;
164	unsigned int i;
165
166	DEBUGP("ct_h225_help: help entered %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n",
167		NIPQUAD(iph->saddr), ntohs(tcph->source),
168		NIPQUAD(iph->daddr), ntohs(tcph->dest));
169
170	/* Can't track connections formed before we registered */
171	if (!info)
172		return NF_ACCEPT;
173
174	/* Until there's been traffic both ways, don't look in packets. */
175	if (ctinfo != IP_CT_ESTABLISHED
176	    && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) {
177		DEBUGP("ct_h225_help: Conntrackinfo = %u\n", ctinfo);
178		return NF_ACCEPT;
179	}
180
181	/* Not whole TCP header or too short packet? */
182	if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4 + 5) {
183		DEBUGP("ct_h225_help: tcplen = %u\n", (unsigned)tcplen);
184		return NF_ACCEPT;
185	}
186
187	/* Checksum invalid?  Ignore. */
188	if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
189			      csum_partial((char *)tcph, tcplen, 0))) {
190		DEBUGP("ct_h225_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
191		       tcph, tcplen, NIPQUAD(iph->saddr),
192		       NIPQUAD(iph->daddr));
193		return NF_ACCEPT;
194	}
195
196	data_limit = (unsigned char *) data + datalen;
197	/* bytes: 0123   45
198	          ipadrr port */
199	for (i = 0; data < (data_limit - 5); data++, i++) {
200		memcpy(&data_ip, data, sizeof(u_int32_t));
201		if (data_ip == iph->saddr) {
202			memcpy(&data_port, data + 4, sizeof(u_int16_t));
203			if (data_port == tcph->source) {
204				/* Signal address */
205				DEBUGP("ct_h225_help: sourceCallSignalAddress from %u.%u.%u.%u\n",
206					NIPQUAD(iph->saddr));
207				/* Update the H.225 info so that NAT can mangle the address/port
208				   even when we have no expected connection! */
209#ifdef CONFIG_IP_NF_NAT_NEEDED
210				LOCK_BH(&ip_h323_lock);
211				info->dir = dir;
212				info->seq[IP_CT_DIR_ORIGINAL] = ntohl(tcph->seq) + i;
213				info->offset[IP_CT_DIR_ORIGINAL] = i;
214				UNLOCK_BH(&ip_h323_lock);
215#endif
216			} else {
217				memset(&expect, 0, sizeof(expect));
218
219				/* update the H.225 info */
220				LOCK_BH(&ip_h323_lock);
221				info->is_h225 = H225_PORT;
222				exp_info->port = data_port;
223				exp_info->dir = dir;
224				exp_info->offset = i;
225
226				exp->seq = ntohl(tcph->seq) + i;
227
228				exp->tuple = ((struct ip_conntrack_tuple)
229					{ { ct->tuplehash[!dir].tuple.src.ip,
230					    { 0 } },
231					  { data_ip,
232					    { data_port },
233					    IPPROTO_TCP }});
234				exp->mask = ((struct ip_conntrack_tuple)
235					{ { 0xFFFFFFFF, { 0 } },
236					  { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }});
237
238				exp->expectfn = h225_expect;
239
240				/* Ignore failure */
241				ip_conntrack_expect_related(ct, exp);
242
243				DEBUGP("ct_h225_help: new H.245 requested %u.%u.%u.%u->%u.%u.%u.%u:%u\n",
244					NIPQUAD(ct->tuplehash[!dir].tuple.src.ip),
245					NIPQUAD(iph->saddr), ntohs(data_port));
246
247				UNLOCK_BH(&ip_h323_lock);
248                	}
249#ifdef CONFIG_IP_NF_NAT_NEEDED
250		} else if (data_ip == iph->daddr) {
251			memcpy(&data_port, data + 4, sizeof(u_int16_t));
252			if (data_port == tcph->dest) {
253				/* Signal address */
254				DEBUGP("ct_h225_help: destCallSignalAddress %u.%u.%u.%u\n",
255					NIPQUAD(iph->daddr));
256				/* Update the H.225 info so that NAT can mangle the address/port
257				   even when we have no expected connection! */
258				LOCK_BH(&ip_h323_lock);
259				info->dir = dir;
260				info->seq[IP_CT_DIR_REPLY] = ntohl(tcph->seq) + i;
261				info->offset[IP_CT_DIR_REPLY] = i;
262				UNLOCK_BH(&ip_h323_lock);
263			}
264#endif
265		}
266	}
267
268	return NF_ACCEPT;
269
270}
271
272static struct ip_conntrack_helper h225 =
273	{ { NULL, NULL },
274	  "H.225",					/* name */
275	  IP_CT_HELPER_F_REUSE_EXPECT,			/* flags */
276	  THIS_MODULE,					/* module */
277	  2,						/* max_expected */
278	  240,						/* timeout */
279	  { { 0, { __constant_htons(H225_PORT) } },	/* tuple */
280	    { 0, { 0 }, IPPROTO_TCP } },
281	  { { 0, { 0xFFFF } },				/* mask */
282	    { 0, { 0 }, 0xFFFF } },
283	  h225_help					/* helper */
284	};
285
286static int __init init(void)
287{
288	return ip_conntrack_helper_register(&h225);
289}
290
291static void __exit fini(void)
292{
293	/* Unregister H.225 helper */
294	ip_conntrack_helper_unregister(&h225);
295}
296
297#ifdef CONFIG_IP_NF_NAT_NEEDED
298EXPORT_SYMBOL(ip_h323_lock);
299#endif
300
301module_init(init);
302module_exit(fini);
303