diff options
author | Patrick McHardy <kaber@trash.net> | 2006-12-03 01:09:57 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2006-12-03 01:09:57 -0500 |
commit | 9fafcd7b203229c3f3893a475741afc27e276306 (patch) | |
tree | cc1c5051142c35dee2c584c97892624cb701bf20 /net/ipv4 | |
parent | f09943fefe6b702e40893d35b4f10fd1064037fe (diff) |
[NETFILTER]: nf_conntrack/nf_nat: add SIP helper port
Add IPv4 and IPv6 capable nf_conntrack port of the SIP conntrack/NAT helper.
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/netfilter/Kconfig | 5 | ||||
-rw-r--r-- | net/ipv4/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/ipv4/netfilter/nf_nat_sip.c | 283 |
3 files changed, 289 insertions, 0 deletions
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index c3327ac024de..83e83f553ccb 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig | |||
@@ -555,6 +555,11 @@ config IP_NF_NAT_SIP | |||
555 | default IP_NF_NAT if IP_NF_SIP=y | 555 | default IP_NF_NAT if IP_NF_SIP=y |
556 | default m if IP_NF_SIP=m | 556 | default m if IP_NF_SIP=m |
557 | 557 | ||
558 | config NF_NAT_SIP | ||
559 | tristate | ||
560 | depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT | ||
561 | default NF_NAT && NF_CONNTRACK_SIP | ||
562 | |||
558 | # mangle + specific targets | 563 | # mangle + specific targets |
559 | config IP_NF_MANGLE | 564 | config IP_NF_MANGLE |
560 | tristate "Packet mangling" | 565 | tristate "Packet mangling" |
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index ef33ff2cdda9..167b65e89aca 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile | |||
@@ -55,6 +55,7 @@ obj-$(CONFIG_NF_NAT_FTP) += nf_nat_ftp.o | |||
55 | obj-$(CONFIG_NF_NAT_H323) += nf_nat_h323.o | 55 | obj-$(CONFIG_NF_NAT_H323) += nf_nat_h323.o |
56 | obj-$(CONFIG_NF_NAT_IRC) += nf_nat_irc.o | 56 | obj-$(CONFIG_NF_NAT_IRC) += nf_nat_irc.o |
57 | obj-$(CONFIG_NF_NAT_PPTP) += nf_nat_pptp.o | 57 | obj-$(CONFIG_NF_NAT_PPTP) += nf_nat_pptp.o |
58 | obj-$(CONFIG_NF_NAT_SIP) += nf_nat_sip.o | ||
58 | 59 | ||
59 | # NAT protocols (nf_nat) | 60 | # NAT protocols (nf_nat) |
60 | obj-$(CONFIG_NF_NAT_PROTO_GRE) += nf_nat_proto_gre.o | 61 | obj-$(CONFIG_NF_NAT_PROTO_GRE) += nf_nat_proto_gre.o |
diff --git a/net/ipv4/netfilter/nf_nat_sip.c b/net/ipv4/netfilter/nf_nat_sip.c new file mode 100644 index 000000000000..3d524b957310 --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_sip.c | |||
@@ -0,0 +1,283 @@ | |||
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 <linux/udp.h> | ||
15 | |||
16 | #include <net/netfilter/nf_nat.h> | ||
17 | #include <net/netfilter/nf_nat_helper.h> | ||
18 | #include <net/netfilter/nf_nat_rule.h> | ||
19 | #include <net/netfilter/nf_conntrack_helper.h> | ||
20 | #include <net/netfilter/nf_conntrack_expect.h> | ||
21 | #include <linux/netfilter/nf_conntrack_sip.h> | ||
22 | |||
23 | MODULE_LICENSE("GPL"); | ||
24 | MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>"); | ||
25 | MODULE_DESCRIPTION("SIP NAT helper"); | ||
26 | MODULE_ALIAS("ip_nat_sip"); | ||
27 | |||
28 | #if 0 | ||
29 | #define DEBUGP printk | ||
30 | #else | ||
31 | #define DEBUGP(format, args...) | ||
32 | #endif | ||
33 | |||
34 | struct addr_map { | ||
35 | struct { | ||
36 | char src[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; | ||
37 | char dst[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; | ||
38 | unsigned int srclen, srciplen; | ||
39 | unsigned int dstlen, dstiplen; | ||
40 | } addr[IP_CT_DIR_MAX]; | ||
41 | }; | ||
42 | |||
43 | static void addr_map_init(struct nf_conn *ct, struct addr_map *map) | ||
44 | { | ||
45 | struct nf_conntrack_tuple *t; | ||
46 | enum ip_conntrack_dir dir; | ||
47 | unsigned int n; | ||
48 | |||
49 | for (dir = 0; dir < IP_CT_DIR_MAX; dir++) { | ||
50 | t = &ct->tuplehash[dir].tuple; | ||
51 | |||
52 | n = sprintf(map->addr[dir].src, "%u.%u.%u.%u", | ||
53 | NIPQUAD(t->src.u3.ip)); | ||
54 | map->addr[dir].srciplen = n; | ||
55 | n += sprintf(map->addr[dir].src + n, ":%u", | ||
56 | ntohs(t->src.u.udp.port)); | ||
57 | map->addr[dir].srclen = n; | ||
58 | |||
59 | n = sprintf(map->addr[dir].dst, "%u.%u.%u.%u", | ||
60 | NIPQUAD(t->dst.u3.ip)); | ||
61 | map->addr[dir].dstiplen = n; | ||
62 | n += sprintf(map->addr[dir].dst + n, ":%u", | ||
63 | ntohs(t->dst.u.udp.port)); | ||
64 | map->addr[dir].dstlen = n; | ||
65 | } | ||
66 | } | ||
67 | |||
68 | static int map_sip_addr(struct sk_buff **pskb, enum ip_conntrack_info ctinfo, | ||
69 | struct nf_conn *ct, const char **dptr, size_t dlen, | ||
70 | enum sip_header_pos pos, struct addr_map *map) | ||
71 | { | ||
72 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
73 | unsigned int matchlen, matchoff, addrlen; | ||
74 | char *addr; | ||
75 | |||
76 | if (ct_sip_get_info(ct, *dptr, dlen, &matchoff, &matchlen, pos) <= 0) | ||
77 | return 1; | ||
78 | |||
79 | if ((matchlen == map->addr[dir].srciplen || | ||
80 | matchlen == map->addr[dir].srclen) && | ||
81 | memcmp(*dptr + matchoff, map->addr[dir].src, matchlen) == 0) { | ||
82 | addr = map->addr[!dir].dst; | ||
83 | addrlen = map->addr[!dir].dstlen; | ||
84 | } else if ((matchlen == map->addr[dir].dstiplen || | ||
85 | matchlen == map->addr[dir].dstlen) && | ||
86 | memcmp(*dptr + matchoff, map->addr[dir].dst, matchlen) == 0) { | ||
87 | addr = map->addr[!dir].src; | ||
88 | addrlen = map->addr[!dir].srclen; | ||
89 | } else | ||
90 | return 1; | ||
91 | |||
92 | if (!nf_nat_mangle_udp_packet(pskb, ct, ctinfo, | ||
93 | matchoff, matchlen, addr, addrlen)) | ||
94 | return 0; | ||
95 | *dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); | ||
96 | return 1; | ||
97 | |||
98 | } | ||
99 | |||
100 | static unsigned int ip_nat_sip(struct sk_buff **pskb, | ||
101 | enum ip_conntrack_info ctinfo, | ||
102 | struct nf_conn *ct, | ||
103 | const char **dptr) | ||
104 | { | ||
105 | enum sip_header_pos pos; | ||
106 | struct addr_map map; | ||
107 | int dataoff, datalen; | ||
108 | |||
109 | dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); | ||
110 | datalen = (*pskb)->len - dataoff; | ||
111 | if (datalen < sizeof("SIP/2.0") - 1) | ||
112 | return NF_DROP; | ||
113 | |||
114 | addr_map_init(ct, &map); | ||
115 | |||
116 | /* Basic rules: requests and responses. */ | ||
117 | if (strncmp(*dptr, "SIP/2.0", sizeof("SIP/2.0") - 1) != 0) { | ||
118 | /* 10.2: Constructing the REGISTER Request: | ||
119 | * | ||
120 | * The "userinfo" and "@" components of the SIP URI MUST NOT | ||
121 | * be present. | ||
122 | */ | ||
123 | if (datalen >= sizeof("REGISTER") - 1 && | ||
124 | strncmp(*dptr, "REGISTER", sizeof("REGISTER") - 1) == 0) | ||
125 | pos = POS_REG_REQ_URI; | ||
126 | else | ||
127 | pos = POS_REQ_URI; | ||
128 | |||
129 | if (!map_sip_addr(pskb, ctinfo, ct, dptr, datalen, pos, &map)) | ||
130 | return NF_DROP; | ||
131 | } | ||
132 | |||
133 | if (!map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_FROM, &map) || | ||
134 | !map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_TO, &map) || | ||
135 | !map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_VIA, &map) || | ||
136 | !map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_CONTACT, &map)) | ||
137 | return NF_DROP; | ||
138 | return NF_ACCEPT; | ||
139 | } | ||
140 | |||
141 | static unsigned int mangle_sip_packet(struct sk_buff **pskb, | ||
142 | enum ip_conntrack_info ctinfo, | ||
143 | struct nf_conn *ct, | ||
144 | const char **dptr, size_t dlen, | ||
145 | char *buffer, int bufflen, | ||
146 | enum sip_header_pos pos) | ||
147 | { | ||
148 | unsigned int matchlen, matchoff; | ||
149 | |||
150 | if (ct_sip_get_info(ct, *dptr, dlen, &matchoff, &matchlen, pos) <= 0) | ||
151 | return 0; | ||
152 | |||
153 | if (!nf_nat_mangle_udp_packet(pskb, ct, ctinfo, | ||
154 | matchoff, matchlen, buffer, bufflen)) | ||
155 | return 0; | ||
156 | |||
157 | /* We need to reload this. Thanks Patrick. */ | ||
158 | *dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); | ||
159 | return 1; | ||
160 | } | ||
161 | |||
162 | static int mangle_content_len(struct sk_buff **pskb, | ||
163 | enum ip_conntrack_info ctinfo, | ||
164 | struct nf_conn *ct, | ||
165 | const char *dptr) | ||
166 | { | ||
167 | unsigned int dataoff, matchoff, matchlen; | ||
168 | char buffer[sizeof("65536")]; | ||
169 | int bufflen; | ||
170 | |||
171 | dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); | ||
172 | |||
173 | /* Get actual SDP lenght */ | ||
174 | if (ct_sip_get_info(ct, dptr, (*pskb)->len - dataoff, &matchoff, | ||
175 | &matchlen, POS_SDP_HEADER) > 0) { | ||
176 | |||
177 | /* since ct_sip_get_info() give us a pointer passing 'v=' | ||
178 | we need to add 2 bytes in this count. */ | ||
179 | int c_len = (*pskb)->len - dataoff - matchoff + 2; | ||
180 | |||
181 | /* Now, update SDP length */ | ||
182 | if (ct_sip_get_info(ct, dptr, (*pskb)->len - dataoff, &matchoff, | ||
183 | &matchlen, POS_CONTENT) > 0) { | ||
184 | |||
185 | bufflen = sprintf(buffer, "%u", c_len); | ||
186 | return nf_nat_mangle_udp_packet(pskb, ct, ctinfo, | ||
187 | matchoff, matchlen, | ||
188 | buffer, bufflen); | ||
189 | } | ||
190 | } | ||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | static unsigned int mangle_sdp(struct sk_buff **pskb, | ||
195 | enum ip_conntrack_info ctinfo, | ||
196 | struct nf_conn *ct, | ||
197 | __be32 newip, u_int16_t port, | ||
198 | const char *dptr) | ||
199 | { | ||
200 | char buffer[sizeof("nnn.nnn.nnn.nnn")]; | ||
201 | unsigned int dataoff, bufflen; | ||
202 | |||
203 | dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); | ||
204 | |||
205 | /* Mangle owner and contact info. */ | ||
206 | bufflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(newip)); | ||
207 | if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff, | ||
208 | buffer, bufflen, POS_OWNER_IP4)) | ||
209 | return 0; | ||
210 | |||
211 | if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff, | ||
212 | buffer, bufflen, POS_CONNECTION_IP4)) | ||
213 | return 0; | ||
214 | |||
215 | /* Mangle media port. */ | ||
216 | bufflen = sprintf(buffer, "%u", port); | ||
217 | if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff, | ||
218 | buffer, bufflen, POS_MEDIA)) | ||
219 | return 0; | ||
220 | |||
221 | return mangle_content_len(pskb, ctinfo, ct, dptr); | ||
222 | } | ||
223 | |||
224 | /* So, this packet has hit the connection tracking matching code. | ||
225 | Mangle it, and change the expectation to match the new version. */ | ||
226 | static unsigned int ip_nat_sdp(struct sk_buff **pskb, | ||
227 | enum ip_conntrack_info ctinfo, | ||
228 | struct nf_conntrack_expect *exp, | ||
229 | const char *dptr) | ||
230 | { | ||
231 | struct nf_conn *ct = exp->master; | ||
232 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
233 | __be32 newip; | ||
234 | u_int16_t port; | ||
235 | |||
236 | DEBUGP("ip_nat_sdp():\n"); | ||
237 | |||
238 | /* Connection will come from reply */ | ||
239 | newip = ct->tuplehash[!dir].tuple.dst.u3.ip; | ||
240 | |||
241 | exp->tuple.dst.u3.ip = newip; | ||
242 | exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port; | ||
243 | exp->dir = !dir; | ||
244 | |||
245 | /* When you see the packet, we need to NAT it the same as the | ||
246 | this one. */ | ||
247 | exp->expectfn = nf_nat_follow_master; | ||
248 | |||
249 | /* Try to get same port: if not, try to change it. */ | ||
250 | for (port = ntohs(exp->saved_proto.udp.port); port != 0; port++) { | ||
251 | exp->tuple.dst.u.udp.port = htons(port); | ||
252 | if (nf_conntrack_expect_related(exp) == 0) | ||
253 | break; | ||
254 | } | ||
255 | |||
256 | if (port == 0) | ||
257 | return NF_DROP; | ||
258 | |||
259 | if (!mangle_sdp(pskb, ctinfo, ct, newip, port, dptr)) { | ||
260 | nf_conntrack_unexpect_related(exp); | ||
261 | return NF_DROP; | ||
262 | } | ||
263 | return NF_ACCEPT; | ||
264 | } | ||
265 | |||
266 | static void __exit nf_nat_sip_fini(void) | ||
267 | { | ||
268 | rcu_assign_pointer(nf_nat_sip_hook, NULL); | ||
269 | rcu_assign_pointer(nf_nat_sdp_hook, NULL); | ||
270 | synchronize_rcu(); | ||
271 | } | ||
272 | |||
273 | static int __init nf_nat_sip_init(void) | ||
274 | { | ||
275 | BUG_ON(rcu_dereference(nf_nat_sip_hook)); | ||
276 | BUG_ON(rcu_dereference(nf_nat_sdp_hook)); | ||
277 | rcu_assign_pointer(nf_nat_sip_hook, ip_nat_sip); | ||
278 | rcu_assign_pointer(nf_nat_sdp_hook, ip_nat_sdp); | ||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | module_init(nf_nat_sip_init); | ||
283 | module_exit(nf_nat_sip_fini); | ||