2 * H.323 extension for NAT alteration.
4 * Copyright (c) 2006 Jing Min Zhao <zhaojingmin@users.sourceforge.net>
6 * This source code is licensed under General Public License version 2.
8 * Based on the 'brute force' H.323 NAT module by
9 * Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
12 #include <linux/module.h>
13 #include <linux/netfilter_ipv4.h>
14 #include <linux/netfilter.h>
16 #include <linux/tcp.h>
17 #include <linux/moduleparam.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_h323.h>
24 #include <linux/netfilter_ipv4/ip_conntrack_helper.h>
29 #define DEBUGP(format, args...)
32 /****************************************************************************/
33 static int set_addr(struct sk_buff
**pskb
,
34 unsigned char **data
, int dataoff
,
35 unsigned int addroff
, __be32 ip
, u_int16_t port
)
37 enum ip_conntrack_info ctinfo
;
38 struct ip_conntrack
*ct
= ip_conntrack_get(*pskb
, &ctinfo
);
42 } __attribute__ ((__packed__
)) buf
;
43 struct tcphdr _tcph
, *th
;
46 buf
.port
= htons(port
);
49 if ((*pskb
)->nh
.iph
->protocol
== IPPROTO_TCP
) {
50 if (!ip_nat_mangle_tcp_packet(pskb
, ct
, ctinfo
,
52 (char *) &buf
, sizeof(buf
))) {
54 printk("ip_nat_h323: ip_nat_mangle_tcp_packet"
59 /* Relocate data pointer */
60 th
= skb_header_pointer(*pskb
, (*pskb
)->nh
.iph
->ihl
* 4,
61 sizeof(_tcph
), &_tcph
);
64 *data
= (*pskb
)->data
+ (*pskb
)->nh
.iph
->ihl
* 4 +
65 th
->doff
* 4 + dataoff
;
67 if (!ip_nat_mangle_udp_packet(pskb
, ct
, ctinfo
,
69 (char *) &buf
, sizeof(buf
))) {
71 printk("ip_nat_h323: ip_nat_mangle_udp_packet"
75 /* ip_nat_mangle_udp_packet uses skb_make_writable() to copy
76 * or pull everything in a linear buffer, so we can safely
77 * use the skb pointers now */
78 *data
= (*pskb
)->data
+ (*pskb
)->nh
.iph
->ihl
* 4 +
79 sizeof(struct udphdr
);
85 /****************************************************************************/
86 static int set_h225_addr(struct sk_buff
**pskb
,
87 unsigned char **data
, int dataoff
,
88 TransportAddress
* addr
,
89 __be32 ip
, u_int16_t port
)
91 return set_addr(pskb
, data
, dataoff
, addr
->ipAddress
.ip
, ip
, port
);
94 /****************************************************************************/
95 static int set_h245_addr(struct sk_buff
**pskb
,
96 unsigned char **data
, int dataoff
,
97 H245_TransportAddress
* addr
,
98 __be32 ip
, u_int16_t port
)
100 return set_addr(pskb
, data
, dataoff
,
101 addr
->unicastAddress
.iPAddress
.network
, ip
, port
);
104 /****************************************************************************/
105 static int set_sig_addr(struct sk_buff
**pskb
, struct ip_conntrack
*ct
,
106 enum ip_conntrack_info ctinfo
,
107 unsigned char **data
,
108 TransportAddress
* addr
, int count
)
110 struct ip_ct_h323_master
*info
= &ct
->help
.ct_h323_info
;
111 int dir
= CTINFO2DIR(ctinfo
);
116 for (i
= 0; i
< count
; i
++) {
117 if (get_h225_addr(*data
, &addr
[i
], &ip
, &port
)) {
118 if (ip
== ct
->tuplehash
[dir
].tuple
.src
.ip
&&
119 port
== info
->sig_port
[dir
]) {
122 /* Fix for Gnomemeeting */
124 get_h225_addr(*data
, &addr
[0],
126 (ntohl(ip
) & 0xff000000) == 0x7f000000)
130 ("ip_nat_ras: set signal address "
131 "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
133 NIPQUAD(ct
->tuplehash
[!dir
].tuple
.dst
.
134 ip
), info
->sig_port
[!dir
]);
135 return set_h225_addr(pskb
, data
, 0, &addr
[i
],
138 info
->sig_port
[!dir
]);
139 } else if (ip
== ct
->tuplehash
[dir
].tuple
.dst
.ip
&&
140 port
== info
->sig_port
[dir
]) {
143 ("ip_nat_ras: set signal address "
144 "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
146 NIPQUAD(ct
->tuplehash
[!dir
].tuple
.src
.
147 ip
), info
->sig_port
[!dir
]);
148 return set_h225_addr(pskb
, data
, 0, &addr
[i
],
151 info
->sig_port
[!dir
]);
159 /****************************************************************************/
160 static int set_ras_addr(struct sk_buff
**pskb
, struct ip_conntrack
*ct
,
161 enum ip_conntrack_info ctinfo
,
162 unsigned char **data
,
163 TransportAddress
* addr
, int count
)
165 int dir
= CTINFO2DIR(ctinfo
);
170 for (i
= 0; i
< count
; i
++) {
171 if (get_h225_addr(*data
, &addr
[i
], &ip
, &port
) &&
172 ip
== ct
->tuplehash
[dir
].tuple
.src
.ip
&&
173 port
== ntohs(ct
->tuplehash
[dir
].tuple
.src
.u
.udp
.port
)) {
174 DEBUGP("ip_nat_ras: set rasAddress "
175 "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
177 NIPQUAD(ct
->tuplehash
[!dir
].tuple
.dst
.ip
),
178 ntohs(ct
->tuplehash
[!dir
].tuple
.dst
.u
.udp
.
180 return set_h225_addr(pskb
, data
, 0, &addr
[i
],
181 ct
->tuplehash
[!dir
].tuple
.dst
.ip
,
182 ntohs(ct
->tuplehash
[!dir
].tuple
.
190 /****************************************************************************/
191 static int nat_rtp_rtcp(struct sk_buff
**pskb
, struct ip_conntrack
*ct
,
192 enum ip_conntrack_info ctinfo
,
193 unsigned char **data
, int dataoff
,
194 H245_TransportAddress
* addr
,
195 u_int16_t port
, u_int16_t rtp_port
,
196 struct ip_conntrack_expect
*rtp_exp
,
197 struct ip_conntrack_expect
*rtcp_exp
)
199 struct ip_ct_h323_master
*info
= &ct
->help
.ct_h323_info
;
200 int dir
= CTINFO2DIR(ctinfo
);
202 u_int16_t nated_port
;
204 /* Set expectations for NAT */
205 rtp_exp
->saved_proto
.udp
.port
= rtp_exp
->tuple
.dst
.u
.udp
.port
;
206 rtp_exp
->expectfn
= ip_nat_follow_master
;
208 rtcp_exp
->saved_proto
.udp
.port
= rtcp_exp
->tuple
.dst
.u
.udp
.port
;
209 rtcp_exp
->expectfn
= ip_nat_follow_master
;
210 rtcp_exp
->dir
= !dir
;
212 /* Lookup existing expects */
213 for (i
= 0; i
< H323_RTP_CHANNEL_MAX
; i
++) {
214 if (info
->rtp_port
[i
][dir
] == rtp_port
) {
217 /* Use allocated ports first. This will refresh
219 rtp_exp
->tuple
.dst
.u
.udp
.port
=
220 htons(info
->rtp_port
[i
][dir
]);
221 rtcp_exp
->tuple
.dst
.u
.udp
.port
=
222 htons(info
->rtp_port
[i
][dir
] + 1);
224 } else if (info
->rtp_port
[i
][dir
] == 0) {
230 /* Run out of expectations */
231 if (i
>= H323_RTP_CHANNEL_MAX
) {
233 printk("ip_nat_h323: out of expectations\n");
237 /* Try to get a pair of ports. */
238 for (nated_port
= ntohs(rtp_exp
->tuple
.dst
.u
.udp
.port
);
239 nated_port
!= 0; nated_port
+= 2) {
240 rtp_exp
->tuple
.dst
.u
.udp
.port
= htons(nated_port
);
241 if (ip_conntrack_expect_related(rtp_exp
) == 0) {
242 rtcp_exp
->tuple
.dst
.u
.udp
.port
=
243 htons(nated_port
+ 1);
244 if (ip_conntrack_expect_related(rtcp_exp
) == 0)
246 ip_conntrack_unexpect_related(rtp_exp
);
250 if (nated_port
== 0) { /* No port available */
252 printk("ip_nat_h323: out of RTP ports\n");
257 if (set_h245_addr(pskb
, data
, dataoff
, addr
,
258 ct
->tuplehash
[!dir
].tuple
.dst
.ip
,
259 (port
& 1) ? nated_port
+ 1 : nated_port
) == 0) {
261 info
->rtp_port
[i
][dir
] = rtp_port
;
262 info
->rtp_port
[i
][!dir
] = nated_port
;
264 ip_conntrack_unexpect_related(rtp_exp
);
265 ip_conntrack_unexpect_related(rtcp_exp
);
270 DEBUGP("ip_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("ip_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
));
284 /****************************************************************************/
285 static int nat_t120(struct sk_buff
**pskb
, struct ip_conntrack
*ct
,
286 enum ip_conntrack_info ctinfo
,
287 unsigned char **data
, int dataoff
,
288 H245_TransportAddress
* addr
, u_int16_t port
,
289 struct ip_conntrack_expect
*exp
)
291 int dir
= CTINFO2DIR(ctinfo
);
292 u_int16_t nated_port
= port
;
294 /* Set expectations for NAT */
295 exp
->saved_proto
.tcp
.port
= exp
->tuple
.dst
.u
.tcp
.port
;
296 exp
->expectfn
= ip_nat_follow_master
;
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 (ip_conntrack_expect_related(exp
) == 0)
306 if (nated_port
== 0) { /* No port available */
308 printk("ip_nat_h323: out of TCP ports\n");
313 if (set_h245_addr(pskb
, data
, dataoff
, addr
,
314 ct
->tuplehash
[!dir
].tuple
.dst
.ip
, nated_port
) < 0) {
315 ip_conntrack_unexpect_related(exp
);
319 DEBUGP("ip_nat_h323: expect T.120 %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
320 NIPQUAD(exp
->tuple
.src
.ip
), ntohs(exp
->tuple
.src
.u
.tcp
.port
),
321 NIPQUAD(exp
->tuple
.dst
.ip
), ntohs(exp
->tuple
.dst
.u
.tcp
.port
));
326 /****************************************************************************
327 * This conntrack expect function replaces ip_conntrack_h245_expect()
328 * which was set by ip_conntrack_helper_h323.c. It calls both
329 * ip_nat_follow_master() and ip_conntrack_h245_expect()
330 ****************************************************************************/
331 static void ip_nat_h245_expect(struct ip_conntrack
*new,
332 struct ip_conntrack_expect
*this)
334 ip_nat_follow_master(new, this);
335 ip_conntrack_h245_expect(new, this);
338 /****************************************************************************/
339 static int nat_h245(struct sk_buff
**pskb
, struct ip_conntrack
*ct
,
340 enum ip_conntrack_info ctinfo
,
341 unsigned char **data
, int dataoff
,
342 TransportAddress
* addr
, u_int16_t port
,
343 struct ip_conntrack_expect
*exp
)
345 struct ip_ct_h323_master
*info
= &ct
->help
.ct_h323_info
;
346 int dir
= CTINFO2DIR(ctinfo
);
347 u_int16_t nated_port
= port
;
349 /* Set expectations for NAT */
350 exp
->saved_proto
.tcp
.port
= exp
->tuple
.dst
.u
.tcp
.port
;
351 exp
->expectfn
= ip_nat_h245_expect
;
354 /* Check existing expects */
355 if (info
->sig_port
[dir
] == port
)
356 nated_port
= info
->sig_port
[!dir
];
358 /* Try to get same port: if not, try to change it. */
359 for (; nated_port
!= 0; nated_port
++) {
360 exp
->tuple
.dst
.u
.tcp
.port
= htons(nated_port
);
361 if (ip_conntrack_expect_related(exp
) == 0)
365 if (nated_port
== 0) { /* No port available */
367 printk("ip_nat_q931: out of TCP ports\n");
372 if (set_h225_addr(pskb
, data
, dataoff
, addr
,
373 ct
->tuplehash
[!dir
].tuple
.dst
.ip
,
376 info
->sig_port
[dir
] = port
;
377 info
->sig_port
[!dir
] = nated_port
;
379 ip_conntrack_unexpect_related(exp
);
383 DEBUGP("ip_nat_q931: expect H.245 %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
384 NIPQUAD(exp
->tuple
.src
.ip
), ntohs(exp
->tuple
.src
.u
.tcp
.port
),
385 NIPQUAD(exp
->tuple
.dst
.ip
), ntohs(exp
->tuple
.dst
.u
.tcp
.port
));
390 /****************************************************************************
391 * This conntrack expect function replaces ip_conntrack_q931_expect()
392 * which was set by ip_conntrack_helper_h323.c.
393 ****************************************************************************/
394 static void ip_nat_q931_expect(struct ip_conntrack
*new,
395 struct ip_conntrack_expect
*this)
397 struct ip_nat_range range
;
399 if (this->tuple
.src
.ip
!= 0) { /* Only accept calls from GK */
400 ip_nat_follow_master(new, this);
404 /* This must be a fresh one. */
405 BUG_ON(new->status
& IPS_NAT_DONE_MASK
);
407 /* Change src to where master sends to */
408 range
.flags
= IP_NAT_RANGE_MAP_IPS
;
409 range
.min_ip
= range
.max_ip
= new->tuplehash
[!this->dir
].tuple
.src
.ip
;
411 /* hook doesn't matter, but it has to do source manip */
412 ip_nat_setup_info(new, &range
, NF_IP_POST_ROUTING
);
414 /* For DST manip, map port here to where it's expected. */
415 range
.flags
= (IP_NAT_RANGE_MAP_IPS
| IP_NAT_RANGE_PROTO_SPECIFIED
);
416 range
.min
= range
.max
= this->saved_proto
;
417 range
.min_ip
= range
.max_ip
=
418 new->master
->tuplehash
[!this->dir
].tuple
.src
.ip
;
420 /* hook doesn't matter, but it has to do destination manip */
421 ip_nat_setup_info(new, &range
, NF_IP_PRE_ROUTING
);
424 ip_conntrack_q931_expect(new, this);
427 /****************************************************************************/
428 static int nat_q931(struct sk_buff
**pskb
, struct ip_conntrack
*ct
,
429 enum ip_conntrack_info ctinfo
,
430 unsigned char **data
, TransportAddress
* addr
, int idx
,
431 u_int16_t port
, struct ip_conntrack_expect
*exp
)
433 struct ip_ct_h323_master
*info
= &ct
->help
.ct_h323_info
;
434 int dir
= CTINFO2DIR(ctinfo
);
435 u_int16_t nated_port
= port
;
438 /* Set expectations for NAT */
439 exp
->saved_proto
.tcp
.port
= exp
->tuple
.dst
.u
.tcp
.port
;
440 exp
->expectfn
= ip_nat_q931_expect
;
443 /* Check existing expects */
444 if (info
->sig_port
[dir
] == port
)
445 nated_port
= info
->sig_port
[!dir
];
447 /* Try to get same port: if not, try to change it. */
448 for (; nated_port
!= 0; nated_port
++) {
449 exp
->tuple
.dst
.u
.tcp
.port
= htons(nated_port
);
450 if (ip_conntrack_expect_related(exp
) == 0)
454 if (nated_port
== 0) { /* No port available */
456 printk("ip_nat_ras: out of TCP ports\n");
461 if (set_h225_addr(pskb
, data
, 0, &addr
[idx
],
462 ct
->tuplehash
[!dir
].tuple
.dst
.ip
,
465 info
->sig_port
[dir
] = port
;
466 info
->sig_port
[!dir
] = nated_port
;
468 /* Fix for Gnomemeeting */
470 get_h225_addr(*data
, &addr
[0], &ip
, &port
) &&
471 (ntohl(ip
) & 0xff000000) == 0x7f000000) {
472 set_h225_addr_hook(pskb
, data
, 0, &addr
[0],
473 ct
->tuplehash
[!dir
].tuple
.dst
.ip
,
474 info
->sig_port
[!dir
]);
477 ip_conntrack_unexpect_related(exp
);
482 DEBUGP("ip_nat_ras: expect Q.931 %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
483 NIPQUAD(exp
->tuple
.src
.ip
), ntohs(exp
->tuple
.src
.u
.tcp
.port
),
484 NIPQUAD(exp
->tuple
.dst
.ip
), ntohs(exp
->tuple
.dst
.u
.tcp
.port
));
489 /****************************************************************************/
490 static void ip_nat_callforwarding_expect(struct ip_conntrack
*new,
491 struct ip_conntrack_expect
*this)
493 struct ip_nat_range range
;
495 /* This must be a fresh one. */
496 BUG_ON(new->status
& IPS_NAT_DONE_MASK
);
498 /* Change src to where master sends to */
499 range
.flags
= IP_NAT_RANGE_MAP_IPS
;
500 range
.min_ip
= range
.max_ip
= new->tuplehash
[!this->dir
].tuple
.src
.ip
;
502 /* hook doesn't matter, but it has to do source manip */
503 ip_nat_setup_info(new, &range
, NF_IP_POST_ROUTING
);
505 /* For DST manip, map port here to where it's expected. */
506 range
.flags
= (IP_NAT_RANGE_MAP_IPS
| IP_NAT_RANGE_PROTO_SPECIFIED
);
507 range
.min
= range
.max
= this->saved_proto
;
508 range
.min_ip
= range
.max_ip
= this->saved_ip
;
510 /* hook doesn't matter, but it has to do destination manip */
511 ip_nat_setup_info(new, &range
, NF_IP_PRE_ROUTING
);
513 ip_conntrack_q931_expect(new, this);
516 /****************************************************************************/
517 static int nat_callforwarding(struct sk_buff
**pskb
, struct ip_conntrack
*ct
,
518 enum ip_conntrack_info ctinfo
,
519 unsigned char **data
, int dataoff
,
520 TransportAddress
* addr
, u_int16_t port
,
521 struct ip_conntrack_expect
*exp
)
523 int dir
= CTINFO2DIR(ctinfo
);
524 u_int16_t nated_port
;
526 /* Set expectations for NAT */
527 exp
->saved_ip
= exp
->tuple
.dst
.ip
;
528 exp
->tuple
.dst
.ip
= ct
->tuplehash
[!dir
].tuple
.dst
.ip
;
529 exp
->saved_proto
.tcp
.port
= exp
->tuple
.dst
.u
.tcp
.port
;
530 exp
->expectfn
= ip_nat_callforwarding_expect
;
533 /* Try to get same port: if not, try to change it. */
534 for (nated_port
= port
; nated_port
!= 0; nated_port
++) {
535 exp
->tuple
.dst
.u
.tcp
.port
= htons(nated_port
);
536 if (ip_conntrack_expect_related(exp
) == 0)
540 if (nated_port
== 0) { /* No port available */
542 printk("ip_nat_q931: out of TCP ports\n");
547 if (!set_h225_addr(pskb
, data
, dataoff
, addr
,
548 ct
->tuplehash
[!dir
].tuple
.dst
.ip
,
550 ip_conntrack_unexpect_related(exp
);
555 DEBUGP("ip_nat_q931: expect Call Forwarding "
556 "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
557 NIPQUAD(exp
->tuple
.src
.ip
), ntohs(exp
->tuple
.src
.u
.tcp
.port
),
558 NIPQUAD(exp
->tuple
.dst
.ip
), ntohs(exp
->tuple
.dst
.u
.tcp
.port
));
563 /****************************************************************************/
564 static int __init
init(void)
566 BUG_ON(rcu_dereference(set_h245_addr_hook
) != NULL
);
567 BUG_ON(rcu_dereference(set_h225_addr_hook
) != NULL
);
568 BUG_ON(rcu_dereference(set_sig_addr_hook
) != NULL
);
569 BUG_ON(rcu_dereference(set_ras_addr_hook
) != NULL
);
570 BUG_ON(rcu_dereference(nat_rtp_rtcp_hook
) != NULL
);
571 BUG_ON(rcu_dereference(nat_t120_hook
) != NULL
);
572 BUG_ON(rcu_dereference(nat_h245_hook
) != NULL
);
573 BUG_ON(rcu_dereference(nat_callforwarding_hook
) != NULL
);
574 BUG_ON(rcu_dereference(nat_q931_hook
) != NULL
);
576 rcu_assign_pointer(set_h245_addr_hook
, set_h245_addr
);
577 rcu_assign_pointer(set_h225_addr_hook
, set_h225_addr
);
578 rcu_assign_pointer(set_sig_addr_hook
, set_sig_addr
);
579 rcu_assign_pointer(set_ras_addr_hook
, set_ras_addr
);
580 rcu_assign_pointer(nat_rtp_rtcp_hook
, nat_rtp_rtcp
);
581 rcu_assign_pointer(nat_t120_hook
, nat_t120
);
582 rcu_assign_pointer(nat_h245_hook
, nat_h245
);
583 rcu_assign_pointer(nat_callforwarding_hook
, nat_callforwarding
);
584 rcu_assign_pointer(nat_q931_hook
, nat_q931
);
586 DEBUGP("ip_nat_h323: init success\n");
590 /****************************************************************************/
591 static void __exit
fini(void)
593 rcu_assign_pointer(set_h245_addr_hook
, NULL
);
594 rcu_assign_pointer(set_h225_addr_hook
, NULL
);
595 rcu_assign_pointer(set_sig_addr_hook
, NULL
);
596 rcu_assign_pointer(set_ras_addr_hook
, NULL
);
597 rcu_assign_pointer(nat_rtp_rtcp_hook
, NULL
);
598 rcu_assign_pointer(nat_t120_hook
, NULL
);
599 rcu_assign_pointer(nat_h245_hook
, NULL
);
600 rcu_assign_pointer(nat_callforwarding_hook
, NULL
);
601 rcu_assign_pointer(nat_q931_hook
, NULL
);
605 /****************************************************************************/
609 MODULE_AUTHOR("Jing Min Zhao <zhaojingmin@users.sourceforge.net>");
610 MODULE_DESCRIPTION("H.323 NAT helper");
611 MODULE_LICENSE("GPL");