]> err.no Git - linux-2.6/blob - net/ipv4/netfilter/nf_nat_sip.c
5b4a5cd23f39cbd8d72aae761fbc4e113aba3fca
[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         unsigned int matchoff, matchlen;
104         union nf_inet_addr addr;
105         __be16 port;
106
107         if (*datalen < strlen("SIP/2.0"))
108                 return NF_ACCEPT;
109
110         /* Basic rules: requests and responses. */
111         if (strnicmp(*dptr, "SIP/2.0", strlen("SIP/2.0")) != 0) {
112                 if (ct_sip_parse_request(ct, *dptr, *datalen,
113                                          &matchoff, &matchlen,
114                                          &addr, &port) > 0 &&
115                     !map_addr(skb, dptr, datalen, matchoff, matchlen,
116                               &addr, port))
117                         return NF_DROP;
118         }
119
120         if (!map_sip_addr(skb, dptr, datalen, SIP_HDR_FROM) ||
121             !map_sip_addr(skb, dptr, datalen, SIP_HDR_TO) ||
122             !map_sip_addr(skb, dptr, datalen, SIP_HDR_VIA) ||
123             !map_sip_addr(skb, dptr, datalen, SIP_HDR_CONTACT))
124                 return NF_DROP;
125         return NF_ACCEPT;
126 }
127
128 static int mangle_content_len(struct sk_buff *skb,
129                               const char **dptr, unsigned int *datalen)
130 {
131         enum ip_conntrack_info ctinfo;
132         struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
133         unsigned int matchoff, matchlen;
134         char buffer[sizeof("65536")];
135         int buflen, c_len;
136
137         /* Get actual SDP length */
138         if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen,
139                                   SDP_HDR_VERSION, SDP_HDR_UNSPEC,
140                                   &matchoff, &matchlen) <= 0)
141                 return 0;
142         c_len = *datalen - matchoff + strlen("v=");
143
144         /* Now, update SDP length */
145         if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CONTENT_LENGTH,
146                               &matchoff, &matchlen) <= 0)
147                 return 0;
148
149         buflen = sprintf(buffer, "%u", c_len);
150         return mangle_packet(skb, dptr, datalen, matchoff, matchlen,
151                              buffer, buflen);
152 }
153
154 static unsigned mangle_sdp_packet(struct sk_buff *skb,
155                                   const char **dptr, unsigned int *datalen,
156                                   enum sdp_header_types type,
157                                   char *buffer, int buflen)
158 {
159         enum ip_conntrack_info ctinfo;
160         struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
161         unsigned int matchlen, matchoff;
162
163         if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen, type, SDP_HDR_UNSPEC,
164                                   &matchoff, &matchlen) <= 0)
165                 return 0;
166         return mangle_packet(skb, dptr, datalen, matchoff, matchlen,
167                              buffer, buflen);
168 }
169
170 static unsigned int mangle_sdp(struct sk_buff *skb,
171                                enum ip_conntrack_info ctinfo,
172                                struct nf_conn *ct,
173                                __be32 newip, u_int16_t port,
174                                const char **dptr, unsigned int *datalen)
175 {
176         char buffer[sizeof("nnn.nnn.nnn.nnn")];
177         unsigned int bufflen;
178
179         /* Mangle owner and contact info. */
180         bufflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(newip));
181         if (!mangle_sdp_packet(skb, dptr, datalen, SDP_HDR_OWNER_IP4,
182                                buffer, bufflen))
183                 return 0;
184
185         if (!mangle_sdp_packet(skb, dptr, datalen, SDP_HDR_CONNECTION_IP4,
186                                buffer, bufflen))
187                 return 0;
188
189         /* Mangle media port. */
190         bufflen = sprintf(buffer, "%u", port);
191         if (!mangle_sdp_packet(skb, dptr, datalen, SDP_HDR_MEDIA,
192                                buffer, bufflen))
193                 return 0;
194
195         return mangle_content_len(skb, dptr, datalen);
196 }
197
198 static void ip_nat_sdp_expect(struct nf_conn *ct,
199                               struct nf_conntrack_expect *exp)
200 {
201         struct nf_nat_range range;
202
203         /* This must be a fresh one. */
204         BUG_ON(ct->status & IPS_NAT_DONE_MASK);
205
206         /* For DST manip, map port here to where it's expected. */
207         range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED);
208         range.min = range.max = exp->saved_proto;
209         range.min_ip = range.max_ip = exp->saved_ip;
210         nf_nat_setup_info(ct, &range, IP_NAT_MANIP_DST);
211
212         /* Change src to where master sends to */
213         range.flags = IP_NAT_RANGE_MAP_IPS;
214         range.min_ip = range.max_ip
215                 = ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip;
216         nf_nat_setup_info(ct, &range, IP_NAT_MANIP_SRC);
217 }
218
219 /* So, this packet has hit the connection tracking matching code.
220    Mangle it, and change the expectation to match the new version. */
221 static unsigned int ip_nat_sdp(struct sk_buff *skb,
222                                const char **dptr, unsigned int *datalen,
223                                struct nf_conntrack_expect *exp)
224 {
225         enum ip_conntrack_info ctinfo;
226         struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
227         enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
228         __be32 newip;
229         u_int16_t port;
230
231         /* Connection will come from reply */
232         if (ct->tuplehash[dir].tuple.src.u3.ip ==
233             ct->tuplehash[!dir].tuple.dst.u3.ip)
234                 newip = exp->tuple.dst.u3.ip;
235         else
236                 newip = ct->tuplehash[!dir].tuple.dst.u3.ip;
237
238         exp->saved_ip = exp->tuple.dst.u3.ip;
239         exp->tuple.dst.u3.ip = newip;
240         exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port;
241         exp->dir = !dir;
242
243         /* When you see the packet, we need to NAT it the same as the
244            this one. */
245         exp->expectfn = ip_nat_sdp_expect;
246
247         /* Try to get same port: if not, try to change it. */
248         for (port = ntohs(exp->saved_proto.udp.port); port != 0; port++) {
249                 exp->tuple.dst.u.udp.port = htons(port);
250                 if (nf_ct_expect_related(exp) == 0)
251                         break;
252         }
253
254         if (port == 0)
255                 return NF_DROP;
256
257         if (!mangle_sdp(skb, ctinfo, ct, newip, port, dptr, datalen)) {
258                 nf_ct_unexpect_related(exp);
259                 return NF_DROP;
260         }
261         return NF_ACCEPT;
262 }
263
264 static void __exit nf_nat_sip_fini(void)
265 {
266         rcu_assign_pointer(nf_nat_sip_hook, NULL);
267         rcu_assign_pointer(nf_nat_sdp_hook, NULL);
268         synchronize_rcu();
269 }
270
271 static int __init nf_nat_sip_init(void)
272 {
273         BUG_ON(nf_nat_sip_hook != NULL);
274         BUG_ON(nf_nat_sdp_hook != NULL);
275         rcu_assign_pointer(nf_nat_sip_hook, ip_nat_sip);
276         rcu_assign_pointer(nf_nat_sdp_hook, ip_nat_sdp);
277         return 0;
278 }
279
280 module_init(nf_nat_sip_init);
281 module_exit(nf_nat_sip_fini);