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