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