[NETFILTER]: Add SIP connection tracking helper
[linux-2.6.git] / net / ipv4 / netfilter / ip_conntrack_sip.c
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);