]>
Commit | Line | Data |
---|---|---|
9fafcd7b PM |
1 | /* SIP extension for UDP NAT alteration. |
2 | * | |
3 | * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar> | |
4 | * based on RR's ip_nat_ftp.c and other modules. | |
f49e1aa1 PM |
5 | * (C) 2007 United Security Providers |
6 | * (C) 2007, 2008 Patrick McHardy <kaber@trash.net> | |
9fafcd7b PM |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License version 2 as | |
10 | * published by the Free Software Foundation. | |
11 | */ | |
12 | ||
13 | #include <linux/module.h> | |
14 | #include <linux/skbuff.h> | |
15 | #include <linux/ip.h> | |
c9bdd4b5 | 16 | #include <net/ip.h> |
9fafcd7b PM |
17 | #include <linux/udp.h> |
18 | ||
19 | #include <net/netfilter/nf_nat.h> | |
20 | #include <net/netfilter/nf_nat_helper.h> | |
21 | #include <net/netfilter/nf_nat_rule.h> | |
22 | #include <net/netfilter/nf_conntrack_helper.h> | |
23 | #include <net/netfilter/nf_conntrack_expect.h> | |
24 | #include <linux/netfilter/nf_conntrack_sip.h> | |
25 | ||
26 | MODULE_LICENSE("GPL"); | |
27 | MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>"); | |
28 | MODULE_DESCRIPTION("SIP NAT helper"); | |
29 | MODULE_ALIAS("ip_nat_sip"); | |
30 | ||
9fafcd7b | 31 | |
2a6cfb22 PM |
32 | static unsigned int mangle_packet(struct sk_buff *skb, |
33 | const char **dptr, unsigned int *datalen, | |
34 | unsigned int matchoff, unsigned int matchlen, | |
35 | const char *buffer, unsigned int buflen) | |
36 | { | |
37 | enum ip_conntrack_info ctinfo; | |
38 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | |
39 | ||
40 | if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, matchoff, matchlen, | |
41 | buffer, buflen)) | |
42 | return 0; | |
43 | ||
44 | /* Reload data pointer and adjust datalen value */ | |
45 | *dptr = skb->data + ip_hdrlen(skb) + sizeof(struct udphdr); | |
46 | *datalen += buflen - matchlen; | |
47 | return 1; | |
48 | } | |
49 | ||
ac367740 PM |
50 | static int map_addr(struct sk_buff *skb, |
51 | const char **dptr, unsigned int *datalen, | |
52 | unsigned int matchoff, unsigned int matchlen, | |
624f8b7b | 53 | union nf_inet_addr *addr, __be16 port) |
9fafcd7b | 54 | { |
212440a7 | 55 | enum ip_conntrack_info ctinfo; |
624f8b7b | 56 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); |
9fafcd7b | 57 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); |
624f8b7b PM |
58 | char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; |
59 | unsigned int buflen; | |
60 | __be32 newaddr; | |
61 | __be16 newport; | |
62 | ||
63 | if (ct->tuplehash[dir].tuple.src.u3.ip == addr->ip && | |
64 | ct->tuplehash[dir].tuple.src.u.udp.port == port) { | |
65 | newaddr = ct->tuplehash[!dir].tuple.dst.u3.ip; | |
66 | newport = ct->tuplehash[!dir].tuple.dst.u.udp.port; | |
67 | } else if (ct->tuplehash[dir].tuple.dst.u3.ip == addr->ip && | |
68 | ct->tuplehash[dir].tuple.dst.u.udp.port == port) { | |
69 | newaddr = ct->tuplehash[!dir].tuple.src.u3.ip; | |
70 | newport = ct->tuplehash[!dir].tuple.src.u.udp.port; | |
9fafcd7b PM |
71 | } else |
72 | return 1; | |
73 | ||
624f8b7b PM |
74 | if (newaddr == addr->ip && newport == port) |
75 | return 1; | |
76 | ||
cffee385 | 77 | buflen = sprintf(buffer, "%pI4:%u", &newaddr, ntohs(newport)); |
624f8b7b | 78 | |
2a6cfb22 | 79 | return mangle_packet(skb, dptr, datalen, matchoff, matchlen, |
624f8b7b | 80 | buffer, buflen); |
9fafcd7b PM |
81 | } |
82 | ||
ac367740 PM |
83 | static int map_sip_addr(struct sk_buff *skb, |
84 | const char **dptr, unsigned int *datalen, | |
624f8b7b | 85 | enum sip_header_types type) |
ac367740 PM |
86 | { |
87 | enum ip_conntrack_info ctinfo; | |
88 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | |
89 | unsigned int matchlen, matchoff; | |
624f8b7b PM |
90 | union nf_inet_addr addr; |
91 | __be16 port; | |
ac367740 | 92 | |
624f8b7b PM |
93 | if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, type, NULL, |
94 | &matchoff, &matchlen, &addr, &port) <= 0) | |
ac367740 | 95 | return 1; |
624f8b7b | 96 | return map_addr(skb, dptr, datalen, matchoff, matchlen, &addr, port); |
ac367740 PM |
97 | } |
98 | ||
3db05fea | 99 | static unsigned int ip_nat_sip(struct sk_buff *skb, |
2a6cfb22 | 100 | const char **dptr, unsigned int *datalen) |
9fafcd7b | 101 | { |
212440a7 PM |
102 | enum ip_conntrack_info ctinfo; |
103 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | |
720ac708 | 104 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); |
c978cd3a | 105 | unsigned int dataoff, matchoff, matchlen; |
624f8b7b PM |
106 | union nf_inet_addr addr; |
107 | __be16 port; | |
c978cd3a | 108 | int request, in_header; |
9fafcd7b | 109 | |
9fafcd7b | 110 | /* Basic rules: requests and responses. */ |
779382eb | 111 | if (strnicmp(*dptr, "SIP/2.0", strlen("SIP/2.0")) != 0) { |
ac367740 | 112 | if (ct_sip_parse_request(ct, *dptr, *datalen, |
624f8b7b PM |
113 | &matchoff, &matchlen, |
114 | &addr, &port) > 0 && | |
115 | !map_addr(skb, dptr, datalen, matchoff, matchlen, | |
116 | &addr, port)) | |
9fafcd7b | 117 | return NF_DROP; |
720ac708 PM |
118 | request = 1; |
119 | } else | |
120 | request = 0; | |
121 | ||
122 | /* Translate topmost Via header and parameters */ | |
123 | if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, | |
124 | SIP_HDR_VIA, NULL, &matchoff, &matchlen, | |
125 | &addr, &port) > 0) { | |
126 | unsigned int matchend, poff, plen, buflen, n; | |
127 | char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; | |
128 | ||
129 | /* We're only interested in headers related to this | |
130 | * connection */ | |
131 | if (request) { | |
132 | if (addr.ip != ct->tuplehash[dir].tuple.src.u3.ip || | |
133 | port != ct->tuplehash[dir].tuple.src.u.udp.port) | |
134 | goto next; | |
135 | } else { | |
136 | if (addr.ip != ct->tuplehash[dir].tuple.dst.u3.ip || | |
137 | port != ct->tuplehash[dir].tuple.dst.u.udp.port) | |
138 | goto next; | |
139 | } | |
140 | ||
141 | if (!map_addr(skb, dptr, datalen, matchoff, matchlen, | |
142 | &addr, port)) | |
143 | return NF_DROP; | |
144 | ||
145 | matchend = matchoff + matchlen; | |
146 | ||
147 | /* The maddr= parameter (RFC 2361) specifies where to send | |
148 | * the reply. */ | |
149 | if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen, | |
150 | "maddr=", &poff, &plen, | |
151 | &addr) > 0 && | |
152 | addr.ip == ct->tuplehash[dir].tuple.src.u3.ip && | |
153 | addr.ip != ct->tuplehash[!dir].tuple.dst.u3.ip) { | |
cffee385 HH |
154 | buflen = sprintf(buffer, "%pI4", |
155 | &ct->tuplehash[!dir].tuple.dst.u3.ip); | |
720ac708 PM |
156 | if (!mangle_packet(skb, dptr, datalen, poff, plen, |
157 | buffer, buflen)) | |
158 | return NF_DROP; | |
159 | } | |
160 | ||
161 | /* The received= parameter (RFC 2361) contains the address | |
162 | * from which the server received the request. */ | |
163 | if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen, | |
164 | "received=", &poff, &plen, | |
165 | &addr) > 0 && | |
166 | addr.ip == ct->tuplehash[dir].tuple.dst.u3.ip && | |
167 | addr.ip != ct->tuplehash[!dir].tuple.src.u3.ip) { | |
cffee385 HH |
168 | buflen = sprintf(buffer, "%pI4", |
169 | &ct->tuplehash[!dir].tuple.src.u3.ip); | |
720ac708 PM |
170 | if (!mangle_packet(skb, dptr, datalen, poff, plen, |
171 | buffer, buflen)) | |
172 | return NF_DROP; | |
173 | } | |
174 | ||
175 | /* The rport= parameter (RFC 3581) contains the port number | |
176 | * from which the server received the request. */ | |
177 | if (ct_sip_parse_numerical_param(ct, *dptr, matchend, *datalen, | |
178 | "rport=", &poff, &plen, | |
179 | &n) > 0 && | |
180 | htons(n) == ct->tuplehash[dir].tuple.dst.u.udp.port && | |
181 | htons(n) != ct->tuplehash[!dir].tuple.src.u.udp.port) { | |
182 | __be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port; | |
183 | buflen = sprintf(buffer, "%u", ntohs(p)); | |
184 | if (!mangle_packet(skb, dptr, datalen, poff, plen, | |
185 | buffer, buflen)) | |
186 | return NF_DROP; | |
187 | } | |
9fafcd7b PM |
188 | } |
189 | ||
720ac708 | 190 | next: |
c978cd3a PM |
191 | /* Translate Contact headers */ |
192 | dataoff = 0; | |
193 | in_header = 0; | |
194 | while (ct_sip_parse_header_uri(ct, *dptr, &dataoff, *datalen, | |
195 | SIP_HDR_CONTACT, &in_header, | |
196 | &matchoff, &matchlen, | |
197 | &addr, &port) > 0) { | |
198 | if (!map_addr(skb, dptr, datalen, matchoff, matchlen, | |
199 | &addr, port)) | |
200 | return NF_DROP; | |
201 | } | |
202 | ||
624f8b7b | 203 | if (!map_sip_addr(skb, dptr, datalen, SIP_HDR_FROM) || |
c978cd3a | 204 | !map_sip_addr(skb, dptr, datalen, SIP_HDR_TO)) |
9fafcd7b PM |
205 | return NF_DROP; |
206 | return NF_ACCEPT; | |
207 | } | |
208 | ||
0f32a40f PM |
209 | /* Handles expected signalling connections and media streams */ |
210 | static void ip_nat_sip_expected(struct nf_conn *ct, | |
211 | struct nf_conntrack_expect *exp) | |
212 | { | |
213 | struct nf_nat_range range; | |
214 | ||
215 | /* This must be a fresh one. */ | |
216 | BUG_ON(ct->status & IPS_NAT_DONE_MASK); | |
217 | ||
218 | /* For DST manip, map port here to where it's expected. */ | |
219 | range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED); | |
220 | range.min = range.max = exp->saved_proto; | |
221 | range.min_ip = range.max_ip = exp->saved_ip; | |
222 | nf_nat_setup_info(ct, &range, IP_NAT_MANIP_DST); | |
223 | ||
224 | /* Change src to where master sends to, but only if the connection | |
225 | * actually came from the same source. */ | |
226 | if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip == | |
227 | ct->master->tuplehash[exp->dir].tuple.src.u3.ip) { | |
228 | range.flags = IP_NAT_RANGE_MAP_IPS; | |
229 | range.min_ip = range.max_ip | |
230 | = ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip; | |
231 | nf_nat_setup_info(ct, &range, IP_NAT_MANIP_SRC); | |
232 | } | |
233 | } | |
234 | ||
235 | static unsigned int ip_nat_sip_expect(struct sk_buff *skb, | |
236 | const char **dptr, unsigned int *datalen, | |
237 | struct nf_conntrack_expect *exp, | |
238 | unsigned int matchoff, | |
239 | unsigned int matchlen) | |
240 | { | |
241 | enum ip_conntrack_info ctinfo; | |
242 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | |
243 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | |
244 | __be32 newip; | |
245 | u_int16_t port; | |
246 | char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; | |
247 | unsigned buflen; | |
248 | ||
249 | /* Connection will come from reply */ | |
250 | if (ct->tuplehash[dir].tuple.src.u3.ip == ct->tuplehash[!dir].tuple.dst.u3.ip) | |
251 | newip = exp->tuple.dst.u3.ip; | |
252 | else | |
253 | newip = ct->tuplehash[!dir].tuple.dst.u3.ip; | |
254 | ||
255 | /* If the signalling port matches the connection's source port in the | |
256 | * original direction, try to use the destination port in the opposite | |
257 | * direction. */ | |
258 | if (exp->tuple.dst.u.udp.port == | |
259 | ct->tuplehash[dir].tuple.src.u.udp.port) | |
260 | port = ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.port); | |
261 | else | |
262 | port = ntohs(exp->tuple.dst.u.udp.port); | |
263 | ||
264 | exp->saved_ip = exp->tuple.dst.u3.ip; | |
265 | exp->tuple.dst.u3.ip = newip; | |
266 | exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port; | |
267 | exp->dir = !dir; | |
268 | exp->expectfn = ip_nat_sip_expected; | |
269 | ||
270 | for (; port != 0; port++) { | |
271 | exp->tuple.dst.u.udp.port = htons(port); | |
272 | if (nf_ct_expect_related(exp) == 0) | |
273 | break; | |
274 | } | |
275 | ||
276 | if (port == 0) | |
277 | return NF_DROP; | |
278 | ||
279 | if (exp->tuple.dst.u3.ip != exp->saved_ip || | |
280 | exp->tuple.dst.u.udp.port != exp->saved_proto.udp.port) { | |
cffee385 | 281 | buflen = sprintf(buffer, "%pI4:%u", &newip, port); |
0f32a40f PM |
282 | if (!mangle_packet(skb, dptr, datalen, matchoff, matchlen, |
283 | buffer, buflen)) | |
284 | goto err; | |
285 | } | |
286 | return NF_ACCEPT; | |
287 | ||
288 | err: | |
289 | nf_ct_unexpect_related(exp); | |
290 | return NF_DROP; | |
291 | } | |
292 | ||
3e9b4600 PM |
293 | static int mangle_content_len(struct sk_buff *skb, |
294 | const char **dptr, unsigned int *datalen) | |
9fafcd7b | 295 | { |
212440a7 PM |
296 | enum ip_conntrack_info ctinfo; |
297 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | |
3e9b4600 PM |
298 | unsigned int matchoff, matchlen; |
299 | char buffer[sizeof("65536")]; | |
300 | int buflen, c_len; | |
9fafcd7b | 301 | |
3e9b4600 PM |
302 | /* Get actual SDP length */ |
303 | if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen, | |
304 | SDP_HDR_VERSION, SDP_HDR_UNSPEC, | |
305 | &matchoff, &matchlen) <= 0) | |
306 | return 0; | |
307 | c_len = *datalen - matchoff + strlen("v="); | |
308 | ||
309 | /* Now, update SDP length */ | |
ea45f12a PM |
310 | if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CONTENT_LENGTH, |
311 | &matchoff, &matchlen) <= 0) | |
9fafcd7b PM |
312 | return 0; |
313 | ||
3e9b4600 | 314 | buflen = sprintf(buffer, "%u", c_len); |
2a6cfb22 | 315 | return mangle_packet(skb, dptr, datalen, matchoff, matchlen, |
3e9b4600 | 316 | buffer, buflen); |
9fafcd7b PM |
317 | } |
318 | ||
c71529e4 HX |
319 | static int mangle_sdp_packet(struct sk_buff *skb, const char **dptr, |
320 | unsigned int dataoff, unsigned int *datalen, | |
321 | enum sdp_header_types type, | |
322 | enum sdp_header_types term, | |
323 | char *buffer, int buflen) | |
9fafcd7b | 324 | { |
212440a7 PM |
325 | enum ip_conntrack_info ctinfo; |
326 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | |
3e9b4600 | 327 | unsigned int matchlen, matchoff; |
9fafcd7b | 328 | |
4ab9e64e | 329 | if (ct_sip_get_sdp_header(ct, *dptr, dataoff, *datalen, type, term, |
3e9b4600 | 330 | &matchoff, &matchlen) <= 0) |
c71529e4 | 331 | return -ENOENT; |
3e9b4600 | 332 | return mangle_packet(skb, dptr, datalen, matchoff, matchlen, |
c71529e4 | 333 | buffer, buflen) ? 0 : -EINVAL; |
9fafcd7b PM |
334 | } |
335 | ||
4ab9e64e PM |
336 | static unsigned int ip_nat_sdp_addr(struct sk_buff *skb, const char **dptr, |
337 | unsigned int dataoff, | |
338 | unsigned int *datalen, | |
339 | enum sdp_header_types type, | |
340 | enum sdp_header_types term, | |
341 | const union nf_inet_addr *addr) | |
9fafcd7b PM |
342 | { |
343 | char buffer[sizeof("nnn.nnn.nnn.nnn")]; | |
4ab9e64e | 344 | unsigned int buflen; |
9fafcd7b | 345 | |
cffee385 | 346 | buflen = sprintf(buffer, "%pI4", &addr->ip); |
c71529e4 HX |
347 | if (mangle_sdp_packet(skb, dptr, dataoff, datalen, type, term, |
348 | buffer, buflen)) | |
9fafcd7b PM |
349 | return 0; |
350 | ||
4ab9e64e PM |
351 | return mangle_content_len(skb, dptr, datalen); |
352 | } | |
353 | ||
354 | static unsigned int ip_nat_sdp_port(struct sk_buff *skb, | |
355 | const char **dptr, | |
356 | unsigned int *datalen, | |
357 | unsigned int matchoff, | |
358 | unsigned int matchlen, | |
359 | u_int16_t port) | |
360 | { | |
361 | char buffer[sizeof("nnnnn")]; | |
362 | unsigned int buflen; | |
363 | ||
364 | buflen = sprintf(buffer, "%u", port); | |
365 | if (!mangle_packet(skb, dptr, datalen, matchoff, matchlen, | |
366 | buffer, buflen)) | |
9fafcd7b PM |
367 | return 0; |
368 | ||
4ab9e64e PM |
369 | return mangle_content_len(skb, dptr, datalen); |
370 | } | |
371 | ||
372 | static unsigned int ip_nat_sdp_session(struct sk_buff *skb, const char **dptr, | |
373 | unsigned int dataoff, | |
374 | unsigned int *datalen, | |
375 | const union nf_inet_addr *addr) | |
376 | { | |
377 | char buffer[sizeof("nnn.nnn.nnn.nnn")]; | |
378 | unsigned int buflen; | |
379 | ||
380 | /* Mangle session description owner and contact addresses */ | |
cffee385 | 381 | buflen = sprintf(buffer, "%pI4", &addr->ip); |
c71529e4 | 382 | if (mangle_sdp_packet(skb, dptr, dataoff, datalen, |
4ab9e64e PM |
383 | SDP_HDR_OWNER_IP4, SDP_HDR_MEDIA, |
384 | buffer, buflen)) | |
385 | return 0; | |
386 | ||
c71529e4 HX |
387 | switch (mangle_sdp_packet(skb, dptr, dataoff, datalen, |
388 | SDP_HDR_CONNECTION_IP4, SDP_HDR_MEDIA, | |
389 | buffer, buflen)) { | |
390 | case 0: | |
391 | /* | |
392 | * RFC 2327: | |
393 | * | |
394 | * Session description | |
395 | * | |
396 | * c=* (connection information - not required if included in all media) | |
397 | */ | |
398 | case -ENOENT: | |
399 | break; | |
400 | default: | |
9fafcd7b | 401 | return 0; |
c71529e4 | 402 | } |
9fafcd7b | 403 | |
212440a7 | 404 | return mangle_content_len(skb, dptr, datalen); |
9fafcd7b PM |
405 | } |
406 | ||
407 | /* So, this packet has hit the connection tracking matching code. | |
408 | Mangle it, and change the expectation to match the new version. */ | |
4ab9e64e PM |
409 | static unsigned int ip_nat_sdp_media(struct sk_buff *skb, |
410 | const char **dptr, | |
411 | unsigned int *datalen, | |
412 | struct nf_conntrack_expect *rtp_exp, | |
413 | struct nf_conntrack_expect *rtcp_exp, | |
414 | unsigned int mediaoff, | |
415 | unsigned int medialen, | |
416 | union nf_inet_addr *rtp_addr) | |
9fafcd7b | 417 | { |
212440a7 PM |
418 | enum ip_conntrack_info ctinfo; |
419 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | |
9fafcd7b | 420 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); |
9fafcd7b PM |
421 | u_int16_t port; |
422 | ||
9fafcd7b | 423 | /* Connection will come from reply */ |
f4a607bf JB |
424 | if (ct->tuplehash[dir].tuple.src.u3.ip == |
425 | ct->tuplehash[!dir].tuple.dst.u3.ip) | |
4ab9e64e | 426 | rtp_addr->ip = rtp_exp->tuple.dst.u3.ip; |
f4a607bf | 427 | else |
4ab9e64e | 428 | rtp_addr->ip = ct->tuplehash[!dir].tuple.dst.u3.ip; |
9fafcd7b | 429 | |
a9c1d359 | 430 | rtp_exp->saved_ip = rtp_exp->tuple.dst.u3.ip; |
4ab9e64e | 431 | rtp_exp->tuple.dst.u3.ip = rtp_addr->ip; |
a9c1d359 PM |
432 | rtp_exp->saved_proto.udp.port = rtp_exp->tuple.dst.u.udp.port; |
433 | rtp_exp->dir = !dir; | |
434 | rtp_exp->expectfn = ip_nat_sip_expected; | |
435 | ||
436 | rtcp_exp->saved_ip = rtcp_exp->tuple.dst.u3.ip; | |
4ab9e64e | 437 | rtcp_exp->tuple.dst.u3.ip = rtp_addr->ip; |
a9c1d359 PM |
438 | rtcp_exp->saved_proto.udp.port = rtcp_exp->tuple.dst.u.udp.port; |
439 | rtcp_exp->dir = !dir; | |
440 | rtcp_exp->expectfn = ip_nat_sip_expected; | |
441 | ||
442 | /* Try to get same pair of ports: if not, try to change them. */ | |
443 | for (port = ntohs(rtp_exp->tuple.dst.u.udp.port); | |
444 | port != 0; port += 2) { | |
445 | rtp_exp->tuple.dst.u.udp.port = htons(port); | |
446 | if (nf_ct_expect_related(rtp_exp) != 0) | |
447 | continue; | |
448 | rtcp_exp->tuple.dst.u.udp.port = htons(port + 1); | |
449 | if (nf_ct_expect_related(rtcp_exp) == 0) | |
9fafcd7b | 450 | break; |
a9c1d359 | 451 | nf_ct_unexpect_related(rtp_exp); |
9fafcd7b PM |
452 | } |
453 | ||
454 | if (port == 0) | |
4ab9e64e PM |
455 | goto err1; |
456 | ||
457 | /* Update media port. */ | |
458 | if (rtp_exp->tuple.dst.u.udp.port != rtp_exp->saved_proto.udp.port && | |
459 | !ip_nat_sdp_port(skb, dptr, datalen, mediaoff, medialen, port)) | |
460 | goto err2; | |
9fafcd7b | 461 | |
9fafcd7b | 462 | return NF_ACCEPT; |
4ab9e64e PM |
463 | |
464 | err2: | |
465 | nf_ct_unexpect_related(rtp_exp); | |
466 | nf_ct_unexpect_related(rtcp_exp); | |
467 | err1: | |
468 | return NF_DROP; | |
9fafcd7b PM |
469 | } |
470 | ||
471 | static void __exit nf_nat_sip_fini(void) | |
472 | { | |
473 | rcu_assign_pointer(nf_nat_sip_hook, NULL); | |
0f32a40f | 474 | rcu_assign_pointer(nf_nat_sip_expect_hook, NULL); |
4ab9e64e | 475 | rcu_assign_pointer(nf_nat_sdp_addr_hook, NULL); |
c7f485ab | 476 | rcu_assign_pointer(nf_nat_sdp_port_hook, NULL); |
4ab9e64e PM |
477 | rcu_assign_pointer(nf_nat_sdp_session_hook, NULL); |
478 | rcu_assign_pointer(nf_nat_sdp_media_hook, NULL); | |
9fafcd7b PM |
479 | synchronize_rcu(); |
480 | } | |
481 | ||
482 | static int __init nf_nat_sip_init(void) | |
483 | { | |
d1332e0a | 484 | BUG_ON(nf_nat_sip_hook != NULL); |
0f32a40f | 485 | BUG_ON(nf_nat_sip_expect_hook != NULL); |
4ab9e64e | 486 | BUG_ON(nf_nat_sdp_addr_hook != NULL); |
c7f485ab | 487 | BUG_ON(nf_nat_sdp_port_hook != NULL); |
4ab9e64e PM |
488 | BUG_ON(nf_nat_sdp_session_hook != NULL); |
489 | BUG_ON(nf_nat_sdp_media_hook != NULL); | |
9fafcd7b | 490 | rcu_assign_pointer(nf_nat_sip_hook, ip_nat_sip); |
0f32a40f | 491 | rcu_assign_pointer(nf_nat_sip_expect_hook, ip_nat_sip_expect); |
4ab9e64e | 492 | rcu_assign_pointer(nf_nat_sdp_addr_hook, ip_nat_sdp_addr); |
c7f485ab | 493 | rcu_assign_pointer(nf_nat_sdp_port_hook, ip_nat_sdp_port); |
4ab9e64e PM |
494 | rcu_assign_pointer(nf_nat_sdp_session_hook, ip_nat_sdp_session); |
495 | rcu_assign_pointer(nf_nat_sdp_media_hook, ip_nat_sdp_media); | |
9fafcd7b PM |
496 | return 0; |
497 | } | |
498 | ||
499 | module_init(nf_nat_sip_init); | |
500 | module_exit(nf_nat_sip_fini); |