]> bbs.cooldavid.org Git - net-next-2.6.git/blame - net/ipv4/netfilter/nf_nat_sip.c
[NETFILTER]: nf_conntrack_sip: remove redundant function arguments
[net-next-2.6.git] / net / ipv4 / netfilter / nf_nat_sip.c
CommitLineData
9fafcd7b
PM
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>
c9bdd4b5 14#include <net/ip.h>
9fafcd7b
PM
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
24MODULE_LICENSE("GPL");
25MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>");
26MODULE_DESCRIPTION("SIP NAT helper");
27MODULE_ALIAS("ip_nat_sip");
28
9fafcd7b
PM
29struct addr_map {
30 struct {
31 char src[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
32 char dst[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
33 unsigned int srclen, srciplen;
34 unsigned int dstlen, dstiplen;
35 } addr[IP_CT_DIR_MAX];
36};
37
13f7d63c 38static void addr_map_init(const struct nf_conn *ct, struct addr_map *map)
9fafcd7b 39{
13f7d63c 40 const struct nf_conntrack_tuple *t;
9fafcd7b
PM
41 enum ip_conntrack_dir dir;
42 unsigned int n;
43
44 for (dir = 0; dir < IP_CT_DIR_MAX; dir++) {
45 t = &ct->tuplehash[dir].tuple;
46
47 n = sprintf(map->addr[dir].src, "%u.%u.%u.%u",
48 NIPQUAD(t->src.u3.ip));
49 map->addr[dir].srciplen = n;
50 n += sprintf(map->addr[dir].src + n, ":%u",
51 ntohs(t->src.u.udp.port));
52 map->addr[dir].srclen = n;
53
54 n = sprintf(map->addr[dir].dst, "%u.%u.%u.%u",
55 NIPQUAD(t->dst.u3.ip));
56 map->addr[dir].dstiplen = n;
57 n += sprintf(map->addr[dir].dst + n, ":%u",
58 ntohs(t->dst.u.udp.port));
59 map->addr[dir].dstlen = n;
60 }
61}
62
2a6cfb22
PM
63static unsigned int mangle_packet(struct sk_buff *skb,
64 const char **dptr, unsigned int *datalen,
65 unsigned int matchoff, unsigned int matchlen,
66 const char *buffer, unsigned int buflen)
67{
68 enum ip_conntrack_info ctinfo;
69 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
70
71 if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, matchoff, matchlen,
72 buffer, buflen))
73 return 0;
74
75 /* Reload data pointer and adjust datalen value */
76 *dptr = skb->data + ip_hdrlen(skb) + sizeof(struct udphdr);
77 *datalen += buflen - matchlen;
78 return 1;
79}
80
212440a7 81static int map_sip_addr(struct sk_buff *skb,
2a6cfb22 82 const char **dptr, unsigned int *datalen,
9fafcd7b
PM
83 enum sip_header_pos pos, struct addr_map *map)
84{
212440a7
PM
85 enum ip_conntrack_info ctinfo;
86 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
9fafcd7b
PM
87 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
88 unsigned int matchlen, matchoff, addrlen;
89 char *addr;
90
2a6cfb22
PM
91 if (ct_sip_get_info(ct, *dptr, *datalen, &matchoff, &matchlen,
92 pos) <= 0)
9fafcd7b
PM
93 return 1;
94
95 if ((matchlen == map->addr[dir].srciplen ||
96 matchlen == map->addr[dir].srclen) &&
97 memcmp(*dptr + matchoff, map->addr[dir].src, matchlen) == 0) {
98 addr = map->addr[!dir].dst;
99 addrlen = map->addr[!dir].dstlen;
100 } else if ((matchlen == map->addr[dir].dstiplen ||
101 matchlen == map->addr[dir].dstlen) &&
102 memcmp(*dptr + matchoff, map->addr[dir].dst, matchlen) == 0) {
103 addr = map->addr[!dir].src;
104 addrlen = map->addr[!dir].srclen;
105 } else
106 return 1;
107
2a6cfb22
PM
108 return mangle_packet(skb, dptr, datalen, matchoff, matchlen,
109 addr, addrlen);
9fafcd7b
PM
110}
111
3db05fea 112static unsigned int ip_nat_sip(struct sk_buff *skb,
2a6cfb22 113 const char **dptr, unsigned int *datalen)
9fafcd7b 114{
212440a7
PM
115 enum ip_conntrack_info ctinfo;
116 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
9fafcd7b
PM
117 enum sip_header_pos pos;
118 struct addr_map map;
9fafcd7b 119
2a6cfb22 120 if (*datalen < sizeof("SIP/2.0") - 1)
45241a7a 121 return NF_ACCEPT;
9fafcd7b
PM
122
123 addr_map_init(ct, &map);
124
125 /* Basic rules: requests and responses. */
126 if (strncmp(*dptr, "SIP/2.0", sizeof("SIP/2.0") - 1) != 0) {
127 /* 10.2: Constructing the REGISTER Request:
128 *
129 * The "userinfo" and "@" components of the SIP URI MUST NOT
130 * be present.
131 */
2a6cfb22 132 if (*datalen >= sizeof("REGISTER") - 1 &&
9fafcd7b
PM
133 strncmp(*dptr, "REGISTER", sizeof("REGISTER") - 1) == 0)
134 pos = POS_REG_REQ_URI;
135 else
136 pos = POS_REQ_URI;
137
212440a7 138 if (!map_sip_addr(skb, dptr, datalen, pos, &map))
9fafcd7b
PM
139 return NF_DROP;
140 }
141
212440a7
PM
142 if (!map_sip_addr(skb, dptr, datalen, POS_FROM, &map) ||
143 !map_sip_addr(skb, dptr, datalen, POS_TO, &map) ||
144 !map_sip_addr(skb, dptr, datalen, POS_VIA, &map) ||
145 !map_sip_addr(skb, dptr, datalen, POS_CONTACT, &map))
9fafcd7b
PM
146 return NF_DROP;
147 return NF_ACCEPT;
148}
149
3db05fea 150static unsigned int mangle_sip_packet(struct sk_buff *skb,
2a6cfb22 151 const char **dptr, unsigned int *datalen,
9fafcd7b
PM
152 char *buffer, int bufflen,
153 enum sip_header_pos pos)
154{
212440a7
PM
155 enum ip_conntrack_info ctinfo;
156 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
9fafcd7b
PM
157 unsigned int matchlen, matchoff;
158
2a6cfb22
PM
159 if (ct_sip_get_info(ct, *dptr, *datalen, &matchoff, &matchlen,
160 pos) <= 0)
9fafcd7b
PM
161 return 0;
162
2a6cfb22
PM
163 return mangle_packet(skb, dptr, datalen, matchoff, matchlen,
164 buffer, bufflen);
9fafcd7b
PM
165}
166
3db05fea 167static int mangle_content_len(struct sk_buff *skb,
2a6cfb22 168 const char **dptr, unsigned int *datalen)
9fafcd7b 169{
212440a7
PM
170 enum ip_conntrack_info ctinfo;
171 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
2a6cfb22 172 unsigned int matchoff, matchlen;
9fafcd7b
PM
173 char buffer[sizeof("65536")];
174 int bufflen;
175
e00ccd4a 176 /* Get actual SDP length */
2a6cfb22 177 if (ct_sip_get_info(ct, *dptr, *datalen, &matchoff,
e905a9ed 178 &matchlen, POS_SDP_HEADER) > 0) {
9fafcd7b
PM
179
180 /* since ct_sip_get_info() give us a pointer passing 'v='
181 we need to add 2 bytes in this count. */
2a6cfb22 182 int c_len = *datalen - matchoff + 2;
9fafcd7b
PM
183
184 /* Now, update SDP length */
2a6cfb22 185 if (ct_sip_get_info(ct, *dptr, *datalen, &matchoff,
e905a9ed 186 &matchlen, POS_CONTENT) > 0) {
9fafcd7b
PM
187
188 bufflen = sprintf(buffer, "%u", c_len);
2a6cfb22
PM
189 return mangle_packet(skb, dptr, datalen,
190 matchoff, matchlen,
191 buffer, bufflen);
9fafcd7b
PM
192 }
193 }
194 return 0;
195}
196
3db05fea 197static unsigned int mangle_sdp(struct sk_buff *skb,
9fafcd7b
PM
198 enum ip_conntrack_info ctinfo,
199 struct nf_conn *ct,
200 __be32 newip, u_int16_t port,
2a6cfb22 201 const char **dptr, unsigned int *datalen)
9fafcd7b
PM
202{
203 char buffer[sizeof("nnn.nnn.nnn.nnn")];
2a6cfb22 204 unsigned int bufflen;
9fafcd7b
PM
205
206 /* Mangle owner and contact info. */
207 bufflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(newip));
212440a7
PM
208 if (!mangle_sip_packet(skb, dptr, datalen, buffer, bufflen,
209 POS_OWNER_IP4))
9fafcd7b
PM
210 return 0;
211
212440a7
PM
212 if (!mangle_sip_packet(skb, dptr, datalen, buffer, bufflen,
213 POS_CONNECTION_IP4))
9fafcd7b
PM
214 return 0;
215
216 /* Mangle media port. */
217 bufflen = sprintf(buffer, "%u", port);
212440a7
PM
218 if (!mangle_sip_packet(skb, dptr, datalen, buffer, bufflen,
219 POS_MEDIA))
9fafcd7b
PM
220 return 0;
221
212440a7 222 return mangle_content_len(skb, dptr, datalen);
9fafcd7b
PM
223}
224
cfd6c380
HX
225static void ip_nat_sdp_expect(struct nf_conn *ct,
226 struct nf_conntrack_expect *exp)
227{
228 struct nf_nat_range range;
229
230 /* This must be a fresh one. */
231 BUG_ON(ct->status & IPS_NAT_DONE_MASK);
232
cfd6c380
HX
233 /* For DST manip, map port here to where it's expected. */
234 range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED);
235 range.min = range.max = exp->saved_proto;
236 range.min_ip = range.max_ip = exp->saved_ip;
cc01dcbd 237 nf_nat_setup_info(ct, &range, IP_NAT_MANIP_DST);
3d244121
PM
238
239 /* Change src to where master sends to */
240 range.flags = IP_NAT_RANGE_MAP_IPS;
241 range.min_ip = range.max_ip
242 = ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip;
243 nf_nat_setup_info(ct, &range, IP_NAT_MANIP_SRC);
cfd6c380
HX
244}
245
9fafcd7b
PM
246/* So, this packet has hit the connection tracking matching code.
247 Mangle it, and change the expectation to match the new version. */
3db05fea 248static unsigned int ip_nat_sdp(struct sk_buff *skb,
212440a7
PM
249 const char **dptr, unsigned int *datalen,
250 struct nf_conntrack_expect *exp)
9fafcd7b 251{
212440a7
PM
252 enum ip_conntrack_info ctinfo;
253 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
9fafcd7b
PM
254 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
255 __be32 newip;
256 u_int16_t port;
257
9fafcd7b 258 /* Connection will come from reply */
f4a607bf
JB
259 if (ct->tuplehash[dir].tuple.src.u3.ip ==
260 ct->tuplehash[!dir].tuple.dst.u3.ip)
261 newip = exp->tuple.dst.u3.ip;
262 else
263 newip = ct->tuplehash[!dir].tuple.dst.u3.ip;
9fafcd7b 264
cfd6c380 265 exp->saved_ip = exp->tuple.dst.u3.ip;
9fafcd7b
PM
266 exp->tuple.dst.u3.ip = newip;
267 exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port;
268 exp->dir = !dir;
269
270 /* When you see the packet, we need to NAT it the same as the
271 this one. */
cfd6c380 272 exp->expectfn = ip_nat_sdp_expect;
9fafcd7b
PM
273
274 /* Try to get same port: if not, try to change it. */
275 for (port = ntohs(exp->saved_proto.udp.port); port != 0; port++) {
276 exp->tuple.dst.u.udp.port = htons(port);
6823645d 277 if (nf_ct_expect_related(exp) == 0)
9fafcd7b
PM
278 break;
279 }
280
281 if (port == 0)
282 return NF_DROP;
283
2a6cfb22 284 if (!mangle_sdp(skb, ctinfo, ct, newip, port, dptr, datalen)) {
6823645d 285 nf_ct_unexpect_related(exp);
9fafcd7b
PM
286 return NF_DROP;
287 }
288 return NF_ACCEPT;
289}
290
291static void __exit nf_nat_sip_fini(void)
292{
293 rcu_assign_pointer(nf_nat_sip_hook, NULL);
294 rcu_assign_pointer(nf_nat_sdp_hook, NULL);
295 synchronize_rcu();
296}
297
298static int __init nf_nat_sip_init(void)
299{
d1332e0a
PM
300 BUG_ON(nf_nat_sip_hook != NULL);
301 BUG_ON(nf_nat_sdp_hook != NULL);
9fafcd7b
PM
302 rcu_assign_pointer(nf_nat_sip_hook, ip_nat_sip);
303 rcu_assign_pointer(nf_nat_sdp_hook, ip_nat_sdp);
304 return 0;
305}
306
307module_init(nf_nat_sip_init);
308module_exit(nf_nat_sip_fini);