blob: 217262e23403dfd99897c14009f58f725988083c [file] [log] [blame]
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001/* 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/module.h>
12#include <linux/ctype.h>
13#include <linux/skbuff.h>
14#include <linux/inet.h>
15#include <linux/in.h>
16#include <linux/udp.h>
Yasuyuki Kozakai1863f092006-12-02 22:12:54 -080017#include <linux/netfilter.h>
Patrick McHardy9fafcd72006-12-02 22:09:57 -080018
19#include <net/netfilter/nf_conntrack.h>
Patrick McHardy9467ee32008-03-25 20:24:04 -070020#include <net/netfilter/nf_conntrack_core.h>
Patrick McHardy9fafcd72006-12-02 22:09:57 -080021#include <net/netfilter/nf_conntrack_expect.h>
22#include <net/netfilter/nf_conntrack_helper.h>
23#include <linux/netfilter/nf_conntrack_sip.h>
24
Patrick McHardy9fafcd72006-12-02 22:09:57 -080025MODULE_LICENSE("GPL");
26MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>");
27MODULE_DESCRIPTION("SIP connection tracking helper");
28MODULE_ALIAS("ip_conntrack_sip");
29
30#define MAX_PORTS 8
31static unsigned short ports[MAX_PORTS];
Stephen Hemminger2f0d2f12008-01-31 04:08:10 -080032static unsigned int ports_c;
Patrick McHardy9fafcd72006-12-02 22:09:57 -080033module_param_array(ports, ushort, &ports_c, 0400);
34MODULE_PARM_DESC(ports, "port numbers of SIP servers");
35
36static unsigned int sip_timeout __read_mostly = SIP_TIMEOUT;
37module_param(sip_timeout, uint, 0600);
38MODULE_PARM_DESC(sip_timeout, "timeout for the master SIP session");
39
Patrick McHardy0f32a402008-03-25 20:25:13 -070040static int sip_direct_signalling __read_mostly = 1;
41module_param(sip_direct_signalling, int, 0600);
42MODULE_PARM_DESC(sip_direct_signalling, "expect incoming calls from registrar "
43 "only (default 1)");
44
Patrick McHardyd901a932008-03-25 20:25:32 -070045static int sip_direct_media __read_mostly = 1;
46module_param(sip_direct_media, int, 0600);
47MODULE_PARM_DESC(sip_direct_media, "Expect Media streams between signalling "
48 "endpoints only (default 1)");
49
Herbert Xu3db05fe2007-10-15 00:53:15 -070050unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb,
Patrick McHardy2a6cfb22008-03-25 20:16:54 -070051 const char **dptr,
52 unsigned int *datalen) __read_mostly;
Patrick McHardy9fafcd72006-12-02 22:09:57 -080053EXPORT_SYMBOL_GPL(nf_nat_sip_hook);
54
Patrick McHardy0f32a402008-03-25 20:25:13 -070055unsigned int (*nf_nat_sip_expect_hook)(struct sk_buff *skb,
56 const char **dptr,
57 unsigned int *datalen,
58 struct nf_conntrack_expect *exp,
59 unsigned int matchoff,
60 unsigned int matchlen) __read_mostly;
61EXPORT_SYMBOL_GPL(nf_nat_sip_expect_hook);
62
Herbert Xu3db05fe2007-10-15 00:53:15 -070063unsigned int (*nf_nat_sdp_hook)(struct sk_buff *skb,
Patrick McHardy2a6cfb22008-03-25 20:16:54 -070064 const char **dptr,
Patrick McHardy212440a2008-03-25 20:17:13 -070065 unsigned int *datalen,
Patrick McHardya9c1d352008-03-25 20:25:49 -070066 struct nf_conntrack_expect *rtp_exp,
67 struct nf_conntrack_expect *rtcp_exp)
68 __read_mostly;
Patrick McHardy9fafcd72006-12-02 22:09:57 -080069EXPORT_SYMBOL_GPL(nf_nat_sdp_hook);
70
Patrick McHardyac367742008-03-25 20:18:40 -070071static int string_len(const struct nf_conn *ct, const char *dptr,
72 const char *limit, int *shift)
73{
74 int len = 0;
75
76 while (dptr < limit && isalpha(*dptr)) {
77 dptr++;
78 len++;
79 }
80 return len;
81}
82
Jan Engelhardt13f7d632008-01-31 04:50:25 -080083static int digits_len(const struct nf_conn *ct, const char *dptr,
Patrick McHardy9fafcd72006-12-02 22:09:57 -080084 const char *limit, int *shift)
85{
86 int len = 0;
Patrick McHardyb1ec4882008-03-25 20:10:11 -070087 while (dptr < limit && isdigit(*dptr)) {
Patrick McHardy9fafcd72006-12-02 22:09:57 -080088 dptr++;
89 len++;
90 }
91 return len;
92}
93
Jan Engelhardt13f7d632008-01-31 04:50:25 -080094static int parse_addr(const struct nf_conn *ct, const char *cp,
95 const char **endp, union nf_inet_addr *addr,
96 const char *limit)
Patrick McHardy9fafcd72006-12-02 22:09:57 -080097{
98 const char *end;
99 int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
100 int ret = 0;
101
102 switch (family) {
103 case AF_INET:
104 ret = in4_pton(cp, limit - cp, (u8 *)&addr->ip, -1, &end);
105 break;
106 case AF_INET6:
107 ret = in6_pton(cp, limit - cp, (u8 *)&addr->ip6, -1, &end);
108 break;
109 default:
110 BUG();
111 }
112
113 if (ret == 0 || end == cp)
114 return 0;
115 if (endp)
116 *endp = end;
117 return 1;
118}
119
120/* skip ip address. returns its length. */
Jan Engelhardt13f7d632008-01-31 04:50:25 -0800121static int epaddr_len(const struct nf_conn *ct, const char *dptr,
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800122 const char *limit, int *shift)
123{
Jan Engelhardt643a2c12007-12-17 22:43:50 -0800124 union nf_inet_addr addr;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800125 const char *aux = dptr;
126
127 if (!parse_addr(ct, dptr, &dptr, &addr, limit)) {
Patrick McHardy0d537782007-07-07 22:39:38 -0700128 pr_debug("ip: %s parse failed.!\n", dptr);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800129 return 0;
130 }
131
132 /* Port number */
133 if (*dptr == ':') {
134 dptr++;
135 dptr += digits_len(ct, dptr, limit, shift);
136 }
137 return dptr - aux;
138}
139
140/* get address length, skiping user info. */
Jan Engelhardt13f7d632008-01-31 04:50:25 -0800141static int skp_epaddr_len(const struct nf_conn *ct, const char *dptr,
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800142 const char *limit, int *shift)
143{
Patrick McHardyaa584ed2007-08-14 13:14:35 -0700144 const char *start = dptr;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800145 int s = *shift;
146
Lars Immisch7da5bfb2007-01-30 14:24:57 -0800147 /* Search for @, but stop at the end of the line.
148 * We are inside a sip: URI, so we don't need to worry about
149 * continuation lines. */
Patrick McHardyb1ec4882008-03-25 20:10:11 -0700150 while (dptr < limit &&
Lars Immisch7da5bfb2007-01-30 14:24:57 -0800151 *dptr != '@' && *dptr != '\r' && *dptr != '\n') {
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800152 (*shift)++;
Lars Immisch7da5bfb2007-01-30 14:24:57 -0800153 dptr++;
154 }
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800155
Patrick McHardyb1ec4882008-03-25 20:10:11 -0700156 if (dptr < limit && *dptr == '@') {
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800157 dptr++;
158 (*shift)++;
Patrick McHardyaa584ed2007-08-14 13:14:35 -0700159 } else {
160 dptr = start;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800161 *shift = s;
Patrick McHardyaa584ed2007-08-14 13:14:35 -0700162 }
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800163
164 return epaddr_len(ct, dptr, limit, shift);
165}
166
Patrick McHardyac367742008-03-25 20:18:40 -0700167/* Parse a SIP request line of the form:
168 *
169 * Request-Line = Method SP Request-URI SP SIP-Version CRLF
170 *
171 * and return the offset and length of the address contained in the Request-URI.
172 */
173int ct_sip_parse_request(const struct nf_conn *ct,
174 const char *dptr, unsigned int datalen,
Patrick McHardy624f8b72008-03-25 20:19:30 -0700175 unsigned int *matchoff, unsigned int *matchlen,
176 union nf_inet_addr *addr, __be16 *port)
Patrick McHardyac367742008-03-25 20:18:40 -0700177{
Patrick McHardy624f8b72008-03-25 20:19:30 -0700178 const char *start = dptr, *limit = dptr + datalen, *end;
Patrick McHardyac367742008-03-25 20:18:40 -0700179 unsigned int mlen;
Patrick McHardy624f8b72008-03-25 20:19:30 -0700180 unsigned int p;
Patrick McHardyac367742008-03-25 20:18:40 -0700181 int shift = 0;
182
183 /* Skip method and following whitespace */
184 mlen = string_len(ct, dptr, limit, NULL);
185 if (!mlen)
186 return 0;
187 dptr += mlen;
188 if (++dptr >= limit)
189 return 0;
190
191 /* Find SIP URI */
192 limit -= strlen("sip:");
193 for (; dptr < limit; dptr++) {
194 if (*dptr == '\r' || *dptr == '\n')
195 return -1;
196 if (strnicmp(dptr, "sip:", strlen("sip:")) == 0)
197 break;
198 }
Patrick McHardy624f8b72008-03-25 20:19:30 -0700199 if (!skp_epaddr_len(ct, dptr, limit, &shift))
Patrick McHardyac367742008-03-25 20:18:40 -0700200 return 0;
Patrick McHardy624f8b72008-03-25 20:19:30 -0700201 dptr += shift;
202
203 if (!parse_addr(ct, dptr, &end, addr, limit))
204 return -1;
205 if (end < limit && *end == ':') {
206 end++;
207 p = simple_strtoul(end, (char **)&end, 10);
208 if (p < 1024 || p > 65535)
209 return -1;
210 *port = htons(p);
211 } else
212 *port = htons(SIP_PORT);
213
214 if (end == dptr)
215 return 0;
216 *matchoff = dptr - start;
217 *matchlen = end - dptr;
Patrick McHardyac367742008-03-25 20:18:40 -0700218 return 1;
219}
220EXPORT_SYMBOL_GPL(ct_sip_parse_request);
221
Patrick McHardyea45f122008-03-25 20:18:57 -0700222/* SIP header parsing: SIP headers are located at the beginning of a line, but
223 * may span several lines, in which case the continuation lines begin with a
224 * whitespace character. RFC 2543 allows lines to be terminated with CR, LF or
225 * CRLF, RFC 3261 allows only CRLF, we support both.
226 *
227 * Headers are followed by (optionally) whitespace, a colon, again (optionally)
228 * whitespace and the values. Whitespace in this context means any amount of
229 * tabs, spaces and continuation lines, which are treated as a single whitespace
230 * character.
Patrick McHardy05e3ced2008-03-25 20:19:13 -0700231 *
232 * Some headers may appear multiple times. A comma seperated list of values is
233 * equivalent to multiple headers.
Patrick McHardyea45f122008-03-25 20:18:57 -0700234 */
235static const struct sip_header ct_sip_hdrs[] = {
Patrick McHardy30f33e62008-03-25 20:22:20 -0700236 [SIP_HDR_CSEQ] = SIP_HDR("CSeq", NULL, NULL, digits_len),
Patrick McHardyea45f122008-03-25 20:18:57 -0700237 [SIP_HDR_FROM] = SIP_HDR("From", "f", "sip:", skp_epaddr_len),
238 [SIP_HDR_TO] = SIP_HDR("To", "t", "sip:", skp_epaddr_len),
239 [SIP_HDR_CONTACT] = SIP_HDR("Contact", "m", "sip:", skp_epaddr_len),
240 [SIP_HDR_VIA] = SIP_HDR("Via", "v", "UDP ", epaddr_len),
Patrick McHardy0f32a402008-03-25 20:25:13 -0700241 [SIP_HDR_EXPIRES] = SIP_HDR("Expires", NULL, NULL, digits_len),
Patrick McHardyea45f122008-03-25 20:18:57 -0700242 [SIP_HDR_CONTENT_LENGTH] = SIP_HDR("Content-Length", "l", NULL, digits_len),
243};
244
245static const char *sip_follow_continuation(const char *dptr, const char *limit)
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800246{
Patrick McHardyea45f122008-03-25 20:18:57 -0700247 /* Walk past newline */
248 if (++dptr >= limit)
249 return NULL;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800250
Patrick McHardyea45f122008-03-25 20:18:57 -0700251 /* Skip '\n' in CR LF */
252 if (*(dptr - 1) == '\r' && *dptr == '\n') {
253 if (++dptr >= limit)
254 return NULL;
255 }
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800256
Patrick McHardyea45f122008-03-25 20:18:57 -0700257 /* Continuation line? */
258 if (*dptr != ' ' && *dptr != '\t')
259 return NULL;
260
261 /* skip leading whitespace */
262 for (; dptr < limit; dptr++) {
263 if (*dptr != ' ' && *dptr != '\t')
264 break;
265 }
266 return dptr;
267}
268
269static const char *sip_skip_whitespace(const char *dptr, const char *limit)
270{
271 for (; dptr < limit; dptr++) {
272 if (*dptr == ' ')
273 continue;
274 if (*dptr != '\r' && *dptr != '\n')
275 break;
276 dptr = sip_follow_continuation(dptr, limit);
277 if (dptr == NULL)
278 return NULL;
279 }
280 return dptr;
281}
282
283/* Search within a SIP header value, dealing with continuation lines */
284static const char *ct_sip_header_search(const char *dptr, const char *limit,
285 const char *needle, unsigned int len)
286{
287 for (limit -= len; dptr < limit; dptr++) {
288 if (*dptr == '\r' || *dptr == '\n') {
289 dptr = sip_follow_continuation(dptr, limit);
290 if (dptr == NULL)
291 break;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800292 continue;
293 }
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800294
Patrick McHardyea45f122008-03-25 20:18:57 -0700295 if (strnicmp(dptr, needle, len) == 0)
296 return dptr;
297 }
298 return NULL;
299}
300
301int ct_sip_get_header(const struct nf_conn *ct, const char *dptr,
302 unsigned int dataoff, unsigned int datalen,
303 enum sip_header_types type,
304 unsigned int *matchoff, unsigned int *matchlen)
305{
306 const struct sip_header *hdr = &ct_sip_hdrs[type];
307 const char *start = dptr, *limit = dptr + datalen;
308 int shift = 0;
309
310 for (dptr += dataoff; dptr < limit; dptr++) {
311 /* Find beginning of line */
312 if (*dptr != '\r' && *dptr != '\n')
313 continue;
314 if (++dptr >= limit)
315 break;
316 if (*(dptr - 1) == '\r' && *dptr == '\n') {
317 if (++dptr >= limit)
318 break;
319 }
320
321 /* Skip continuation lines */
322 if (*dptr == ' ' || *dptr == '\t')
323 continue;
324
325 /* Find header. Compact headers must be followed by a
326 * non-alphabetic character to avoid mismatches. */
327 if (limit - dptr >= hdr->len &&
328 strnicmp(dptr, hdr->name, hdr->len) == 0)
329 dptr += hdr->len;
330 else if (hdr->cname && limit - dptr >= hdr->clen + 1 &&
331 strnicmp(dptr, hdr->cname, hdr->clen) == 0 &&
332 !isalpha(*(dptr + hdr->clen + 1)))
333 dptr += hdr->clen;
334 else
335 continue;
336
337 /* Find and skip colon */
338 dptr = sip_skip_whitespace(dptr, limit);
339 if (dptr == NULL)
340 break;
341 if (*dptr != ':' || ++dptr >= limit)
342 break;
343
344 /* Skip whitespace after colon */
345 dptr = sip_skip_whitespace(dptr, limit);
346 if (dptr == NULL)
347 break;
348
349 *matchoff = dptr - start;
350 if (hdr->search) {
351 dptr = ct_sip_header_search(dptr, limit, hdr->search,
352 hdr->slen);
353 if (!dptr)
354 return -1;
355 dptr += hdr->slen;
356 }
357
358 *matchlen = hdr->match_len(ct, dptr, limit, &shift);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800359 if (!*matchlen)
360 return -1;
Patrick McHardyea45f122008-03-25 20:18:57 -0700361 *matchoff = dptr - start + shift;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800362 return 1;
363 }
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800364 return 0;
365}
Patrick McHardyea45f122008-03-25 20:18:57 -0700366EXPORT_SYMBOL_GPL(ct_sip_get_header);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800367
Patrick McHardy05e3ced2008-03-25 20:19:13 -0700368/* Get next header field in a list of comma seperated values */
369static int ct_sip_next_header(const struct nf_conn *ct, const char *dptr,
370 unsigned int dataoff, unsigned int datalen,
371 enum sip_header_types type,
372 unsigned int *matchoff, unsigned int *matchlen)
373{
374 const struct sip_header *hdr = &ct_sip_hdrs[type];
375 const char *start = dptr, *limit = dptr + datalen;
376 int shift = 0;
377
378 dptr += dataoff;
379
380 dptr = ct_sip_header_search(dptr, limit, ",", strlen(","));
381 if (!dptr)
382 return 0;
383
384 dptr = ct_sip_header_search(dptr, limit, hdr->search, hdr->slen);
385 if (!dptr)
386 return 0;
387 dptr += hdr->slen;
388
389 *matchoff = dptr - start;
390 *matchlen = hdr->match_len(ct, dptr, limit, &shift);
391 if (!*matchlen)
392 return -1;
393 *matchoff += shift;
394 return 1;
395}
396
397/* Walk through headers until a parsable one is found or no header of the
398 * given type is left. */
399static int ct_sip_walk_headers(const struct nf_conn *ct, const char *dptr,
400 unsigned int dataoff, unsigned int datalen,
401 enum sip_header_types type, int *in_header,
402 unsigned int *matchoff, unsigned int *matchlen)
403{
404 int ret;
405
406 if (in_header && *in_header) {
407 while (1) {
408 ret = ct_sip_next_header(ct, dptr, dataoff, datalen,
409 type, matchoff, matchlen);
410 if (ret > 0)
411 return ret;
412 if (ret == 0)
413 break;
414 dataoff += *matchoff;
415 }
416 *in_header = 0;
417 }
418
419 while (1) {
420 ret = ct_sip_get_header(ct, dptr, dataoff, datalen,
421 type, matchoff, matchlen);
422 if (ret > 0)
423 break;
424 if (ret == 0)
425 return ret;
426 dataoff += *matchoff;
427 }
428
429 if (in_header)
430 *in_header = 1;
431 return 1;
432}
433
434/* Locate a SIP header, parse the URI and return the offset and length of
435 * the address as well as the address and port themselves. A stream of
436 * headers can be parsed by handing in a non-NULL datalen and in_header
437 * pointer.
438 */
439int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr,
440 unsigned int *dataoff, unsigned int datalen,
441 enum sip_header_types type, int *in_header,
442 unsigned int *matchoff, unsigned int *matchlen,
443 union nf_inet_addr *addr, __be16 *port)
444{
445 const char *c, *limit = dptr + datalen;
446 unsigned int p;
447 int ret;
448
449 ret = ct_sip_walk_headers(ct, dptr, dataoff ? *dataoff : 0, datalen,
450 type, in_header, matchoff, matchlen);
451 WARN_ON(ret < 0);
452 if (ret == 0)
453 return ret;
454
455 if (!parse_addr(ct, dptr + *matchoff, &c, addr, limit))
456 return -1;
457 if (*c == ':') {
458 c++;
459 p = simple_strtoul(c, (char **)&c, 10);
460 if (p < 1024 || p > 65535)
461 return -1;
462 *port = htons(p);
463 } else
464 *port = htons(SIP_PORT);
465
466 if (dataoff)
467 *dataoff = c - dptr;
468 return 1;
469}
470EXPORT_SYMBOL_GPL(ct_sip_parse_header_uri);
471
Patrick McHardy2bbb2112008-03-25 20:24:24 -0700472/* Parse address from header parameter and return address, offset and length */
473int ct_sip_parse_address_param(const struct nf_conn *ct, const char *dptr,
474 unsigned int dataoff, unsigned int datalen,
475 const char *name,
476 unsigned int *matchoff, unsigned int *matchlen,
477 union nf_inet_addr *addr)
478{
479 const char *limit = dptr + datalen;
480 const char *start, *end;
481
482 limit = ct_sip_header_search(dptr + dataoff, limit, ",", strlen(","));
483 if (!limit)
484 limit = dptr + datalen;
485
486 start = ct_sip_header_search(dptr + dataoff, limit, name, strlen(name));
487 if (!start)
488 return 0;
489
490 start += strlen(name);
491 if (!parse_addr(ct, start, &end, addr, limit))
492 return 0;
493 *matchoff = start - dptr;
494 *matchlen = end - start;
495 return 1;
496}
497EXPORT_SYMBOL_GPL(ct_sip_parse_address_param);
498
499/* Parse numerical header parameter and return value, offset and length */
500int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr,
501 unsigned int dataoff, unsigned int datalen,
502 const char *name,
503 unsigned int *matchoff, unsigned int *matchlen,
504 unsigned int *val)
505{
506 const char *limit = dptr + datalen;
507 const char *start;
508 char *end;
509
510 limit = ct_sip_header_search(dptr + dataoff, limit, ",", strlen(","));
511 if (!limit)
512 limit = dptr + datalen;
513
514 start = ct_sip_header_search(dptr + dataoff, limit, name, strlen(name));
515 if (!start)
516 return 0;
517
518 start += strlen(name);
519 *val = simple_strtoul(start, &end, 0);
520 if (start == end)
521 return 0;
522 if (matchoff && matchlen) {
523 *matchoff = start - dptr;
524 *matchlen = end - start;
525 }
526 return 1;
527}
528EXPORT_SYMBOL_GPL(ct_sip_parse_numerical_param);
529
Patrick McHardy3e9b4600b2008-03-25 20:17:55 -0700530/* SDP header parsing: a SDP session description contains an ordered set of
531 * headers, starting with a section containing general session parameters,
532 * optionally followed by multiple media descriptions.
533 *
534 * SDP headers always start at the beginning of a line. According to RFC 2327:
535 * "The sequence CRLF (0x0d0a) is used to end a record, although parsers should
536 * be tolerant and also accept records terminated with a single newline
537 * character". We handle both cases.
538 */
539static const struct sip_header ct_sdp_hdrs[] = {
540 [SDP_HDR_VERSION] = SDP_HDR("v=", NULL, digits_len),
541 [SDP_HDR_OWNER_IP4] = SDP_HDR("o=", "IN IP4 ", epaddr_len),
542 [SDP_HDR_CONNECTION_IP4] = SDP_HDR("c=", "IN IP4 ", epaddr_len),
543 [SDP_HDR_OWNER_IP6] = SDP_HDR("o=", "IN IP6 ", epaddr_len),
544 [SDP_HDR_CONNECTION_IP6] = SDP_HDR("c=", "IN IP6 ", epaddr_len),
545 [SDP_HDR_MEDIA] = SDP_HDR("m=", "audio ", digits_len),
546};
547
548/* Linear string search within SDP header values */
549static const char *ct_sdp_header_search(const char *dptr, const char *limit,
550 const char *needle, unsigned int len)
551{
552 for (limit -= len; dptr < limit; dptr++) {
553 if (*dptr == '\r' || *dptr == '\n')
554 break;
555 if (strncmp(dptr, needle, len) == 0)
556 return dptr;
557 }
558 return NULL;
559}
560
561/* Locate a SDP header (optionally a substring within the header value),
562 * optionally stopping at the first occurence of the term header, parse
563 * it and return the offset and length of the data we're interested in.
564 */
565int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr,
566 unsigned int dataoff, unsigned int datalen,
567 enum sdp_header_types type,
568 enum sdp_header_types term,
569 unsigned int *matchoff, unsigned int *matchlen)
570{
571 const struct sip_header *hdr = &ct_sdp_hdrs[type];
572 const struct sip_header *thdr = &ct_sdp_hdrs[term];
573 const char *start = dptr, *limit = dptr + datalen;
574 int shift = 0;
575
576 for (dptr += dataoff; dptr < limit; dptr++) {
577 /* Find beginning of line */
578 if (*dptr != '\r' && *dptr != '\n')
579 continue;
580 if (++dptr >= limit)
581 break;
582 if (*(dptr - 1) == '\r' && *dptr == '\n') {
583 if (++dptr >= limit)
584 break;
585 }
586
587 if (term != SDP_HDR_UNSPEC &&
588 limit - dptr >= thdr->len &&
589 strnicmp(dptr, thdr->name, thdr->len) == 0)
590 break;
591 else if (limit - dptr >= hdr->len &&
592 strnicmp(dptr, hdr->name, hdr->len) == 0)
593 dptr += hdr->len;
594 else
595 continue;
596
597 *matchoff = dptr - start;
598 if (hdr->search) {
599 dptr = ct_sdp_header_search(dptr, limit, hdr->search,
600 hdr->slen);
601 if (!dptr)
602 return -1;
603 dptr += hdr->slen;
604 }
605
606 *matchlen = hdr->match_len(ct, dptr, limit, &shift);
607 if (!*matchlen)
608 return -1;
609 *matchoff = dptr - start + shift;
610 return 1;
611 }
612 return 0;
613}
614EXPORT_SYMBOL_GPL(ct_sip_get_sdp_header);
615
Patrick McHardy0f32a402008-03-25 20:25:13 -0700616static int refresh_signalling_expectation(struct nf_conn *ct,
617 union nf_inet_addr *addr,
618 __be16 port,
619 unsigned int expires)
620{
621 struct nf_conn_help *help = nfct_help(ct);
622 struct nf_conntrack_expect *exp;
623 struct hlist_node *n, *next;
624 int found = 0;
625
626 spin_lock_bh(&nf_conntrack_lock);
627 hlist_for_each_entry_safe(exp, n, next, &help->expectations, lnode) {
628 if (exp->class != SIP_EXPECT_SIGNALLING ||
629 !nf_inet_addr_cmp(&exp->tuple.dst.u3, addr) ||
630 exp->tuple.dst.u.udp.port != port)
631 continue;
632 if (!del_timer(&exp->timeout))
633 continue;
634 exp->flags &= ~NF_CT_EXPECT_INACTIVE;
635 exp->timeout.expires = jiffies + expires * HZ;
636 add_timer(&exp->timeout);
637 found = 1;
638 break;
639 }
640 spin_unlock_bh(&nf_conntrack_lock);
641 return found;
642}
643
644static void flush_expectations(struct nf_conn *ct, bool media)
Patrick McHardy9467ee32008-03-25 20:24:04 -0700645{
646 struct nf_conn_help *help = nfct_help(ct);
647 struct nf_conntrack_expect *exp;
648 struct hlist_node *n, *next;
649
650 spin_lock_bh(&nf_conntrack_lock);
651 hlist_for_each_entry_safe(exp, n, next, &help->expectations, lnode) {
Patrick McHardy0f32a402008-03-25 20:25:13 -0700652 if ((exp->class != SIP_EXPECT_SIGNALLING) ^ media)
653 continue;
Patrick McHardy9467ee32008-03-25 20:24:04 -0700654 if (!del_timer(&exp->timeout))
655 continue;
656 nf_ct_unlink_expect(exp);
657 nf_ct_expect_put(exp);
Patrick McHardy0f32a402008-03-25 20:25:13 -0700658 if (!media)
659 break;
Patrick McHardy9467ee32008-03-25 20:24:04 -0700660 }
661 spin_unlock_bh(&nf_conntrack_lock);
662}
663
Patrick McHardya9c1d352008-03-25 20:25:49 -0700664static int set_expected_rtp_rtcp(struct sk_buff *skb,
665 const char **dptr, unsigned int *datalen,
666 union nf_inet_addr *daddr, __be16 port)
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800667{
Patrick McHardya9c1d352008-03-25 20:25:49 -0700668 struct nf_conntrack_expect *exp, *rtp_exp, *rtcp_exp;
Patrick McHardy212440a2008-03-25 20:17:13 -0700669 enum ip_conntrack_info ctinfo;
670 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800671 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
Patrick McHardyd901a932008-03-25 20:25:32 -0700672 union nf_inet_addr *saddr;
673 struct nf_conntrack_tuple tuple;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800674 int family = ct->tuplehash[!dir].tuple.src.l3num;
Patrick McHardya9c1d352008-03-25 20:25:49 -0700675 int skip_expect = 0, ret = NF_DROP;
676 u_int16_t base_port;
677 __be16 rtp_port, rtcp_port;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800678 typeof(nf_nat_sdp_hook) nf_nat_sdp;
679
Patrick McHardyd901a932008-03-25 20:25:32 -0700680 saddr = NULL;
681 if (sip_direct_media) {
682 if (!nf_inet_addr_cmp(daddr, &ct->tuplehash[dir].tuple.src.u3))
683 return NF_ACCEPT;
684 saddr = &ct->tuplehash[!dir].tuple.src.u3;
685 }
686
687 /* We need to check whether the registration exists before attempting
688 * to register it since we can see the same media description multiple
689 * times on different connections in case multiple endpoints receive
690 * the same call.
691 */
692 memset(&tuple, 0, sizeof(tuple));
693 if (saddr)
694 tuple.src.u3 = *saddr;
695 tuple.src.l3num = family;
696 tuple.dst.protonum = IPPROTO_UDP;
697 tuple.dst.u3 = *daddr;
698 tuple.dst.u.udp.port = port;
699
700 rcu_read_lock();
701 exp = __nf_ct_expect_find(&tuple);
702 if (exp && exp->master != ct &&
703 nfct_help(exp->master)->helper == nfct_help(ct)->helper &&
704 exp->class == SIP_EXPECT_AUDIO)
705 skip_expect = 1;
706 rcu_read_unlock();
707
708 if (skip_expect)
709 return NF_ACCEPT;
710
Patrick McHardya9c1d352008-03-25 20:25:49 -0700711 base_port = ntohs(tuple.dst.u.udp.port) & ~1;
712 rtp_port = htons(base_port);
713 rtcp_port = htons(base_port + 1);
714
715 rtp_exp = nf_ct_expect_alloc(ct);
716 if (rtp_exp == NULL)
717 goto err1;
718 nf_ct_expect_init(rtp_exp, SIP_EXPECT_AUDIO, family, saddr, daddr,
719 IPPROTO_UDP, NULL, &rtp_port);
720
721 rtcp_exp = nf_ct_expect_alloc(ct);
722 if (rtcp_exp == NULL)
723 goto err2;
724 nf_ct_expect_init(rtcp_exp, SIP_EXPECT_AUDIO, family, saddr, daddr,
725 IPPROTO_UDP, NULL, &rtcp_port);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800726
727 nf_nat_sdp = rcu_dereference(nf_nat_sdp_hook);
728 if (nf_nat_sdp && ct->status & IPS_NAT_MASK)
Patrick McHardya9c1d352008-03-25 20:25:49 -0700729 ret = nf_nat_sdp(skb, dptr, datalen, rtp_exp, rtcp_exp);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800730 else {
Patrick McHardya9c1d352008-03-25 20:25:49 -0700731 if (nf_ct_expect_related(rtp_exp) == 0) {
732 if (nf_ct_expect_related(rtcp_exp) != 0)
733 nf_ct_unexpect_related(rtp_exp);
734 else
735 ret = NF_ACCEPT;
736 }
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800737 }
Patrick McHardya9c1d352008-03-25 20:25:49 -0700738 nf_ct_expect_put(rtcp_exp);
739err2:
740 nf_ct_expect_put(rtp_exp);
741err1:
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800742 return ret;
743}
744
Patrick McHardy7d3dd042008-03-25 20:19:46 -0700745static int process_sdp(struct sk_buff *skb,
Patrick McHardy30f33e62008-03-25 20:22:20 -0700746 const char **dptr, unsigned int *datalen,
747 unsigned int cseq)
Patrick McHardy7d3dd042008-03-25 20:19:46 -0700748{
749 enum ip_conntrack_info ctinfo;
750 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
751 int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
752 unsigned int matchoff, matchlen;
753 union nf_inet_addr addr;
754 unsigned int port;
755 enum sdp_header_types type;
756
757 /* Get address and port from SDP packet. */
758 type = family == AF_INET ? SDP_HDR_CONNECTION_IP4 :
759 SDP_HDR_CONNECTION_IP6;
760
761 if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen,
762 type, SDP_HDR_UNSPEC,
763 &matchoff, &matchlen) <= 0)
764 return NF_ACCEPT;
765
766 /* We'll drop only if there are parse problems. */
767 if (!parse_addr(ct, *dptr + matchoff, NULL, &addr, *dptr + *datalen))
768 return NF_DROP;
769
770 if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen,
771 SDP_HDR_MEDIA, SDP_HDR_UNSPEC,
772 &matchoff, &matchlen) <= 0)
773 return NF_ACCEPT;
774
775 port = simple_strtoul(*dptr + matchoff, NULL, 10);
776 if (port < 1024 || port > 65535)
777 return NF_DROP;
778
Patrick McHardya9c1d352008-03-25 20:25:49 -0700779 return set_expected_rtp_rtcp(skb, dptr, datalen, &addr, htons(port));
Patrick McHardy7d3dd042008-03-25 20:19:46 -0700780}
Patrick McHardy30f33e62008-03-25 20:22:20 -0700781static int process_invite_response(struct sk_buff *skb,
782 const char **dptr, unsigned int *datalen,
783 unsigned int cseq, unsigned int code)
784{
Patrick McHardy9467ee32008-03-25 20:24:04 -0700785 enum ip_conntrack_info ctinfo;
786 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
787
Patrick McHardy30f33e62008-03-25 20:22:20 -0700788 if ((code >= 100 && code <= 199) ||
789 (code >= 200 && code <= 299))
790 return process_sdp(skb, dptr, datalen, cseq);
Patrick McHardy9467ee32008-03-25 20:24:04 -0700791 else {
Patrick McHardy0f32a402008-03-25 20:25:13 -0700792 flush_expectations(ct, true);
Patrick McHardy9467ee32008-03-25 20:24:04 -0700793 return NF_ACCEPT;
794 }
Patrick McHardy30f33e62008-03-25 20:22:20 -0700795}
796
797static int process_update_response(struct sk_buff *skb,
798 const char **dptr, unsigned int *datalen,
799 unsigned int cseq, unsigned int code)
800{
Patrick McHardy9467ee32008-03-25 20:24:04 -0700801 enum ip_conntrack_info ctinfo;
802 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
803
Patrick McHardy30f33e62008-03-25 20:22:20 -0700804 if ((code >= 100 && code <= 199) ||
805 (code >= 200 && code <= 299))
806 return process_sdp(skb, dptr, datalen, cseq);
Patrick McHardy9467ee32008-03-25 20:24:04 -0700807 else {
Patrick McHardy0f32a402008-03-25 20:25:13 -0700808 flush_expectations(ct, true);
Patrick McHardy9467ee32008-03-25 20:24:04 -0700809 return NF_ACCEPT;
810 }
Patrick McHardy30f33e62008-03-25 20:22:20 -0700811}
812
Patrick McHardy595a8ec2008-03-25 20:22:53 -0700813static int process_prack_response(struct sk_buff *skb,
814 const char **dptr, unsigned int *datalen,
815 unsigned int cseq, unsigned int code)
816{
Patrick McHardy9467ee32008-03-25 20:24:04 -0700817 enum ip_conntrack_info ctinfo;
818 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
819
Patrick McHardy595a8ec2008-03-25 20:22:53 -0700820 if ((code >= 100 && code <= 199) ||
821 (code >= 200 && code <= 299))
822 return process_sdp(skb, dptr, datalen, cseq);
Patrick McHardy9467ee32008-03-25 20:24:04 -0700823 else {
Patrick McHardy0f32a402008-03-25 20:25:13 -0700824 flush_expectations(ct, true);
Patrick McHardy9467ee32008-03-25 20:24:04 -0700825 return NF_ACCEPT;
826 }
827}
Patrick McHardy595a8ec2008-03-25 20:22:53 -0700828
Patrick McHardy9467ee32008-03-25 20:24:04 -0700829static int process_bye_request(struct sk_buff *skb,
830 const char **dptr, unsigned int *datalen,
831 unsigned int cseq)
832{
833 enum ip_conntrack_info ctinfo;
834 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
835
Patrick McHardy0f32a402008-03-25 20:25:13 -0700836 flush_expectations(ct, true);
837 return NF_ACCEPT;
838}
839
840/* Parse a REGISTER request and create a permanent expectation for incoming
841 * signalling connections. The expectation is marked inactive and is activated
842 * when receiving a response indicating success from the registrar.
843 */
844static int process_register_request(struct sk_buff *skb,
845 const char **dptr, unsigned int *datalen,
846 unsigned int cseq)
847{
848 enum ip_conntrack_info ctinfo;
849 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
850 struct nf_conn_help *help = nfct_help(ct);
851 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
852 int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
853 unsigned int matchoff, matchlen;
854 struct nf_conntrack_expect *exp;
855 union nf_inet_addr *saddr, daddr;
856 __be16 port;
857 unsigned int expires = 0;
858 int ret;
859 typeof(nf_nat_sip_expect_hook) nf_nat_sip_expect;
860
861 /* Expected connections can not register again. */
862 if (ct->status & IPS_EXPECTED)
863 return NF_ACCEPT;
864
865 /* We must check the expiration time: a value of zero signals the
866 * registrar to release the binding. We'll remove our expectation
867 * when receiving the new bindings in the response, but we don't
868 * want to create new ones.
869 *
870 * The expiration time may be contained in Expires: header, the
871 * Contact: header parameters or the URI parameters.
872 */
873 if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_EXPIRES,
874 &matchoff, &matchlen) > 0)
875 expires = simple_strtoul(*dptr + matchoff, NULL, 10);
876
877 ret = ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen,
878 SIP_HDR_CONTACT, NULL,
879 &matchoff, &matchlen, &daddr, &port);
880 if (ret < 0)
881 return NF_DROP;
882 else if (ret == 0)
883 return NF_ACCEPT;
884
885 /* We don't support third-party registrations */
886 if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, &daddr))
887 return NF_ACCEPT;
888
889 if (ct_sip_parse_numerical_param(ct, *dptr,
890 matchoff + matchlen, *datalen,
891 "expires=", NULL, NULL, &expires) < 0)
892 return NF_DROP;
893
894 if (expires == 0) {
895 ret = NF_ACCEPT;
896 goto store_cseq;
897 }
898
899 exp = nf_ct_expect_alloc(ct);
900 if (!exp)
901 return NF_DROP;
902
903 saddr = NULL;
904 if (sip_direct_signalling)
905 saddr = &ct->tuplehash[!dir].tuple.src.u3;
906
907 nf_ct_expect_init(exp, SIP_EXPECT_SIGNALLING, family, saddr, &daddr,
908 IPPROTO_UDP, NULL, &port);
909 exp->timeout.expires = sip_timeout * HZ;
910 exp->helper = nfct_help(ct)->helper;
911 exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE;
912
913 nf_nat_sip_expect = rcu_dereference(nf_nat_sip_expect_hook);
914 if (nf_nat_sip_expect && ct->status & IPS_NAT_MASK)
915 ret = nf_nat_sip_expect(skb, dptr, datalen, exp,
916 matchoff, matchlen);
917 else {
918 if (nf_ct_expect_related(exp) != 0)
919 ret = NF_DROP;
920 else
921 ret = NF_ACCEPT;
922 }
923 nf_ct_expect_put(exp);
924
925store_cseq:
926 if (ret == NF_ACCEPT)
927 help->help.ct_sip_info.register_cseq = cseq;
928 return ret;
929}
930
931static int process_register_response(struct sk_buff *skb,
932 const char **dptr, unsigned int *datalen,
933 unsigned int cseq, unsigned int code)
934{
935 enum ip_conntrack_info ctinfo;
936 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
937 struct nf_conn_help *help = nfct_help(ct);
938 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
939 union nf_inet_addr addr;
940 __be16 port;
941 unsigned int matchoff, matchlen, dataoff = 0;
942 unsigned int expires = 0;
943 int in_contact = 0, ret;
944
945 /* According to RFC 3261, "UAs MUST NOT send a new registration until
946 * they have received a final response from the registrar for the
947 * previous one or the previous REGISTER request has timed out".
948 *
949 * However, some servers fail to detect retransmissions and send late
950 * responses, so we store the sequence number of the last valid
951 * request and compare it here.
952 */
953 if (help->help.ct_sip_info.register_cseq != cseq)
954 return NF_ACCEPT;
955
956 if (code >= 100 && code <= 199)
957 return NF_ACCEPT;
958 if (code < 200 || code > 299)
959 goto flush;
960
961 if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_EXPIRES,
962 &matchoff, &matchlen) > 0)
963 expires = simple_strtoul(*dptr + matchoff, NULL, 10);
964
965 while (1) {
966 unsigned int c_expires = expires;
967
968 ret = ct_sip_parse_header_uri(ct, *dptr, &dataoff, *datalen,
969 SIP_HDR_CONTACT, &in_contact,
970 &matchoff, &matchlen,
971 &addr, &port);
972 if (ret < 0)
973 return NF_DROP;
974 else if (ret == 0)
975 break;
976
977 /* We don't support third-party registrations */
978 if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, &addr))
979 continue;
980
981 ret = ct_sip_parse_numerical_param(ct, *dptr,
982 matchoff + matchlen,
983 *datalen, "expires=",
984 NULL, NULL, &c_expires);
985 if (ret < 0)
986 return NF_DROP;
987 if (c_expires == 0)
988 break;
989 if (refresh_signalling_expectation(ct, &addr, port, c_expires))
990 return NF_ACCEPT;
991 }
992
993flush:
994 flush_expectations(ct, false);
Patrick McHardy595a8ec2008-03-25 20:22:53 -0700995 return NF_ACCEPT;
996}
997
Patrick McHardy30f33e62008-03-25 20:22:20 -0700998static const struct sip_handler sip_handlers[] = {
999 SIP_HANDLER("INVITE", process_sdp, process_invite_response),
1000 SIP_HANDLER("UPDATE", process_sdp, process_update_response),
Patrick McHardy595a8ec2008-03-25 20:22:53 -07001001 SIP_HANDLER("ACK", process_sdp, NULL),
1002 SIP_HANDLER("PRACK", process_sdp, process_prack_response),
Patrick McHardy9467ee32008-03-25 20:24:04 -07001003 SIP_HANDLER("BYE", process_bye_request, NULL),
Patrick McHardy0f32a402008-03-25 20:25:13 -07001004 SIP_HANDLER("REGISTER", process_register_request, process_register_response),
Patrick McHardy30f33e62008-03-25 20:22:20 -07001005};
1006
1007static int process_sip_response(struct sk_buff *skb,
1008 const char **dptr, unsigned int *datalen)
1009{
1010 static const struct sip_handler *handler;
1011 enum ip_conntrack_info ctinfo;
1012 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
1013 unsigned int matchoff, matchlen;
1014 unsigned int code, cseq, dataoff, i;
1015
1016 if (*datalen < strlen("SIP/2.0 200"))
1017 return NF_ACCEPT;
1018 code = simple_strtoul(*dptr + strlen("SIP/2.0 "), NULL, 10);
1019 if (!code)
1020 return NF_DROP;
1021
1022 if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ,
1023 &matchoff, &matchlen) <= 0)
1024 return NF_DROP;
1025 cseq = simple_strtoul(*dptr + matchoff, NULL, 10);
1026 if (!cseq)
1027 return NF_DROP;
1028 dataoff = matchoff + matchlen + 1;
1029
1030 for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) {
1031 handler = &sip_handlers[i];
1032 if (handler->response == NULL)
1033 continue;
1034 if (*datalen < dataoff + handler->len ||
1035 strnicmp(*dptr + dataoff, handler->method, handler->len))
1036 continue;
1037 return handler->response(skb, dptr, datalen, cseq, code);
1038 }
1039 return NF_ACCEPT;
1040}
1041
1042static int process_sip_request(struct sk_buff *skb,
1043 const char **dptr, unsigned int *datalen)
1044{
1045 static const struct sip_handler *handler;
1046 enum ip_conntrack_info ctinfo;
1047 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
1048 unsigned int matchoff, matchlen;
1049 unsigned int cseq, i;
1050
1051 for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) {
1052 handler = &sip_handlers[i];
1053 if (handler->request == NULL)
1054 continue;
1055 if (*datalen < handler->len ||
1056 strnicmp(*dptr, handler->method, handler->len))
1057 continue;
1058
1059 if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ,
1060 &matchoff, &matchlen) <= 0)
1061 return NF_DROP;
1062 cseq = simple_strtoul(*dptr + matchoff, NULL, 10);
1063 if (!cseq)
1064 return NF_DROP;
1065
1066 return handler->request(skb, dptr, datalen, cseq);
1067 }
1068 return NF_ACCEPT;
1069}
Patrick McHardy7d3dd042008-03-25 20:19:46 -07001070
Herbert Xu3db05fe2007-10-15 00:53:15 -07001071static int sip_help(struct sk_buff *skb,
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001072 unsigned int protoff,
1073 struct nf_conn *ct,
1074 enum ip_conntrack_info ctinfo)
1075{
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001076 unsigned int dataoff, datalen;
1077 const char *dptr;
Patrick McHardy33cb1e92008-03-25 20:22:37 -07001078 int ret;
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001079 typeof(nf_nat_sip_hook) nf_nat_sip;
1080
1081 /* No Data ? */
1082 dataoff = protoff + sizeof(struct udphdr);
Herbert Xu3db05fe2007-10-15 00:53:15 -07001083 if (dataoff >= skb->len)
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001084 return NF_ACCEPT;
1085
Herbert Xu3db05fe2007-10-15 00:53:15 -07001086 nf_ct_refresh(ct, skb, sip_timeout * HZ);
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001087
Herbert Xu3db05fe2007-10-15 00:53:15 -07001088 if (!skb_is_nonlinear(skb))
1089 dptr = skb->data + dataoff;
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001090 else {
Patrick McHardy0d537782007-07-07 22:39:38 -07001091 pr_debug("Copy of skbuff not supported yet.\n");
Patrick McHardy7d3dd042008-03-25 20:19:46 -07001092 return NF_ACCEPT;
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001093 }
1094
Herbert Xu3db05fe2007-10-15 00:53:15 -07001095 datalen = skb->len - dataoff;
Patrick McHardy779382e2008-03-25 20:17:36 -07001096 if (datalen < strlen("SIP/2.0 200"))
Patrick McHardy7d3dd042008-03-25 20:19:46 -07001097 return NF_ACCEPT;
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001098
Patrick McHardy30f33e62008-03-25 20:22:20 -07001099 if (strnicmp(dptr, "SIP/2.0 ", strlen("SIP/2.0 ")) != 0)
Patrick McHardy33cb1e92008-03-25 20:22:37 -07001100 ret = process_sip_request(skb, &dptr, &datalen);
Patrick McHardy30f33e62008-03-25 20:22:20 -07001101 else
Patrick McHardy33cb1e92008-03-25 20:22:37 -07001102 ret = process_sip_response(skb, &dptr, &datalen);
1103
1104 if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) {
1105 nf_nat_sip = rcu_dereference(nf_nat_sip_hook);
1106 if (nf_nat_sip && !nf_nat_sip(skb, &dptr, &datalen))
1107 ret = NF_DROP;
1108 }
1109
1110 return ret;
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001111}
1112
1113static struct nf_conntrack_helper sip[MAX_PORTS][2] __read_mostly;
1114static char sip_names[MAX_PORTS][2][sizeof("sip-65535")] __read_mostly;
1115
Patrick McHardy0f32a402008-03-25 20:25:13 -07001116static const struct nf_conntrack_expect_policy sip_exp_policy[SIP_EXPECT_MAX + 1] = {
1117 [SIP_EXPECT_SIGNALLING] = {
1118 .max_expected = 1,
1119 .timeout = 3 * 60,
1120 },
1121 [SIP_EXPECT_AUDIO] = {
Patrick McHardya9c1d352008-03-25 20:25:49 -07001122 .max_expected = 2 * IP_CT_DIR_MAX,
Patrick McHardy0f32a402008-03-25 20:25:13 -07001123 .timeout = 3 * 60,
1124 },
Patrick McHardy6002f262008-03-25 20:09:15 -07001125};
1126
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001127static void nf_conntrack_sip_fini(void)
1128{
1129 int i, j;
1130
1131 for (i = 0; i < ports_c; i++) {
1132 for (j = 0; j < 2; j++) {
1133 if (sip[i][j].me == NULL)
1134 continue;
1135 nf_conntrack_helper_unregister(&sip[i][j]);
1136 }
1137 }
1138}
1139
1140static int __init nf_conntrack_sip_init(void)
1141{
1142 int i, j, ret;
1143 char *tmpname;
1144
1145 if (ports_c == 0)
1146 ports[ports_c++] = SIP_PORT;
1147
1148 for (i = 0; i < ports_c; i++) {
1149 memset(&sip[i], 0, sizeof(sip[i]));
1150
1151 sip[i][0].tuple.src.l3num = AF_INET;
1152 sip[i][1].tuple.src.l3num = AF_INET6;
1153 for (j = 0; j < 2; j++) {
1154 sip[i][j].tuple.dst.protonum = IPPROTO_UDP;
1155 sip[i][j].tuple.src.u.udp.port = htons(ports[i]);
Patrick McHardy0f32a402008-03-25 20:25:13 -07001156 sip[i][j].expect_policy = sip_exp_policy;
1157 sip[i][j].expect_class_max = SIP_EXPECT_MAX;
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001158 sip[i][j].me = THIS_MODULE;
1159 sip[i][j].help = sip_help;
1160
1161 tmpname = &sip_names[i][j][0];
1162 if (ports[i] == SIP_PORT)
1163 sprintf(tmpname, "sip");
1164 else
1165 sprintf(tmpname, "sip-%u", i);
1166 sip[i][j].name = tmpname;
1167
Patrick McHardy0d537782007-07-07 22:39:38 -07001168 pr_debug("port #%u: %u\n", i, ports[i]);
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001169
1170 ret = nf_conntrack_helper_register(&sip[i][j]);
1171 if (ret) {
1172 printk("nf_ct_sip: failed to register helper "
1173 "for pf: %u port: %u\n",
1174 sip[i][j].tuple.src.l3num, ports[i]);
1175 nf_conntrack_sip_fini();
1176 return ret;
1177 }
1178 }
1179 }
1180 return 0;
1181}
1182
1183module_init(nf_conntrack_sip_init);
1184module_exit(nf_conntrack_sip_fini);