diff options
author | Patrick McHardy <kaber@trash.net> | 2006-05-29 21:27:09 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-06-18 00:29:15 -0400 |
commit | ae5b7d8ba2c28d7d9835856fe0ca5f6ec95ea768 (patch) | |
tree | 54de467b6ee9344a51c8b254be3da933b57cc531 | |
parent | e44ab66a75e20c02193440a5e27c16c91630109b (diff) |
[NETFILTER]: Add SIP connection tracking helper
Add SIP connection tracking helper. Originally written by
Christian Hentschel <chentschel@arnet.com.ar>, some cleanup, minor
fixes and bidirectional SIP support added by myself.
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/netfilter_ipv4/ip_conntrack_sip.h | 44 | ||||
-rw-r--r-- | net/ipv4/netfilter/Kconfig | 18 | ||||
-rw-r--r-- | net/ipv4/netfilter/Makefile | 2 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_conntrack_sip.c | 471 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_nat_sip.c | 249 |
5 files changed, 784 insertions, 0 deletions
diff --git a/include/linux/netfilter_ipv4/ip_conntrack_sip.h b/include/linux/netfilter_ipv4/ip_conntrack_sip.h new file mode 100644 index 000000000000..913dad66c0fb --- /dev/null +++ b/include/linux/netfilter_ipv4/ip_conntrack_sip.h | |||
@@ -0,0 +1,44 @@ | |||
1 | #ifndef __IP_CONNTRACK_SIP_H__ | ||
2 | #define __IP_CONNTRACK_SIP_H__ | ||
3 | #ifdef __KERNEL__ | ||
4 | |||
5 | #define SIP_PORT 5060 | ||
6 | #define SIP_TIMEOUT 3600 | ||
7 | |||
8 | #define POS_VIA 0 | ||
9 | #define POS_CONTACT 1 | ||
10 | #define POS_CONTENT 2 | ||
11 | #define POS_MEDIA 3 | ||
12 | #define POS_OWNER 4 | ||
13 | #define POS_CONNECTION 5 | ||
14 | #define POS_REQ_HEADER 6 | ||
15 | #define POS_SDP_HEADER 7 | ||
16 | |||
17 | struct sip_header_nfo { | ||
18 | const char *lname; | ||
19 | const char *sname; | ||
20 | const char *ln_str; | ||
21 | size_t lnlen; | ||
22 | size_t snlen; | ||
23 | size_t ln_strlen; | ||
24 | int (*match_len)(const char *, const char *, int *); | ||
25 | }; | ||
26 | |||
27 | extern unsigned int (*ip_nat_sip_hook)(struct sk_buff **pskb, | ||
28 | enum ip_conntrack_info ctinfo, | ||
29 | struct ip_conntrack *ct, | ||
30 | const char **dptr); | ||
31 | extern unsigned int (*ip_nat_sdp_hook)(struct sk_buff **pskb, | ||
32 | enum ip_conntrack_info ctinfo, | ||
33 | struct ip_conntrack_expect *exp, | ||
34 | const char *dptr); | ||
35 | |||
36 | extern int ct_sip_get_info(const char *dptr, size_t dlen, | ||
37 | unsigned int *matchoff, | ||
38 | unsigned int *matchlen, | ||
39 | struct sip_header_nfo *hnfo); | ||
40 | extern int ct_sip_lnlen(const char *line, const char *limit); | ||
41 | extern const char *ct_sip_search(const char *needle, const char *haystack, | ||
42 | size_t needle_len, size_t haystack_len); | ||
43 | #endif /* __KERNEL__ */ | ||
44 | #endif /* __IP_CONNTRACK_SIP_H__ */ | ||
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index f86aeda3a5a0..ff4b118f14a9 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig | |||
@@ -191,6 +191,18 @@ config IP_NF_H323 | |||
191 | If you want to compile it as a module, say 'M' here and read | 191 | If you want to compile it as a module, say 'M' here and read |
192 | Documentation/modules.txt. If unsure, say 'N'. | 192 | Documentation/modules.txt. If unsure, say 'N'. |
193 | 193 | ||
194 | config IP_NF_SIP | ||
195 | tristate "SIP protocol support (EXPERIMENTAL)" | ||
196 | depends on IP_NF_CONNTRACK && EXPERIMENTAL | ||
197 | help | ||
198 | SIP is an application-layer control protocol that can establish, | ||
199 | modify, and terminate multimedia sessions (conferences) such as | ||
200 | Internet telephony calls. With the ip_conntrack_sip and | ||
201 | the ip_nat_sip modules you can support the protocol on a connection | ||
202 | tracking/NATing firewall. | ||
203 | |||
204 | To compile it as a module, choose M here. If unsure, say Y. | ||
205 | |||
194 | config IP_NF_QUEUE | 206 | config IP_NF_QUEUE |
195 | tristate "IP Userspace queueing via NETLINK (OBSOLETE)" | 207 | tristate "IP Userspace queueing via NETLINK (OBSOLETE)" |
196 | help | 208 | help |
@@ -503,6 +515,12 @@ config IP_NF_NAT_H323 | |||
503 | default IP_NF_NAT if IP_NF_H323=y | 515 | default IP_NF_NAT if IP_NF_H323=y |
504 | default m if IP_NF_H323=m | 516 | default m if IP_NF_H323=m |
505 | 517 | ||
518 | config IP_NF_NAT_SIP | ||
519 | tristate | ||
520 | depends on IP_NF_IPTABLES!=n && IP_NF_CONNTRACK!=n && IP_NF_NAT!=n | ||
521 | default IP_NF_NAT if IP_NF_SIP=y | ||
522 | default m if IP_NF_SIP=m | ||
523 | |||
506 | # mangle + specific targets | 524 | # mangle + specific targets |
507 | config IP_NF_MANGLE | 525 | config IP_NF_MANGLE |
508 | tristate "Packet mangling" | 526 | tristate "Packet mangling" |
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index 461cb1eb5de7..3ded4a3af59c 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile | |||
@@ -31,6 +31,7 @@ obj-$(CONFIG_IP_NF_AMANDA) += ip_conntrack_amanda.o | |||
31 | obj-$(CONFIG_IP_NF_TFTP) += ip_conntrack_tftp.o | 31 | obj-$(CONFIG_IP_NF_TFTP) += ip_conntrack_tftp.o |
32 | obj-$(CONFIG_IP_NF_FTP) += ip_conntrack_ftp.o | 32 | obj-$(CONFIG_IP_NF_FTP) += ip_conntrack_ftp.o |
33 | obj-$(CONFIG_IP_NF_IRC) += ip_conntrack_irc.o | 33 | obj-$(CONFIG_IP_NF_IRC) += ip_conntrack_irc.o |
34 | obj-$(CONFIG_IP_NF_SIP) += ip_conntrack_sip.o | ||
34 | obj-$(CONFIG_IP_NF_NETBIOS_NS) += ip_conntrack_netbios_ns.o | 35 | obj-$(CONFIG_IP_NF_NETBIOS_NS) += ip_conntrack_netbios_ns.o |
35 | 36 | ||
36 | # NAT helpers | 37 | # NAT helpers |
@@ -40,6 +41,7 @@ obj-$(CONFIG_IP_NF_NAT_AMANDA) += ip_nat_amanda.o | |||
40 | obj-$(CONFIG_IP_NF_NAT_TFTP) += ip_nat_tftp.o | 41 | obj-$(CONFIG_IP_NF_NAT_TFTP) += ip_nat_tftp.o |
41 | obj-$(CONFIG_IP_NF_NAT_FTP) += ip_nat_ftp.o | 42 | obj-$(CONFIG_IP_NF_NAT_FTP) += ip_nat_ftp.o |
42 | obj-$(CONFIG_IP_NF_NAT_IRC) += ip_nat_irc.o | 43 | obj-$(CONFIG_IP_NF_NAT_IRC) += ip_nat_irc.o |
44 | obj-$(CONFIG_IP_NF_NAT_SIP) += ip_nat_sip.o | ||
43 | 45 | ||
44 | # generic IP tables | 46 | # generic IP tables |
45 | obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o | 47 | obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o |
diff --git a/net/ipv4/netfilter/ip_conntrack_sip.c b/net/ipv4/netfilter/ip_conntrack_sip.c new file mode 100644 index 000000000000..fc87ce0da40d --- /dev/null +++ b/net/ipv4/netfilter/ip_conntrack_sip.c | |||
@@ -0,0 +1,471 @@ | |||
1 | /* SIP extension for IP connection tracking. | ||
2 | * | ||
3 | * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar> | ||
4 | * based on RR's ip_conntrack_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/config.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/ctype.h> | ||
14 | #include <linux/skbuff.h> | ||
15 | #include <linux/in.h> | ||
16 | #include <linux/ip.h> | ||
17 | #include <linux/udp.h> | ||
18 | |||
19 | #include <linux/netfilter.h> | ||
20 | #include <linux/netfilter_ipv4.h> | ||
21 | #include <linux/netfilter_ipv4/ip_conntrack_helper.h> | ||
22 | #include <linux/netfilter_ipv4/ip_conntrack_sip.h> | ||
23 | |||
24 | #if 0 | ||
25 | #define DEBUGP printk | ||
26 | #else | ||
27 | #define DEBUGP(format, args...) | ||
28 | #endif | ||
29 | |||
30 | MODULE_LICENSE("GPL"); | ||
31 | MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>"); | ||
32 | MODULE_DESCRIPTION("SIP connection tracking helper"); | ||
33 | |||
34 | #define MAX_PORTS 8 | ||
35 | static unsigned short ports[MAX_PORTS]; | ||
36 | static int ports_c; | ||
37 | module_param_array(ports, ushort, &ports_c, 0400); | ||
38 | MODULE_PARM_DESC(ports, "port numbers of sip servers"); | ||
39 | |||
40 | static unsigned int sip_timeout = SIP_TIMEOUT; | ||
41 | module_param(sip_timeout, uint, 0600); | ||
42 | MODULE_PARM_DESC(sip_timeout, "timeout for the master SIP session"); | ||
43 | |||
44 | unsigned int (*ip_nat_sip_hook)(struct sk_buff **pskb, | ||
45 | enum ip_conntrack_info ctinfo, | ||
46 | struct ip_conntrack *ct, | ||
47 | const char **dptr); | ||
48 | EXPORT_SYMBOL_GPL(ip_nat_sip_hook); | ||
49 | |||
50 | unsigned int (*ip_nat_sdp_hook)(struct sk_buff **pskb, | ||
51 | enum ip_conntrack_info ctinfo, | ||
52 | struct ip_conntrack_expect *exp, | ||
53 | const char *dptr); | ||
54 | EXPORT_SYMBOL_GPL(ip_nat_sdp_hook); | ||
55 | |||
56 | int ct_sip_get_info(const char *dptr, size_t dlen, | ||
57 | unsigned int *matchoff, | ||
58 | unsigned int *matchlen, | ||
59 | struct sip_header_nfo *hnfo); | ||
60 | EXPORT_SYMBOL_GPL(ct_sip_get_info); | ||
61 | |||
62 | |||
63 | static int digits_len(const char *dptr, const char *limit, int *shift); | ||
64 | static int epaddr_len(const char *dptr, const char *limit, int *shift); | ||
65 | static int skp_digits_len(const char *dptr, const char *limit, int *shift); | ||
66 | static int skp_epaddr_len(const char *dptr, const char *limit, int *shift); | ||
67 | |||
68 | struct sip_header_nfo ct_sip_hdrs[] = { | ||
69 | { /* Via header */ | ||
70 | .lname = "Via:", | ||
71 | .lnlen = sizeof("Via:") - 1, | ||
72 | .sname = "\r\nv:", | ||
73 | .snlen = sizeof("\r\nv:") - 1, /* rfc3261 "\r\n" */ | ||
74 | .ln_str = "UDP ", | ||
75 | .ln_strlen = sizeof("UDP ") - 1, | ||
76 | .match_len = epaddr_len, | ||
77 | }, | ||
78 | { /* Contact header */ | ||
79 | .lname = "Contact:", | ||
80 | .lnlen = sizeof("Contact:") - 1, | ||
81 | .sname = "\r\nm:", | ||
82 | .snlen = sizeof("\r\nm:") - 1, | ||
83 | .ln_str = "sip:", | ||
84 | .ln_strlen = sizeof("sip:") - 1, | ||
85 | .match_len = skp_epaddr_len | ||
86 | }, | ||
87 | { /* Content length header */ | ||
88 | .lname = "Content-Length:", | ||
89 | .lnlen = sizeof("Content-Length:") - 1, | ||
90 | .sname = "\r\nl:", | ||
91 | .snlen = sizeof("\r\nl:") - 1, | ||
92 | .ln_str = ":", | ||
93 | .ln_strlen = sizeof(":") - 1, | ||
94 | .match_len = skp_digits_len | ||
95 | }, | ||
96 | { /* SDP media info */ | ||
97 | .lname = "\nm=", | ||
98 | .lnlen = sizeof("\nm=") - 1, | ||
99 | .sname = "\rm=", | ||
100 | .snlen = sizeof("\rm=") - 1, | ||
101 | .ln_str = "audio ", | ||
102 | .ln_strlen = sizeof("audio ") - 1, | ||
103 | .match_len = digits_len | ||
104 | }, | ||
105 | { /* SDP owner address*/ | ||
106 | .lname = "\no=", | ||
107 | .lnlen = sizeof("\no=") - 1, | ||
108 | .sname = "\ro=", | ||
109 | .snlen = sizeof("\ro=") - 1, | ||
110 | .ln_str = "IN IP4 ", | ||
111 | .ln_strlen = sizeof("IN IP4 ") - 1, | ||
112 | .match_len = epaddr_len | ||
113 | }, | ||
114 | { /* SDP connection info */ | ||
115 | .lname = "\nc=", | ||
116 | .lnlen = sizeof("\nc=") - 1, | ||
117 | .sname = "\rc=", | ||
118 | .snlen = sizeof("\rc=") - 1, | ||
119 | .ln_str = "IN IP4 ", | ||
120 | .ln_strlen = sizeof("IN IP4 ") - 1, | ||
121 | .match_len = epaddr_len | ||
122 | }, | ||
123 | { /* Requests headers */ | ||
124 | .lname = "sip:", | ||
125 | .lnlen = sizeof("sip:") - 1, | ||
126 | .sname = "sip:", | ||
127 | .snlen = sizeof("sip:") - 1, /* yes, i know.. ;) */ | ||
128 | .ln_str = "@", | ||
129 | .ln_strlen = sizeof("@") - 1, | ||
130 | .match_len = epaddr_len | ||
131 | }, | ||
132 | { /* SDP version header */ | ||
133 | .lname = "\nv=", | ||
134 | .lnlen = sizeof("\nv=") - 1, | ||
135 | .sname = "\rv=", | ||
136 | .snlen = sizeof("\rv=") - 1, | ||
137 | .ln_str = "=", | ||
138 | .ln_strlen = sizeof("=") - 1, | ||
139 | .match_len = digits_len | ||
140 | } | ||
141 | }; | ||
142 | EXPORT_SYMBOL_GPL(ct_sip_hdrs); | ||
143 | |||
144 | /* get line lenght until first CR or LF seen. */ | ||
145 | int ct_sip_lnlen(const char *line, const char *limit) | ||
146 | { | ||
147 | const char *k = line; | ||
148 | |||
149 | while ((line <= limit) && (*line == '\r' || *line == '\n')) | ||
150 | line++; | ||
151 | |||
152 | while (line <= limit) { | ||
153 | if (*line == '\r' || *line == '\n') | ||
154 | break; | ||
155 | line++; | ||
156 | } | ||
157 | return line - k; | ||
158 | } | ||
159 | EXPORT_SYMBOL_GPL(ct_sip_lnlen); | ||
160 | |||
161 | /* Linear string search, case sensitive. */ | ||
162 | const char *ct_sip_search(const char *needle, const char *haystack, | ||
163 | size_t needle_len, size_t haystack_len) | ||
164 | { | ||
165 | const char *limit = haystack + (haystack_len - needle_len); | ||
166 | |||
167 | while (haystack <= limit) { | ||
168 | if (memcmp(haystack, needle, needle_len) == 0) | ||
169 | return haystack; | ||
170 | haystack++; | ||
171 | } | ||
172 | return NULL; | ||
173 | } | ||
174 | EXPORT_SYMBOL_GPL(ct_sip_search); | ||
175 | |||
176 | static int digits_len(const char *dptr, const char *limit, int *shift) | ||
177 | { | ||
178 | int len = 0; | ||
179 | while (dptr <= limit && isdigit(*dptr)) { | ||
180 | dptr++; | ||
181 | len++; | ||
182 | } | ||
183 | return len; | ||
184 | } | ||
185 | |||
186 | /* get digits lenght, skiping blank spaces. */ | ||
187 | static int skp_digits_len(const char *dptr, const char *limit, int *shift) | ||
188 | { | ||
189 | for (; dptr <= limit && *dptr == ' '; dptr++) | ||
190 | (*shift)++; | ||
191 | |||
192 | return digits_len(dptr, limit, shift); | ||
193 | } | ||
194 | |||
195 | /* Simple ipaddr parser.. */ | ||
196 | static int parse_ipaddr(const char *cp, const char **endp, | ||
197 | u_int32_t *ipaddr, const char *limit) | ||
198 | { | ||
199 | unsigned long int val; | ||
200 | int i, digit = 0; | ||
201 | |||
202 | for (i = 0, *ipaddr = 0; cp <= limit && i < 4; i++) { | ||
203 | digit = 0; | ||
204 | if (!isdigit(*cp)) | ||
205 | break; | ||
206 | |||
207 | val = simple_strtoul(cp, (char **)&cp, 10); | ||
208 | if (val > 0xFF) | ||
209 | return -1; | ||
210 | |||
211 | ((u_int8_t *)ipaddr)[i] = val; | ||
212 | digit = 1; | ||
213 | |||
214 | if (*cp != '.') | ||
215 | break; | ||
216 | cp++; | ||
217 | } | ||
218 | if (!digit) | ||
219 | return -1; | ||
220 | |||
221 | if (endp) | ||
222 | *endp = cp; | ||
223 | |||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | /* skip ip address. returns it lenght. */ | ||
228 | static int epaddr_len(const char *dptr, const char *limit, int *shift) | ||
229 | { | ||
230 | const char *aux = dptr; | ||
231 | u_int32_t ip; | ||
232 | |||
233 | if (parse_ipaddr(dptr, &dptr, &ip, limit) < 0) { | ||
234 | DEBUGP("ip: %s parse failed.!\n", dptr); | ||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | /* Port number */ | ||
239 | if (*dptr == ':') { | ||
240 | dptr++; | ||
241 | dptr += digits_len(dptr, limit, shift); | ||
242 | } | ||
243 | return dptr - aux; | ||
244 | } | ||
245 | |||
246 | /* get address length, skiping user info. */ | ||
247 | static int skp_epaddr_len(const char *dptr, const char *limit, int *shift) | ||
248 | { | ||
249 | int s = *shift; | ||
250 | |||
251 | for (; dptr <= limit && *dptr != '@'; dptr++) | ||
252 | (*shift)++; | ||
253 | |||
254 | if (*dptr == '@') { | ||
255 | dptr++; | ||
256 | (*shift)++; | ||
257 | } else | ||
258 | *shift = s; | ||
259 | |||
260 | return epaddr_len(dptr, limit, shift); | ||
261 | } | ||
262 | |||
263 | /* Returns 0 if not found, -1 error parsing. */ | ||
264 | int ct_sip_get_info(const char *dptr, size_t dlen, | ||
265 | unsigned int *matchoff, | ||
266 | unsigned int *matchlen, | ||
267 | struct sip_header_nfo *hnfo) | ||
268 | { | ||
269 | const char *limit, *aux, *k = dptr; | ||
270 | int shift = 0; | ||
271 | |||
272 | limit = dptr + (dlen - hnfo->lnlen); | ||
273 | |||
274 | while (dptr <= limit) { | ||
275 | if ((strncmp(dptr, hnfo->lname, hnfo->lnlen) != 0) && | ||
276 | (strncmp(dptr, hnfo->sname, hnfo->snlen) != 0)) { | ||
277 | dptr++; | ||
278 | continue; | ||
279 | } | ||
280 | aux = ct_sip_search(hnfo->ln_str, dptr, hnfo->ln_strlen, | ||
281 | ct_sip_lnlen(dptr, limit)); | ||
282 | if (!aux) { | ||
283 | DEBUGP("'%s' not found in '%s'.\n", hnfo->ln_str, | ||
284 | hnfo->lname); | ||
285 | return -1; | ||
286 | } | ||
287 | aux += hnfo->ln_strlen; | ||
288 | |||
289 | *matchlen = hnfo->match_len(aux, limit, &shift); | ||
290 | if (!*matchlen) | ||
291 | return -1; | ||
292 | |||
293 | *matchoff = (aux - k) + shift; | ||
294 | |||
295 | DEBUGP("%s match succeeded! - len: %u\n", hnfo->lname, | ||
296 | *matchlen); | ||
297 | return 1; | ||
298 | } | ||
299 | DEBUGP("%s header not found.\n", hnfo->lname); | ||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | static int set_expected_rtp(struct sk_buff **pskb, | ||
304 | struct ip_conntrack *ct, | ||
305 | enum ip_conntrack_info ctinfo, | ||
306 | u_int32_t ipaddr, u_int16_t port, | ||
307 | const char *dptr) | ||
308 | { | ||
309 | struct ip_conntrack_expect *exp; | ||
310 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
311 | int ret; | ||
312 | |||
313 | exp = ip_conntrack_expect_alloc(ct); | ||
314 | if (exp == NULL) | ||
315 | return NF_DROP; | ||
316 | |||
317 | exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; | ||
318 | exp->tuple.src.u.udp.port = 0; | ||
319 | exp->tuple.dst.ip = ipaddr; | ||
320 | exp->tuple.dst.u.udp.port = htons(port); | ||
321 | exp->tuple.dst.protonum = IPPROTO_UDP; | ||
322 | |||
323 | exp->mask.src.ip = 0xFFFFFFFF; | ||
324 | exp->mask.src.u.udp.port = 0; | ||
325 | exp->mask.dst.ip = 0xFFFFFFFF; | ||
326 | exp->mask.dst.u.udp.port = 0xFFFF; | ||
327 | exp->mask.dst.protonum = 0xFF; | ||
328 | |||
329 | exp->expectfn = NULL; | ||
330 | exp->flags = 0; | ||
331 | |||
332 | if (ip_nat_sdp_hook) | ||
333 | ret = ip_nat_sdp_hook(pskb, ctinfo, exp, dptr); | ||
334 | else { | ||
335 | if (ip_conntrack_expect_related(exp) != 0) | ||
336 | ret = NF_DROP; | ||
337 | else | ||
338 | ret = NF_ACCEPT; | ||
339 | } | ||
340 | ip_conntrack_expect_put(exp); | ||
341 | |||
342 | return ret; | ||
343 | } | ||
344 | |||
345 | static int sip_help(struct sk_buff **pskb, | ||
346 | struct ip_conntrack *ct, | ||
347 | enum ip_conntrack_info ctinfo) | ||
348 | { | ||
349 | unsigned int dataoff, datalen; | ||
350 | const char *dptr; | ||
351 | int ret = NF_ACCEPT; | ||
352 | int matchoff, matchlen; | ||
353 | u_int32_t ipaddr; | ||
354 | u_int16_t port; | ||
355 | |||
356 | /* No Data ? */ | ||
357 | dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); | ||
358 | if (dataoff >= (*pskb)->len) { | ||
359 | DEBUGP("skb->len = %u\n", (*pskb)->len); | ||
360 | return NF_ACCEPT; | ||
361 | } | ||
362 | |||
363 | ip_ct_refresh(ct, *pskb, sip_timeout * HZ); | ||
364 | |||
365 | if (!skb_is_nonlinear(*pskb)) | ||
366 | dptr = (*pskb)->data + dataoff; | ||
367 | else { | ||
368 | DEBUGP("Copy of skbuff not supported yet.\n"); | ||
369 | goto out; | ||
370 | } | ||
371 | |||
372 | if (ip_nat_sip_hook) { | ||
373 | if (!ip_nat_sip_hook(pskb, ctinfo, ct, &dptr)) { | ||
374 | ret = NF_DROP; | ||
375 | goto out; | ||
376 | } | ||
377 | } | ||
378 | |||
379 | /* After this point NAT, could have mangled skb, so | ||
380 | we need to recalculate payload lenght. */ | ||
381 | datalen = (*pskb)->len - dataoff; | ||
382 | |||
383 | if (datalen < (sizeof("SIP/2.0 200") - 1)) | ||
384 | goto out; | ||
385 | |||
386 | /* RTP info only in some SDP pkts */ | ||
387 | if (memcmp(dptr, "INVITE", sizeof("INVITE") - 1) != 0 && | ||
388 | memcmp(dptr, "SIP/2.0 200", sizeof("SIP/2.0 200") - 1) != 0) { | ||
389 | goto out; | ||
390 | } | ||
391 | /* Get ip and port address from SDP packet. */ | ||
392 | if (ct_sip_get_info(dptr, datalen, &matchoff, &matchlen, | ||
393 | &ct_sip_hdrs[POS_CONNECTION]) > 0) { | ||
394 | |||
395 | /* We'll drop only if there are parse problems. */ | ||
396 | if (parse_ipaddr(dptr + matchoff, NULL, &ipaddr, | ||
397 | dptr + datalen) < 0) { | ||
398 | ret = NF_DROP; | ||
399 | goto out; | ||
400 | } | ||
401 | if (ct_sip_get_info(dptr, datalen, &matchoff, &matchlen, | ||
402 | &ct_sip_hdrs[POS_MEDIA]) > 0) { | ||
403 | |||
404 | port = simple_strtoul(dptr + matchoff, NULL, 10); | ||
405 | if (port < 1024) { | ||
406 | ret = NF_DROP; | ||
407 | goto out; | ||
408 | } | ||
409 | ret = set_expected_rtp(pskb, ct, ctinfo, | ||
410 | ipaddr, port, dptr); | ||
411 | } | ||
412 | } | ||
413 | out: | ||
414 | return ret; | ||
415 | } | ||
416 | |||
417 | static struct ip_conntrack_helper sip[MAX_PORTS]; | ||
418 | static char sip_names[MAX_PORTS][10]; | ||
419 | |||
420 | static void fini(void) | ||
421 | { | ||
422 | int i; | ||
423 | for (i = 0; i < ports_c; i++) { | ||
424 | DEBUGP("unregistering helper for port %d\n", ports[i]); | ||
425 | ip_conntrack_helper_unregister(&sip[i]); | ||
426 | } | ||
427 | } | ||
428 | |||
429 | static int __init init(void) | ||
430 | { | ||
431 | int i, ret; | ||
432 | char *tmpname; | ||
433 | |||
434 | if (ports_c == 0) | ||
435 | ports[ports_c++] = SIP_PORT; | ||
436 | |||
437 | for (i = 0; i < ports_c; i++) { | ||
438 | /* Create helper structure */ | ||
439 | memset(&sip[i], 0, sizeof(struct ip_conntrack_helper)); | ||
440 | |||
441 | sip[i].tuple.dst.protonum = IPPROTO_UDP; | ||
442 | sip[i].tuple.src.u.udp.port = htons(ports[i]); | ||
443 | sip[i].mask.src.u.udp.port = 0xFFFF; | ||
444 | sip[i].mask.dst.protonum = 0xFF; | ||
445 | sip[i].max_expected = 1; | ||
446 | sip[i].timeout = 3 * 60; /* 3 minutes */ | ||
447 | sip[i].me = THIS_MODULE; | ||
448 | sip[i].help = sip_help; | ||
449 | |||
450 | tmpname = &sip_names[i][0]; | ||
451 | if (ports[i] == SIP_PORT) | ||
452 | sprintf(tmpname, "sip"); | ||
453 | else | ||
454 | sprintf(tmpname, "sip-%d", i); | ||
455 | sip[i].name = tmpname; | ||
456 | |||
457 | DEBUGP("port #%d: %d\n", i, ports[i]); | ||
458 | |||
459 | ret = ip_conntrack_helper_register(&sip[i]); | ||
460 | if (ret) { | ||
461 | printk("ERROR registering helper for port %d\n", | ||
462 | ports[i]); | ||
463 | fini(); | ||
464 | return ret; | ||
465 | } | ||
466 | } | ||
467 | return 0; | ||
468 | } | ||
469 | |||
470 | module_init(init); | ||
471 | module_exit(fini); | ||
diff --git a/net/ipv4/netfilter/ip_nat_sip.c b/net/ipv4/netfilter/ip_nat_sip.c new file mode 100644 index 000000000000..6ffba63adca2 --- /dev/null +++ b/net/ipv4/netfilter/ip_nat_sip.c | |||
@@ -0,0 +1,249 @@ | |||
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 <linux/netfilter_ipv4.h> | ||
17 | #include <linux/netfilter_ipv4/ip_nat.h> | ||
18 | #include <linux/netfilter_ipv4/ip_nat_helper.h> | ||
19 | #include <linux/netfilter_ipv4/ip_conntrack_helper.h> | ||
20 | #include <linux/netfilter_ipv4/ip_conntrack_sip.h> | ||
21 | |||
22 | MODULE_LICENSE("GPL"); | ||
23 | MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>"); | ||
24 | MODULE_DESCRIPTION("SIP NAT helper"); | ||
25 | |||
26 | #if 0 | ||
27 | #define DEBUGP printk | ||
28 | #else | ||
29 | #define DEBUGP(format, args...) | ||
30 | #endif | ||
31 | |||
32 | extern struct sip_header_nfo ct_sip_hdrs[]; | ||
33 | |||
34 | static unsigned int mangle_sip_packet(struct sk_buff **pskb, | ||
35 | enum ip_conntrack_info ctinfo, | ||
36 | struct ip_conntrack *ct, | ||
37 | const char **dptr, size_t dlen, | ||
38 | char *buffer, int bufflen, | ||
39 | struct sip_header_nfo *hnfo) | ||
40 | { | ||
41 | unsigned int matchlen, matchoff; | ||
42 | |||
43 | if (ct_sip_get_info(*dptr, dlen, &matchoff, &matchlen, hnfo) <= 0) | ||
44 | return 0; | ||
45 | |||
46 | if (!ip_nat_mangle_udp_packet(pskb, ct, ctinfo, | ||
47 | matchoff, matchlen, buffer, bufflen)) | ||
48 | return 0; | ||
49 | |||
50 | /* We need to reload this. Thanks Patrick. */ | ||
51 | *dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); | ||
52 | return 1; | ||
53 | } | ||
54 | |||
55 | static unsigned int ip_nat_sip(struct sk_buff **pskb, | ||
56 | enum ip_conntrack_info ctinfo, | ||
57 | struct ip_conntrack *ct, | ||
58 | const char **dptr) | ||
59 | { | ||
60 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
61 | char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; | ||
62 | unsigned int bufflen, dataoff; | ||
63 | u_int32_t ip; | ||
64 | u_int16_t port; | ||
65 | |||
66 | dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); | ||
67 | |||
68 | ip = ct->tuplehash[!dir].tuple.dst.ip; | ||
69 | port = ct->tuplehash[!dir].tuple.dst.u.udp.port; | ||
70 | bufflen = sprintf(buffer, "%u.%u.%u.%u:%u", NIPQUAD(ip), ntohs(port)); | ||
71 | |||
72 | /* short packet ? */ | ||
73 | if (((*pskb)->len - dataoff) < (sizeof("SIP/2.0") - 1)) | ||
74 | return 0; | ||
75 | |||
76 | /* Basic rules: requests and responses. */ | ||
77 | if (memcmp(*dptr, "SIP/2.0", sizeof("SIP/2.0") - 1) == 0) { | ||
78 | const char *aux; | ||
79 | |||
80 | if ((ctinfo) < IP_CT_IS_REPLY) { | ||
81 | mangle_sip_packet(pskb, ctinfo, ct, dptr, | ||
82 | (*pskb)->len - dataoff, | ||
83 | buffer, bufflen, | ||
84 | &ct_sip_hdrs[POS_CONTACT]); | ||
85 | return 1; | ||
86 | } | ||
87 | |||
88 | if (!mangle_sip_packet(pskb, ctinfo, ct, dptr, | ||
89 | (*pskb)->len - dataoff, | ||
90 | buffer, bufflen, &ct_sip_hdrs[POS_VIA])) | ||
91 | return 0; | ||
92 | |||
93 | /* This search should ignore case, but later.. */ | ||
94 | aux = ct_sip_search("CSeq:", *dptr, sizeof("CSeq:") - 1, | ||
95 | (*pskb)->len - dataoff); | ||
96 | if (!aux) | ||
97 | return 0; | ||
98 | |||
99 | if (!ct_sip_search("REGISTER", aux, sizeof("REGISTER"), | ||
100 | ct_sip_lnlen(aux, *dptr + (*pskb)->len - dataoff))) | ||
101 | return 1; | ||
102 | |||
103 | return mangle_sip_packet(pskb, ctinfo, ct, dptr, | ||
104 | (*pskb)->len - dataoff, | ||
105 | buffer, bufflen, | ||
106 | &ct_sip_hdrs[POS_CONTACT]); | ||
107 | } | ||
108 | if ((ctinfo) < IP_CT_IS_REPLY) { | ||
109 | if (!mangle_sip_packet(pskb, ctinfo, ct, dptr, | ||
110 | (*pskb)->len - dataoff, | ||
111 | buffer, bufflen, &ct_sip_hdrs[POS_VIA])) | ||
112 | return 0; | ||
113 | |||
114 | /* Mangle Contact if exists only. - watch udp_nat_mangle()! */ | ||
115 | mangle_sip_packet(pskb, ctinfo, ct, dptr, (*pskb)->len - dataoff, | ||
116 | buffer, bufflen, &ct_sip_hdrs[POS_CONTACT]); | ||
117 | return 1; | ||
118 | } | ||
119 | /* This mangle requests headers. */ | ||
120 | return mangle_sip_packet(pskb, ctinfo, ct, dptr, | ||
121 | ct_sip_lnlen(*dptr, | ||
122 | *dptr + (*pskb)->len - dataoff), | ||
123 | buffer, bufflen, &ct_sip_hdrs[POS_REQ_HEADER]); | ||
124 | } | ||
125 | |||
126 | static int mangle_content_len(struct sk_buff **pskb, | ||
127 | enum ip_conntrack_info ctinfo, | ||
128 | struct ip_conntrack *ct, | ||
129 | const char *dptr) | ||
130 | { | ||
131 | unsigned int dataoff, matchoff, matchlen; | ||
132 | char buffer[sizeof("65536")]; | ||
133 | int bufflen; | ||
134 | |||
135 | dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); | ||
136 | |||
137 | /* Get actual SDP lenght */ | ||
138 | if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff, | ||
139 | &matchlen, &ct_sip_hdrs[POS_SDP_HEADER]) > 0) { | ||
140 | |||
141 | /* since ct_sip_get_info() give us a pointer passing 'v=' | ||
142 | we need to add 2 bytes in this count. */ | ||
143 | int c_len = (*pskb)->len - dataoff - matchoff + 2; | ||
144 | |||
145 | /* Now, update SDP lenght */ | ||
146 | if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff, | ||
147 | &matchlen, &ct_sip_hdrs[POS_CONTENT]) > 0) { | ||
148 | |||
149 | bufflen = sprintf(buffer, "%u", c_len); | ||
150 | |||
151 | return ip_nat_mangle_udp_packet(pskb, ct, ctinfo, | ||
152 | matchoff, matchlen, | ||
153 | buffer, bufflen); | ||
154 | } | ||
155 | } | ||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | static unsigned int mangle_sdp(struct sk_buff **pskb, | ||
160 | enum ip_conntrack_info ctinfo, | ||
161 | struct ip_conntrack *ct, | ||
162 | u_int32_t newip, u_int16_t port, | ||
163 | const char *dptr) | ||
164 | { | ||
165 | char buffer[sizeof("nnn.nnn.nnn.nnn")]; | ||
166 | unsigned int dataoff, bufflen; | ||
167 | |||
168 | dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); | ||
169 | |||
170 | /* Mangle owner and contact info. */ | ||
171 | bufflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(newip)); | ||
172 | if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff, | ||
173 | buffer, bufflen, &ct_sip_hdrs[POS_OWNER])) | ||
174 | return 0; | ||
175 | |||
176 | if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff, | ||
177 | buffer, bufflen, &ct_sip_hdrs[POS_CONNECTION])) | ||
178 | return 0; | ||
179 | |||
180 | /* Mangle media port. */ | ||
181 | bufflen = sprintf(buffer, "%u", port); | ||
182 | if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff, | ||
183 | buffer, bufflen, &ct_sip_hdrs[POS_MEDIA])) | ||
184 | return 0; | ||
185 | |||
186 | return mangle_content_len(pskb, ctinfo, ct, dptr); | ||
187 | } | ||
188 | |||
189 | /* So, this packet has hit the connection tracking matching code. | ||
190 | Mangle it, and change the expectation to match the new version. */ | ||
191 | static unsigned int ip_nat_sdp(struct sk_buff **pskb, | ||
192 | enum ip_conntrack_info ctinfo, | ||
193 | struct ip_conntrack_expect *exp, | ||
194 | const char *dptr) | ||
195 | { | ||
196 | struct ip_conntrack *ct = exp->master; | ||
197 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
198 | u_int32_t newip; | ||
199 | u_int16_t port; | ||
200 | |||
201 | DEBUGP("ip_nat_sdp():\n"); | ||
202 | |||
203 | /* Connection will come from reply */ | ||
204 | newip = ct->tuplehash[!dir].tuple.dst.ip; | ||
205 | |||
206 | exp->tuple.dst.ip = newip; | ||
207 | exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port; | ||
208 | exp->dir = !dir; | ||
209 | |||
210 | /* When you see the packet, we need to NAT it the same as the | ||
211 | this one. */ | ||
212 | exp->expectfn = ip_nat_follow_master; | ||
213 | |||
214 | /* Try to get same port: if not, try to change it. */ | ||
215 | for (port = ntohs(exp->saved_proto.udp.port); port != 0; port++) { | ||
216 | exp->tuple.dst.u.udp.port = htons(port); | ||
217 | if (ip_conntrack_expect_related(exp) == 0) | ||
218 | break; | ||
219 | } | ||
220 | |||
221 | if (port == 0) | ||
222 | return NF_DROP; | ||
223 | |||
224 | if (!mangle_sdp(pskb, ctinfo, ct, newip, port, dptr)) { | ||
225 | ip_conntrack_unexpect_related(exp); | ||
226 | return NF_DROP; | ||
227 | } | ||
228 | return NF_ACCEPT; | ||
229 | } | ||
230 | |||
231 | static void __exit fini(void) | ||
232 | { | ||
233 | ip_nat_sip_hook = NULL; | ||
234 | ip_nat_sdp_hook = NULL; | ||
235 | /* Make sure noone calls it, meanwhile. */ | ||
236 | synchronize_net(); | ||
237 | } | ||
238 | |||
239 | static int __init init(void) | ||
240 | { | ||
241 | BUG_ON(ip_nat_sip_hook); | ||
242 | BUG_ON(ip_nat_sdp_hook); | ||
243 | ip_nat_sip_hook = ip_nat_sip; | ||
244 | ip_nat_sdp_hook = ip_nat_sdp; | ||
245 | return 0; | ||
246 | } | ||
247 | |||
248 | module_init(init); | ||
249 | module_exit(fini); | ||