1 /* SIP extension for NAT alteration.
3 * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar>
4 * based on RR's ip_nat_ftp.c and other modules.
5 * (C) 2007 United Security Providers
6 * (C) 2007, 2008 Patrick McHardy <kaber@trash.net>
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.
13 #include <linux/module.h>
14 #include <linux/skbuff.h>
17 #include <linux/udp.h>
18 #include <linux/tcp.h>
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>
27 MODULE_LICENSE("GPL");
28 MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>");
29 MODULE_DESCRIPTION("SIP NAT helper");
30 MODULE_ALIAS("ip_nat_sip");
33 static unsigned int mangle_packet(struct sk_buff
*skb
, unsigned int dataoff
,
34 const char **dptr
, unsigned int *datalen
,
35 unsigned int matchoff
, unsigned int matchlen
,
36 const char *buffer
, unsigned int buflen
)
38 enum ip_conntrack_info ctinfo
;
39 struct nf_conn
*ct
= nf_ct_get(skb
, &ctinfo
);
43 if (nf_ct_protonum(ct
) == IPPROTO_TCP
) {
44 th
= (struct tcphdr
*)(skb
->data
+ ip_hdrlen(skb
));
45 baseoff
= ip_hdrlen(skb
) + th
->doff
* 4;
46 matchoff
+= dataoff
- baseoff
;
48 if (!__nf_nat_mangle_tcp_packet(skb
, ct
, ctinfo
,
50 buffer
, buflen
, false))
53 baseoff
= ip_hdrlen(skb
) + sizeof(struct udphdr
);
54 matchoff
+= dataoff
- baseoff
;
56 if (!nf_nat_mangle_udp_packet(skb
, ct
, ctinfo
,
62 /* Reload data pointer and adjust datalen value */
63 *dptr
= skb
->data
+ dataoff
;
64 *datalen
+= buflen
- matchlen
;
68 static int map_addr(struct sk_buff
*skb
, unsigned int dataoff
,
69 const char **dptr
, unsigned int *datalen
,
70 unsigned int matchoff
, unsigned int matchlen
,
71 union nf_inet_addr
*addr
, __be16 port
)
73 enum ip_conntrack_info ctinfo
;
74 struct nf_conn
*ct
= nf_ct_get(skb
, &ctinfo
);
75 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
76 char buffer
[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
81 if (ct
->tuplehash
[dir
].tuple
.src
.u3
.ip
== addr
->ip
&&
82 ct
->tuplehash
[dir
].tuple
.src
.u
.udp
.port
== port
) {
83 newaddr
= ct
->tuplehash
[!dir
].tuple
.dst
.u3
.ip
;
84 newport
= ct
->tuplehash
[!dir
].tuple
.dst
.u
.udp
.port
;
85 } else if (ct
->tuplehash
[dir
].tuple
.dst
.u3
.ip
== addr
->ip
&&
86 ct
->tuplehash
[dir
].tuple
.dst
.u
.udp
.port
== port
) {
87 newaddr
= ct
->tuplehash
[!dir
].tuple
.src
.u3
.ip
;
88 newport
= ct
->tuplehash
[!dir
].tuple
.src
.u
.udp
.port
;
92 if (newaddr
== addr
->ip
&& newport
== port
)
95 buflen
= sprintf(buffer
, "%u.%u.%u.%u:%u",
96 NIPQUAD(newaddr
), ntohs(newport
));
98 return mangle_packet(skb
, dataoff
, dptr
, datalen
, matchoff
, matchlen
,
102 static int map_sip_addr(struct sk_buff
*skb
, unsigned int dataoff
,
103 const char **dptr
, unsigned int *datalen
,
104 enum sip_header_types type
)
106 enum ip_conntrack_info ctinfo
;
107 struct nf_conn
*ct
= nf_ct_get(skb
, &ctinfo
);
108 unsigned int matchlen
, matchoff
;
109 union nf_inet_addr addr
;
112 if (ct_sip_parse_header_uri(ct
, *dptr
, NULL
, *datalen
, type
, NULL
,
113 &matchoff
, &matchlen
, &addr
, &port
) <= 0)
115 return map_addr(skb
, dataoff
, dptr
, datalen
, matchoff
, matchlen
,
119 static unsigned int ip_nat_sip(struct sk_buff
*skb
, unsigned int dataoff
,
120 const char **dptr
, unsigned int *datalen
)
122 enum ip_conntrack_info ctinfo
;
123 struct nf_conn
*ct
= nf_ct_get(skb
, &ctinfo
);
124 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
125 unsigned int coff
, matchoff
, matchlen
;
126 enum sip_header_types hdr
;
127 union nf_inet_addr addr
;
129 int request
, in_header
;
131 /* Basic rules: requests and responses. */
132 if (strnicmp(*dptr
, "SIP/2.0", strlen("SIP/2.0")) != 0) {
133 if (ct_sip_parse_request(ct
, *dptr
, *datalen
,
134 &matchoff
, &matchlen
,
136 !map_addr(skb
, dataoff
, dptr
, datalen
, matchoff
, matchlen
,
143 if (nf_ct_protonum(ct
) == IPPROTO_TCP
)
144 hdr
= SIP_HDR_VIA_TCP
;
146 hdr
= SIP_HDR_VIA_UDP
;
148 /* Translate topmost Via header and parameters */
149 if (ct_sip_parse_header_uri(ct
, *dptr
, NULL
, *datalen
,
150 hdr
, NULL
, &matchoff
, &matchlen
,
152 unsigned int matchend
, poff
, plen
, buflen
, n
;
153 char buffer
[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
155 /* We're only interested in headers related to this
158 if (addr
.ip
!= ct
->tuplehash
[dir
].tuple
.src
.u3
.ip
||
159 port
!= ct
->tuplehash
[dir
].tuple
.src
.u
.udp
.port
)
162 if (addr
.ip
!= ct
->tuplehash
[dir
].tuple
.dst
.u3
.ip
||
163 port
!= ct
->tuplehash
[dir
].tuple
.dst
.u
.udp
.port
)
167 if (!map_addr(skb
, dataoff
, dptr
, datalen
, matchoff
, matchlen
,
171 matchend
= matchoff
+ matchlen
;
173 /* The maddr= parameter (RFC 2361) specifies where to send
175 if (ct_sip_parse_address_param(ct
, *dptr
, matchend
, *datalen
,
176 "maddr=", &poff
, &plen
,
178 addr
.ip
== ct
->tuplehash
[dir
].tuple
.src
.u3
.ip
&&
179 addr
.ip
!= ct
->tuplehash
[!dir
].tuple
.dst
.u3
.ip
) {
180 __be32 ip
= ct
->tuplehash
[!dir
].tuple
.dst
.u3
.ip
;
181 buflen
= sprintf(buffer
, "%u.%u.%u.%u", NIPQUAD(ip
));
182 if (!mangle_packet(skb
, dataoff
, dptr
, datalen
,
183 poff
, plen
, buffer
, buflen
))
187 /* The received= parameter (RFC 2361) contains the address
188 * from which the server received the request. */
189 if (ct_sip_parse_address_param(ct
, *dptr
, matchend
, *datalen
,
190 "received=", &poff
, &plen
,
192 addr
.ip
== ct
->tuplehash
[dir
].tuple
.dst
.u3
.ip
&&
193 addr
.ip
!= ct
->tuplehash
[!dir
].tuple
.src
.u3
.ip
) {
194 __be32 ip
= ct
->tuplehash
[!dir
].tuple
.src
.u3
.ip
;
195 buflen
= sprintf(buffer
, "%u.%u.%u.%u", NIPQUAD(ip
));
196 if (!mangle_packet(skb
, dataoff
, dptr
, datalen
,
197 poff
, plen
, buffer
, buflen
))
201 /* The rport= parameter (RFC 3581) contains the port number
202 * from which the server received the request. */
203 if (ct_sip_parse_numerical_param(ct
, *dptr
, matchend
, *datalen
,
204 "rport=", &poff
, &plen
,
206 htons(n
) == ct
->tuplehash
[dir
].tuple
.dst
.u
.udp
.port
&&
207 htons(n
) != ct
->tuplehash
[!dir
].tuple
.src
.u
.udp
.port
) {
208 __be16 p
= ct
->tuplehash
[!dir
].tuple
.src
.u
.udp
.port
;
209 buflen
= sprintf(buffer
, "%u", ntohs(p
));
210 if (!mangle_packet(skb
, dataoff
, dptr
, datalen
,
211 poff
, plen
, buffer
, buflen
))
217 /* Translate Contact headers */
220 while (ct_sip_parse_header_uri(ct
, *dptr
, &coff
, *datalen
,
221 SIP_HDR_CONTACT
, &in_header
,
222 &matchoff
, &matchlen
,
224 if (!map_addr(skb
, dataoff
, dptr
, datalen
, matchoff
, matchlen
,
229 if (!map_sip_addr(skb
, dataoff
, dptr
, datalen
, SIP_HDR_FROM
) ||
230 !map_sip_addr(skb
, dataoff
, dptr
, datalen
, SIP_HDR_TO
))
236 static void ip_nat_sip_seq_adjust(struct sk_buff
*skb
, s16 off
)
238 enum ip_conntrack_info ctinfo
;
239 struct nf_conn
*ct
= nf_ct_get(skb
, &ctinfo
);
240 const struct tcphdr
*th
;
242 if (nf_ct_protonum(ct
) != IPPROTO_TCP
|| off
== 0)
245 th
= (struct tcphdr
*)(skb
->data
+ ip_hdrlen(skb
));
246 nf_nat_set_seq_adjust(ct
, ctinfo
, th
->seq
, off
);
249 /* Handles expected signalling connections and media streams */
250 static void ip_nat_sip_expected(struct nf_conn
*ct
,
251 struct nf_conntrack_expect
*exp
)
253 struct nf_nat_range range
;
255 /* This must be a fresh one. */
256 BUG_ON(ct
->status
& IPS_NAT_DONE_MASK
);
258 /* For DST manip, map port here to where it's expected. */
259 range
.flags
= (IP_NAT_RANGE_MAP_IPS
| IP_NAT_RANGE_PROTO_SPECIFIED
);
260 range
.min
= range
.max
= exp
->saved_proto
;
261 range
.min_ip
= range
.max_ip
= exp
->saved_ip
;
262 /* hook doesn't matter, but it has to do destination manip */
263 nf_nat_setup_info(ct
, &range
, NF_IP_PRE_ROUTING
);
265 /* Change src to where master sends to, but only if the connection
266 * actually came from the same source. */
267 if (ct
->tuplehash
[IP_CT_DIR_ORIGINAL
].tuple
.src
.u3
.ip
==
268 ct
->master
->tuplehash
[exp
->dir
].tuple
.src
.u3
.ip
) {
269 range
.flags
= IP_NAT_RANGE_MAP_IPS
;
270 range
.min_ip
= range
.max_ip
271 = ct
->master
->tuplehash
[!exp
->dir
].tuple
.dst
.u3
.ip
;
272 /* hook doesn't matter, but it has to do source manip */
273 nf_nat_setup_info(ct
, &range
, NF_IP_POST_ROUTING
);
277 static unsigned int ip_nat_sip_expect(struct sk_buff
*skb
, unsigned int dataoff
,
278 const char **dptr
, unsigned int *datalen
,
279 struct nf_conntrack_expect
*exp
,
280 unsigned int matchoff
,
281 unsigned int matchlen
)
283 enum ip_conntrack_info ctinfo
;
284 struct nf_conn
*ct
= nf_ct_get(skb
, &ctinfo
);
285 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
288 char buffer
[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
291 /* Connection will come from reply */
292 if (ct
->tuplehash
[dir
].tuple
.src
.u3
.ip
== ct
->tuplehash
[!dir
].tuple
.dst
.u3
.ip
)
293 newip
= exp
->tuple
.dst
.u3
.ip
;
295 newip
= ct
->tuplehash
[!dir
].tuple
.dst
.u3
.ip
;
297 /* If the signalling port matches the connection's source port in the
298 * original direction, try to use the destination port in the opposite
300 if (exp
->tuple
.dst
.u
.udp
.port
==
301 ct
->tuplehash
[dir
].tuple
.src
.u
.udp
.port
)
302 port
= ntohs(ct
->tuplehash
[!dir
].tuple
.dst
.u
.udp
.port
);
304 port
= ntohs(exp
->tuple
.dst
.u
.udp
.port
);
306 exp
->saved_ip
= exp
->tuple
.dst
.u3
.ip
;
307 exp
->tuple
.dst
.u3
.ip
= newip
;
308 exp
->saved_proto
.udp
.port
= exp
->tuple
.dst
.u
.udp
.port
;
310 exp
->expectfn
= ip_nat_sip_expected
;
312 for (; port
!= 0; port
++) {
313 exp
->tuple
.dst
.u
.udp
.port
= htons(port
);
314 if (nf_conntrack_expect_related(exp
) == 0)
321 if (exp
->tuple
.dst
.u3
.ip
!= exp
->saved_ip
||
322 exp
->tuple
.dst
.u
.udp
.port
!= exp
->saved_proto
.udp
.port
) {
323 buflen
= sprintf(buffer
, "%u.%u.%u.%u:%u",
324 NIPQUAD(newip
), port
);
325 if (!mangle_packet(skb
, dataoff
, dptr
, datalen
,
326 matchoff
, matchlen
, buffer
, buflen
))
332 nf_conntrack_unexpect_related(exp
);
336 static int mangle_content_len(struct sk_buff
*skb
, unsigned int dataoff
,
337 const char **dptr
, unsigned int *datalen
)
339 enum ip_conntrack_info ctinfo
;
340 struct nf_conn
*ct
= nf_ct_get(skb
, &ctinfo
);
341 unsigned int matchoff
, matchlen
;
342 char buffer
[sizeof("65536")];
345 /* Get actual SDP length */
346 if (ct_sip_get_sdp_header(ct
, *dptr
, 0, *datalen
,
347 SDP_HDR_VERSION
, SDP_HDR_UNSPEC
,
348 &matchoff
, &matchlen
) <= 0)
350 c_len
= *datalen
- matchoff
+ strlen("v=");
352 /* Now, update SDP length */
353 if (ct_sip_get_header(ct
, *dptr
, 0, *datalen
, SIP_HDR_CONTENT_LENGTH
,
354 &matchoff
, &matchlen
) <= 0)
357 buflen
= sprintf(buffer
, "%u", c_len
);
358 return mangle_packet(skb
, dataoff
, dptr
, datalen
, matchoff
, matchlen
,
362 static int mangle_sdp_packet(struct sk_buff
*skb
, unsigned int dataoff
,
363 const char **dptr
, unsigned int *datalen
,
365 enum sdp_header_types type
,
366 enum sdp_header_types term
,
367 char *buffer
, int buflen
)
369 enum ip_conntrack_info ctinfo
;
370 struct nf_conn
*ct
= nf_ct_get(skb
, &ctinfo
);
371 unsigned int matchlen
, matchoff
;
373 if (ct_sip_get_sdp_header(ct
, *dptr
, sdpoff
, *datalen
, type
, term
,
374 &matchoff
, &matchlen
) <= 0)
376 return mangle_packet(skb
, dataoff
, dptr
, datalen
, matchoff
, matchlen
,
377 buffer
, buflen
) ? 0 : -EINVAL
;
380 static unsigned int ip_nat_sdp_addr(struct sk_buff
*skb
, unsigned int dataoff
,
381 const char **dptr
, unsigned int *datalen
,
383 enum sdp_header_types type
,
384 enum sdp_header_types term
,
385 const union nf_inet_addr
*addr
)
387 char buffer
[sizeof("nnn.nnn.nnn.nnn")];
390 buflen
= sprintf(buffer
, NIPQUAD_FMT
, NIPQUAD(addr
->ip
));
391 if (mangle_sdp_packet(skb
, dataoff
, dptr
, datalen
, sdpoff
, type
, term
,
395 return mangle_content_len(skb
, dataoff
, dptr
, datalen
);
398 static unsigned int ip_nat_sdp_port(struct sk_buff
*skb
, unsigned int dataoff
,
399 const char **dptr
, unsigned int *datalen
,
400 unsigned int matchoff
,
401 unsigned int matchlen
,
404 char buffer
[sizeof("nnnnn")];
407 buflen
= sprintf(buffer
, "%u", port
);
408 if (!mangle_packet(skb
, dataoff
, dptr
, datalen
, matchoff
, matchlen
,
412 return mangle_content_len(skb
, dataoff
, dptr
, datalen
);
415 static unsigned int ip_nat_sdp_session(struct sk_buff
*skb
, unsigned int dataoff
,
416 const char **dptr
, unsigned int *datalen
,
418 const union nf_inet_addr
*addr
)
420 char buffer
[sizeof("nnn.nnn.nnn.nnn")];
423 /* Mangle session description owner and contact addresses */
424 buflen
= sprintf(buffer
, "%u.%u.%u.%u", NIPQUAD(addr
->ip
));
425 if (mangle_sdp_packet(skb
, dataoff
, dptr
, datalen
, sdpoff
,
426 SDP_HDR_OWNER_IP4
, SDP_HDR_MEDIA
,
430 switch (mangle_sdp_packet(skb
, dataoff
, dptr
, datalen
, sdpoff
,
431 SDP_HDR_CONNECTION_IP4
, SDP_HDR_MEDIA
,
437 * Session description
439 * c=* (connection information - not required if included in all media)
447 return mangle_content_len(skb
, dataoff
, dptr
, datalen
);
450 /* So, this packet has hit the connection tracking matching code.
451 Mangle it, and change the expectation to match the new version. */
452 static unsigned int ip_nat_sdp_media(struct sk_buff
*skb
, unsigned int dataoff
,
453 const char **dptr
, unsigned int *datalen
,
454 struct nf_conntrack_expect
*rtp_exp
,
455 struct nf_conntrack_expect
*rtcp_exp
,
456 unsigned int mediaoff
,
457 unsigned int medialen
,
458 union nf_inet_addr
*rtp_addr
)
460 enum ip_conntrack_info ctinfo
;
461 struct nf_conn
*ct
= nf_ct_get(skb
, &ctinfo
);
462 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
465 /* Connection will come from reply */
466 if (ct
->tuplehash
[dir
].tuple
.src
.u3
.ip
==
467 ct
->tuplehash
[!dir
].tuple
.dst
.u3
.ip
)
468 rtp_addr
->ip
= rtp_exp
->tuple
.dst
.u3
.ip
;
470 rtp_addr
->ip
= ct
->tuplehash
[!dir
].tuple
.dst
.u3
.ip
;
472 rtp_exp
->saved_ip
= rtp_exp
->tuple
.dst
.u3
.ip
;
473 rtp_exp
->tuple
.dst
.u3
.ip
= rtp_addr
->ip
;
474 rtp_exp
->saved_proto
.udp
.port
= rtp_exp
->tuple
.dst
.u
.udp
.port
;
476 rtp_exp
->expectfn
= ip_nat_sip_expected
;
478 rtcp_exp
->saved_ip
= rtcp_exp
->tuple
.dst
.u3
.ip
;
479 rtcp_exp
->tuple
.dst
.u3
.ip
= rtp_addr
->ip
;
480 rtcp_exp
->saved_proto
.udp
.port
= rtcp_exp
->tuple
.dst
.u
.udp
.port
;
481 rtcp_exp
->dir
= !dir
;
482 rtcp_exp
->expectfn
= ip_nat_sip_expected
;
484 /* Try to get same pair of ports: if not, try to change them. */
485 for (port
= ntohs(rtp_exp
->tuple
.dst
.u
.udp
.port
);
486 port
!= 0; port
+= 2) {
487 rtp_exp
->tuple
.dst
.u
.udp
.port
= htons(port
);
488 if (nf_conntrack_expect_related(rtp_exp
) != 0)
490 rtcp_exp
->tuple
.dst
.u
.udp
.port
= htons(port
+ 1);
491 if (nf_conntrack_expect_related(rtcp_exp
) == 0)
493 nf_conntrack_unexpect_related(rtp_exp
);
499 /* Update media port. */
500 if (rtp_exp
->tuple
.dst
.u
.udp
.port
!= rtp_exp
->saved_proto
.udp
.port
&&
501 !ip_nat_sdp_port(skb
, dataoff
, dptr
, datalen
,
502 mediaoff
, medialen
, port
))
508 nf_conntrack_unexpect_related(rtp_exp
);
509 nf_conntrack_unexpect_related(rtcp_exp
);
514 static void __exit
nf_nat_sip_fini(void)
516 rcu_assign_pointer(nf_nat_sip_hook
, NULL
);
517 rcu_assign_pointer(nf_nat_sip_seq_adjust_hook
, NULL
);
518 rcu_assign_pointer(nf_nat_sip_expect_hook
, NULL
);
519 rcu_assign_pointer(nf_nat_sdp_addr_hook
, NULL
);
520 rcu_assign_pointer(nf_nat_sdp_port_hook
, NULL
);
521 rcu_assign_pointer(nf_nat_sdp_session_hook
, NULL
);
522 rcu_assign_pointer(nf_nat_sdp_media_hook
, NULL
);
526 static int __init
nf_nat_sip_init(void)
528 BUG_ON(rcu_dereference(nf_nat_sip_hook
));
529 BUG_ON(rcu_dereference(nf_nat_sip_seq_adjust_hook
));
530 BUG_ON(rcu_dereference(nf_nat_sip_expect_hook
));
531 BUG_ON(rcu_dereference(nf_nat_sdp_addr_hook
));
532 BUG_ON(rcu_dereference(nf_nat_sdp_port_hook
));
533 BUG_ON(rcu_dereference(nf_nat_sdp_session_hook
));
534 BUG_ON(rcu_dereference(nf_nat_sdp_media_hook
));
535 rcu_assign_pointer(nf_nat_sip_hook
, ip_nat_sip
);
536 rcu_assign_pointer(nf_nat_sip_seq_adjust_hook
, ip_nat_sip_seq_adjust
);
537 rcu_assign_pointer(nf_nat_sip_expect_hook
, ip_nat_sip_expect
);
538 rcu_assign_pointer(nf_nat_sdp_addr_hook
, ip_nat_sdp_addr
);
539 rcu_assign_pointer(nf_nat_sdp_port_hook
, ip_nat_sdp_port
);
540 rcu_assign_pointer(nf_nat_sdp_session_hook
, ip_nat_sdp_session
);
541 rcu_assign_pointer(nf_nat_sdp_media_hook
, ip_nat_sdp_media
);
545 module_init(nf_nat_sip_init
);
546 module_exit(nf_nat_sip_fini
);