blob: 57de22c770a3bbf50d3b48c129b16c7c47e6c610 [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
Patrick McHardy4ab9e642008-03-25 20:26:08 -070063unsigned int (*nf_nat_sdp_addr_hook)(struct sk_buff *skb,
64 const char **dptr,
65 unsigned int dataoff,
66 unsigned int *datalen,
67 enum sdp_header_types type,
68 enum sdp_header_types term,
69 const union nf_inet_addr *addr)
70 __read_mostly;
71EXPORT_SYMBOL_GPL(nf_nat_sdp_addr_hook);
72
Patrick McHardyc7f485a2008-03-25 20:26:43 -070073unsigned int (*nf_nat_sdp_port_hook)(struct sk_buff *skb,
74 const char **dptr,
75 unsigned int *datalen,
76 unsigned int matchoff,
77 unsigned int matchlen,
78 u_int16_t port) __read_mostly;
79EXPORT_SYMBOL_GPL(nf_nat_sdp_port_hook);
80
Patrick McHardy4ab9e642008-03-25 20:26:08 -070081unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb,
82 const char **dptr,
83 unsigned int dataoff,
84 unsigned int *datalen,
85 const union nf_inet_addr *addr)
86 __read_mostly;
87EXPORT_SYMBOL_GPL(nf_nat_sdp_session_hook);
88
89unsigned int (*nf_nat_sdp_media_hook)(struct sk_buff *skb,
90 const char **dptr,
91 unsigned int *datalen,
92 struct nf_conntrack_expect *rtp_exp,
93 struct nf_conntrack_expect *rtcp_exp,
94 unsigned int mediaoff,
95 unsigned int medialen,
96 union nf_inet_addr *rtp_addr)
97 __read_mostly;
98EXPORT_SYMBOL_GPL(nf_nat_sdp_media_hook);
Patrick McHardy9fafcd72006-12-02 22:09:57 -080099
Patrick McHardyac367742008-03-25 20:18:40 -0700100static int string_len(const struct nf_conn *ct, const char *dptr,
101 const char *limit, int *shift)
102{
103 int len = 0;
104
105 while (dptr < limit && isalpha(*dptr)) {
106 dptr++;
107 len++;
108 }
109 return len;
110}
111
Jan Engelhardt13f7d632008-01-31 04:50:25 -0800112static int digits_len(const struct nf_conn *ct, const char *dptr,
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800113 const char *limit, int *shift)
114{
115 int len = 0;
Patrick McHardyb1ec4882008-03-25 20:10:11 -0700116 while (dptr < limit && isdigit(*dptr)) {
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800117 dptr++;
118 len++;
119 }
120 return len;
121}
122
Patrick McHardy0d0ab032008-03-25 20:26:24 -0700123/* get media type + port length */
124static int media_len(const struct nf_conn *ct, const char *dptr,
125 const char *limit, int *shift)
126{
127 int len = string_len(ct, dptr, limit, shift);
128
129 dptr += len;
130 if (dptr >= limit || *dptr != ' ')
131 return 0;
132 len++;
133 dptr++;
134
135 return len + digits_len(ct, dptr, limit, shift);
136}
137
Jan Engelhardt13f7d632008-01-31 04:50:25 -0800138static int parse_addr(const struct nf_conn *ct, const char *cp,
139 const char **endp, union nf_inet_addr *addr,
140 const char *limit)
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800141{
142 const char *end;
143 int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
144 int ret = 0;
145
146 switch (family) {
147 case AF_INET:
148 ret = in4_pton(cp, limit - cp, (u8 *)&addr->ip, -1, &end);
149 break;
150 case AF_INET6:
151 ret = in6_pton(cp, limit - cp, (u8 *)&addr->ip6, -1, &end);
152 break;
153 default:
154 BUG();
155 }
156
157 if (ret == 0 || end == cp)
158 return 0;
159 if (endp)
160 *endp = end;
161 return 1;
162}
163
164/* skip ip address. returns its length. */
Jan Engelhardt13f7d632008-01-31 04:50:25 -0800165static int epaddr_len(const struct nf_conn *ct, const char *dptr,
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800166 const char *limit, int *shift)
167{
Jan Engelhardt643a2c12007-12-17 22:43:50 -0800168 union nf_inet_addr addr;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800169 const char *aux = dptr;
170
171 if (!parse_addr(ct, dptr, &dptr, &addr, limit)) {
Patrick McHardy0d537782007-07-07 22:39:38 -0700172 pr_debug("ip: %s parse failed.!\n", dptr);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800173 return 0;
174 }
175
176 /* Port number */
177 if (*dptr == ':') {
178 dptr++;
179 dptr += digits_len(ct, dptr, limit, shift);
180 }
181 return dptr - aux;
182}
183
184/* get address length, skiping user info. */
Jan Engelhardt13f7d632008-01-31 04:50:25 -0800185static int skp_epaddr_len(const struct nf_conn *ct, const char *dptr,
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800186 const char *limit, int *shift)
187{
Patrick McHardyaa584ed2007-08-14 13:14:35 -0700188 const char *start = dptr;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800189 int s = *shift;
190
Lars Immisch7da5bfb2007-01-30 14:24:57 -0800191 /* Search for @, but stop at the end of the line.
192 * We are inside a sip: URI, so we don't need to worry about
193 * continuation lines. */
Patrick McHardyb1ec4882008-03-25 20:10:11 -0700194 while (dptr < limit &&
Lars Immisch7da5bfb2007-01-30 14:24:57 -0800195 *dptr != '@' && *dptr != '\r' && *dptr != '\n') {
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800196 (*shift)++;
Lars Immisch7da5bfb2007-01-30 14:24:57 -0800197 dptr++;
198 }
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800199
Patrick McHardyb1ec4882008-03-25 20:10:11 -0700200 if (dptr < limit && *dptr == '@') {
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800201 dptr++;
202 (*shift)++;
Patrick McHardyaa584ed2007-08-14 13:14:35 -0700203 } else {
204 dptr = start;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800205 *shift = s;
Patrick McHardyaa584ed2007-08-14 13:14:35 -0700206 }
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800207
208 return epaddr_len(ct, dptr, limit, shift);
209}
210
Patrick McHardyac367742008-03-25 20:18:40 -0700211/* Parse a SIP request line of the form:
212 *
213 * Request-Line = Method SP Request-URI SP SIP-Version CRLF
214 *
215 * and return the offset and length of the address contained in the Request-URI.
216 */
217int ct_sip_parse_request(const struct nf_conn *ct,
218 const char *dptr, unsigned int datalen,
Patrick McHardy624f8b72008-03-25 20:19:30 -0700219 unsigned int *matchoff, unsigned int *matchlen,
220 union nf_inet_addr *addr, __be16 *port)
Patrick McHardyac367742008-03-25 20:18:40 -0700221{
Patrick McHardy624f8b72008-03-25 20:19:30 -0700222 const char *start = dptr, *limit = dptr + datalen, *end;
Patrick McHardyac367742008-03-25 20:18:40 -0700223 unsigned int mlen;
Patrick McHardy624f8b72008-03-25 20:19:30 -0700224 unsigned int p;
Patrick McHardyac367742008-03-25 20:18:40 -0700225 int shift = 0;
226
227 /* Skip method and following whitespace */
228 mlen = string_len(ct, dptr, limit, NULL);
229 if (!mlen)
230 return 0;
231 dptr += mlen;
232 if (++dptr >= limit)
233 return 0;
234
235 /* Find SIP URI */
236 limit -= strlen("sip:");
237 for (; dptr < limit; dptr++) {
238 if (*dptr == '\r' || *dptr == '\n')
239 return -1;
240 if (strnicmp(dptr, "sip:", strlen("sip:")) == 0)
241 break;
242 }
Patrick McHardy624f8b72008-03-25 20:19:30 -0700243 if (!skp_epaddr_len(ct, dptr, limit, &shift))
Patrick McHardyac367742008-03-25 20:18:40 -0700244 return 0;
Patrick McHardy624f8b72008-03-25 20:19:30 -0700245 dptr += shift;
246
247 if (!parse_addr(ct, dptr, &end, addr, limit))
248 return -1;
249 if (end < limit && *end == ':') {
250 end++;
251 p = simple_strtoul(end, (char **)&end, 10);
252 if (p < 1024 || p > 65535)
253 return -1;
254 *port = htons(p);
255 } else
256 *port = htons(SIP_PORT);
257
258 if (end == dptr)
259 return 0;
260 *matchoff = dptr - start;
261 *matchlen = end - dptr;
Patrick McHardyac367742008-03-25 20:18:40 -0700262 return 1;
263}
264EXPORT_SYMBOL_GPL(ct_sip_parse_request);
265
Patrick McHardyea45f122008-03-25 20:18:57 -0700266/* SIP header parsing: SIP headers are located at the beginning of a line, but
267 * may span several lines, in which case the continuation lines begin with a
268 * whitespace character. RFC 2543 allows lines to be terminated with CR, LF or
269 * CRLF, RFC 3261 allows only CRLF, we support both.
270 *
271 * Headers are followed by (optionally) whitespace, a colon, again (optionally)
272 * whitespace and the values. Whitespace in this context means any amount of
273 * tabs, spaces and continuation lines, which are treated as a single whitespace
274 * character.
Patrick McHardy05e3ced2008-03-25 20:19:13 -0700275 *
276 * Some headers may appear multiple times. A comma seperated list of values is
277 * equivalent to multiple headers.
Patrick McHardyea45f122008-03-25 20:18:57 -0700278 */
279static const struct sip_header ct_sip_hdrs[] = {
Patrick McHardy30f33e62008-03-25 20:22:20 -0700280 [SIP_HDR_CSEQ] = SIP_HDR("CSeq", NULL, NULL, digits_len),
Patrick McHardyea45f122008-03-25 20:18:57 -0700281 [SIP_HDR_FROM] = SIP_HDR("From", "f", "sip:", skp_epaddr_len),
282 [SIP_HDR_TO] = SIP_HDR("To", "t", "sip:", skp_epaddr_len),
283 [SIP_HDR_CONTACT] = SIP_HDR("Contact", "m", "sip:", skp_epaddr_len),
284 [SIP_HDR_VIA] = SIP_HDR("Via", "v", "UDP ", epaddr_len),
Patrick McHardy0f32a402008-03-25 20:25:13 -0700285 [SIP_HDR_EXPIRES] = SIP_HDR("Expires", NULL, NULL, digits_len),
Patrick McHardyea45f122008-03-25 20:18:57 -0700286 [SIP_HDR_CONTENT_LENGTH] = SIP_HDR("Content-Length", "l", NULL, digits_len),
287};
288
289static const char *sip_follow_continuation(const char *dptr, const char *limit)
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800290{
Patrick McHardyea45f122008-03-25 20:18:57 -0700291 /* Walk past newline */
292 if (++dptr >= limit)
293 return NULL;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800294
Patrick McHardyea45f122008-03-25 20:18:57 -0700295 /* Skip '\n' in CR LF */
296 if (*(dptr - 1) == '\r' && *dptr == '\n') {
297 if (++dptr >= limit)
298 return NULL;
299 }
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800300
Patrick McHardyea45f122008-03-25 20:18:57 -0700301 /* Continuation line? */
302 if (*dptr != ' ' && *dptr != '\t')
303 return NULL;
304
305 /* skip leading whitespace */
306 for (; dptr < limit; dptr++) {
307 if (*dptr != ' ' && *dptr != '\t')
308 break;
309 }
310 return dptr;
311}
312
313static const char *sip_skip_whitespace(const char *dptr, const char *limit)
314{
315 for (; dptr < limit; dptr++) {
316 if (*dptr == ' ')
317 continue;
318 if (*dptr != '\r' && *dptr != '\n')
319 break;
320 dptr = sip_follow_continuation(dptr, limit);
321 if (dptr == NULL)
322 return NULL;
323 }
324 return dptr;
325}
326
327/* Search within a SIP header value, dealing with continuation lines */
328static const char *ct_sip_header_search(const char *dptr, const char *limit,
329 const char *needle, unsigned int len)
330{
331 for (limit -= len; dptr < limit; dptr++) {
332 if (*dptr == '\r' || *dptr == '\n') {
333 dptr = sip_follow_continuation(dptr, limit);
334 if (dptr == NULL)
335 break;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800336 continue;
337 }
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800338
Patrick McHardyea45f122008-03-25 20:18:57 -0700339 if (strnicmp(dptr, needle, len) == 0)
340 return dptr;
341 }
342 return NULL;
343}
344
345int ct_sip_get_header(const struct nf_conn *ct, const char *dptr,
346 unsigned int dataoff, unsigned int datalen,
347 enum sip_header_types type,
348 unsigned int *matchoff, unsigned int *matchlen)
349{
350 const struct sip_header *hdr = &ct_sip_hdrs[type];
351 const char *start = dptr, *limit = dptr + datalen;
352 int shift = 0;
353
354 for (dptr += dataoff; dptr < limit; dptr++) {
355 /* Find beginning of line */
356 if (*dptr != '\r' && *dptr != '\n')
357 continue;
358 if (++dptr >= limit)
359 break;
360 if (*(dptr - 1) == '\r' && *dptr == '\n') {
361 if (++dptr >= limit)
362 break;
363 }
364
365 /* Skip continuation lines */
366 if (*dptr == ' ' || *dptr == '\t')
367 continue;
368
369 /* Find header. Compact headers must be followed by a
370 * non-alphabetic character to avoid mismatches. */
371 if (limit - dptr >= hdr->len &&
372 strnicmp(dptr, hdr->name, hdr->len) == 0)
373 dptr += hdr->len;
374 else if (hdr->cname && limit - dptr >= hdr->clen + 1 &&
375 strnicmp(dptr, hdr->cname, hdr->clen) == 0 &&
376 !isalpha(*(dptr + hdr->clen + 1)))
377 dptr += hdr->clen;
378 else
379 continue;
380
381 /* Find and skip colon */
382 dptr = sip_skip_whitespace(dptr, limit);
383 if (dptr == NULL)
384 break;
385 if (*dptr != ':' || ++dptr >= limit)
386 break;
387
388 /* Skip whitespace after colon */
389 dptr = sip_skip_whitespace(dptr, limit);
390 if (dptr == NULL)
391 break;
392
393 *matchoff = dptr - start;
394 if (hdr->search) {
395 dptr = ct_sip_header_search(dptr, limit, hdr->search,
396 hdr->slen);
397 if (!dptr)
398 return -1;
399 dptr += hdr->slen;
400 }
401
402 *matchlen = hdr->match_len(ct, dptr, limit, &shift);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800403 if (!*matchlen)
404 return -1;
Patrick McHardyea45f122008-03-25 20:18:57 -0700405 *matchoff = dptr - start + shift;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800406 return 1;
407 }
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800408 return 0;
409}
Patrick McHardyea45f122008-03-25 20:18:57 -0700410EXPORT_SYMBOL_GPL(ct_sip_get_header);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800411
Patrick McHardy05e3ced2008-03-25 20:19:13 -0700412/* Get next header field in a list of comma seperated values */
413static int ct_sip_next_header(const struct nf_conn *ct, const char *dptr,
414 unsigned int dataoff, unsigned int datalen,
415 enum sip_header_types type,
416 unsigned int *matchoff, unsigned int *matchlen)
417{
418 const struct sip_header *hdr = &ct_sip_hdrs[type];
419 const char *start = dptr, *limit = dptr + datalen;
420 int shift = 0;
421
422 dptr += dataoff;
423
424 dptr = ct_sip_header_search(dptr, limit, ",", strlen(","));
425 if (!dptr)
426 return 0;
427
428 dptr = ct_sip_header_search(dptr, limit, hdr->search, hdr->slen);
429 if (!dptr)
430 return 0;
431 dptr += hdr->slen;
432
433 *matchoff = dptr - start;
434 *matchlen = hdr->match_len(ct, dptr, limit, &shift);
435 if (!*matchlen)
436 return -1;
437 *matchoff += shift;
438 return 1;
439}
440
441/* Walk through headers until a parsable one is found or no header of the
442 * given type is left. */
443static int ct_sip_walk_headers(const struct nf_conn *ct, const char *dptr,
444 unsigned int dataoff, unsigned int datalen,
445 enum sip_header_types type, int *in_header,
446 unsigned int *matchoff, unsigned int *matchlen)
447{
448 int ret;
449
450 if (in_header && *in_header) {
451 while (1) {
452 ret = ct_sip_next_header(ct, dptr, dataoff, datalen,
453 type, matchoff, matchlen);
454 if (ret > 0)
455 return ret;
456 if (ret == 0)
457 break;
458 dataoff += *matchoff;
459 }
460 *in_header = 0;
461 }
462
463 while (1) {
464 ret = ct_sip_get_header(ct, dptr, dataoff, datalen,
465 type, matchoff, matchlen);
466 if (ret > 0)
467 break;
468 if (ret == 0)
469 return ret;
470 dataoff += *matchoff;
471 }
472
473 if (in_header)
474 *in_header = 1;
475 return 1;
476}
477
478/* Locate a SIP header, parse the URI and return the offset and length of
479 * the address as well as the address and port themselves. A stream of
480 * headers can be parsed by handing in a non-NULL datalen and in_header
481 * pointer.
482 */
483int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr,
484 unsigned int *dataoff, unsigned int datalen,
485 enum sip_header_types type, int *in_header,
486 unsigned int *matchoff, unsigned int *matchlen,
487 union nf_inet_addr *addr, __be16 *port)
488{
489 const char *c, *limit = dptr + datalen;
490 unsigned int p;
491 int ret;
492
493 ret = ct_sip_walk_headers(ct, dptr, dataoff ? *dataoff : 0, datalen,
494 type, in_header, matchoff, matchlen);
495 WARN_ON(ret < 0);
496 if (ret == 0)
497 return ret;
498
499 if (!parse_addr(ct, dptr + *matchoff, &c, addr, limit))
500 return -1;
501 if (*c == ':') {
502 c++;
503 p = simple_strtoul(c, (char **)&c, 10);
504 if (p < 1024 || p > 65535)
505 return -1;
506 *port = htons(p);
507 } else
508 *port = htons(SIP_PORT);
509
510 if (dataoff)
511 *dataoff = c - dptr;
512 return 1;
513}
514EXPORT_SYMBOL_GPL(ct_sip_parse_header_uri);
515
Patrick McHardy2bbb2112008-03-25 20:24:24 -0700516/* Parse address from header parameter and return address, offset and length */
517int ct_sip_parse_address_param(const struct nf_conn *ct, const char *dptr,
518 unsigned int dataoff, unsigned int datalen,
519 const char *name,
520 unsigned int *matchoff, unsigned int *matchlen,
521 union nf_inet_addr *addr)
522{
523 const char *limit = dptr + datalen;
524 const char *start, *end;
525
526 limit = ct_sip_header_search(dptr + dataoff, limit, ",", strlen(","));
527 if (!limit)
528 limit = dptr + datalen;
529
530 start = ct_sip_header_search(dptr + dataoff, limit, name, strlen(name));
531 if (!start)
532 return 0;
533
534 start += strlen(name);
535 if (!parse_addr(ct, start, &end, addr, limit))
536 return 0;
537 *matchoff = start - dptr;
538 *matchlen = end - start;
539 return 1;
540}
541EXPORT_SYMBOL_GPL(ct_sip_parse_address_param);
542
543/* Parse numerical header parameter and return value, offset and length */
544int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr,
545 unsigned int dataoff, unsigned int datalen,
546 const char *name,
547 unsigned int *matchoff, unsigned int *matchlen,
548 unsigned int *val)
549{
550 const char *limit = dptr + datalen;
551 const char *start;
552 char *end;
553
554 limit = ct_sip_header_search(dptr + dataoff, limit, ",", strlen(","));
555 if (!limit)
556 limit = dptr + datalen;
557
558 start = ct_sip_header_search(dptr + dataoff, limit, name, strlen(name));
559 if (!start)
560 return 0;
561
562 start += strlen(name);
563 *val = simple_strtoul(start, &end, 0);
564 if (start == end)
565 return 0;
566 if (matchoff && matchlen) {
567 *matchoff = start - dptr;
568 *matchlen = end - start;
569 }
570 return 1;
571}
572EXPORT_SYMBOL_GPL(ct_sip_parse_numerical_param);
573
Patrick McHardy3e9b4600b2008-03-25 20:17:55 -0700574/* SDP header parsing: a SDP session description contains an ordered set of
575 * headers, starting with a section containing general session parameters,
576 * optionally followed by multiple media descriptions.
577 *
578 * SDP headers always start at the beginning of a line. According to RFC 2327:
579 * "The sequence CRLF (0x0d0a) is used to end a record, although parsers should
580 * be tolerant and also accept records terminated with a single newline
581 * character". We handle both cases.
582 */
583static const struct sip_header ct_sdp_hdrs[] = {
584 [SDP_HDR_VERSION] = SDP_HDR("v=", NULL, digits_len),
585 [SDP_HDR_OWNER_IP4] = SDP_HDR("o=", "IN IP4 ", epaddr_len),
586 [SDP_HDR_CONNECTION_IP4] = SDP_HDR("c=", "IN IP4 ", epaddr_len),
587 [SDP_HDR_OWNER_IP6] = SDP_HDR("o=", "IN IP6 ", epaddr_len),
588 [SDP_HDR_CONNECTION_IP6] = SDP_HDR("c=", "IN IP6 ", epaddr_len),
Patrick McHardy0d0ab032008-03-25 20:26:24 -0700589 [SDP_HDR_MEDIA] = SDP_HDR("m=", NULL, media_len),
Patrick McHardy3e9b4600b2008-03-25 20:17:55 -0700590};
591
592/* Linear string search within SDP header values */
593static const char *ct_sdp_header_search(const char *dptr, const char *limit,
594 const char *needle, unsigned int len)
595{
596 for (limit -= len; dptr < limit; dptr++) {
597 if (*dptr == '\r' || *dptr == '\n')
598 break;
599 if (strncmp(dptr, needle, len) == 0)
600 return dptr;
601 }
602 return NULL;
603}
604
605/* Locate a SDP header (optionally a substring within the header value),
606 * optionally stopping at the first occurence of the term header, parse
607 * it and return the offset and length of the data we're interested in.
608 */
609int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr,
610 unsigned int dataoff, unsigned int datalen,
611 enum sdp_header_types type,
612 enum sdp_header_types term,
613 unsigned int *matchoff, unsigned int *matchlen)
614{
615 const struct sip_header *hdr = &ct_sdp_hdrs[type];
616 const struct sip_header *thdr = &ct_sdp_hdrs[term];
617 const char *start = dptr, *limit = dptr + datalen;
618 int shift = 0;
619
620 for (dptr += dataoff; dptr < limit; dptr++) {
621 /* Find beginning of line */
622 if (*dptr != '\r' && *dptr != '\n')
623 continue;
624 if (++dptr >= limit)
625 break;
626 if (*(dptr - 1) == '\r' && *dptr == '\n') {
627 if (++dptr >= limit)
628 break;
629 }
630
631 if (term != SDP_HDR_UNSPEC &&
632 limit - dptr >= thdr->len &&
633 strnicmp(dptr, thdr->name, thdr->len) == 0)
634 break;
635 else if (limit - dptr >= hdr->len &&
636 strnicmp(dptr, hdr->name, hdr->len) == 0)
637 dptr += hdr->len;
638 else
639 continue;
640
641 *matchoff = dptr - start;
642 if (hdr->search) {
643 dptr = ct_sdp_header_search(dptr, limit, hdr->search,
644 hdr->slen);
645 if (!dptr)
646 return -1;
647 dptr += hdr->slen;
648 }
649
650 *matchlen = hdr->match_len(ct, dptr, limit, &shift);
651 if (!*matchlen)
652 return -1;
653 *matchoff = dptr - start + shift;
654 return 1;
655 }
656 return 0;
657}
658EXPORT_SYMBOL_GPL(ct_sip_get_sdp_header);
659
Patrick McHardy4ab9e642008-03-25 20:26:08 -0700660static int ct_sip_parse_sdp_addr(const struct nf_conn *ct, const char *dptr,
661 unsigned int dataoff, unsigned int datalen,
662 enum sdp_header_types type,
663 enum sdp_header_types term,
664 unsigned int *matchoff, unsigned int *matchlen,
665 union nf_inet_addr *addr)
666{
667 int ret;
668
669 ret = ct_sip_get_sdp_header(ct, dptr, dataoff, datalen, type, term,
670 matchoff, matchlen);
671 if (ret <= 0)
672 return ret;
673
674 if (!parse_addr(ct, dptr + *matchoff, NULL, addr,
675 dptr + *matchoff + *matchlen))
676 return -1;
677 return 1;
678}
679
Patrick McHardy0f32a402008-03-25 20:25:13 -0700680static int refresh_signalling_expectation(struct nf_conn *ct,
681 union nf_inet_addr *addr,
682 __be16 port,
683 unsigned int expires)
684{
685 struct nf_conn_help *help = nfct_help(ct);
686 struct nf_conntrack_expect *exp;
687 struct hlist_node *n, *next;
688 int found = 0;
689
690 spin_lock_bh(&nf_conntrack_lock);
691 hlist_for_each_entry_safe(exp, n, next, &help->expectations, lnode) {
692 if (exp->class != SIP_EXPECT_SIGNALLING ||
693 !nf_inet_addr_cmp(&exp->tuple.dst.u3, addr) ||
694 exp->tuple.dst.u.udp.port != port)
695 continue;
696 if (!del_timer(&exp->timeout))
697 continue;
698 exp->flags &= ~NF_CT_EXPECT_INACTIVE;
699 exp->timeout.expires = jiffies + expires * HZ;
700 add_timer(&exp->timeout);
701 found = 1;
702 break;
703 }
704 spin_unlock_bh(&nf_conntrack_lock);
705 return found;
706}
707
708static void flush_expectations(struct nf_conn *ct, bool media)
Patrick McHardy9467ee32008-03-25 20:24:04 -0700709{
710 struct nf_conn_help *help = nfct_help(ct);
711 struct nf_conntrack_expect *exp;
712 struct hlist_node *n, *next;
713
714 spin_lock_bh(&nf_conntrack_lock);
715 hlist_for_each_entry_safe(exp, n, next, &help->expectations, lnode) {
Patrick McHardy0f32a402008-03-25 20:25:13 -0700716 if ((exp->class != SIP_EXPECT_SIGNALLING) ^ media)
717 continue;
Patrick McHardy9467ee32008-03-25 20:24:04 -0700718 if (!del_timer(&exp->timeout))
719 continue;
720 nf_ct_unlink_expect(exp);
721 nf_ct_expect_put(exp);
Patrick McHardy0f32a402008-03-25 20:25:13 -0700722 if (!media)
723 break;
Patrick McHardy9467ee32008-03-25 20:24:04 -0700724 }
725 spin_unlock_bh(&nf_conntrack_lock);
726}
727
Patrick McHardya9c1d352008-03-25 20:25:49 -0700728static int set_expected_rtp_rtcp(struct sk_buff *skb,
729 const char **dptr, unsigned int *datalen,
Patrick McHardy4ab9e642008-03-25 20:26:08 -0700730 union nf_inet_addr *daddr, __be16 port,
Patrick McHardy0d0ab032008-03-25 20:26:24 -0700731 enum sip_expectation_classes class,
Patrick McHardy4ab9e642008-03-25 20:26:08 -0700732 unsigned int mediaoff, unsigned int medialen)
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800733{
Patrick McHardya9c1d352008-03-25 20:25:49 -0700734 struct nf_conntrack_expect *exp, *rtp_exp, *rtcp_exp;
Patrick McHardy212440a2008-03-25 20:17:13 -0700735 enum ip_conntrack_info ctinfo;
736 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800737 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
Patrick McHardyd901a932008-03-25 20:25:32 -0700738 union nf_inet_addr *saddr;
739 struct nf_conntrack_tuple tuple;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800740 int family = ct->tuplehash[!dir].tuple.src.l3num;
Patrick McHardyc7f485a2008-03-25 20:26:43 -0700741 int direct_rtp = 0, skip_expect = 0, ret = NF_DROP;
Patrick McHardya9c1d352008-03-25 20:25:49 -0700742 u_int16_t base_port;
743 __be16 rtp_port, rtcp_port;
Patrick McHardyc7f485a2008-03-25 20:26:43 -0700744 typeof(nf_nat_sdp_port_hook) nf_nat_sdp_port;
Patrick McHardy4ab9e642008-03-25 20:26:08 -0700745 typeof(nf_nat_sdp_media_hook) nf_nat_sdp_media;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800746
Patrick McHardyd901a932008-03-25 20:25:32 -0700747 saddr = NULL;
748 if (sip_direct_media) {
749 if (!nf_inet_addr_cmp(daddr, &ct->tuplehash[dir].tuple.src.u3))
750 return NF_ACCEPT;
751 saddr = &ct->tuplehash[!dir].tuple.src.u3;
752 }
753
754 /* We need to check whether the registration exists before attempting
755 * to register it since we can see the same media description multiple
756 * times on different connections in case multiple endpoints receive
757 * the same call.
Patrick McHardyc7f485a2008-03-25 20:26:43 -0700758 *
759 * RTP optimization: if we find a matching media channel expectation
760 * and both the expectation and this connection are SNATed, we assume
761 * both sides can reach each other directly and use the final
762 * destination address from the expectation. We still need to keep
763 * the NATed expectations for media that might arrive from the
764 * outside, and additionally need to expect the direct RTP stream
765 * in case it passes through us even without NAT.
Patrick McHardyd901a932008-03-25 20:25:32 -0700766 */
767 memset(&tuple, 0, sizeof(tuple));
768 if (saddr)
769 tuple.src.u3 = *saddr;
770 tuple.src.l3num = family;
771 tuple.dst.protonum = IPPROTO_UDP;
772 tuple.dst.u3 = *daddr;
773 tuple.dst.u.udp.port = port;
774
775 rcu_read_lock();
Patrick McHardyc7f485a2008-03-25 20:26:43 -0700776 do {
777 exp = __nf_ct_expect_find(&tuple);
Patrick McHardyd901a932008-03-25 20:25:32 -0700778
Patrick McHardyc7f485a2008-03-25 20:26:43 -0700779 if (!exp || exp->master == ct ||
780 nfct_help(exp->master)->helper != nfct_help(ct)->helper ||
781 exp->class != class)
782 break;
783
784 if (exp->tuple.src.l3num == AF_INET && !direct_rtp &&
785 (exp->saved_ip != exp->tuple.dst.u3.ip ||
786 exp->saved_proto.udp.port != exp->tuple.dst.u.udp.port) &&
787 ct->status & IPS_NAT_MASK) {
788 daddr->ip = exp->saved_ip;
789 tuple.dst.u3.ip = exp->saved_ip;
790 tuple.dst.u.udp.port = exp->saved_proto.udp.port;
791 direct_rtp = 1;
792 } else
793 skip_expect = 1;
794 } while (!skip_expect);
795 rcu_read_unlock();
Patrick McHardyd901a932008-03-25 20:25:32 -0700796
Patrick McHardya9c1d352008-03-25 20:25:49 -0700797 base_port = ntohs(tuple.dst.u.udp.port) & ~1;
798 rtp_port = htons(base_port);
799 rtcp_port = htons(base_port + 1);
800
Patrick McHardyc7f485a2008-03-25 20:26:43 -0700801 if (direct_rtp) {
802 nf_nat_sdp_port = rcu_dereference(nf_nat_sdp_port_hook);
803 if (nf_nat_sdp_port &&
804 !nf_nat_sdp_port(skb, dptr, datalen,
805 mediaoff, medialen, ntohs(rtp_port)))
806 goto err1;
807 }
808
809 if (skip_expect)
810 return NF_ACCEPT;
811
Patrick McHardya9c1d352008-03-25 20:25:49 -0700812 rtp_exp = nf_ct_expect_alloc(ct);
813 if (rtp_exp == NULL)
814 goto err1;
Patrick McHardy0d0ab032008-03-25 20:26:24 -0700815 nf_ct_expect_init(rtp_exp, class, family, saddr, daddr,
Patrick McHardya9c1d352008-03-25 20:25:49 -0700816 IPPROTO_UDP, NULL, &rtp_port);
817
818 rtcp_exp = nf_ct_expect_alloc(ct);
819 if (rtcp_exp == NULL)
820 goto err2;
Patrick McHardy0d0ab032008-03-25 20:26:24 -0700821 nf_ct_expect_init(rtcp_exp, class, family, saddr, daddr,
Patrick McHardya9c1d352008-03-25 20:25:49 -0700822 IPPROTO_UDP, NULL, &rtcp_port);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800823
Patrick McHardy4ab9e642008-03-25 20:26:08 -0700824 nf_nat_sdp_media = rcu_dereference(nf_nat_sdp_media_hook);
Patrick McHardyc7f485a2008-03-25 20:26:43 -0700825 if (nf_nat_sdp_media && ct->status & IPS_NAT_MASK && !direct_rtp)
Patrick McHardy4ab9e642008-03-25 20:26:08 -0700826 ret = nf_nat_sdp_media(skb, dptr, datalen, rtp_exp, rtcp_exp,
827 mediaoff, medialen, daddr);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800828 else {
Patrick McHardya9c1d352008-03-25 20:25:49 -0700829 if (nf_ct_expect_related(rtp_exp) == 0) {
830 if (nf_ct_expect_related(rtcp_exp) != 0)
831 nf_ct_unexpect_related(rtp_exp);
832 else
833 ret = NF_ACCEPT;
834 }
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800835 }
Patrick McHardya9c1d352008-03-25 20:25:49 -0700836 nf_ct_expect_put(rtcp_exp);
837err2:
838 nf_ct_expect_put(rtp_exp);
839err1:
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800840 return ret;
841}
842
Patrick McHardy0d0ab032008-03-25 20:26:24 -0700843static const struct sdp_media_type sdp_media_types[] = {
844 SDP_MEDIA_TYPE("audio ", SIP_EXPECT_AUDIO),
845 SDP_MEDIA_TYPE("video ", SIP_EXPECT_VIDEO),
846};
847
848static const struct sdp_media_type *sdp_media_type(const char *dptr,
849 unsigned int matchoff,
850 unsigned int matchlen)
851{
852 const struct sdp_media_type *t;
853 unsigned int i;
854
855 for (i = 0; i < ARRAY_SIZE(sdp_media_types); i++) {
856 t = &sdp_media_types[i];
857 if (matchlen < t->len ||
858 strncmp(dptr + matchoff, t->name, t->len))
859 continue;
860 return t;
861 }
862 return NULL;
863}
864
Patrick McHardy7d3dd042008-03-25 20:19:46 -0700865static int process_sdp(struct sk_buff *skb,
Patrick McHardy30f33e62008-03-25 20:22:20 -0700866 const char **dptr, unsigned int *datalen,
867 unsigned int cseq)
Patrick McHardy7d3dd042008-03-25 20:19:46 -0700868{
869 enum ip_conntrack_info ctinfo;
870 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
871 int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
872 unsigned int matchoff, matchlen;
Patrick McHardy4ab9e642008-03-25 20:26:08 -0700873 unsigned int mediaoff, medialen;
874 unsigned int sdpoff;
875 unsigned int caddr_len, maddr_len;
Patrick McHardy0d0ab032008-03-25 20:26:24 -0700876 unsigned int i;
Patrick McHardy4ab9e642008-03-25 20:26:08 -0700877 union nf_inet_addr caddr, maddr, rtp_addr;
Patrick McHardy7d3dd042008-03-25 20:19:46 -0700878 unsigned int port;
Patrick McHardy4ab9e642008-03-25 20:26:08 -0700879 enum sdp_header_types c_hdr;
Patrick McHardy0d0ab032008-03-25 20:26:24 -0700880 const struct sdp_media_type *t;
881 int ret = NF_ACCEPT;
Patrick McHardy4ab9e642008-03-25 20:26:08 -0700882 typeof(nf_nat_sdp_addr_hook) nf_nat_sdp_addr;
883 typeof(nf_nat_sdp_session_hook) nf_nat_sdp_session;
Patrick McHardy7d3dd042008-03-25 20:19:46 -0700884
Patrick McHardy0d0ab032008-03-25 20:26:24 -0700885 nf_nat_sdp_addr = rcu_dereference(nf_nat_sdp_addr_hook);
Patrick McHardy4ab9e642008-03-25 20:26:08 -0700886 c_hdr = family == AF_INET ? SDP_HDR_CONNECTION_IP4 :
887 SDP_HDR_CONNECTION_IP6;
Patrick McHardy7d3dd042008-03-25 20:19:46 -0700888
Patrick McHardy4ab9e642008-03-25 20:26:08 -0700889 /* Find beginning of session description */
Patrick McHardy7d3dd042008-03-25 20:19:46 -0700890 if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen,
Patrick McHardy4ab9e642008-03-25 20:26:08 -0700891 SDP_HDR_VERSION, SDP_HDR_UNSPEC,
Patrick McHardy7d3dd042008-03-25 20:19:46 -0700892 &matchoff, &matchlen) <= 0)
893 return NF_ACCEPT;
Patrick McHardy4ab9e642008-03-25 20:26:08 -0700894 sdpoff = matchoff;
Patrick McHardy7d3dd042008-03-25 20:19:46 -0700895
Patrick McHardy4ab9e642008-03-25 20:26:08 -0700896 /* The connection information is contained in the session description
897 * and/or once per media description. The first media description marks
898 * the end of the session description. */
899 caddr_len = 0;
900 if (ct_sip_parse_sdp_addr(ct, *dptr, sdpoff, *datalen,
901 c_hdr, SDP_HDR_MEDIA,
902 &matchoff, &matchlen, &caddr) > 0)
903 caddr_len = matchlen;
Patrick McHardy7d3dd042008-03-25 20:19:46 -0700904
Patrick McHardy0d0ab032008-03-25 20:26:24 -0700905 mediaoff = sdpoff;
906 for (i = 0; i < ARRAY_SIZE(sdp_media_types); ) {
907 if (ct_sip_get_sdp_header(ct, *dptr, mediaoff, *datalen,
908 SDP_HDR_MEDIA, SDP_HDR_UNSPEC,
909 &mediaoff, &medialen) <= 0)
910 break;
Patrick McHardy7d3dd042008-03-25 20:19:46 -0700911
Patrick McHardy0d0ab032008-03-25 20:26:24 -0700912 /* Get media type and port number. A media port value of zero
913 * indicates an inactive stream. */
914 t = sdp_media_type(*dptr, mediaoff, medialen);
915 if (!t) {
916 mediaoff += medialen;
917 continue;
918 }
919 mediaoff += t->len;
920 medialen -= t->len;
Patrick McHardy7d3dd042008-03-25 20:19:46 -0700921
Patrick McHardy0d0ab032008-03-25 20:26:24 -0700922 port = simple_strtoul(*dptr + mediaoff, NULL, 10);
923 if (port == 0)
924 continue;
925 if (port < 1024 || port > 65535)
926 return NF_DROP;
Patrick McHardy4ab9e642008-03-25 20:26:08 -0700927
Patrick McHardy0d0ab032008-03-25 20:26:24 -0700928 /* The media description overrides the session description. */
929 maddr_len = 0;
930 if (ct_sip_parse_sdp_addr(ct, *dptr, mediaoff, *datalen,
931 c_hdr, SDP_HDR_MEDIA,
932 &matchoff, &matchlen, &maddr) > 0) {
933 maddr_len = matchlen;
934 memcpy(&rtp_addr, &maddr, sizeof(rtp_addr));
935 } else if (caddr_len)
936 memcpy(&rtp_addr, &caddr, sizeof(rtp_addr));
937 else
938 return NF_DROP;
Patrick McHardy4ab9e642008-03-25 20:26:08 -0700939
Patrick McHardy0d0ab032008-03-25 20:26:24 -0700940 ret = set_expected_rtp_rtcp(skb, dptr, datalen,
941 &rtp_addr, htons(port), t->class,
942 mediaoff, medialen);
943 if (ret != NF_ACCEPT)
944 return ret;
945
946 /* Update media connection address if present */
947 if (maddr_len && nf_nat_sdp_addr && ct->status & IPS_NAT_MASK) {
Patrick McHardy4ab9e642008-03-25 20:26:08 -0700948 ret = nf_nat_sdp_addr(skb, dptr, mediaoff, datalen,
949 c_hdr, SDP_HDR_MEDIA, &rtp_addr);
950 if (ret != NF_ACCEPT)
951 return ret;
952 }
Patrick McHardy0d0ab032008-03-25 20:26:24 -0700953 i++;
Patrick McHardy4ab9e642008-03-25 20:26:08 -0700954 }
955
956 /* Update session connection and owner addresses */
957 nf_nat_sdp_session = rcu_dereference(nf_nat_sdp_session_hook);
958 if (nf_nat_sdp_session && ct->status & IPS_NAT_MASK)
959 ret = nf_nat_sdp_session(skb, dptr, sdpoff, datalen, &rtp_addr);
960
961 return ret;
Patrick McHardy7d3dd042008-03-25 20:19:46 -0700962}
Patrick McHardy30f33e62008-03-25 20:22:20 -0700963static int process_invite_response(struct sk_buff *skb,
964 const char **dptr, unsigned int *datalen,
965 unsigned int cseq, unsigned int code)
966{
Patrick McHardy9467ee32008-03-25 20:24:04 -0700967 enum ip_conntrack_info ctinfo;
968 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
969
Patrick McHardy30f33e62008-03-25 20:22:20 -0700970 if ((code >= 100 && code <= 199) ||
971 (code >= 200 && code <= 299))
972 return process_sdp(skb, dptr, datalen, cseq);
Patrick McHardy9467ee32008-03-25 20:24:04 -0700973 else {
Patrick McHardy0f32a402008-03-25 20:25:13 -0700974 flush_expectations(ct, true);
Patrick McHardy9467ee32008-03-25 20:24:04 -0700975 return NF_ACCEPT;
976 }
Patrick McHardy30f33e62008-03-25 20:22:20 -0700977}
978
979static int process_update_response(struct sk_buff *skb,
980 const char **dptr, unsigned int *datalen,
981 unsigned int cseq, unsigned int code)
982{
Patrick McHardy9467ee32008-03-25 20:24:04 -0700983 enum ip_conntrack_info ctinfo;
984 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
985
Patrick McHardy30f33e62008-03-25 20:22:20 -0700986 if ((code >= 100 && code <= 199) ||
987 (code >= 200 && code <= 299))
988 return process_sdp(skb, dptr, datalen, cseq);
Patrick McHardy9467ee32008-03-25 20:24:04 -0700989 else {
Patrick McHardy0f32a402008-03-25 20:25:13 -0700990 flush_expectations(ct, true);
Patrick McHardy9467ee32008-03-25 20:24:04 -0700991 return NF_ACCEPT;
992 }
Patrick McHardy30f33e62008-03-25 20:22:20 -0700993}
994
Patrick McHardy595a8ec2008-03-25 20:22:53 -0700995static int process_prack_response(struct sk_buff *skb,
996 const char **dptr, unsigned int *datalen,
997 unsigned int cseq, unsigned int code)
998{
Patrick McHardy9467ee32008-03-25 20:24:04 -0700999 enum ip_conntrack_info ctinfo;
1000 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
1001
Patrick McHardy595a8ec2008-03-25 20:22:53 -07001002 if ((code >= 100 && code <= 199) ||
1003 (code >= 200 && code <= 299))
1004 return process_sdp(skb, dptr, datalen, cseq);
Patrick McHardy9467ee32008-03-25 20:24:04 -07001005 else {
Patrick McHardy0f32a402008-03-25 20:25:13 -07001006 flush_expectations(ct, true);
Patrick McHardy9467ee32008-03-25 20:24:04 -07001007 return NF_ACCEPT;
1008 }
1009}
Patrick McHardy595a8ec2008-03-25 20:22:53 -07001010
Patrick McHardy9467ee32008-03-25 20:24:04 -07001011static int process_bye_request(struct sk_buff *skb,
1012 const char **dptr, unsigned int *datalen,
1013 unsigned int cseq)
1014{
1015 enum ip_conntrack_info ctinfo;
1016 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
1017
Patrick McHardy0f32a402008-03-25 20:25:13 -07001018 flush_expectations(ct, true);
1019 return NF_ACCEPT;
1020}
1021
1022/* Parse a REGISTER request and create a permanent expectation for incoming
1023 * signalling connections. The expectation is marked inactive and is activated
1024 * when receiving a response indicating success from the registrar.
1025 */
1026static int process_register_request(struct sk_buff *skb,
1027 const char **dptr, unsigned int *datalen,
1028 unsigned int cseq)
1029{
1030 enum ip_conntrack_info ctinfo;
1031 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
1032 struct nf_conn_help *help = nfct_help(ct);
1033 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
1034 int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
1035 unsigned int matchoff, matchlen;
1036 struct nf_conntrack_expect *exp;
1037 union nf_inet_addr *saddr, daddr;
1038 __be16 port;
1039 unsigned int expires = 0;
1040 int ret;
1041 typeof(nf_nat_sip_expect_hook) nf_nat_sip_expect;
1042
1043 /* Expected connections can not register again. */
1044 if (ct->status & IPS_EXPECTED)
1045 return NF_ACCEPT;
1046
1047 /* We must check the expiration time: a value of zero signals the
1048 * registrar to release the binding. We'll remove our expectation
1049 * when receiving the new bindings in the response, but we don't
1050 * want to create new ones.
1051 *
1052 * The expiration time may be contained in Expires: header, the
1053 * Contact: header parameters or the URI parameters.
1054 */
1055 if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_EXPIRES,
1056 &matchoff, &matchlen) > 0)
1057 expires = simple_strtoul(*dptr + matchoff, NULL, 10);
1058
1059 ret = ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen,
1060 SIP_HDR_CONTACT, NULL,
1061 &matchoff, &matchlen, &daddr, &port);
1062 if (ret < 0)
1063 return NF_DROP;
1064 else if (ret == 0)
1065 return NF_ACCEPT;
1066
1067 /* We don't support third-party registrations */
1068 if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, &daddr))
1069 return NF_ACCEPT;
1070
1071 if (ct_sip_parse_numerical_param(ct, *dptr,
1072 matchoff + matchlen, *datalen,
1073 "expires=", NULL, NULL, &expires) < 0)
1074 return NF_DROP;
1075
1076 if (expires == 0) {
1077 ret = NF_ACCEPT;
1078 goto store_cseq;
1079 }
1080
1081 exp = nf_ct_expect_alloc(ct);
1082 if (!exp)
1083 return NF_DROP;
1084
1085 saddr = NULL;
1086 if (sip_direct_signalling)
1087 saddr = &ct->tuplehash[!dir].tuple.src.u3;
1088
1089 nf_ct_expect_init(exp, SIP_EXPECT_SIGNALLING, family, saddr, &daddr,
1090 IPPROTO_UDP, NULL, &port);
1091 exp->timeout.expires = sip_timeout * HZ;
1092 exp->helper = nfct_help(ct)->helper;
1093 exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE;
1094
1095 nf_nat_sip_expect = rcu_dereference(nf_nat_sip_expect_hook);
1096 if (nf_nat_sip_expect && ct->status & IPS_NAT_MASK)
1097 ret = nf_nat_sip_expect(skb, dptr, datalen, exp,
1098 matchoff, matchlen);
1099 else {
1100 if (nf_ct_expect_related(exp) != 0)
1101 ret = NF_DROP;
1102 else
1103 ret = NF_ACCEPT;
1104 }
1105 nf_ct_expect_put(exp);
1106
1107store_cseq:
1108 if (ret == NF_ACCEPT)
1109 help->help.ct_sip_info.register_cseq = cseq;
1110 return ret;
1111}
1112
1113static int process_register_response(struct sk_buff *skb,
1114 const char **dptr, unsigned int *datalen,
1115 unsigned int cseq, unsigned int code)
1116{
1117 enum ip_conntrack_info ctinfo;
1118 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
1119 struct nf_conn_help *help = nfct_help(ct);
1120 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
1121 union nf_inet_addr addr;
1122 __be16 port;
1123 unsigned int matchoff, matchlen, dataoff = 0;
1124 unsigned int expires = 0;
1125 int in_contact = 0, ret;
1126
1127 /* According to RFC 3261, "UAs MUST NOT send a new registration until
1128 * they have received a final response from the registrar for the
1129 * previous one or the previous REGISTER request has timed out".
1130 *
1131 * However, some servers fail to detect retransmissions and send late
1132 * responses, so we store the sequence number of the last valid
1133 * request and compare it here.
1134 */
1135 if (help->help.ct_sip_info.register_cseq != cseq)
1136 return NF_ACCEPT;
1137
1138 if (code >= 100 && code <= 199)
1139 return NF_ACCEPT;
1140 if (code < 200 || code > 299)
1141 goto flush;
1142
1143 if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_EXPIRES,
1144 &matchoff, &matchlen) > 0)
1145 expires = simple_strtoul(*dptr + matchoff, NULL, 10);
1146
1147 while (1) {
1148 unsigned int c_expires = expires;
1149
1150 ret = ct_sip_parse_header_uri(ct, *dptr, &dataoff, *datalen,
1151 SIP_HDR_CONTACT, &in_contact,
1152 &matchoff, &matchlen,
1153 &addr, &port);
1154 if (ret < 0)
1155 return NF_DROP;
1156 else if (ret == 0)
1157 break;
1158
1159 /* We don't support third-party registrations */
1160 if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, &addr))
1161 continue;
1162
1163 ret = ct_sip_parse_numerical_param(ct, *dptr,
1164 matchoff + matchlen,
1165 *datalen, "expires=",
1166 NULL, NULL, &c_expires);
1167 if (ret < 0)
1168 return NF_DROP;
1169 if (c_expires == 0)
1170 break;
1171 if (refresh_signalling_expectation(ct, &addr, port, c_expires))
1172 return NF_ACCEPT;
1173 }
1174
1175flush:
1176 flush_expectations(ct, false);
Patrick McHardy595a8ec2008-03-25 20:22:53 -07001177 return NF_ACCEPT;
1178}
1179
Patrick McHardy30f33e62008-03-25 20:22:20 -07001180static const struct sip_handler sip_handlers[] = {
1181 SIP_HANDLER("INVITE", process_sdp, process_invite_response),
1182 SIP_HANDLER("UPDATE", process_sdp, process_update_response),
Patrick McHardy595a8ec2008-03-25 20:22:53 -07001183 SIP_HANDLER("ACK", process_sdp, NULL),
1184 SIP_HANDLER("PRACK", process_sdp, process_prack_response),
Patrick McHardy9467ee32008-03-25 20:24:04 -07001185 SIP_HANDLER("BYE", process_bye_request, NULL),
Patrick McHardy0f32a402008-03-25 20:25:13 -07001186 SIP_HANDLER("REGISTER", process_register_request, process_register_response),
Patrick McHardy30f33e62008-03-25 20:22:20 -07001187};
1188
1189static int process_sip_response(struct sk_buff *skb,
1190 const char **dptr, unsigned int *datalen)
1191{
1192 static const struct sip_handler *handler;
1193 enum ip_conntrack_info ctinfo;
1194 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
1195 unsigned int matchoff, matchlen;
1196 unsigned int code, cseq, dataoff, i;
1197
1198 if (*datalen < strlen("SIP/2.0 200"))
1199 return NF_ACCEPT;
1200 code = simple_strtoul(*dptr + strlen("SIP/2.0 "), NULL, 10);
1201 if (!code)
1202 return NF_DROP;
1203
1204 if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ,
1205 &matchoff, &matchlen) <= 0)
1206 return NF_DROP;
1207 cseq = simple_strtoul(*dptr + matchoff, NULL, 10);
1208 if (!cseq)
1209 return NF_DROP;
1210 dataoff = matchoff + matchlen + 1;
1211
1212 for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) {
1213 handler = &sip_handlers[i];
1214 if (handler->response == NULL)
1215 continue;
1216 if (*datalen < dataoff + handler->len ||
1217 strnicmp(*dptr + dataoff, handler->method, handler->len))
1218 continue;
1219 return handler->response(skb, dptr, datalen, cseq, code);
1220 }
1221 return NF_ACCEPT;
1222}
1223
1224static int process_sip_request(struct sk_buff *skb,
1225 const char **dptr, unsigned int *datalen)
1226{
1227 static const struct sip_handler *handler;
1228 enum ip_conntrack_info ctinfo;
1229 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
1230 unsigned int matchoff, matchlen;
1231 unsigned int cseq, i;
1232
1233 for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) {
1234 handler = &sip_handlers[i];
1235 if (handler->request == NULL)
1236 continue;
1237 if (*datalen < handler->len ||
1238 strnicmp(*dptr, handler->method, handler->len))
1239 continue;
1240
1241 if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ,
1242 &matchoff, &matchlen) <= 0)
1243 return NF_DROP;
1244 cseq = simple_strtoul(*dptr + matchoff, NULL, 10);
1245 if (!cseq)
1246 return NF_DROP;
1247
1248 return handler->request(skb, dptr, datalen, cseq);
1249 }
1250 return NF_ACCEPT;
1251}
Patrick McHardy7d3dd042008-03-25 20:19:46 -07001252
Herbert Xu3db05fe2007-10-15 00:53:15 -07001253static int sip_help(struct sk_buff *skb,
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001254 unsigned int protoff,
1255 struct nf_conn *ct,
1256 enum ip_conntrack_info ctinfo)
1257{
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001258 unsigned int dataoff, datalen;
1259 const char *dptr;
Patrick McHardy33cb1e92008-03-25 20:22:37 -07001260 int ret;
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001261 typeof(nf_nat_sip_hook) nf_nat_sip;
1262
1263 /* No Data ? */
1264 dataoff = protoff + sizeof(struct udphdr);
Herbert Xu3db05fe2007-10-15 00:53:15 -07001265 if (dataoff >= skb->len)
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001266 return NF_ACCEPT;
1267
Herbert Xu3db05fe2007-10-15 00:53:15 -07001268 nf_ct_refresh(ct, skb, sip_timeout * HZ);
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001269
Herbert Xu3db05fe2007-10-15 00:53:15 -07001270 if (!skb_is_nonlinear(skb))
1271 dptr = skb->data + dataoff;
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001272 else {
Patrick McHardy0d537782007-07-07 22:39:38 -07001273 pr_debug("Copy of skbuff not supported yet.\n");
Patrick McHardy7d3dd042008-03-25 20:19:46 -07001274 return NF_ACCEPT;
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001275 }
1276
Herbert Xu3db05fe2007-10-15 00:53:15 -07001277 datalen = skb->len - dataoff;
Patrick McHardy779382e2008-03-25 20:17:36 -07001278 if (datalen < strlen("SIP/2.0 200"))
Patrick McHardy7d3dd042008-03-25 20:19:46 -07001279 return NF_ACCEPT;
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001280
Patrick McHardy30f33e62008-03-25 20:22:20 -07001281 if (strnicmp(dptr, "SIP/2.0 ", strlen("SIP/2.0 ")) != 0)
Patrick McHardy33cb1e92008-03-25 20:22:37 -07001282 ret = process_sip_request(skb, &dptr, &datalen);
Patrick McHardy30f33e62008-03-25 20:22:20 -07001283 else
Patrick McHardy33cb1e92008-03-25 20:22:37 -07001284 ret = process_sip_response(skb, &dptr, &datalen);
1285
1286 if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) {
1287 nf_nat_sip = rcu_dereference(nf_nat_sip_hook);
1288 if (nf_nat_sip && !nf_nat_sip(skb, &dptr, &datalen))
1289 ret = NF_DROP;
1290 }
1291
1292 return ret;
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001293}
1294
1295static struct nf_conntrack_helper sip[MAX_PORTS][2] __read_mostly;
1296static char sip_names[MAX_PORTS][2][sizeof("sip-65535")] __read_mostly;
1297
Patrick McHardy0f32a402008-03-25 20:25:13 -07001298static const struct nf_conntrack_expect_policy sip_exp_policy[SIP_EXPECT_MAX + 1] = {
1299 [SIP_EXPECT_SIGNALLING] = {
1300 .max_expected = 1,
1301 .timeout = 3 * 60,
1302 },
1303 [SIP_EXPECT_AUDIO] = {
Patrick McHardya9c1d352008-03-25 20:25:49 -07001304 .max_expected = 2 * IP_CT_DIR_MAX,
Patrick McHardy0f32a402008-03-25 20:25:13 -07001305 .timeout = 3 * 60,
1306 },
Patrick McHardy0d0ab032008-03-25 20:26:24 -07001307 [SIP_EXPECT_VIDEO] = {
1308 .max_expected = 2 * IP_CT_DIR_MAX,
1309 .timeout = 3 * 60,
1310 },
Patrick McHardy6002f262008-03-25 20:09:15 -07001311};
1312
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001313static void nf_conntrack_sip_fini(void)
1314{
1315 int i, j;
1316
1317 for (i = 0; i < ports_c; i++) {
1318 for (j = 0; j < 2; j++) {
1319 if (sip[i][j].me == NULL)
1320 continue;
1321 nf_conntrack_helper_unregister(&sip[i][j]);
1322 }
1323 }
1324}
1325
1326static int __init nf_conntrack_sip_init(void)
1327{
1328 int i, j, ret;
1329 char *tmpname;
1330
1331 if (ports_c == 0)
1332 ports[ports_c++] = SIP_PORT;
1333
1334 for (i = 0; i < ports_c; i++) {
1335 memset(&sip[i], 0, sizeof(sip[i]));
1336
1337 sip[i][0].tuple.src.l3num = AF_INET;
1338 sip[i][1].tuple.src.l3num = AF_INET6;
1339 for (j = 0; j < 2; j++) {
1340 sip[i][j].tuple.dst.protonum = IPPROTO_UDP;
1341 sip[i][j].tuple.src.u.udp.port = htons(ports[i]);
Patrick McHardy0f32a402008-03-25 20:25:13 -07001342 sip[i][j].expect_policy = sip_exp_policy;
1343 sip[i][j].expect_class_max = SIP_EXPECT_MAX;
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001344 sip[i][j].me = THIS_MODULE;
1345 sip[i][j].help = sip_help;
1346
1347 tmpname = &sip_names[i][j][0];
1348 if (ports[i] == SIP_PORT)
1349 sprintf(tmpname, "sip");
1350 else
1351 sprintf(tmpname, "sip-%u", i);
1352 sip[i][j].name = tmpname;
1353
Patrick McHardy0d537782007-07-07 22:39:38 -07001354 pr_debug("port #%u: %u\n", i, ports[i]);
Patrick McHardy9fafcd72006-12-02 22:09:57 -08001355
1356 ret = nf_conntrack_helper_register(&sip[i][j]);
1357 if (ret) {
1358 printk("nf_ct_sip: failed to register helper "
1359 "for pf: %u port: %u\n",
1360 sip[i][j].tuple.src.l3num, ports[i]);
1361 nf_conntrack_sip_fini();
1362 return ret;
1363 }
1364 }
1365 }
1366 return 0;
1367}
1368
1369module_init(nf_conntrack_sip_init);
1370module_exit(nf_conntrack_sip_fini);