]> err.no Git - linux-2.6/blob - net/ipv4/netfilter/nf_nat_sip.c
b443618a857f95cd23579ecae8158ae40625e5ab
[linux-2.6] / net / ipv4 / netfilter / nf_nat_sip.c
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.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10
11 #include <linux/module.h>
12 #include <linux/skbuff.h>
13 #include <linux/ip.h>
14 #include <net/ip.h>
15 #include <linux/udp.h>
16
17 #include <net/netfilter/nf_nat.h>
18 #include <net/netfilter/nf_nat_helper.h>
19 #include <net/netfilter/nf_nat_rule.h>
20 #include <net/netfilter/nf_conntrack_helper.h>
21 #include <net/netfilter/nf_conntrack_expect.h>
22 #include <linux/netfilter/nf_conntrack_sip.h>
23
24 MODULE_LICENSE("GPL");
25 MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>");
26 MODULE_DESCRIPTION("SIP NAT helper");
27 MODULE_ALIAS("ip_nat_sip");
28
29
30 static unsigned int mangle_packet(struct sk_buff *skb,
31                                   const char **dptr, unsigned int *datalen,
32                                   unsigned int matchoff, unsigned int matchlen,
33                                   const char *buffer, unsigned int buflen)
34 {
35         enum ip_conntrack_info ctinfo;
36         struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
37
38         if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, matchoff, matchlen,
39                                       buffer, buflen))
40                 return 0;
41
42         /* Reload data pointer and adjust datalen value */
43         *dptr = skb->data + ip_hdrlen(skb) + sizeof(struct udphdr);
44         *datalen += buflen - matchlen;
45         return 1;
46 }
47
48 static int map_addr(struct sk_buff *skb,
49                     const char **dptr, unsigned int *datalen,
50                     unsigned int matchoff, unsigned int matchlen,
51                     union nf_inet_addr *addr, __be16 port)
52 {
53         enum ip_conntrack_info ctinfo;
54         struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
55         enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
56         char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
57         unsigned int buflen;
58         __be32 newaddr;
59         __be16 newport;
60
61         if (ct->tuplehash[dir].tuple.src.u3.ip == addr->ip &&
62             ct->tuplehash[dir].tuple.src.u.udp.port == port) {
63                 newaddr = ct->tuplehash[!dir].tuple.dst.u3.ip;
64                 newport = ct->tuplehash[!dir].tuple.dst.u.udp.port;
65         } else if (ct->tuplehash[dir].tuple.dst.u3.ip == addr->ip &&
66                    ct->tuplehash[dir].tuple.dst.u.udp.port == port) {
67                 newaddr = ct->tuplehash[!dir].tuple.src.u3.ip;
68                 newport = ct->tuplehash[!dir].tuple.src.u.udp.port;
69         } else
70                 return 1;
71
72         if (newaddr == addr->ip && newport == port)
73                 return 1;
74
75         buflen = sprintf(buffer, "%u.%u.%u.%u:%u",
76                          NIPQUAD(newaddr), ntohs(newport));
77
78         return mangle_packet(skb, dptr, datalen, matchoff, matchlen,
79                              buffer, buflen);
80 }
81
82 static int map_sip_addr(struct sk_buff *skb,
83                         const char **dptr, unsigned int *datalen,
84                         enum sip_header_types type)
85 {
86         enum ip_conntrack_info ctinfo;
87         struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
88         unsigned int matchlen, matchoff;
89         union nf_inet_addr addr;
90         __be16 port;
91
92         if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, type, NULL,
93                                     &matchoff, &matchlen, &addr, &port) <= 0)
94                 return 1;
95         return map_addr(skb, dptr, datalen, matchoff, matchlen, &addr, port);
96 }
97
98 static unsigned int ip_nat_sip(struct sk_buff *skb,
99                                const char **dptr, unsigned int *datalen)
100 {
101         enum ip_conntrack_info ctinfo;
102         struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
103         enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
104         unsigned int dataoff, matchoff, matchlen;
105         union nf_inet_addr addr;
106         __be16 port;
107         int request, in_header;
108
109         /* Basic rules: requests and responses. */
110         if (strnicmp(*dptr, "SIP/2.0", strlen("SIP/2.0")) != 0) {
111                 if (ct_sip_parse_request(ct, *dptr, *datalen,
112                                          &matchoff, &matchlen,
113                                          &addr, &port) > 0 &&
114                     !map_addr(skb, dptr, datalen, matchoff, matchlen,
115                               &addr, port))
116                         return NF_DROP;
117                 request = 1;
118         } else
119                 request = 0;
120
121         /* Translate topmost Via header and parameters */
122         if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen,
123                                     SIP_HDR_VIA, NULL, &matchoff, &matchlen,
124                                     &addr, &port) > 0) {
125                 unsigned int matchend, poff, plen, buflen, n;
126                 char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
127
128                 /* We're only interested in headers related to this
129                  * connection */
130                 if (request) {
131                         if (addr.ip != ct->tuplehash[dir].tuple.src.u3.ip ||
132                             port != ct->tuplehash[dir].tuple.src.u.udp.port)
133                                 goto next;
134                 } else {
135                         if (addr.ip != ct->tuplehash[dir].tuple.dst.u3.ip ||
136                             port != ct->tuplehash[dir].tuple.dst.u.udp.port)
137                                 goto next;
138                 }
139
140                 if (!map_addr(skb, dptr, datalen, matchoff, matchlen,
141                               &addr, port))
142                         return NF_DROP;
143
144                 matchend = matchoff + matchlen;
145
146                 /* The maddr= parameter (RFC 2361) specifies where to send
147                  * the reply. */
148                 if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen,
149                                                "maddr=", &poff, &plen,
150                                                &addr) > 0 &&
151                     addr.ip == ct->tuplehash[dir].tuple.src.u3.ip &&
152                     addr.ip != ct->tuplehash[!dir].tuple.dst.u3.ip) {
153                         __be32 ip = ct->tuplehash[!dir].tuple.dst.u3.ip;
154                         buflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(ip));
155                         if (!mangle_packet(skb, dptr, datalen, poff, plen,
156                                            buffer, buflen))
157                                 return NF_DROP;
158                 }
159
160                 /* The received= parameter (RFC 2361) contains the address
161                  * from which the server received the request. */
162                 if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen,
163                                                "received=", &poff, &plen,
164                                                &addr) > 0 &&
165                     addr.ip == ct->tuplehash[dir].tuple.dst.u3.ip &&
166                     addr.ip != ct->tuplehash[!dir].tuple.src.u3.ip) {
167                         __be32 ip = ct->tuplehash[!dir].tuple.src.u3.ip;
168                         buflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(ip));
169                         if (!mangle_packet(skb, dptr, datalen, poff, plen,
170                                            buffer, buflen))
171                                 return NF_DROP;
172                 }
173
174                 /* The rport= parameter (RFC 3581) contains the port number
175                  * from which the server received the request. */
176                 if (ct_sip_parse_numerical_param(ct, *dptr, matchend, *datalen,
177                                                  "rport=", &poff, &plen,
178                                                  &n) > 0 &&
179                     htons(n) == ct->tuplehash[dir].tuple.dst.u.udp.port &&
180                     htons(n) != ct->tuplehash[!dir].tuple.src.u.udp.port) {
181                         __be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port;
182                         buflen = sprintf(buffer, "%u", ntohs(p));
183                         if (!mangle_packet(skb, dptr, datalen, poff, plen,
184                                            buffer, buflen))
185                                 return NF_DROP;
186                 }
187         }
188
189 next:
190         /* Translate Contact headers */
191         dataoff = 0;
192         in_header = 0;
193         while (ct_sip_parse_header_uri(ct, *dptr, &dataoff, *datalen,
194                                        SIP_HDR_CONTACT, &in_header,
195                                        &matchoff, &matchlen,
196                                        &addr, &port) > 0) {
197                 if (!map_addr(skb, dptr, datalen, matchoff, matchlen,
198                               &addr, port))
199                         return NF_DROP;
200         }
201
202         if (!map_sip_addr(skb, dptr, datalen, SIP_HDR_FROM) ||
203             !map_sip_addr(skb, dptr, datalen, SIP_HDR_TO))
204                 return NF_DROP;
205         return NF_ACCEPT;
206 }
207
208 static int mangle_content_len(struct sk_buff *skb,
209                               const char **dptr, unsigned int *datalen)
210 {
211         enum ip_conntrack_info ctinfo;
212         struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
213         unsigned int matchoff, matchlen;
214         char buffer[sizeof("65536")];
215         int buflen, c_len;
216
217         /* Get actual SDP length */
218         if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen,
219                                   SDP_HDR_VERSION, SDP_HDR_UNSPEC,
220                                   &matchoff, &matchlen) <= 0)
221                 return 0;
222         c_len = *datalen - matchoff + strlen("v=");
223
224         /* Now, update SDP length */
225         if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CONTENT_LENGTH,
226                               &matchoff, &matchlen) <= 0)
227                 return 0;
228
229         buflen = sprintf(buffer, "%u", c_len);
230         return mangle_packet(skb, dptr, datalen, matchoff, matchlen,
231                              buffer, buflen);
232 }
233
234 static unsigned mangle_sdp_packet(struct sk_buff *skb,
235                                   const char **dptr, unsigned int *datalen,
236                                   enum sdp_header_types type,
237                                   char *buffer, int buflen)
238 {
239         enum ip_conntrack_info ctinfo;
240         struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
241         unsigned int matchlen, matchoff;
242
243         if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen, type, SDP_HDR_UNSPEC,
244                                   &matchoff, &matchlen) <= 0)
245                 return 0;
246         return mangle_packet(skb, dptr, datalen, matchoff, matchlen,
247                              buffer, buflen);
248 }
249
250 static unsigned int mangle_sdp(struct sk_buff *skb,
251                                enum ip_conntrack_info ctinfo,
252                                struct nf_conn *ct,
253                                __be32 newip, u_int16_t port,
254                                const char **dptr, unsigned int *datalen)
255 {
256         char buffer[sizeof("nnn.nnn.nnn.nnn")];
257         unsigned int bufflen;
258
259         /* Mangle owner and contact info. */
260         bufflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(newip));
261         if (!mangle_sdp_packet(skb, dptr, datalen, SDP_HDR_OWNER_IP4,
262                                buffer, bufflen))
263                 return 0;
264
265         if (!mangle_sdp_packet(skb, dptr, datalen, SDP_HDR_CONNECTION_IP4,
266                                buffer, bufflen))
267                 return 0;
268
269         /* Mangle media port. */
270         bufflen = sprintf(buffer, "%u", port);
271         if (!mangle_sdp_packet(skb, dptr, datalen, SDP_HDR_MEDIA,
272                                buffer, bufflen))
273                 return 0;
274
275         return mangle_content_len(skb, dptr, datalen);
276 }
277
278 static void ip_nat_sdp_expect(struct nf_conn *ct,
279                               struct nf_conntrack_expect *exp)
280 {
281         struct nf_nat_range range;
282
283         /* This must be a fresh one. */
284         BUG_ON(ct->status & IPS_NAT_DONE_MASK);
285
286         /* For DST manip, map port here to where it's expected. */
287         range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED);
288         range.min = range.max = exp->saved_proto;
289         range.min_ip = range.max_ip = exp->saved_ip;
290         nf_nat_setup_info(ct, &range, IP_NAT_MANIP_DST);
291
292         /* Change src to where master sends to */
293         range.flags = IP_NAT_RANGE_MAP_IPS;
294         range.min_ip = range.max_ip
295                 = ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip;
296         nf_nat_setup_info(ct, &range, IP_NAT_MANIP_SRC);
297 }
298
299 /* So, this packet has hit the connection tracking matching code.
300    Mangle it, and change the expectation to match the new version. */
301 static unsigned int ip_nat_sdp(struct sk_buff *skb,
302                                const char **dptr, unsigned int *datalen,
303                                struct nf_conntrack_expect *exp)
304 {
305         enum ip_conntrack_info ctinfo;
306         struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
307         enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
308         __be32 newip;
309         u_int16_t port;
310
311         /* Connection will come from reply */
312         if (ct->tuplehash[dir].tuple.src.u3.ip ==
313             ct->tuplehash[!dir].tuple.dst.u3.ip)
314                 newip = exp->tuple.dst.u3.ip;
315         else
316                 newip = ct->tuplehash[!dir].tuple.dst.u3.ip;
317
318         exp->saved_ip = exp->tuple.dst.u3.ip;
319         exp->tuple.dst.u3.ip = newip;
320         exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port;
321         exp->dir = !dir;
322
323         /* When you see the packet, we need to NAT it the same as the
324            this one. */
325         exp->expectfn = ip_nat_sdp_expect;
326
327         /* Try to get same port: if not, try to change it. */
328         for (port = ntohs(exp->saved_proto.udp.port); port != 0; port++) {
329                 exp->tuple.dst.u.udp.port = htons(port);
330                 if (nf_ct_expect_related(exp) == 0)
331                         break;
332         }
333
334         if (port == 0)
335                 return NF_DROP;
336
337         if (!mangle_sdp(skb, ctinfo, ct, newip, port, dptr, datalen)) {
338                 nf_ct_unexpect_related(exp);
339                 return NF_DROP;
340         }
341         return NF_ACCEPT;
342 }
343
344 static void __exit nf_nat_sip_fini(void)
345 {
346         rcu_assign_pointer(nf_nat_sip_hook, NULL);
347         rcu_assign_pointer(nf_nat_sdp_hook, NULL);
348         synchronize_rcu();
349 }
350
351 static int __init nf_nat_sip_init(void)
352 {
353         BUG_ON(nf_nat_sip_hook != NULL);
354         BUG_ON(nf_nat_sdp_hook != NULL);
355         rcu_assign_pointer(nf_nat_sip_hook, ip_nat_sip);
356         rcu_assign_pointer(nf_nat_sdp_hook, ip_nat_sdp);
357         return 0;
358 }
359
360 module_init(nf_nat_sip_init);
361 module_exit(nf_nat_sip_fini);