blob: fb9ab0114c2380f32c2abee0c6a12a531657aa98 [file] [log] [blame]
Patrick McHardyf587de02006-12-02 22:08:46 -08001/*
2 * H.323 extension for NAT alteration.
3 *
4 * Copyright (c) 2006 Jing Min Zhao <zhaojingmin@users.sourceforge.net>
5 *
6 * This source code is licensed under General Public License version 2.
7 *
8 * Based on the 'brute force' H.323 NAT module by
9 * Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
10 */
11
12#include <linux/module.h>
13#include <linux/moduleparam.h>
14#include <linux/tcp.h>
15#include <net/tcp.h>
16
17#include <net/netfilter/nf_nat.h>
18#include <net/netfilter/nf_nat_helper.h>
19#include <net/netfilter/nf_nat_rule.h>
20#include <net/netfilter/nf_conntrack_helper.h>
21#include <net/netfilter/nf_conntrack_expect.h>
22#include <linux/netfilter/nf_conntrack_h323.h>
23
24#if 0
25#define DEBUGP printk
26#else
27#define DEBUGP(format, args...)
28#endif
29
30/****************************************************************************/
31static int set_addr(struct sk_buff **pskb,
32 unsigned char **data, int dataoff,
33 unsigned int addroff, __be32 ip, __be16 port)
34{
35 enum ip_conntrack_info ctinfo;
36 struct nf_conn *ct = ip_conntrack_get(*pskb, &ctinfo);
37 struct {
38 __be32 ip;
39 __be16 port;
40 } __attribute__ ((__packed__)) buf;
41 struct tcphdr _tcph, *th;
42
43 buf.ip = ip;
44 buf.port = port;
45 addroff += dataoff;
46
47 if ((*pskb)->nh.iph->protocol == IPPROTO_TCP) {
48 if (!nf_nat_mangle_tcp_packet(pskb, ct, ctinfo,
49 addroff, sizeof(buf),
50 (char *) &buf, sizeof(buf))) {
51 if (net_ratelimit())
52 printk("nf_nat_h323: nf_nat_mangle_tcp_packet"
53 " error\n");
54 return -1;
55 }
56
57 /* Relocate data pointer */
58 th = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl * 4,
59 sizeof(_tcph), &_tcph);
60 if (th == NULL)
61 return -1;
62 *data = (*pskb)->data + (*pskb)->nh.iph->ihl * 4 +
63 th->doff * 4 + dataoff;
64 } else {
65 if (!nf_nat_mangle_udp_packet(pskb, ct, ctinfo,
66 addroff, sizeof(buf),
67 (char *) &buf, sizeof(buf))) {
68 if (net_ratelimit())
69 printk("nf_nat_h323: nf_nat_mangle_udp_packet"
70 " error\n");
71 return -1;
72 }
73 /* nf_nat_mangle_udp_packet uses skb_make_writable() to copy
74 * or pull everything in a linear buffer, so we can safely
75 * use the skb pointers now */
76 *data = (*pskb)->data + (*pskb)->nh.iph->ihl * 4 +
77 sizeof(struct udphdr);
78 }
79
80 return 0;
81}
82
83/****************************************************************************/
84static int set_h225_addr(struct sk_buff **pskb,
85 unsigned char **data, int dataoff,
86 TransportAddress *taddr,
87 union nf_conntrack_address *addr, __be16 port)
88{
89 return set_addr(pskb, data, dataoff, taddr->ipAddress.ip,
90 addr->ip, port);
91}
92
93/****************************************************************************/
94static int set_h245_addr(struct sk_buff **pskb,
95 unsigned char **data, int dataoff,
96 H245_TransportAddress *taddr,
97 union nf_conntrack_address *addr, __be16 port)
98{
99 return set_addr(pskb, data, dataoff,
100 taddr->unicastAddress.iPAddress.network,
101 addr->ip, port);
102}
103
104/****************************************************************************/
105static int set_sig_addr(struct sk_buff **pskb, struct nf_conn *ct,
106 enum ip_conntrack_info ctinfo,
107 unsigned char **data,
108 TransportAddress *taddr, int count)
109{
110 struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info;
111 int dir = CTINFO2DIR(ctinfo);
112 int i;
113 __be16 port;
114 union nf_conntrack_address addr;
115
116 for (i = 0; i < count; i++) {
117 if (get_h225_addr(ct, *data, &taddr[i], &addr, &port)) {
118 if (addr.ip == ct->tuplehash[dir].tuple.src.u3.ip &&
119 port == info->sig_port[dir]) {
120 /* GW->GK */
121
122 /* Fix for Gnomemeeting */
123 if (i > 0 &&
124 get_h225_addr(ct, *data, &taddr[0],
125 &addr, &port) &&
126 (ntohl(addr.ip) & 0xff000000) == 0x7f000000)
127 i = 0;
128
129 DEBUGP
130 ("nf_nat_ras: set signal address "
131 "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
132 NIPQUAD(ip), port,
133 NIPQUAD(ct->tuplehash[!dir].tuple.dst.
134 ip), info->sig_port[!dir]);
135 return set_h225_addr(pskb, data, 0, &taddr[i],
136 &ct->tuplehash[!dir].
137 tuple.dst.u3,
138 info->sig_port[!dir]);
139 } else if (addr.ip == ct->tuplehash[dir].tuple.dst.u3.ip &&
140 port == info->sig_port[dir]) {
141 /* GK->GW */
142 DEBUGP
143 ("nf_nat_ras: set signal address "
144 "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
145 NIPQUAD(ip), port,
146 NIPQUAD(ct->tuplehash[!dir].tuple.src.
147 ip), info->sig_port[!dir]);
148 return set_h225_addr(pskb, data, 0, &taddr[i],
149 &ct->tuplehash[!dir].
150 tuple.src.u3,
151 info->sig_port[!dir]);
152 }
153 }
154 }
155
156 return 0;
157}
158
159/****************************************************************************/
160static int set_ras_addr(struct sk_buff **pskb, struct nf_conn *ct,
161 enum ip_conntrack_info ctinfo,
162 unsigned char **data,
163 TransportAddress *taddr, int count)
164{
165 int dir = CTINFO2DIR(ctinfo);
166 int i;
167 __be16 port;
168 union nf_conntrack_address addr;
169
170 for (i = 0; i < count; i++) {
171 if (get_h225_addr(ct, *data, &taddr[i], &addr, &port) &&
172 addr.ip == ct->tuplehash[dir].tuple.src.u3.ip &&
173 port == ct->tuplehash[dir].tuple.src.u.udp.port) {
174 DEBUGP("nf_nat_ras: set rasAddress "
175 "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
176 NIPQUAD(ip), ntohs(port),
177 NIPQUAD(ct->tuplehash[!dir].tuple.dst.u3.ip),
178 ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.
179 port));
180 return set_h225_addr(pskb, data, 0, &taddr[i],
181 &ct->tuplehash[!dir].tuple.dst.u3,
182 ct->tuplehash[!dir].tuple.
183 dst.u.udp.port);
184 }
185 }
186
187 return 0;
188}
189
190/****************************************************************************/
191static int nat_rtp_rtcp(struct sk_buff **pskb, struct nf_conn *ct,
192 enum ip_conntrack_info ctinfo,
193 unsigned char **data, int dataoff,
194 H245_TransportAddress *taddr,
195 __be16 port, __be16 rtp_port,
196 struct nf_conntrack_expect *rtp_exp,
197 struct nf_conntrack_expect *rtcp_exp)
198{
199 struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info;
200 int dir = CTINFO2DIR(ctinfo);
201 int i;
202 u_int16_t nated_port;
203
204 /* Set expectations for NAT */
205 rtp_exp->saved_proto.udp.port = rtp_exp->tuple.dst.u.udp.port;
206 rtp_exp->expectfn = nf_nat_follow_master;
207 rtp_exp->dir = !dir;
208 rtcp_exp->saved_proto.udp.port = rtcp_exp->tuple.dst.u.udp.port;
209 rtcp_exp->expectfn = nf_nat_follow_master;
210 rtcp_exp->dir = !dir;
211
212 /* Lookup existing expects */
213 for (i = 0; i < H323_RTP_CHANNEL_MAX; i++) {
214 if (info->rtp_port[i][dir] == rtp_port) {
215 /* Expected */
216
217 /* Use allocated ports first. This will refresh
218 * the expects */
219 rtp_exp->tuple.dst.u.udp.port = info->rtp_port[i][dir];
220 rtcp_exp->tuple.dst.u.udp.port =
221 htons(ntohs(info->rtp_port[i][dir]) + 1);
222 break;
223 } else if (info->rtp_port[i][dir] == 0) {
224 /* Not expected */
225 break;
226 }
227 }
228
229 /* Run out of expectations */
230 if (i >= H323_RTP_CHANNEL_MAX) {
231 if (net_ratelimit())
232 printk("nf_nat_h323: out of expectations\n");
233 return 0;
234 }
235
236 /* Try to get a pair of ports. */
237 for (nated_port = ntohs(rtp_exp->tuple.dst.u.udp.port);
238 nated_port != 0; nated_port += 2) {
239 rtp_exp->tuple.dst.u.udp.port = htons(nated_port);
240 if (nf_conntrack_expect_related(rtp_exp) == 0) {
241 rtcp_exp->tuple.dst.u.udp.port =
242 htons(nated_port + 1);
243 if (nf_conntrack_expect_related(rtcp_exp) == 0)
244 break;
245 nf_conntrack_unexpect_related(rtp_exp);
246 }
247 }
248
249 if (nated_port == 0) { /* No port available */
250 if (net_ratelimit())
251 printk("nf_nat_h323: out of RTP ports\n");
252 return 0;
253 }
254
255 /* Modify signal */
256 if (set_h245_addr(pskb, data, dataoff, taddr,
257 &ct->tuplehash[!dir].tuple.dst.u3,
258 htons((port & htons(1)) ? nated_port + 1 :
259 nated_port)) == 0) {
260 /* Save ports */
261 info->rtp_port[i][dir] = rtp_port;
262 info->rtp_port[i][!dir] = htons(nated_port);
263 } else {
264 nf_conntrack_unexpect_related(rtp_exp);
265 nf_conntrack_unexpect_related(rtcp_exp);
266 return -1;
267 }
268
269 /* Success */
270 DEBUGP("nf_nat_h323: expect RTP %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
271 NIPQUAD(rtp_exp->tuple.src.ip),
272 ntohs(rtp_exp->tuple.src.u.udp.port),
273 NIPQUAD(rtp_exp->tuple.dst.ip),
274 ntohs(rtp_exp->tuple.dst.u.udp.port));
275 DEBUGP("nf_nat_h323: expect RTCP %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
276 NIPQUAD(rtcp_exp->tuple.src.ip),
277 ntohs(rtcp_exp->tuple.src.u.udp.port),
278 NIPQUAD(rtcp_exp->tuple.dst.ip),
279 ntohs(rtcp_exp->tuple.dst.u.udp.port));
280
281 return 0;
282}
283
284/****************************************************************************/
285static int nat_t120(struct sk_buff **pskb, struct nf_conn *ct,
286 enum ip_conntrack_info ctinfo,
287 unsigned char **data, int dataoff,
288 H245_TransportAddress *taddr, __be16 port,
289 struct nf_conntrack_expect *exp)
290{
291 int dir = CTINFO2DIR(ctinfo);
292 u_int16_t nated_port = ntohs(port);
293
294 /* Set expectations for NAT */
295 exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
296 exp->expectfn = nf_nat_follow_master;
297 exp->dir = !dir;
298
299 /* Try to get same port: if not, try to change it. */
300 for (; nated_port != 0; nated_port++) {
301 exp->tuple.dst.u.tcp.port = htons(nated_port);
302 if (nf_conntrack_expect_related(exp) == 0)
303 break;
304 }
305
306 if (nated_port == 0) { /* No port available */
307 if (net_ratelimit())
308 printk("nf_nat_h323: out of TCP ports\n");
309 return 0;
310 }
311
312 /* Modify signal */
313 if (set_h245_addr(pskb, data, dataoff, taddr,
314 &ct->tuplehash[!dir].tuple.dst.u3,
315 htons(nated_port)) < 0) {
316 nf_conntrack_unexpect_related(exp);
317 return -1;
318 }
319
320 DEBUGP("nf_nat_h323: expect T.120 %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
321 NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port),
322 NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port));
323
324 return 0;
325}
326
327/****************************************************************************/
328static int nat_h245(struct sk_buff **pskb, struct nf_conn *ct,
329 enum ip_conntrack_info ctinfo,
330 unsigned char **data, int dataoff,
331 TransportAddress *taddr, __be16 port,
332 struct nf_conntrack_expect *exp)
333{
334 struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info;
335 int dir = CTINFO2DIR(ctinfo);
336 u_int16_t nated_port = ntohs(port);
337
338 /* Set expectations for NAT */
339 exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
340 exp->expectfn = nf_nat_follow_master;
341 exp->dir = !dir;
342
343 /* Check existing expects */
344 if (info->sig_port[dir] == port)
345 nated_port = ntohs(info->sig_port[!dir]);
346
347 /* Try to get same port: if not, try to change it. */
348 for (; nated_port != 0; nated_port++) {
349 exp->tuple.dst.u.tcp.port = htons(nated_port);
350 if (nf_conntrack_expect_related(exp) == 0)
351 break;
352 }
353
354 if (nated_port == 0) { /* No port available */
355 if (net_ratelimit())
356 printk("nf_nat_q931: out of TCP ports\n");
357 return 0;
358 }
359
360 /* Modify signal */
361 if (set_h225_addr(pskb, data, dataoff, taddr,
362 &ct->tuplehash[!dir].tuple.dst.u3,
363 htons(nated_port)) == 0) {
364 /* Save ports */
365 info->sig_port[dir] = port;
366 info->sig_port[!dir] = htons(nated_port);
367 } else {
368 nf_conntrack_unexpect_related(exp);
369 return -1;
370 }
371
372 DEBUGP("nf_nat_q931: expect H.245 %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
373 NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port),
374 NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port));
375
376 return 0;
377}
378
379/****************************************************************************
380 * This conntrack expect function replaces nf_conntrack_q931_expect()
381 * which was set by nf_conntrack_h323.c.
382 ****************************************************************************/
383static void ip_nat_q931_expect(struct nf_conn *new,
384 struct nf_conntrack_expect *this)
385{
386 struct ip_nat_range range;
387
388 if (this->tuple.src.u3.ip != 0) { /* Only accept calls from GK */
389 nf_nat_follow_master(new, this);
390 return;
391 }
392
393 /* This must be a fresh one. */
394 BUG_ON(new->status & IPS_NAT_DONE_MASK);
395
396 /* Change src to where master sends to */
397 range.flags = IP_NAT_RANGE_MAP_IPS;
398 range.min_ip = range.max_ip = new->tuplehash[!this->dir].tuple.src.u3.ip;
399
400 /* hook doesn't matter, but it has to do source manip */
401 nf_nat_setup_info(new, &range, NF_IP_POST_ROUTING);
402
403 /* For DST manip, map port here to where it's expected. */
404 range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED);
405 range.min = range.max = this->saved_proto;
406 range.min_ip = range.max_ip =
407 new->master->tuplehash[!this->dir].tuple.src.u3.ip;
408
409 /* hook doesn't matter, but it has to do destination manip */
410 nf_nat_setup_info(new, &range, NF_IP_PRE_ROUTING);
411}
412
413/****************************************************************************/
414static int nat_q931(struct sk_buff **pskb, struct nf_conn *ct,
415 enum ip_conntrack_info ctinfo,
416 unsigned char **data, TransportAddress *taddr, int idx,
417 __be16 port, struct nf_conntrack_expect *exp)
418{
419 struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info;
420 int dir = CTINFO2DIR(ctinfo);
421 u_int16_t nated_port = ntohs(port);
422 union nf_conntrack_address addr;
423
424 /* Set expectations for NAT */
425 exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
426 exp->expectfn = ip_nat_q931_expect;
427 exp->dir = !dir;
428
429 /* Check existing expects */
430 if (info->sig_port[dir] == port)
431 nated_port = ntohs(info->sig_port[!dir]);
432
433 /* Try to get same port: if not, try to change it. */
434 for (; nated_port != 0; nated_port++) {
435 exp->tuple.dst.u.tcp.port = htons(nated_port);
436 if (nf_conntrack_expect_related(exp) == 0)
437 break;
438 }
439
440 if (nated_port == 0) { /* No port available */
441 if (net_ratelimit())
442 printk("nf_nat_ras: out of TCP ports\n");
443 return 0;
444 }
445
446 /* Modify signal */
447 if (set_h225_addr(pskb, data, 0, &taddr[idx],
448 &ct->tuplehash[!dir].tuple.dst.u3,
449 htons(nated_port)) == 0) {
450 /* Save ports */
451 info->sig_port[dir] = port;
452 info->sig_port[!dir] = htons(nated_port);
453
454 /* Fix for Gnomemeeting */
455 if (idx > 0 &&
456 get_h225_addr(ct, *data, &taddr[0], &addr, &port) &&
457 (ntohl(addr.ip) & 0xff000000) == 0x7f000000) {
458 set_h225_addr_hook(pskb, data, 0, &taddr[0],
459 &ct->tuplehash[!dir].tuple.dst.u3,
460 info->sig_port[!dir]);
461 }
462 } else {
463 nf_conntrack_unexpect_related(exp);
464 return -1;
465 }
466
467 /* Success */
468 DEBUGP("nf_nat_ras: expect Q.931 %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
469 NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port),
470 NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port));
471
472 return 0;
473}
474
475/****************************************************************************/
476static void ip_nat_callforwarding_expect(struct nf_conn *new,
477 struct nf_conntrack_expect *this)
478{
479 struct nf_nat_range range;
480
481 /* This must be a fresh one. */
482 BUG_ON(new->status & IPS_NAT_DONE_MASK);
483
484 /* Change src to where master sends to */
485 range.flags = IP_NAT_RANGE_MAP_IPS;
486 range.min_ip = range.max_ip = new->tuplehash[!this->dir].tuple.src.u3.ip;
487
488 /* hook doesn't matter, but it has to do source manip */
489 nf_nat_setup_info(new, &range, NF_IP_POST_ROUTING);
490
491 /* For DST manip, map port here to where it's expected. */
492 range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED);
493 range.min = range.max = this->saved_proto;
494 range.min_ip = range.max_ip = this->saved_ip;
495
496 /* hook doesn't matter, but it has to do destination manip */
497 nf_nat_setup_info(new, &range, NF_IP_PRE_ROUTING);
498}
499
500/****************************************************************************/
501static int nat_callforwarding(struct sk_buff **pskb, struct nf_conn *ct,
502 enum ip_conntrack_info ctinfo,
503 unsigned char **data, int dataoff,
504 TransportAddress *taddr, __be16 port,
505 struct nf_conntrack_expect *exp)
506{
507 int dir = CTINFO2DIR(ctinfo);
508 u_int16_t nated_port;
509
510 /* Set expectations for NAT */
511 exp->saved_ip = exp->tuple.dst.u3.ip;
512 exp->tuple.dst.u3.ip = ct->tuplehash[!dir].tuple.dst.u3.ip;
513 exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
514 exp->expectfn = ip_nat_callforwarding_expect;
515 exp->dir = !dir;
516
517 /* Try to get same port: if not, try to change it. */
518 for (nated_port = ntohs(port); nated_port != 0; nated_port++) {
519 exp->tuple.dst.u.tcp.port = htons(nated_port);
520 if (nf_conntrack_expect_related(exp) == 0)
521 break;
522 }
523
524 if (nated_port == 0) { /* No port available */
525 if (net_ratelimit())
526 printk("nf_nat_q931: out of TCP ports\n");
527 return 0;
528 }
529
530 /* Modify signal */
531 if (!set_h225_addr(pskb, data, dataoff, taddr,
532 &ct->tuplehash[!dir].tuple.dst.u3,
533 htons(nated_port)) == 0) {
534 nf_conntrack_unexpect_related(exp);
535 return -1;
536 }
537
538 /* Success */
539 DEBUGP("nf_nat_q931: expect Call Forwarding "
540 "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
541 NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port),
542 NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port));
543
544 return 0;
545}
546
547/****************************************************************************/
548static int __init init(void)
549{
550 BUG_ON(rcu_dereference(set_h245_addr_hook) != NULL);
551 BUG_ON(rcu_dereference(set_h225_addr_hook) != NULL);
552 BUG_ON(rcu_dereference(set_sig_addr_hook) != NULL);
553 BUG_ON(rcu_dereference(set_ras_addr_hook) != NULL);
554 BUG_ON(rcu_dereference(nat_rtp_rtcp_hook) != NULL);
555 BUG_ON(rcu_dereference(nat_t120_hook) != NULL);
556 BUG_ON(rcu_dereference(nat_h245_hook) != NULL);
557 BUG_ON(rcu_dereference(nat_callforwarding_hook) != NULL);
558 BUG_ON(rcu_dereference(nat_q931_hook) != NULL);
559
560 rcu_assign_pointer(set_h245_addr_hook, set_h245_addr);
561 rcu_assign_pointer(set_h225_addr_hook, set_h225_addr);
562 rcu_assign_pointer(set_sig_addr_hook, set_sig_addr);
563 rcu_assign_pointer(set_ras_addr_hook, set_ras_addr);
564 rcu_assign_pointer(nat_rtp_rtcp_hook, nat_rtp_rtcp);
565 rcu_assign_pointer(nat_t120_hook, nat_t120);
566 rcu_assign_pointer(nat_h245_hook, nat_h245);
567 rcu_assign_pointer(nat_callforwarding_hook, nat_callforwarding);
568 rcu_assign_pointer(nat_q931_hook, nat_q931);
569
570 DEBUGP("nf_nat_h323: init success\n");
571 return 0;
572}
573
574/****************************************************************************/
575static void __exit fini(void)
576{
577 rcu_assign_pointer(set_h245_addr_hook, NULL);
578 rcu_assign_pointer(set_h225_addr_hook, NULL);
579 rcu_assign_pointer(set_sig_addr_hook, NULL);
580 rcu_assign_pointer(set_ras_addr_hook, NULL);
581 rcu_assign_pointer(nat_rtp_rtcp_hook, NULL);
582 rcu_assign_pointer(nat_t120_hook, NULL);
583 rcu_assign_pointer(nat_h245_hook, NULL);
584 rcu_assign_pointer(nat_callforwarding_hook, NULL);
585 rcu_assign_pointer(nat_q931_hook, NULL);
586 synchronize_rcu();
587}
588
589/****************************************************************************/
590module_init(init);
591module_exit(fini);
592
593MODULE_AUTHOR("Jing Min Zhao <zhaojingmin@users.sourceforge.net>");
594MODULE_DESCRIPTION("H.323 NAT helper");
595MODULE_LICENSE("GPL");
596MODULE_ALIAS("ip_nat_h323");