NETFILTER: remove unnecessary goto statement for error recovery
[tomato.git] / release / src-rt / linux / linux-2.6 / net / ipv4 / netfilter / nf_nat_sip.c
blob8465d0ece191acffc75f07c53d17b305548a94ac
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>
15 #include <linux/ip.h>
16 #include <net/ip.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);
40 struct tcphdr *th;
41 unsigned int baseoff;
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,
49 matchoff, matchlen,
50 buffer, buflen, false))
51 return 0;
52 } else {
53 baseoff = ip_hdrlen(skb) + sizeof(struct udphdr);
54 matchoff += dataoff - baseoff;
56 if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo,
57 matchoff, matchlen,
58 buffer, buflen))
59 return 0;
62 /* Reload data pointer and adjust datalen value */
63 *dptr = skb->data + dataoff;
64 *datalen += buflen - matchlen;
65 return 1;
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")];
77 unsigned int buflen;
78 __be32 newaddr;
79 __be16 newport;
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;
89 } else
90 return 1;
92 if (newaddr == addr->ip && newport == port)
93 return 1;
95 buflen = sprintf(buffer, "%u.%u.%u.%u:%u",
96 NIPQUAD(newaddr), ntohs(newport));
98 return mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen,
99 buffer, buflen);
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;
110 __be16 port;
112 if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, type, NULL,
113 &matchoff, &matchlen, &addr, &port) <= 0)
114 return 1;
115 return map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen,
116 &addr, port);
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;
128 __be16 port;
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,
135 &addr, &port) > 0 &&
136 !map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen,
137 &addr, port))
138 return NF_DROP;
139 request = 1;
140 } else
141 request = 0;
143 if (nf_ct_protonum(ct) == IPPROTO_TCP)
144 hdr = SIP_HDR_VIA_TCP;
145 else
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,
151 &addr, &port) > 0) {
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
156 * connection */
157 if (request) {
158 if (addr.ip != ct->tuplehash[dir].tuple.src.u3.ip ||
159 port != ct->tuplehash[dir].tuple.src.u.udp.port)
160 goto next;
161 } else {
162 if (addr.ip != ct->tuplehash[dir].tuple.dst.u3.ip ||
163 port != ct->tuplehash[dir].tuple.dst.u.udp.port)
164 goto next;
167 if (!map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen,
168 &addr, port))
169 return NF_DROP;
171 matchend = matchoff + matchlen;
173 /* The maddr= parameter (RFC 2361) specifies where to send
174 * the reply. */
175 if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen,
176 "maddr=", &poff, &plen,
177 &addr) > 0 &&
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))
184 return NF_DROP;
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,
191 &addr) > 0 &&
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))
198 return NF_DROP;
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,
205 &n) > 0 &&
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))
212 return NF_DROP;
216 next:
217 /* Translate Contact headers */
218 coff = 0;
219 in_header = 0;
220 while (ct_sip_parse_header_uri(ct, *dptr, &coff, *datalen,
221 SIP_HDR_CONTACT, &in_header,
222 &matchoff, &matchlen,
223 &addr, &port) > 0) {
224 if (!map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen,
225 &addr, port))
226 return NF_DROP;
229 if (!map_sip_addr(skb, dataoff, dptr, datalen, SIP_HDR_FROM) ||
230 !map_sip_addr(skb, dataoff, dptr, datalen, SIP_HDR_TO))
231 return NF_DROP;
233 return NF_ACCEPT;
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)
243 return;
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);
286 __be32 newip;
287 u_int16_t port;
288 char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
289 unsigned buflen;
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;
294 else
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
299 * direction. */
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);
303 else
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;
309 exp->dir = !dir;
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)
315 break;
318 if (port == 0)
319 return NF_DROP;
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))
327 goto err;
329 return NF_ACCEPT;
331 err:
332 nf_conntrack_unexpect_related(exp);
333 return NF_DROP;
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")];
343 int buflen, c_len;
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)
349 return 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)
355 return 0;
357 buflen = sprintf(buffer, "%u", c_len);
358 return mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen,
359 buffer, buflen);
362 static int mangle_sdp_packet(struct sk_buff *skb, unsigned int dataoff,
363 const char **dptr, unsigned int *datalen,
364 unsigned int sdpoff,
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)
375 return -ENOENT;
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,
382 unsigned int sdpoff,
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")];
388 unsigned int buflen;
390 buflen = sprintf(buffer, NIPQUAD_FMT, NIPQUAD(addr->ip));
391 if (mangle_sdp_packet(skb, dataoff, dptr, datalen, sdpoff, type, term,
392 buffer, buflen))
393 return 0;
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,
402 u_int16_t port)
404 char buffer[sizeof("nnnnn")];
405 unsigned int buflen;
407 buflen = sprintf(buffer, "%u", port);
408 if (!mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen,
409 buffer, buflen))
410 return 0;
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,
417 unsigned int sdpoff,
418 const union nf_inet_addr *addr)
420 char buffer[sizeof("nnn.nnn.nnn.nnn")];
421 unsigned int buflen;
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,
427 buffer, buflen))
428 return 0;
430 switch (mangle_sdp_packet(skb, dataoff, dptr, datalen, sdpoff,
431 SDP_HDR_CONNECTION_IP4, SDP_HDR_MEDIA,
432 buffer, buflen)) {
433 case 0:
435 * RFC 2327:
437 * Session description
439 * c=* (connection information - not required if included in all media)
441 case -ENOENT:
442 break;
443 default:
444 return 0;
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);
463 u_int16_t port;
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;
469 else
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;
475 rtp_exp->dir = !dir;
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)
489 continue;
490 rtcp_exp->tuple.dst.u.udp.port = htons(port + 1);
491 if (nf_conntrack_expect_related(rtcp_exp) == 0)
492 break;
493 nf_conntrack_unexpect_related(rtp_exp);
496 if (port == 0)
497 goto err1;
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))
503 goto err2;
505 return NF_ACCEPT;
507 err2:
508 nf_conntrack_unexpect_related(rtp_exp);
509 nf_conntrack_unexpect_related(rtcp_exp);
510 err1:
511 return NF_DROP;
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);
523 synchronize_rcu();
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);
542 return 0;
545 module_init(nf_nat_sip_init);
546 module_exit(nf_nat_sip_fini);