netfilter: nf_nat: add protoff argument to packet mangling functions
[deliverable/linux.git] / net / ipv4 / netfilter / nf_nat_sip.c
CommitLineData
48f8ac26 1/* SIP extension for NAT alteration.
9fafcd7b
PM
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 17#include <linux/udp.h>
48f8ac26 18#include <linux/tcp.h>
9fafcd7b
PM
19
20#include <net/netfilter/nf_nat.h>
21#include <net/netfilter/nf_nat_helper.h>
22#include <net/netfilter/nf_nat_rule.h>
23#include <net/netfilter/nf_conntrack_helper.h>
24#include <net/netfilter/nf_conntrack_expect.h>
25#include <linux/netfilter/nf_conntrack_sip.h>
26
27MODULE_LICENSE("GPL");
28MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>");
29MODULE_DESCRIPTION("SIP NAT helper");
30MODULE_ALIAS("ip_nat_sip");
31
9fafcd7b 32
051966c0
PM
33static unsigned int mangle_packet(struct sk_buff *skb, unsigned int protoff,
34 unsigned int dataoff,
2a6cfb22
PM
35 const char **dptr, unsigned int *datalen,
36 unsigned int matchoff, unsigned int matchlen,
37 const char *buffer, unsigned int buflen)
38{
39 enum ip_conntrack_info ctinfo;
40 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
48f8ac26
PM
41 struct tcphdr *th;
42 unsigned int baseoff;
43
44 if (nf_ct_protonum(ct) == IPPROTO_TCP) {
45 th = (struct tcphdr *)(skb->data + ip_hdrlen(skb));
46 baseoff = ip_hdrlen(skb) + th->doff * 4;
47 matchoff += dataoff - baseoff;
48
49 if (!__nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
051966c0 50 protoff, matchoff, matchlen,
48f8ac26
PM
51 buffer, buflen, false))
52 return 0;
53 } else {
54 baseoff = ip_hdrlen(skb) + sizeof(struct udphdr);
55 matchoff += dataoff - baseoff;
56
57 if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo,
051966c0 58 protoff, matchoff, matchlen,
48f8ac26
PM
59 buffer, buflen))
60 return 0;
61 }
2a6cfb22
PM
62
63 /* Reload data pointer and adjust datalen value */
3b6b9fab 64 *dptr = skb->data + dataoff;
2a6cfb22
PM
65 *datalen += buflen - matchlen;
66 return 1;
67}
68
051966c0
PM
69static int map_addr(struct sk_buff *skb, unsigned int protoff,
70 unsigned int dataoff,
ac367740
PM
71 const char **dptr, unsigned int *datalen,
72 unsigned int matchoff, unsigned int matchlen,
624f8b7b 73 union nf_inet_addr *addr, __be16 port)
9fafcd7b 74{
212440a7 75 enum ip_conntrack_info ctinfo;
624f8b7b 76 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
9fafcd7b 77 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
624f8b7b
PM
78 char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
79 unsigned int buflen;
80 __be32 newaddr;
81 __be16 newport;
82
83 if (ct->tuplehash[dir].tuple.src.u3.ip == addr->ip &&
84 ct->tuplehash[dir].tuple.src.u.udp.port == port) {
85 newaddr = ct->tuplehash[!dir].tuple.dst.u3.ip;
86 newport = ct->tuplehash[!dir].tuple.dst.u.udp.port;
87 } else if (ct->tuplehash[dir].tuple.dst.u3.ip == addr->ip &&
88 ct->tuplehash[dir].tuple.dst.u.udp.port == port) {
89 newaddr = ct->tuplehash[!dir].tuple.src.u3.ip;
90 newport = ct->tuplehash[!dir].tuple.src.u.udp.port;
9fafcd7b
PM
91 } else
92 return 1;
93
624f8b7b
PM
94 if (newaddr == addr->ip && newport == port)
95 return 1;
96
cffee385 97 buflen = sprintf(buffer, "%pI4:%u", &newaddr, ntohs(newport));
624f8b7b 98
051966c0
PM
99 return mangle_packet(skb, protoff, dataoff, dptr, datalen,
100 matchoff, matchlen, buffer, buflen);
9fafcd7b
PM
101}
102
051966c0
PM
103static int map_sip_addr(struct sk_buff *skb, unsigned int protoff,
104 unsigned int dataoff,
ac367740 105 const char **dptr, unsigned int *datalen,
624f8b7b 106 enum sip_header_types type)
ac367740
PM
107{
108 enum ip_conntrack_info ctinfo;
109 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
110 unsigned int matchlen, matchoff;
624f8b7b
PM
111 union nf_inet_addr addr;
112 __be16 port;
ac367740 113
624f8b7b
PM
114 if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, type, NULL,
115 &matchoff, &matchlen, &addr, &port) <= 0)
ac367740 116 return 1;
051966c0
PM
117 return map_addr(skb, protoff, dataoff, dptr, datalen,
118 matchoff, matchlen, &addr, port);
ac367740
PM
119}
120
051966c0
PM
121static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int protoff,
122 unsigned int dataoff,
2a6cfb22 123 const char **dptr, unsigned int *datalen)
9fafcd7b 124{
212440a7
PM
125 enum ip_conntrack_info ctinfo;
126 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
720ac708 127 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
3b6b9fab 128 unsigned int coff, matchoff, matchlen;
48f8ac26 129 enum sip_header_types hdr;
624f8b7b
PM
130 union nf_inet_addr addr;
131 __be16 port;
c978cd3a 132 int request, in_header;
9fafcd7b 133
9fafcd7b 134 /* Basic rules: requests and responses. */
779382eb 135 if (strnicmp(*dptr, "SIP/2.0", strlen("SIP/2.0")) != 0) {
ac367740 136 if (ct_sip_parse_request(ct, *dptr, *datalen,
624f8b7b
PM
137 &matchoff, &matchlen,
138 &addr, &port) > 0 &&
051966c0
PM
139 !map_addr(skb, protoff, dataoff, dptr, datalen,
140 matchoff, matchlen, &addr, port))
9fafcd7b 141 return NF_DROP;
720ac708
PM
142 request = 1;
143 } else
144 request = 0;
145
48f8ac26
PM
146 if (nf_ct_protonum(ct) == IPPROTO_TCP)
147 hdr = SIP_HDR_VIA_TCP;
148 else
149 hdr = SIP_HDR_VIA_UDP;
150
720ac708
PM
151 /* Translate topmost Via header and parameters */
152 if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen,
48f8ac26 153 hdr, NULL, &matchoff, &matchlen,
720ac708 154 &addr, &port) > 0) {
f22eb25c 155 unsigned int olen, matchend, poff, plen, buflen, n;
720ac708
PM
156 char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
157
158 /* We're only interested in headers related to this
159 * connection */
160 if (request) {
161 if (addr.ip != ct->tuplehash[dir].tuple.src.u3.ip ||
162 port != ct->tuplehash[dir].tuple.src.u.udp.port)
163 goto next;
164 } else {
165 if (addr.ip != ct->tuplehash[dir].tuple.dst.u3.ip ||
166 port != ct->tuplehash[dir].tuple.dst.u.udp.port)
167 goto next;
168 }
169
f22eb25c 170 olen = *datalen;
051966c0
PM
171 if (!map_addr(skb, protoff, dataoff, dptr, datalen,
172 matchoff, matchlen, &addr, port))
720ac708
PM
173 return NF_DROP;
174
f22eb25c 175 matchend = matchoff + matchlen + *datalen - olen;
720ac708
PM
176
177 /* The maddr= parameter (RFC 2361) specifies where to send
178 * the reply. */
179 if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen,
180 "maddr=", &poff, &plen,
02b69cbd 181 &addr, true) > 0 &&
720ac708
PM
182 addr.ip == ct->tuplehash[dir].tuple.src.u3.ip &&
183 addr.ip != ct->tuplehash[!dir].tuple.dst.u3.ip) {
cffee385
HH
184 buflen = sprintf(buffer, "%pI4",
185 &ct->tuplehash[!dir].tuple.dst.u3.ip);
051966c0 186 if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
3b6b9fab 187 poff, plen, buffer, buflen))
720ac708
PM
188 return NF_DROP;
189 }
190
191 /* The received= parameter (RFC 2361) contains the address
192 * from which the server received the request. */
193 if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen,
194 "received=", &poff, &plen,
02b69cbd 195 &addr, false) > 0 &&
720ac708
PM
196 addr.ip == ct->tuplehash[dir].tuple.dst.u3.ip &&
197 addr.ip != ct->tuplehash[!dir].tuple.src.u3.ip) {
cffee385
HH
198 buflen = sprintf(buffer, "%pI4",
199 &ct->tuplehash[!dir].tuple.src.u3.ip);
051966c0 200 if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
3b6b9fab 201 poff, plen, buffer, buflen))
720ac708
PM
202 return NF_DROP;
203 }
204
205 /* The rport= parameter (RFC 3581) contains the port number
206 * from which the server received the request. */
207 if (ct_sip_parse_numerical_param(ct, *dptr, matchend, *datalen,
208 "rport=", &poff, &plen,
209 &n) > 0 &&
210 htons(n) == ct->tuplehash[dir].tuple.dst.u.udp.port &&
211 htons(n) != ct->tuplehash[!dir].tuple.src.u.udp.port) {
212 __be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port;
213 buflen = sprintf(buffer, "%u", ntohs(p));
051966c0 214 if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
3b6b9fab 215 poff, plen, buffer, buflen))
720ac708
PM
216 return NF_DROP;
217 }
9fafcd7b
PM
218 }
219
720ac708 220next:
c978cd3a 221 /* Translate Contact headers */
3b6b9fab 222 coff = 0;
c978cd3a 223 in_header = 0;
3b6b9fab 224 while (ct_sip_parse_header_uri(ct, *dptr, &coff, *datalen,
c978cd3a
PM
225 SIP_HDR_CONTACT, &in_header,
226 &matchoff, &matchlen,
227 &addr, &port) > 0) {
051966c0
PM
228 if (!map_addr(skb, protoff, dataoff, dptr, datalen,
229 matchoff, matchlen,
c978cd3a
PM
230 &addr, port))
231 return NF_DROP;
232 }
233
051966c0
PM
234 if (!map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_FROM) ||
235 !map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_TO))
9fafcd7b 236 return NF_DROP;
48f8ac26 237
9fafcd7b
PM
238 return NF_ACCEPT;
239}
240
48f8ac26
PM
241static void ip_nat_sip_seq_adjust(struct sk_buff *skb, s16 off)
242{
243 enum ip_conntrack_info ctinfo;
244 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
245 const struct tcphdr *th;
246
247 if (nf_ct_protonum(ct) != IPPROTO_TCP || off == 0)
248 return;
249
250 th = (struct tcphdr *)(skb->data + ip_hdrlen(skb));
251 nf_nat_set_seq_adjust(ct, ctinfo, th->seq, off);
252}
253
0f32a40f
PM
254/* Handles expected signalling connections and media streams */
255static void ip_nat_sip_expected(struct nf_conn *ct,
256 struct nf_conntrack_expect *exp)
257{
cbc9f2f4 258 struct nf_nat_ipv4_range range;
0f32a40f
PM
259
260 /* This must be a fresh one. */
261 BUG_ON(ct->status & IPS_NAT_DONE_MASK);
262
263 /* For DST manip, map port here to where it's expected. */
cbc9f2f4 264 range.flags = (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED);
0f32a40f
PM
265 range.min = range.max = exp->saved_proto;
266 range.min_ip = range.max_ip = exp->saved_ip;
cbc9f2f4 267 nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST);
0f32a40f
PM
268
269 /* Change src to where master sends to, but only if the connection
270 * actually came from the same source. */
271 if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip ==
272 ct->master->tuplehash[exp->dir].tuple.src.u3.ip) {
cbc9f2f4 273 range.flags = NF_NAT_RANGE_MAP_IPS;
0f32a40f
PM
274 range.min_ip = range.max_ip
275 = ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip;
cbc9f2f4 276 nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC);
0f32a40f
PM
277 }
278}
279
051966c0
PM
280static unsigned int ip_nat_sip_expect(struct sk_buff *skb, unsigned int protoff,
281 unsigned int dataoff,
0f32a40f
PM
282 const char **dptr, unsigned int *datalen,
283 struct nf_conntrack_expect *exp,
284 unsigned int matchoff,
285 unsigned int matchlen)
286{
287 enum ip_conntrack_info ctinfo;
288 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
289 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
290 __be32 newip;
291 u_int16_t port;
292 char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
95c96174 293 unsigned int buflen;
0f32a40f
PM
294
295 /* Connection will come from reply */
296 if (ct->tuplehash[dir].tuple.src.u3.ip == ct->tuplehash[!dir].tuple.dst.u3.ip)
297 newip = exp->tuple.dst.u3.ip;
298 else
299 newip = ct->tuplehash[!dir].tuple.dst.u3.ip;
300
301 /* If the signalling port matches the connection's source port in the
302 * original direction, try to use the destination port in the opposite
303 * direction. */
304 if (exp->tuple.dst.u.udp.port ==
305 ct->tuplehash[dir].tuple.src.u.udp.port)
306 port = ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.port);
307 else
308 port = ntohs(exp->tuple.dst.u.udp.port);
309
310 exp->saved_ip = exp->tuple.dst.u3.ip;
311 exp->tuple.dst.u3.ip = newip;
312 exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port;
313 exp->dir = !dir;
314 exp->expectfn = ip_nat_sip_expected;
315
316 for (; port != 0; port++) {
5b92b61f
PNA
317 int ret;
318
0f32a40f 319 exp->tuple.dst.u.udp.port = htons(port);
5b92b61f
PNA
320 ret = nf_ct_expect_related(exp);
321 if (ret == 0)
322 break;
323 else if (ret != -EBUSY) {
324 port = 0;
0f32a40f 325 break;
5b92b61f 326 }
0f32a40f
PM
327 }
328
329 if (port == 0)
330 return NF_DROP;
331
332 if (exp->tuple.dst.u3.ip != exp->saved_ip ||
333 exp->tuple.dst.u.udp.port != exp->saved_proto.udp.port) {
cffee385 334 buflen = sprintf(buffer, "%pI4:%u", &newip, port);
051966c0 335 if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
3b6b9fab 336 matchoff, matchlen, buffer, buflen))
0f32a40f
PM
337 goto err;
338 }
339 return NF_ACCEPT;
340
341err:
342 nf_ct_unexpect_related(exp);
343 return NF_DROP;
344}
345
051966c0
PM
346static int mangle_content_len(struct sk_buff *skb, unsigned int protoff,
347 unsigned int dataoff,
3e9b4600 348 const char **dptr, unsigned int *datalen)
9fafcd7b 349{
212440a7
PM
350 enum ip_conntrack_info ctinfo;
351 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
3e9b4600
PM
352 unsigned int matchoff, matchlen;
353 char buffer[sizeof("65536")];
354 int buflen, c_len;
9fafcd7b 355
3e9b4600
PM
356 /* Get actual SDP length */
357 if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen,
358 SDP_HDR_VERSION, SDP_HDR_UNSPEC,
359 &matchoff, &matchlen) <= 0)
360 return 0;
361 c_len = *datalen - matchoff + strlen("v=");
362
363 /* Now, update SDP length */
ea45f12a
PM
364 if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CONTENT_LENGTH,
365 &matchoff, &matchlen) <= 0)
9fafcd7b
PM
366 return 0;
367
3e9b4600 368 buflen = sprintf(buffer, "%u", c_len);
051966c0
PM
369 return mangle_packet(skb, protoff, dataoff, dptr, datalen,
370 matchoff, matchlen, buffer, buflen);
9fafcd7b
PM
371}
372
051966c0
PM
373static int mangle_sdp_packet(struct sk_buff *skb, unsigned int protoff,
374 unsigned int dataoff,
3b6b9fab
PM
375 const char **dptr, unsigned int *datalen,
376 unsigned int sdpoff,
c71529e4
HX
377 enum sdp_header_types type,
378 enum sdp_header_types term,
379 char *buffer, int buflen)
9fafcd7b 380{
212440a7
PM
381 enum ip_conntrack_info ctinfo;
382 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
3e9b4600 383 unsigned int matchlen, matchoff;
9fafcd7b 384
3b6b9fab 385 if (ct_sip_get_sdp_header(ct, *dptr, sdpoff, *datalen, type, term,
3e9b4600 386 &matchoff, &matchlen) <= 0)
c71529e4 387 return -ENOENT;
051966c0
PM
388 return mangle_packet(skb, protoff, dataoff, dptr, datalen,
389 matchoff, matchlen, buffer, buflen) ? 0 : -EINVAL;
9fafcd7b
PM
390}
391
051966c0
PM
392static unsigned int ip_nat_sdp_addr(struct sk_buff *skb, unsigned int protoff,
393 unsigned int dataoff,
3b6b9fab
PM
394 const char **dptr, unsigned int *datalen,
395 unsigned int sdpoff,
4ab9e64e
PM
396 enum sdp_header_types type,
397 enum sdp_header_types term,
398 const union nf_inet_addr *addr)
9fafcd7b
PM
399{
400 char buffer[sizeof("nnn.nnn.nnn.nnn")];
4ab9e64e 401 unsigned int buflen;
9fafcd7b 402
cffee385 403 buflen = sprintf(buffer, "%pI4", &addr->ip);
051966c0
PM
404 if (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen,
405 sdpoff, type, term, buffer, buflen))
9fafcd7b
PM
406 return 0;
407
051966c0 408 return mangle_content_len(skb, protoff, dataoff, dptr, datalen);
4ab9e64e
PM
409}
410
051966c0
PM
411static unsigned int ip_nat_sdp_port(struct sk_buff *skb, unsigned int protoff,
412 unsigned int dataoff,
3b6b9fab 413 const char **dptr, unsigned int *datalen,
4ab9e64e
PM
414 unsigned int matchoff,
415 unsigned int matchlen,
416 u_int16_t port)
417{
418 char buffer[sizeof("nnnnn")];
419 unsigned int buflen;
420
421 buflen = sprintf(buffer, "%u", port);
051966c0
PM
422 if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
423 matchoff, matchlen, buffer, buflen))
9fafcd7b
PM
424 return 0;
425
051966c0 426 return mangle_content_len(skb, protoff, dataoff, dptr, datalen);
4ab9e64e
PM
427}
428
051966c0
PM
429static unsigned int ip_nat_sdp_session(struct sk_buff *skb, unsigned int protoff,
430 unsigned int dataoff,
3b6b9fab
PM
431 const char **dptr, unsigned int *datalen,
432 unsigned int sdpoff,
4ab9e64e
PM
433 const union nf_inet_addr *addr)
434{
435 char buffer[sizeof("nnn.nnn.nnn.nnn")];
436 unsigned int buflen;
437
438 /* Mangle session description owner and contact addresses */
cffee385 439 buflen = sprintf(buffer, "%pI4", &addr->ip);
051966c0 440 if (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen, sdpoff,
4ab9e64e
PM
441 SDP_HDR_OWNER_IP4, SDP_HDR_MEDIA,
442 buffer, buflen))
443 return 0;
444
051966c0 445 switch (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen, sdpoff,
c71529e4
HX
446 SDP_HDR_CONNECTION_IP4, SDP_HDR_MEDIA,
447 buffer, buflen)) {
448 case 0:
449 /*
450 * RFC 2327:
451 *
452 * Session description
453 *
454 * c=* (connection information - not required if included in all media)
455 */
456 case -ENOENT:
457 break;
458 default:
9fafcd7b 459 return 0;
c71529e4 460 }
9fafcd7b 461
051966c0 462 return mangle_content_len(skb, protoff, dataoff, dptr, datalen);
9fafcd7b
PM
463}
464
465/* So, this packet has hit the connection tracking matching code.
466 Mangle it, and change the expectation to match the new version. */
051966c0
PM
467static unsigned int ip_nat_sdp_media(struct sk_buff *skb, unsigned int protoff,
468 unsigned int dataoff,
3b6b9fab 469 const char **dptr, unsigned int *datalen,
4ab9e64e
PM
470 struct nf_conntrack_expect *rtp_exp,
471 struct nf_conntrack_expect *rtcp_exp,
472 unsigned int mediaoff,
473 unsigned int medialen,
474 union nf_inet_addr *rtp_addr)
9fafcd7b 475{
212440a7
PM
476 enum ip_conntrack_info ctinfo;
477 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
9fafcd7b 478 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
9fafcd7b
PM
479 u_int16_t port;
480
9fafcd7b 481 /* Connection will come from reply */
f4a607bf
JB
482 if (ct->tuplehash[dir].tuple.src.u3.ip ==
483 ct->tuplehash[!dir].tuple.dst.u3.ip)
4ab9e64e 484 rtp_addr->ip = rtp_exp->tuple.dst.u3.ip;
f4a607bf 485 else
4ab9e64e 486 rtp_addr->ip = ct->tuplehash[!dir].tuple.dst.u3.ip;
9fafcd7b 487
a9c1d359 488 rtp_exp->saved_ip = rtp_exp->tuple.dst.u3.ip;
4ab9e64e 489 rtp_exp->tuple.dst.u3.ip = rtp_addr->ip;
a9c1d359
PM
490 rtp_exp->saved_proto.udp.port = rtp_exp->tuple.dst.u.udp.port;
491 rtp_exp->dir = !dir;
492 rtp_exp->expectfn = ip_nat_sip_expected;
493
494 rtcp_exp->saved_ip = rtcp_exp->tuple.dst.u3.ip;
4ab9e64e 495 rtcp_exp->tuple.dst.u3.ip = rtp_addr->ip;
a9c1d359
PM
496 rtcp_exp->saved_proto.udp.port = rtcp_exp->tuple.dst.u.udp.port;
497 rtcp_exp->dir = !dir;
498 rtcp_exp->expectfn = ip_nat_sip_expected;
499
500 /* Try to get same pair of ports: if not, try to change them. */
501 for (port = ntohs(rtp_exp->tuple.dst.u.udp.port);
502 port != 0; port += 2) {
5b92b61f
PNA
503 int ret;
504
a9c1d359 505 rtp_exp->tuple.dst.u.udp.port = htons(port);
5b92b61f
PNA
506 ret = nf_ct_expect_related(rtp_exp);
507 if (ret == -EBUSY)
a9c1d359 508 continue;
5b92b61f
PNA
509 else if (ret < 0) {
510 port = 0;
511 break;
512 }
a9c1d359 513 rtcp_exp->tuple.dst.u.udp.port = htons(port + 1);
5b92b61f
PNA
514 ret = nf_ct_expect_related(rtcp_exp);
515 if (ret == 0)
9fafcd7b 516 break;
5b92b61f
PNA
517 else if (ret != -EBUSY) {
518 nf_ct_unexpect_related(rtp_exp);
519 port = 0;
520 break;
521 }
9fafcd7b
PM
522 }
523
524 if (port == 0)
4ab9e64e
PM
525 goto err1;
526
527 /* Update media port. */
528 if (rtp_exp->tuple.dst.u.udp.port != rtp_exp->saved_proto.udp.port &&
051966c0 529 !ip_nat_sdp_port(skb, protoff, dataoff, dptr, datalen,
3b6b9fab 530 mediaoff, medialen, port))
4ab9e64e 531 goto err2;
9fafcd7b 532
9fafcd7b 533 return NF_ACCEPT;
4ab9e64e
PM
534
535err2:
536 nf_ct_unexpect_related(rtp_exp);
537 nf_ct_unexpect_related(rtcp_exp);
538err1:
539 return NF_DROP;
9fafcd7b
PM
540}
541
544d5c7d
PNA
542static struct nf_ct_helper_expectfn sip_nat = {
543 .name = "sip",
544 .expectfn = ip_nat_sip_expected,
545};
546
9fafcd7b
PM
547static void __exit nf_nat_sip_fini(void)
548{
a9b3cd7f
SH
549 RCU_INIT_POINTER(nf_nat_sip_hook, NULL);
550 RCU_INIT_POINTER(nf_nat_sip_seq_adjust_hook, NULL);
551 RCU_INIT_POINTER(nf_nat_sip_expect_hook, NULL);
552 RCU_INIT_POINTER(nf_nat_sdp_addr_hook, NULL);
553 RCU_INIT_POINTER(nf_nat_sdp_port_hook, NULL);
554 RCU_INIT_POINTER(nf_nat_sdp_session_hook, NULL);
555 RCU_INIT_POINTER(nf_nat_sdp_media_hook, NULL);
544d5c7d 556 nf_ct_helper_expectfn_unregister(&sip_nat);
9fafcd7b
PM
557 synchronize_rcu();
558}
559
560static int __init nf_nat_sip_init(void)
561{
d1332e0a 562 BUG_ON(nf_nat_sip_hook != NULL);
48f8ac26 563 BUG_ON(nf_nat_sip_seq_adjust_hook != NULL);
0f32a40f 564 BUG_ON(nf_nat_sip_expect_hook != NULL);
4ab9e64e 565 BUG_ON(nf_nat_sdp_addr_hook != NULL);
c7f485ab 566 BUG_ON(nf_nat_sdp_port_hook != NULL);
4ab9e64e
PM
567 BUG_ON(nf_nat_sdp_session_hook != NULL);
568 BUG_ON(nf_nat_sdp_media_hook != NULL);
a9b3cd7f
SH
569 RCU_INIT_POINTER(nf_nat_sip_hook, ip_nat_sip);
570 RCU_INIT_POINTER(nf_nat_sip_seq_adjust_hook, ip_nat_sip_seq_adjust);
571 RCU_INIT_POINTER(nf_nat_sip_expect_hook, ip_nat_sip_expect);
572 RCU_INIT_POINTER(nf_nat_sdp_addr_hook, ip_nat_sdp_addr);
573 RCU_INIT_POINTER(nf_nat_sdp_port_hook, ip_nat_sdp_port);
574 RCU_INIT_POINTER(nf_nat_sdp_session_hook, ip_nat_sdp_session);
575 RCU_INIT_POINTER(nf_nat_sdp_media_hook, ip_nat_sdp_media);
544d5c7d 576 nf_ct_helper_expectfn_register(&sip_nat);
9fafcd7b
PM
577 return 0;
578}
579
580module_init(nf_nat_sip_init);
581module_exit(nf_nat_sip_fini);
This page took 0.501107 seconds and 5 git commands to generate.