blob: 043aa557e7a882918ced81bb22111f3199e6494a [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
Herbert Xu3db05fe2007-10-15 00:53:15 -070045unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb,
Patrick McHardy2a6cfb22008-03-25 20:16:54 -070046 const char **dptr,
47 unsigned int *datalen) __read_mostly;
Patrick McHardy9fafcd72006-12-02 22:09:57 -080048EXPORT_SYMBOL_GPL(nf_nat_sip_hook);
49
Patrick McHardy0f32a402008-03-25 20:25:13 -070050unsigned int (*nf_nat_sip_expect_hook)(struct sk_buff *skb,
51 const char **dptr,
52 unsigned int *datalen,
53 struct nf_conntrack_expect *exp,
54 unsigned int matchoff,
55 unsigned int matchlen) __read_mostly;
56EXPORT_SYMBOL_GPL(nf_nat_sip_expect_hook);
57
Herbert Xu3db05fe2007-10-15 00:53:15 -070058unsigned int (*nf_nat_sdp_hook)(struct sk_buff *skb,
Patrick McHardy2a6cfb22008-03-25 20:16:54 -070059 const char **dptr,
Patrick McHardy212440a2008-03-25 20:17:13 -070060 unsigned int *datalen,
61 struct nf_conntrack_expect *exp) __read_mostly;
Patrick McHardy9fafcd72006-12-02 22:09:57 -080062EXPORT_SYMBOL_GPL(nf_nat_sdp_hook);
63
Patrick McHardyac367742008-03-25 20:18:40 -070064static int string_len(const struct nf_conn *ct, const char *dptr,
65 const char *limit, int *shift)
66{
67 int len = 0;
68
69 while (dptr < limit && isalpha(*dptr)) {
70 dptr++;
71 len++;
72 }
73 return len;
74}
75
Jan Engelhardt13f7d632008-01-31 04:50:25 -080076static int digits_len(const struct nf_conn *ct, const char *dptr,
Patrick McHardy9fafcd72006-12-02 22:09:57 -080077 const char *limit, int *shift)
78{
79 int len = 0;
Patrick McHardyb1ec4882008-03-25 20:10:11 -070080 while (dptr < limit && isdigit(*dptr)) {
Patrick McHardy9fafcd72006-12-02 22:09:57 -080081 dptr++;
82 len++;
83 }
84 return len;
85}
86
Jan Engelhardt13f7d632008-01-31 04:50:25 -080087static int parse_addr(const struct nf_conn *ct, const char *cp,
88 const char **endp, union nf_inet_addr *addr,
89 const char *limit)
Patrick McHardy9fafcd72006-12-02 22:09:57 -080090{
91 const char *end;
92 int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
93 int ret = 0;
94
95 switch (family) {
96 case AF_INET:
97 ret = in4_pton(cp, limit - cp, (u8 *)&addr->ip, -1, &end);
98 break;
99 case AF_INET6:
100 ret = in6_pton(cp, limit - cp, (u8 *)&addr->ip6, -1, &end);
101 break;
102 default:
103 BUG();
104 }
105
106 if (ret == 0 || end == cp)
107 return 0;
108 if (endp)
109 *endp = end;
110 return 1;
111}
112
113/* skip ip address. returns its length. */
Jan Engelhardt13f7d632008-01-31 04:50:25 -0800114static int epaddr_len(const struct nf_conn *ct, const char *dptr,
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800115 const char *limit, int *shift)
116{
Jan Engelhardt643a2c12007-12-17 22:43:50 -0800117 union nf_inet_addr addr;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800118 const char *aux = dptr;
119
120 if (!parse_addr(ct, dptr, &dptr, &addr, limit)) {
Patrick McHardy0d537782007-07-07 22:39:38 -0700121 pr_debug("ip: %s parse failed.!\n", dptr);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800122 return 0;
123 }
124
125 /* Port number */
126 if (*dptr == ':') {
127 dptr++;
128 dptr += digits_len(ct, dptr, limit, shift);
129 }
130 return dptr - aux;
131}
132
133/* get address length, skiping user info. */
Jan Engelhardt13f7d632008-01-31 04:50:25 -0800134static int skp_epaddr_len(const struct nf_conn *ct, const char *dptr,
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800135 const char *limit, int *shift)
136{
Patrick McHardyaa584ed2007-08-14 13:14:35 -0700137 const char *start = dptr;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800138 int s = *shift;
139
Lars Immisch7da5bfb2007-01-30 14:24:57 -0800140 /* Search for @, but stop at the end of the line.
141 * We are inside a sip: URI, so we don't need to worry about
142 * continuation lines. */
Patrick McHardyb1ec4882008-03-25 20:10:11 -0700143 while (dptr < limit &&
Lars Immisch7da5bfb2007-01-30 14:24:57 -0800144 *dptr != '@' && *dptr != '\r' && *dptr != '\n') {
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800145 (*shift)++;
Lars Immisch7da5bfb2007-01-30 14:24:57 -0800146 dptr++;
147 }
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800148
Patrick McHardyb1ec4882008-03-25 20:10:11 -0700149 if (dptr < limit && *dptr == '@') {
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800150 dptr++;
151 (*shift)++;
Patrick McHardyaa584ed2007-08-14 13:14:35 -0700152 } else {
153 dptr = start;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800154 *shift = s;
Patrick McHardyaa584ed2007-08-14 13:14:35 -0700155 }
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800156
157 return epaddr_len(ct, dptr, limit, shift);
158}
159
Patrick McHardyac367742008-03-25 20:18:40 -0700160/* Parse a SIP request line of the form:
161 *
162 * Request-Line = Method SP Request-URI SP SIP-Version CRLF
163 *
164 * and return the offset and length of the address contained in the Request-URI.
165 */
166int ct_sip_parse_request(const struct nf_conn *ct,
167 const char *dptr, unsigned int datalen,
Patrick McHardy624f8b72008-03-25 20:19:30 -0700168 unsigned int *matchoff, unsigned int *matchlen,
169 union nf_inet_addr *addr, __be16 *port)
Patrick McHardyac367742008-03-25 20:18:40 -0700170{
Patrick McHardy624f8b72008-03-25 20:19:30 -0700171 const char *start = dptr, *limit = dptr + datalen, *end;
Patrick McHardyac367742008-03-25 20:18:40 -0700172 unsigned int mlen;
Patrick McHardy624f8b72008-03-25 20:19:30 -0700173 unsigned int p;
Patrick McHardyac367742008-03-25 20:18:40 -0700174 int shift = 0;
175
176 /* Skip method and following whitespace */
177 mlen = string_len(ct, dptr, limit, NULL);
178 if (!mlen)
179 return 0;
180 dptr += mlen;
181 if (++dptr >= limit)
182 return 0;
183
184 /* Find SIP URI */
185 limit -= strlen("sip:");
186 for (; dptr < limit; dptr++) {
187 if (*dptr == '\r' || *dptr == '\n')
188 return -1;
189 if (strnicmp(dptr, "sip:", strlen("sip:")) == 0)
190 break;
191 }
Patrick McHardy624f8b72008-03-25 20:19:30 -0700192 if (!skp_epaddr_len(ct, dptr, limit, &shift))
Patrick McHardyac367742008-03-25 20:18:40 -0700193 return 0;
Patrick McHardy624f8b72008-03-25 20:19:30 -0700194 dptr += shift;
195
196 if (!parse_addr(ct, dptr, &end, addr, limit))
197 return -1;
198 if (end < limit && *end == ':') {
199 end++;
200 p = simple_strtoul(end, (char **)&end, 10);
201 if (p < 1024 || p > 65535)
202 return -1;
203 *port = htons(p);
204 } else
205 *port = htons(SIP_PORT);
206
207 if (end == dptr)
208 return 0;
209 *matchoff = dptr - start;
210 *matchlen = end - dptr;
Patrick McHardyac367742008-03-25 20:18:40 -0700211 return 1;
212}
213EXPORT_SYMBOL_GPL(ct_sip_parse_request);
214
Patrick McHardyea45f122008-03-25 20:18:57 -0700215/* SIP header parsing: SIP headers are located at the beginning of a line, but
216 * may span several lines, in which case the continuation lines begin with a
217 * whitespace character. RFC 2543 allows lines to be terminated with CR, LF or
218 * CRLF, RFC 3261 allows only CRLF, we support both.
219 *
220 * Headers are followed by (optionally) whitespace, a colon, again (optionally)
221 * whitespace and the values. Whitespace in this context means any amount of
222 * tabs, spaces and continuation lines, which are treated as a single whitespace
223 * character.
Patrick McHardy05e3ced2008-03-25 20:19:13 -0700224 *
225 * Some headers may appear multiple times. A comma seperated list of values is
226 * equivalent to multiple headers.
Patrick McHardyea45f122008-03-25 20:18:57 -0700227 */
228static const struct sip_header ct_sip_hdrs[] = {
Patrick McHardy30f33e62008-03-25 20:22:20 -0700229 [SIP_HDR_CSEQ] = SIP_HDR("CSeq", NULL, NULL, digits_len),
Patrick McHardyea45f122008-03-25 20:18:57 -0700230 [SIP_HDR_FROM] = SIP_HDR("From", "f", "sip:", skp_epaddr_len),
231 [SIP_HDR_TO] = SIP_HDR("To", "t", "sip:", skp_epaddr_len),
232 [SIP_HDR_CONTACT] = SIP_HDR("Contact", "m", "sip:", skp_epaddr_len),
233 [SIP_HDR_VIA] = SIP_HDR("Via", "v", "UDP ", epaddr_len),
Patrick McHardy0f32a402008-03-25 20:25:13 -0700234 [SIP_HDR_EXPIRES] = SIP_HDR("Expires", NULL, NULL, digits_len),
Patrick McHardyea45f122008-03-25 20:18:57 -0700235 [SIP_HDR_CONTENT_LENGTH] = SIP_HDR("Content-Length", "l", NULL, digits_len),
236};
237
238static const char *sip_follow_continuation(const char *dptr, const char *limit)
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800239{
Patrick McHardyea45f122008-03-25 20:18:57 -0700240 /* Walk past newline */
241 if (++dptr >= limit)
242 return NULL;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800243
Patrick McHardyea45f122008-03-25 20:18:57 -0700244 /* Skip '\n' in CR LF */
245 if (*(dptr - 1) == '\r' && *dptr == '\n') {
246 if (++dptr >= limit)
247 return NULL;
248 }
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800249
Patrick McHardyea45f122008-03-25 20:18:57 -0700250 /* Continuation line? */
251 if (*dptr != ' ' && *dptr != '\t')
252 return NULL;
253
254 /* skip leading whitespace */
255 for (; dptr < limit; dptr++) {
256 if (*dptr != ' ' && *dptr != '\t')
257 break;
258 }
259 return dptr;
260}
261
262static const char *sip_skip_whitespace(const char *dptr, const char *limit)
263{
264 for (; dptr < limit; dptr++) {
265 if (*dptr == ' ')
266 continue;
267 if (*dptr != '\r' && *dptr != '\n')
268 break;
269 dptr = sip_follow_continuation(dptr, limit);
270 if (dptr == NULL)
271 return NULL;
272 }
273 return dptr;
274}
275
276/* Search within a SIP header value, dealing with continuation lines */
277static const char *ct_sip_header_search(const char *dptr, const char *limit,
278 const char *needle, unsigned int len)
279{
280 for (limit -= len; dptr < limit; dptr++) {
281 if (*dptr == '\r' || *dptr == '\n') {
282 dptr = sip_follow_continuation(dptr, limit);
283 if (dptr == NULL)
284 break;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800285 continue;
286 }
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800287
Patrick McHardyea45f122008-03-25 20:18:57 -0700288 if (strnicmp(dptr, needle, len) == 0)
289 return dptr;
290 }
291 return NULL;
292}
293
294int ct_sip_get_header(const struct nf_conn *ct, const char *dptr,
295 unsigned int dataoff, unsigned int datalen,
296 enum sip_header_types type,
297 unsigned int *matchoff, unsigned int *matchlen)
298{
299 const struct sip_header *hdr = &ct_sip_hdrs[type];
300 const char *start = dptr, *limit = dptr + datalen;
301 int shift = 0;
302
303 for (dptr += dataoff; dptr < limit; dptr++) {
304 /* Find beginning of line */
305 if (*dptr != '\r' && *dptr != '\n')
306 continue;
307 if (++dptr >= limit)
308 break;
309 if (*(dptr - 1) == '\r' && *dptr == '\n') {
310 if (++dptr >= limit)
311 break;
312 }
313
314 /* Skip continuation lines */
315 if (*dptr == ' ' || *dptr == '\t')
316 continue;
317
318 /* Find header. Compact headers must be followed by a
319 * non-alphabetic character to avoid mismatches. */
320 if (limit - dptr >= hdr->len &&
321 strnicmp(dptr, hdr->name, hdr->len) == 0)
322 dptr += hdr->len;
323 else if (hdr->cname && limit - dptr >= hdr->clen + 1 &&
324 strnicmp(dptr, hdr->cname, hdr->clen) == 0 &&
325 !isalpha(*(dptr + hdr->clen + 1)))
326 dptr += hdr->clen;
327 else
328 continue;
329
330 /* Find and skip colon */
331 dptr = sip_skip_whitespace(dptr, limit);
332 if (dptr == NULL)
333 break;
334 if (*dptr != ':' || ++dptr >= limit)
335 break;
336
337 /* Skip whitespace after colon */
338 dptr = sip_skip_whitespace(dptr, limit);
339 if (dptr == NULL)
340 break;
341
342 *matchoff = dptr - start;
343 if (hdr->search) {
344 dptr = ct_sip_header_search(dptr, limit, hdr->search,
345 hdr->slen);
346 if (!dptr)
347 return -1;
348 dptr += hdr->slen;
349 }
350
351 *matchlen = hdr->match_len(ct, dptr, limit, &shift);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800352 if (!*matchlen)
353 return -1;
Patrick McHardyea45f122008-03-25 20:18:57 -0700354 *matchoff = dptr - start + shift;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800355 return 1;
356 }
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800357 return 0;
358}
Patrick McHardyea45f122008-03-25 20:18:57 -0700359EXPORT_SYMBOL_GPL(ct_sip_get_header);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800360
Patrick McHardy05e3ced2008-03-25 20:19:13 -0700361/* Get next header field in a list of comma seperated values */
362static int ct_sip_next_header(const struct nf_conn *ct, const char *dptr,
363 unsigned int dataoff, unsigned int datalen,
364 enum sip_header_types type,
365 unsigned int *matchoff, unsigned int *matchlen)
366{
367 const struct sip_header *hdr = &ct_sip_hdrs[type];
368 const char *start = dptr, *limit = dptr + datalen;
369 int shift = 0;
370
371 dptr += dataoff;
372
373 dptr = ct_sip_header_search(dptr, limit, ",", strlen(","));
374 if (!dptr)
375 return 0;
376
377 dptr = ct_sip_header_search(dptr, limit, hdr->search, hdr->slen);
378 if (!dptr)
379 return 0;
380 dptr += hdr->slen;
381
382 *matchoff = dptr - start;
383 *matchlen = hdr->match_len(ct, dptr, limit, &shift);
384 if (!*matchlen)
385 return -1;
386 *matchoff += shift;
387 return 1;
388}
389
390/* Walk through headers until a parsable one is found or no header of the
391 * given type is left. */
392static int ct_sip_walk_headers(const struct nf_conn *ct, const char *dptr,
393 unsigned int dataoff, unsigned int datalen,
394 enum sip_header_types type, int *in_header,
395 unsigned int *matchoff, unsigned int *matchlen)
396{
397 int ret;
398
399 if (in_header && *in_header) {
400 while (1) {
401 ret = ct_sip_next_header(ct, dptr, dataoff, datalen,
402 type, matchoff, matchlen);
403 if (ret > 0)
404 return ret;
405 if (ret == 0)
406 break;
407 dataoff += *matchoff;
408 }
409 *in_header = 0;
410 }
411
412 while (1) {
413 ret = ct_sip_get_header(ct, dptr, dataoff, datalen,
414 type, matchoff, matchlen);
415 if (ret > 0)
416 break;
417 if (ret == 0)
418 return ret;
419 dataoff += *matchoff;
420 }
421
422 if (in_header)
423 *in_header = 1;
424 return 1;
425}
426
427/* Locate a SIP header, parse the URI and return the offset and length of
428 * the address as well as the address and port themselves. A stream of
429 * headers can be parsed by handing in a non-NULL datalen and in_header
430 * pointer.
431 */
432int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr,
433 unsigned int *dataoff, unsigned int datalen,
434 enum sip_header_types type, int *in_header,
435 unsigned int *matchoff, unsigned int *matchlen,
436 union nf_inet_addr *addr, __be16 *port)
437{
438 const char *c, *limit = dptr + datalen;
439 unsigned int p;
440 int ret;
441
442 ret = ct_sip_walk_headers(ct, dptr, dataoff ? *dataoff : 0, datalen,
443 type, in_header, matchoff, matchlen);
444 WARN_ON(ret < 0);
445 if (ret == 0)
446 return ret;
447
448 if (!parse_addr(ct, dptr + *matchoff, &c, addr, limit))
449 return -1;
450 if (*c == ':') {
451 c++;
452 p = simple_strtoul(c, (char **)&c, 10);
453 if (p < 1024 || p > 65535)
454 return -1;
455 *port = htons(p);
456 } else
457 *port = htons(SIP_PORT);
458
459 if (dataoff)
460 *dataoff = c - dptr;
461 return 1;
462}
463EXPORT_SYMBOL_GPL(ct_sip_parse_header_uri);
464
Patrick McHardy2bbb2112008-03-25 20:24:24 -0700465/* Parse address from header parameter and return address, offset and length */
466int ct_sip_parse_address_param(const struct nf_conn *ct, const char *dptr,
467 unsigned int dataoff, unsigned int datalen,
468 const char *name,
469 unsigned int *matchoff, unsigned int *matchlen,
470 union nf_inet_addr *addr)
471{
472 const char *limit = dptr + datalen;
473 const char *start, *end;
474
475 limit = ct_sip_header_search(dptr + dataoff, limit, ",", strlen(","));
476 if (!limit)
477 limit = dptr + datalen;
478
479 start = ct_sip_header_search(dptr + dataoff, limit, name, strlen(name));
480 if (!start)
481 return 0;
482
483 start += strlen(name);
484 if (!parse_addr(ct, start, &end, addr, limit))
485 return 0;
486 *matchoff = start - dptr;
487 *matchlen = end - start;
488 return 1;
489}
490EXPORT_SYMBOL_GPL(ct_sip_parse_address_param);
491
492/* Parse numerical header parameter and return value, offset and length */
493int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr,
494 unsigned int dataoff, unsigned int datalen,
495 const char *name,
496 unsigned int *matchoff, unsigned int *matchlen,
497 unsigned int *val)
498{
499 const char *limit = dptr + datalen;
500 const char *start;
501 char *end;
502
503 limit = ct_sip_header_search(dptr + dataoff, limit, ",", strlen(","));
504 if (!limit)
505 limit = dptr + datalen;
506
507 start = ct_sip_header_search(dptr + dataoff, limit, name, strlen(name));
508 if (!start)
509 return 0;
510
511 start += strlen(name);
512 *val = simple_strtoul(start, &end, 0);
513 if (start == end)
514 return 0;
515 if (matchoff && matchlen) {
516 *matchoff = start - dptr;
517 *matchlen = end - start;
518 }
519 return 1;
520}
521EXPORT_SYMBOL_GPL(ct_sip_parse_numerical_param);
522
Patrick McHardy3e9b4600b2008-03-25 20:17:55 -0700523/* SDP header parsing: a SDP session description contains an ordered set of
524 * headers, starting with a section containing general session parameters,
525 * optionally followed by multiple media descriptions.
526 *
527 * SDP headers always start at the beginning of a line. According to RFC 2327:
528 * "The sequence CRLF (0x0d0a) is used to end a record, although parsers should
529 * be tolerant and also accept records terminated with a single newline
530 * character". We handle both cases.
531 */
532static const struct sip_header ct_sdp_hdrs[] = {
533 [SDP_HDR_VERSION] = SDP_HDR("v=", NULL, digits_len),
534 [SDP_HDR_OWNER_IP4] = SDP_HDR("o=", "IN IP4 ", epaddr_len),
535 [SDP_HDR_CONNECTION_IP4] = SDP_HDR("c=", "IN IP4 ", epaddr_len),
536 [SDP_HDR_OWNER_IP6] = SDP_HDR("o=", "IN IP6 ", epaddr_len),
537 [SDP_HDR_CONNECTION_IP6] = SDP_HDR("c=", "IN IP6 ", epaddr_len),
538 [SDP_HDR_MEDIA] = SDP_HDR("m=", "audio ", digits_len),
539};
540
541/* Linear string search within SDP header values */
542static const char *ct_sdp_header_search(const char *dptr, const char *limit,
543 const char *needle, unsigned int len)
544{
545 for (limit -= len; dptr < limit; dptr++) {
546 if (*dptr == '\r' || *dptr == '\n')
547 break;
548 if (strncmp(dptr, needle, len) == 0)
549 return dptr;
550 }
551 return NULL;
552}
553
554/* Locate a SDP header (optionally a substring within the header value),
555 * optionally stopping at the first occurence of the term header, parse
556 * it and return the offset and length of the data we're interested in.
557 */
558int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr,
559 unsigned int dataoff, unsigned int datalen,
560 enum sdp_header_types type,
561 enum sdp_header_types term,
562 unsigned int *matchoff, unsigned int *matchlen)
563{
564 const struct sip_header *hdr = &ct_sdp_hdrs[type];
565 const struct sip_header *thdr = &ct_sdp_hdrs[term];
566 const char *start = dptr, *limit = dptr + datalen;
567 int shift = 0;
568
569 for (dptr += dataoff; dptr < limit; dptr++) {
570 /* Find beginning of line */
571 if (*dptr != '\r' && *dptr != '\n')
572 continue;
573 if (++dptr >= limit)
574 break;
575 if (*(dptr - 1) == '\r' && *dptr == '\n') {
576 if (++dptr >= limit)
577 break;
578 }
579
580 if (term != SDP_HDR_UNSPEC &&
581 limit - dptr >= thdr->len &&
582 strnicmp(dptr, thdr->name, thdr->len) == 0)
583 break;
584 else if (limit - dptr >= hdr->len &&
585 strnicmp(dptr, hdr->name, hdr->len) == 0)
586 dptr += hdr->len;
587 else
588 continue;
589
590 *matchoff = dptr - start;
591 if (hdr->search) {
592 dptr = ct_sdp_header_search(dptr, limit, hdr->search,
593 hdr->slen);
594 if (!dptr)
595 return -1;
596 dptr += hdr->slen;
597 }
598
599 *matchlen = hdr->match_len(ct, dptr, limit, &shift);
600 if (!*matchlen)
601 return -1;
602 *matchoff = dptr - start + shift;
603 return 1;
604 }
605 return 0;
606}
607EXPORT_SYMBOL_GPL(ct_sip_get_sdp_header);
608
Patrick McHardy0f32a402008-03-25 20:25:13 -0700609static int refresh_signalling_expectation(struct nf_conn *ct,
610 union nf_inet_addr *addr,
611 __be16 port,
612 unsigned int expires)
613{
614 struct nf_conn_help *help = nfct_help(ct);
615 struct nf_conntrack_expect *exp;
616 struct hlist_node *n, *next;
617 int found = 0;
618
619 spin_lock_bh(&nf_conntrack_lock);
620 hlist_for_each_entry_safe(exp, n, next, &help->expectations, lnode) {
621 if (exp->class != SIP_EXPECT_SIGNALLING ||
622 !nf_inet_addr_cmp(&exp->tuple.dst.u3, addr) ||
623 exp->tuple.dst.u.udp.port != port)
624 continue;
625 if (!del_timer(&exp->timeout))
626 continue;
627 exp->flags &= ~NF_CT_EXPECT_INACTIVE;
628 exp->timeout.expires = jiffies + expires * HZ;
629 add_timer(&exp->timeout);
630 found = 1;
631 break;
632 }
633 spin_unlock_bh(&nf_conntrack_lock);
634 return found;
635}
636
637static void flush_expectations(struct nf_conn *ct, bool media)
Patrick McHardy9467ee32008-03-25 20:24:04 -0700638{
639 struct nf_conn_help *help = nfct_help(ct);
640 struct nf_conntrack_expect *exp;
641 struct hlist_node *n, *next;
642
643 spin_lock_bh(&nf_conntrack_lock);
644 hlist_for_each_entry_safe(exp, n, next, &help->expectations, lnode) {
Patrick McHardy0f32a402008-03-25 20:25:13 -0700645 if ((exp->class != SIP_EXPECT_SIGNALLING) ^ media)
646 continue;
Patrick McHardy9467ee32008-03-25 20:24:04 -0700647 if (!del_timer(&exp->timeout))
648 continue;
649 nf_ct_unlink_expect(exp);
650 nf_ct_expect_put(exp);
Patrick McHardy0f32a402008-03-25 20:25:13 -0700651 if (!media)
652 break;
Patrick McHardy9467ee32008-03-25 20:24:04 -0700653 }
654 spin_unlock_bh(&nf_conntrack_lock);
655}
656
Herbert Xu3db05fe2007-10-15 00:53:15 -0700657static int set_expected_rtp(struct sk_buff *skb,
Patrick McHardy212440a2008-03-25 20:17:13 -0700658 const char **dptr, unsigned int *datalen,
659 union nf_inet_addr *addr, __be16 port)
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800660{
661 struct nf_conntrack_expect *exp;
Patrick McHardy212440a2008-03-25 20:17:13 -0700662 enum ip_conntrack_info ctinfo;
663 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800664 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
665 int family = ct->tuplehash[!dir].tuple.src.l3num;
666 int ret;
667 typeof(nf_nat_sdp_hook) nf_nat_sdp;
668
Patrick McHardy68236452007-07-07 22:30:49 -0700669 exp = nf_ct_expect_alloc(ct);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800670 if (exp == NULL)
671 return NF_DROP;
Patrick McHardy0f32a402008-03-25 20:25:13 -0700672 nf_ct_expect_init(exp, SIP_EXPECT_AUDIO, family,
Patrick McHardy68236452007-07-07 22:30:49 -0700673 &ct->tuplehash[!dir].tuple.src.u3, addr,
674 IPPROTO_UDP, NULL, &port);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800675
676 nf_nat_sdp = rcu_dereference(nf_nat_sdp_hook);
677 if (nf_nat_sdp && ct->status & IPS_NAT_MASK)
Patrick McHardy212440a2008-03-25 20:17:13 -0700678 ret = nf_nat_sdp(skb, dptr, datalen, exp);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800679 else {
Patrick McHardy68236452007-07-07 22:30:49 -0700680 if (nf_ct_expect_related(exp) != 0)
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800681 ret = NF_DROP;
682 else
683 ret = NF_ACCEPT;
684 }
Patrick McHardy68236452007-07-07 22:30:49 -0700685 nf_ct_expect_put(exp);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800686
687 return ret;
688}
689
Patrick McHardy7d3dd042008-03-25 20:19:46 -0700690static int process_sdp(struct sk_buff *skb,
Patrick McHardy30f33e62008-03-25 20:22:20 -0700691 const char **dptr, unsigned int *datalen,
692 unsigned int cseq)
Patrick McHardy7d3dd042008-03-25 20:19:46 -0700693{
694 enum ip_conntrack_info ctinfo;
695 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
696 int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
697 unsigned int matchoff, matchlen;
698 union nf_inet_addr addr;
699 unsigned int port;
700 enum sdp_header_types type;
701
702 /* Get address and port from SDP packet. */
703 type = family == AF_INET ? SDP_HDR_CONNECTION_IP4 :
704 SDP_HDR_CONNECTION_IP6;
705
706 if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen,
707 type, SDP_HDR_UNSPEC,
708 &matchoff, &matchlen) <= 0)
709 return NF_ACCEPT;
710
711 /* We'll drop only if there are parse problems. */
712 if (!parse_addr(ct, *dptr + matchoff, NULL, &addr, *dptr + *datalen))
713 return NF_DROP;
714
715 if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen,
716 SDP_HDR_MEDIA, SDP_HDR_UNSPEC,
717 &matchoff, &matchlen) <= 0)
718 return NF_ACCEPT;
719
720 port = simple_strtoul(*dptr + matchoff, NULL, 10);
721 if (port < 1024 || port > 65535)
722 return NF_DROP;
723
724 return set_expected_rtp(skb, dptr, datalen, &addr, htons(port));
725}
Patrick McHardy30f33e62008-03-25 20:22:20 -0700726static int process_invite_response(struct sk_buff *skb,
727 const char **dptr, unsigned int *datalen,
728 unsigned int cseq, unsigned int code)
729{
Patrick McHardy9467ee32008-03-25 20:24:04 -0700730 enum ip_conntrack_info ctinfo;
731 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
732
Patrick McHardy30f33e62008-03-25 20:22:20 -0700733 if ((code >= 100 && code <= 199) ||
734 (code >= 200 && code <= 299))
735 return process_sdp(skb, dptr, datalen, cseq);
Patrick McHardy9467ee32008-03-25 20:24:04 -0700736 else {
Patrick McHardy0f32a402008-03-25 20:25:13 -0700737 flush_expectations(ct, true);
Patrick McHardy9467ee32008-03-25 20:24:04 -0700738 return NF_ACCEPT;
739 }
Patrick McHardy30f33e62008-03-25 20:22:20 -0700740}
741
742static int process_update_response(struct sk_buff *skb,
743 const char **dptr, unsigned int *datalen,
744 unsigned int cseq, unsigned int code)
745{
Patrick McHardy9467ee32008-03-25 20:24:04 -0700746 enum ip_conntrack_info ctinfo;
747 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
748
Patrick McHardy30f33e62008-03-25 20:22:20 -0700749 if ((code >= 100 && code <= 199) ||
750 (code >= 200 && code <= 299))
751 return process_sdp(skb, dptr, datalen, cseq);
Patrick McHardy9467ee32008-03-25 20:24:04 -0700752 else {
Patrick McHardy0f32a402008-03-25 20:25:13 -0700753 flush_expectations(ct, true);
Patrick McHardy9467ee32008-03-25 20:24:04 -0700754 return NF_ACCEPT;
755 }
Patrick McHardy30f33e62008-03-25 20:22:20 -0700756}
757
Patrick McHardy595a8ec2008-03-25 20:22:53 -0700758static int process_prack_response(struct sk_buff *skb,
759 const char **dptr, unsigned int *datalen,
760 unsigned int cseq, unsigned int code)
761{
Patrick McHardy9467ee32008-03-25 20:24:04 -0700762 enum ip_conntrack_info ctinfo;
763 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
764
Patrick McHardy595a8ec2008-03-25 20:22:53 -0700765 if ((code >= 100 && code <= 199) ||
766 (code >= 200 && code <= 299))
767 return process_sdp(skb, dptr, datalen, cseq);
Patrick McHardy9467ee32008-03-25 20:24:04 -0700768 else {
Patrick McHardy0f32a402008-03-25 20:25:13 -0700769 flush_expectations(ct, true);
Patrick McHardy9467ee32008-03-25 20:24:04 -0700770 return NF_ACCEPT;
771 }
772}
Patrick McHardy595a8ec2008-03-25 20:22:53 -0700773
Patrick McHardy9467ee32008-03-25 20:24:04 -0700774static int process_bye_request(struct sk_buff *skb,
775 const char **dptr, unsigned int *datalen,
776 unsigned int cseq)
777{
778 enum ip_conntrack_info ctinfo;
779 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
780
Patrick McHardy0f32a402008-03-25 20:25:13 -0700781 flush_expectations(ct, true);
782 return NF_ACCEPT;
783}
784
785/* Parse a REGISTER request and create a permanent expectation for incoming
786 * signalling connections. The expectation is marked inactive and is activated
787 * when receiving a response indicating success from the registrar.
788 */
789static int process_register_request(struct sk_buff *skb,
790 const char **dptr, unsigned int *datalen,
791 unsigned int cseq)
792{
793 enum ip_conntrack_info ctinfo;
794 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
795 struct nf_conn_help *help = nfct_help(ct);
796 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
797 int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
798 unsigned int matchoff, matchlen;
799 struct nf_conntrack_expect *exp;
800 union nf_inet_addr *saddr, daddr;
801 __be16 port;
802 unsigned int expires = 0;
803 int ret;
804 typeof(nf_nat_sip_expect_hook) nf_nat_sip_expect;
805
806 /* Expected connections can not register again. */
807 if (ct->status & IPS_EXPECTED)
808 return NF_ACCEPT;
809
810 /* We must check the expiration time: a value of zero signals the
811 * registrar to release the binding. We'll remove our expectation
812 * when receiving the new bindings in the response, but we don't
813 * want to create new ones.
814 *
815 * The expiration time may be contained in Expires: header, the
816 * Contact: header parameters or the URI parameters.
817 */
818 if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_EXPIRES,
819 &matchoff, &matchlen) > 0)
820 expires = simple_strtoul(*dptr + matchoff, NULL, 10);
821
822 ret = ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen,
823 SIP_HDR_CONTACT, NULL,
824 &matchoff, &matchlen, &daddr, &port);
825 if (ret < 0)
826 return NF_DROP;
827 else if (ret == 0)
828 return NF_ACCEPT;
829
830 /* We don't support third-party registrations */
831 if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, &daddr))
832 return NF_ACCEPT;
833
834 if (ct_sip_parse_numerical_param(ct, *dptr,
835 matchoff + matchlen, *datalen,
836 "expires=", NULL, NULL, &expires) < 0)
837 return NF_DROP;
838
839 if (expires == 0) {
840 ret = NF_ACCEPT;
841 goto store_cseq;
842 }
843
844 exp = nf_ct_expect_alloc(ct);
845 if (!exp)
846 return NF_DROP;
847
848 saddr = NULL;
849 if (sip_direct_signalling)
850 saddr = &ct->tuplehash[!dir].tuple.src.u3;
851
852 nf_ct_expect_init(exp, SIP_EXPECT_SIGNALLING, family, saddr, &daddr,
853 IPPROTO_UDP, NULL, &port);
854 exp->timeout.expires = sip_timeout * HZ;
855 exp->helper = nfct_help(ct)->helper;
856 exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE;
857
858 nf_nat_sip_expect = rcu_dereference(nf_nat_sip_expect_hook);
859 if (nf_nat_sip_expect && ct->status & IPS_NAT_MASK)
860 ret = nf_nat_sip_expect(skb, dptr, datalen, exp,
861 matchoff, matchlen);
862 else {
863 if (nf_ct_expect_related(exp) != 0)
864 ret = NF_DROP;
865 else
866 ret = NF_ACCEPT;
867 }
868 nf_ct_expect_put(exp);
869
870store_cseq:
871 if (ret == NF_ACCEPT)
872 help->help.ct_sip_info.register_cseq = cseq;
873 return ret;
874}
875
876static int process_register_response(struct sk_buff *skb,
877 const char **dptr, unsigned int *datalen,
878 unsigned int cseq, unsigned int code)
879{
880 enum ip_conntrack_info ctinfo;
881 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
882 struct nf_conn_help *help = nfct_help(ct);
883 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
884 union nf_inet_addr addr;
885 __be16 port;
886 unsigned int matchoff, matchlen, dataoff = 0;
887 unsigned int expires = 0;
888 int in_contact = 0, ret;
889
890 /* According to RFC 3261, "UAs MUST NOT send a new registration until
891 * they have received a final response from the registrar for the
892 * previous one or the previous REGISTER request has timed out".
893 *
894 * However, some servers fail to detect retransmissions and send late
895 * responses, so we store the sequence number of the last valid
896 * request and compare it here.
897 */
898 if (help->help.ct_sip_info.register_cseq != cseq)
899 return NF_ACCEPT;
900
901 if (code >= 100 && code <= 199)
902 return NF_ACCEPT;
903 if (code < 200 || code > 299)
904 goto flush;
905
906 if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_EXPIRES,
907 &matchoff, &matchlen) > 0)
908 expires = simple_strtoul(*dptr + matchoff, NULL, 10);
909
910 while (1) {
911 unsigned int c_expires = expires;
912
913 ret = ct_sip_parse_header_uri(ct, *dptr, &dataoff, *datalen,
914 SIP_HDR_CONTACT, &in_contact,
915 &matchoff, &matchlen,
916 &addr, &port);
917 if (ret < 0)
918 return NF_DROP;
919 else if (ret == 0)
920 break;
921
922 /* We don't support third-party registrations */
923 if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, &addr))
924 continue;
925
926 ret = ct_sip_parse_numerical_param(ct, *dptr,
927 matchoff + matchlen,
928 *datalen, "expires=",
929 NULL, NULL, &c_expires);
930 if (ret < 0)
931 return NF_DROP;
932 if (c_expires == 0)
933 break;
934 if (refresh_signalling_expectation(ct, &addr, port, c_expires))
935 return NF_ACCEPT;
936 }
937
938flush:
939 flush_expectations(ct, false);
Patrick McHardy595a8ec2008-03-25 20:22:53 -0700940 return NF_ACCEPT;
941}
942
Patrick McHardy30f33e62008-03-25 20:22:20 -0700943static const struct sip_handler sip_handlers[] = {
944 SIP_HANDLER("INVITE", process_sdp, process_invite_response),
945 SIP_HANDLER("UPDATE", process_sdp, process_update_response),
Patrick McHardy595a8ec2008-03-25 20:22:53 -0700946 SIP_HANDLER("ACK", process_sdp, NULL),
947 SIP_HANDLER("PRACK", process_sdp, process_prack_response),
Patrick McHardy9467ee32008-03-25 20:24:04 -0700948 SIP_HANDLER("BYE", process_bye_request, NULL),
Patrick McHardy0f32a402008-03-25 20:25:13 -0700949 SIP_HANDLER("REGISTER", process_register_request, process_register_response),
Patrick McHardy30f33e62008-03-25 20:22:20 -0700950};
951
952static int process_sip_response(struct sk_buff *skb,
953 const char **dptr, unsigned int *datalen)
954{
955 static const struct sip_handler *handler;
956 enum ip_conntrack_info ctinfo;
957 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
958 unsigned int matchoff, matchlen;
959 unsigned int code, cseq, dataoff, i;
960
961 if (*datalen < strlen("SIP/2.0 200"))
962 return NF_ACCEPT;
963 code = simple_strtoul(*dptr + strlen("SIP/2.0 "), NULL, 10);
964 if (!code)
965 return NF_DROP;
966
967 if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ,
968 &matchoff, &matchlen) <= 0)
969 return NF_DROP;
970 cseq = simple_strtoul(*dptr + matchoff, NULL, 10);
971 if (!cseq)
972 return NF_DROP;
973 dataoff = matchoff + matchlen + 1;
974
975 for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) {
976 handler = &sip_handlers[i];
977 if (handler->response == NULL)
978 continue;
979 if (*datalen < dataoff + handler->len ||
980 strnicmp(*dptr + dataoff, handler->method, handler->len))
981 continue;
982 return handler->response(skb, dptr, datalen, cseq, code);
983 }
984 return NF_ACCEPT;
985}
986
987static int process_sip_request(struct sk_buff *skb,
988 const char **dptr, unsigned int *datalen)
989{
990 static const struct sip_handler *handler;
991 enum ip_conntrack_info ctinfo;
992 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
993 unsigned int matchoff, matchlen;
994 unsigned int cseq, i;
995
996 for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) {
997 handler = &sip_handlers[i];
998 if (handler->request == NULL)
999 continue;
1000 if (*datalen < handler->len ||
1001 strnicmp(*dptr, handler->method, handler->len))
1002 continue;
1003
1004 if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ,
1005 &matchoff, &matchlen) <= 0)
1006 return NF_DROP;
1007 cseq = simple_strtoul(*dptr + matchoff, NULL, 10);
1008 if (!cseq)
1009 return NF_DROP;
1010
1011 return handler->request(skb, dptr, datalen, cseq);
1012 }
1013 return NF_ACCEPT;
1014}
Patrick McHardy7d3dd042008-03-25 20:19:46 -07001015
Herbert Xu3db05fe2007-10-15 00:53:15 -07001016static int sip_help(struct sk_buff *skb,
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001017 unsigned int protoff,
1018 struct nf_conn *ct,
1019 enum ip_conntrack_info ctinfo)
1020{
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001021 unsigned int dataoff, datalen;
1022 const char *dptr;
Patrick McHardy33cb1e92008-03-25 20:22:37 -07001023 int ret;
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001024 typeof(nf_nat_sip_hook) nf_nat_sip;
1025
1026 /* No Data ? */
1027 dataoff = protoff + sizeof(struct udphdr);
Herbert Xu3db05fe2007-10-15 00:53:15 -07001028 if (dataoff >= skb->len)
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001029 return NF_ACCEPT;
1030
Herbert Xu3db05fe2007-10-15 00:53:15 -07001031 nf_ct_refresh(ct, skb, sip_timeout * HZ);
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001032
Herbert Xu3db05fe2007-10-15 00:53:15 -07001033 if (!skb_is_nonlinear(skb))
1034 dptr = skb->data + dataoff;
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001035 else {
Patrick McHardy0d537782007-07-07 22:39:38 -07001036 pr_debug("Copy of skbuff not supported yet.\n");
Patrick McHardy7d3dd042008-03-25 20:19:46 -07001037 return NF_ACCEPT;
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001038 }
1039
Herbert Xu3db05fe2007-10-15 00:53:15 -07001040 datalen = skb->len - dataoff;
Patrick McHardy779382e2008-03-25 20:17:36 -07001041 if (datalen < strlen("SIP/2.0 200"))
Patrick McHardy7d3dd042008-03-25 20:19:46 -07001042 return NF_ACCEPT;
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001043
Patrick McHardy30f33e62008-03-25 20:22:20 -07001044 if (strnicmp(dptr, "SIP/2.0 ", strlen("SIP/2.0 ")) != 0)
Patrick McHardy33cb1e92008-03-25 20:22:37 -07001045 ret = process_sip_request(skb, &dptr, &datalen);
Patrick McHardy30f33e62008-03-25 20:22:20 -07001046 else
Patrick McHardy33cb1e92008-03-25 20:22:37 -07001047 ret = process_sip_response(skb, &dptr, &datalen);
1048
1049 if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) {
1050 nf_nat_sip = rcu_dereference(nf_nat_sip_hook);
1051 if (nf_nat_sip && !nf_nat_sip(skb, &dptr, &datalen))
1052 ret = NF_DROP;
1053 }
1054
1055 return ret;
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001056}
1057
1058static struct nf_conntrack_helper sip[MAX_PORTS][2] __read_mostly;
1059static char sip_names[MAX_PORTS][2][sizeof("sip-65535")] __read_mostly;
1060
Patrick McHardy0f32a402008-03-25 20:25:13 -07001061static const struct nf_conntrack_expect_policy sip_exp_policy[SIP_EXPECT_MAX + 1] = {
1062 [SIP_EXPECT_SIGNALLING] = {
1063 .max_expected = 1,
1064 .timeout = 3 * 60,
1065 },
1066 [SIP_EXPECT_AUDIO] = {
1067 .max_expected = IP_CT_DIR_MAX,
1068 .timeout = 3 * 60,
1069 },
Patrick McHardy6002f262008-03-25 20:09:15 -07001070};
1071
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001072static void nf_conntrack_sip_fini(void)
1073{
1074 int i, j;
1075
1076 for (i = 0; i < ports_c; i++) {
1077 for (j = 0; j < 2; j++) {
1078 if (sip[i][j].me == NULL)
1079 continue;
1080 nf_conntrack_helper_unregister(&sip[i][j]);
1081 }
1082 }
1083}
1084
1085static int __init nf_conntrack_sip_init(void)
1086{
1087 int i, j, ret;
1088 char *tmpname;
1089
1090 if (ports_c == 0)
1091 ports[ports_c++] = SIP_PORT;
1092
1093 for (i = 0; i < ports_c; i++) {
1094 memset(&sip[i], 0, sizeof(sip[i]));
1095
1096 sip[i][0].tuple.src.l3num = AF_INET;
1097 sip[i][1].tuple.src.l3num = AF_INET6;
1098 for (j = 0; j < 2; j++) {
1099 sip[i][j].tuple.dst.protonum = IPPROTO_UDP;
1100 sip[i][j].tuple.src.u.udp.port = htons(ports[i]);
Patrick McHardy0f32a402008-03-25 20:25:13 -07001101 sip[i][j].expect_policy = sip_exp_policy;
1102 sip[i][j].expect_class_max = SIP_EXPECT_MAX;
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001103 sip[i][j].me = THIS_MODULE;
1104 sip[i][j].help = sip_help;
1105
1106 tmpname = &sip_names[i][j][0];
1107 if (ports[i] == SIP_PORT)
1108 sprintf(tmpname, "sip");
1109 else
1110 sprintf(tmpname, "sip-%u", i);
1111 sip[i][j].name = tmpname;
1112
Patrick McHardy0d537782007-07-07 22:39:38 -07001113 pr_debug("port #%u: %u\n", i, ports[i]);
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001114
1115 ret = nf_conntrack_helper_register(&sip[i][j]);
1116 if (ret) {
1117 printk("nf_ct_sip: failed to register helper "
1118 "for pf: %u port: %u\n",
1119 sip[i][j].tuple.src.l3num, ports[i]);
1120 nf_conntrack_sip_fini();
1121 return ret;
1122 }
1123 }
1124 }
1125 return 0;
1126}
1127
1128module_init(nf_conntrack_sip_init);
1129module_exit(nf_conntrack_sip_fini);