blob: 801fcb3c749f1d0c6ab5a73c7172fc7b43f9f9eb [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>
20#include <net/netfilter/nf_conntrack_expect.h>
21#include <net/netfilter/nf_conntrack_helper.h>
22#include <linux/netfilter/nf_conntrack_sip.h>
23
Patrick McHardy9fafcd72006-12-02 22:09:57 -080024MODULE_LICENSE("GPL");
25MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>");
26MODULE_DESCRIPTION("SIP connection tracking helper");
27MODULE_ALIAS("ip_conntrack_sip");
28
29#define MAX_PORTS 8
30static unsigned short ports[MAX_PORTS];
Stephen Hemminger2f0d2f12008-01-31 04:08:10 -080031static unsigned int ports_c;
Patrick McHardy9fafcd72006-12-02 22:09:57 -080032module_param_array(ports, ushort, &ports_c, 0400);
33MODULE_PARM_DESC(ports, "port numbers of SIP servers");
34
35static unsigned int sip_timeout __read_mostly = SIP_TIMEOUT;
36module_param(sip_timeout, uint, 0600);
37MODULE_PARM_DESC(sip_timeout, "timeout for the master SIP session");
38
Herbert Xu3db05fe2007-10-15 00:53:15 -070039unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb,
Patrick McHardy2a6cfb22008-03-25 20:16:54 -070040 const char **dptr,
41 unsigned int *datalen) __read_mostly;
Patrick McHardy9fafcd72006-12-02 22:09:57 -080042EXPORT_SYMBOL_GPL(nf_nat_sip_hook);
43
Herbert Xu3db05fe2007-10-15 00:53:15 -070044unsigned int (*nf_nat_sdp_hook)(struct sk_buff *skb,
Patrick McHardy2a6cfb22008-03-25 20:16:54 -070045 const char **dptr,
Patrick McHardy212440a2008-03-25 20:17:13 -070046 unsigned int *datalen,
47 struct nf_conntrack_expect *exp) __read_mostly;
Patrick McHardy9fafcd72006-12-02 22:09:57 -080048EXPORT_SYMBOL_GPL(nf_nat_sdp_hook);
49
Jan Engelhardt13f7d632008-01-31 04:50:25 -080050static int digits_len(const struct nf_conn *, const char *, const char *, int *);
51static int epaddr_len(const struct nf_conn *, const char *, const char *, int *);
52static int skp_digits_len(const struct nf_conn *, const char *, const char *, int *);
53static int skp_epaddr_len(const struct nf_conn *, const char *, const char *, int *);
Patrick McHardy9fafcd72006-12-02 22:09:57 -080054
55struct sip_header_nfo {
56 const char *lname;
57 const char *sname;
58 const char *ln_str;
59 size_t lnlen;
60 size_t snlen;
61 size_t ln_strlen;
62 int case_sensitive;
Jan Engelhardt13f7d632008-01-31 04:50:25 -080063 int (*match_len)(const struct nf_conn *, const char *,
Patrick McHardy9fafcd72006-12-02 22:09:57 -080064 const char *, int *);
65};
66
67static const struct sip_header_nfo ct_sip_hdrs[] = {
68 [POS_REG_REQ_URI] = { /* SIP REGISTER request URI */
69 .lname = "sip:",
70 .lnlen = sizeof("sip:") - 1,
71 .ln_str = ":",
72 .ln_strlen = sizeof(":") - 1,
73 .match_len = epaddr_len,
74 },
75 [POS_REQ_URI] = { /* SIP request URI */
76 .lname = "sip:",
77 .lnlen = sizeof("sip:") - 1,
78 .ln_str = "@",
79 .ln_strlen = sizeof("@") - 1,
80 .match_len = epaddr_len,
81 },
82 [POS_FROM] = { /* SIP From header */
83 .lname = "From:",
84 .lnlen = sizeof("From:") - 1,
85 .sname = "\r\nf:",
86 .snlen = sizeof("\r\nf:") - 1,
87 .ln_str = "sip:",
88 .ln_strlen = sizeof("sip:") - 1,
89 .match_len = skp_epaddr_len,
90 },
91 [POS_TO] = { /* SIP To header */
92 .lname = "To:",
93 .lnlen = sizeof("To:") - 1,
94 .sname = "\r\nt:",
95 .snlen = sizeof("\r\nt:") - 1,
96 .ln_str = "sip:",
97 .ln_strlen = sizeof("sip:") - 1,
98 .match_len = skp_epaddr_len
99 },
100 [POS_VIA] = { /* SIP Via header */
101 .lname = "Via:",
102 .lnlen = sizeof("Via:") - 1,
103 .sname = "\r\nv:",
104 .snlen = sizeof("\r\nv:") - 1, /* rfc3261 "\r\n" */
105 .ln_str = "UDP ",
106 .ln_strlen = sizeof("UDP ") - 1,
107 .match_len = epaddr_len,
108 },
109 [POS_CONTACT] = { /* SIP Contact header */
110 .lname = "Contact:",
111 .lnlen = sizeof("Contact:") - 1,
112 .sname = "\r\nm:",
113 .snlen = sizeof("\r\nm:") - 1,
114 .ln_str = "sip:",
115 .ln_strlen = sizeof("sip:") - 1,
116 .match_len = skp_epaddr_len
117 },
118 [POS_CONTENT] = { /* SIP Content length header */
119 .lname = "Content-Length:",
120 .lnlen = sizeof("Content-Length:") - 1,
121 .sname = "\r\nl:",
122 .snlen = sizeof("\r\nl:") - 1,
123 .ln_str = ":",
124 .ln_strlen = sizeof(":") - 1,
125 .match_len = skp_digits_len
126 },
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800127};
128
Joe Perchesc8238172007-12-20 14:04:24 -0800129/* get line length until first CR or LF seen. */
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800130int ct_sip_lnlen(const char *line, const char *limit)
131{
132 const char *k = line;
133
Patrick McHardyb1ec4882008-03-25 20:10:11 -0700134 while ((line < limit) && (*line == '\r' || *line == '\n'))
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800135 line++;
136
Patrick McHardyb1ec4882008-03-25 20:10:11 -0700137 while (line < limit) {
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800138 if (*line == '\r' || *line == '\n')
139 break;
140 line++;
141 }
142 return line - k;
143}
144EXPORT_SYMBOL_GPL(ct_sip_lnlen);
145
146/* Linear string search, case sensitive. */
147const char *ct_sip_search(const char *needle, const char *haystack,
148 size_t needle_len, size_t haystack_len,
149 int case_sensitive)
150{
151 const char *limit = haystack + (haystack_len - needle_len);
152
Patrick McHardyb1ec4882008-03-25 20:10:11 -0700153 while (haystack < limit) {
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800154 if (case_sensitive) {
155 if (strncmp(haystack, needle, needle_len) == 0)
156 return haystack;
157 } else {
158 if (strnicmp(haystack, needle, needle_len) == 0)
159 return haystack;
160 }
161 haystack++;
162 }
163 return NULL;
164}
165EXPORT_SYMBOL_GPL(ct_sip_search);
166
Jan Engelhardt13f7d632008-01-31 04:50:25 -0800167static int digits_len(const struct nf_conn *ct, const char *dptr,
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800168 const char *limit, int *shift)
169{
170 int len = 0;
Patrick McHardyb1ec4882008-03-25 20:10:11 -0700171 while (dptr < limit && isdigit(*dptr)) {
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800172 dptr++;
173 len++;
174 }
175 return len;
176}
177
Joe Perchesc8238172007-12-20 14:04:24 -0800178/* get digits length, skipping blank spaces. */
Jan Engelhardt13f7d632008-01-31 04:50:25 -0800179static int skp_digits_len(const struct nf_conn *ct, const char *dptr,
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800180 const char *limit, int *shift)
181{
Patrick McHardyb1ec4882008-03-25 20:10:11 -0700182 for (; dptr < limit && *dptr == ' '; dptr++)
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800183 (*shift)++;
184
185 return digits_len(ct, dptr, limit, shift);
186}
187
Jan Engelhardt13f7d632008-01-31 04:50:25 -0800188static int parse_addr(const struct nf_conn *ct, const char *cp,
189 const char **endp, union nf_inet_addr *addr,
190 const char *limit)
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800191{
192 const char *end;
193 int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
194 int ret = 0;
195
196 switch (family) {
197 case AF_INET:
198 ret = in4_pton(cp, limit - cp, (u8 *)&addr->ip, -1, &end);
199 break;
200 case AF_INET6:
201 ret = in6_pton(cp, limit - cp, (u8 *)&addr->ip6, -1, &end);
202 break;
203 default:
204 BUG();
205 }
206
207 if (ret == 0 || end == cp)
208 return 0;
209 if (endp)
210 *endp = end;
211 return 1;
212}
213
214/* skip ip address. returns its length. */
Jan Engelhardt13f7d632008-01-31 04:50:25 -0800215static int epaddr_len(const struct nf_conn *ct, const char *dptr,
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800216 const char *limit, int *shift)
217{
Jan Engelhardt643a2c12007-12-17 22:43:50 -0800218 union nf_inet_addr addr;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800219 const char *aux = dptr;
220
221 if (!parse_addr(ct, dptr, &dptr, &addr, limit)) {
Patrick McHardy0d537782007-07-07 22:39:38 -0700222 pr_debug("ip: %s parse failed.!\n", dptr);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800223 return 0;
224 }
225
226 /* Port number */
227 if (*dptr == ':') {
228 dptr++;
229 dptr += digits_len(ct, dptr, limit, shift);
230 }
231 return dptr - aux;
232}
233
234/* get address length, skiping user info. */
Jan Engelhardt13f7d632008-01-31 04:50:25 -0800235static int skp_epaddr_len(const struct nf_conn *ct, const char *dptr,
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800236 const char *limit, int *shift)
237{
Patrick McHardyaa584ed2007-08-14 13:14:35 -0700238 const char *start = dptr;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800239 int s = *shift;
240
Lars Immisch7da5bfb2007-01-30 14:24:57 -0800241 /* Search for @, but stop at the end of the line.
242 * We are inside a sip: URI, so we don't need to worry about
243 * continuation lines. */
Patrick McHardyb1ec4882008-03-25 20:10:11 -0700244 while (dptr < limit &&
Lars Immisch7da5bfb2007-01-30 14:24:57 -0800245 *dptr != '@' && *dptr != '\r' && *dptr != '\n') {
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800246 (*shift)++;
Lars Immisch7da5bfb2007-01-30 14:24:57 -0800247 dptr++;
248 }
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800249
Patrick McHardyb1ec4882008-03-25 20:10:11 -0700250 if (dptr < limit && *dptr == '@') {
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800251 dptr++;
252 (*shift)++;
Patrick McHardyaa584ed2007-08-14 13:14:35 -0700253 } else {
254 dptr = start;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800255 *shift = s;
Patrick McHardyaa584ed2007-08-14 13:14:35 -0700256 }
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800257
258 return epaddr_len(ct, dptr, limit, shift);
259}
260
261/* Returns 0 if not found, -1 error parsing. */
Jan Engelhardt13f7d632008-01-31 04:50:25 -0800262int ct_sip_get_info(const struct nf_conn *ct,
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800263 const char *dptr, size_t dlen,
264 unsigned int *matchoff,
265 unsigned int *matchlen,
266 enum sip_header_pos pos)
267{
268 const struct sip_header_nfo *hnfo = &ct_sip_hdrs[pos];
269 const char *limit, *aux, *k = dptr;
270 int shift = 0;
271
272 limit = dptr + (dlen - hnfo->lnlen);
273
Patrick McHardyb1ec4882008-03-25 20:10:11 -0700274 while (dptr < limit) {
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800275 if ((strncmp(dptr, hnfo->lname, hnfo->lnlen) != 0) &&
Patrick McHardy465f90a2007-08-14 13:13:54 -0700276 (hnfo->sname == NULL ||
277 strncmp(dptr, hnfo->sname, hnfo->snlen) != 0)) {
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800278 dptr++;
279 continue;
280 }
281 aux = ct_sip_search(hnfo->ln_str, dptr, hnfo->ln_strlen,
YOSHIFUJI Hideaki601e68e2007-02-12 11:15:49 -0800282 ct_sip_lnlen(dptr, limit),
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800283 hnfo->case_sensitive);
284 if (!aux) {
Patrick McHardy0d537782007-07-07 22:39:38 -0700285 pr_debug("'%s' not found in '%s'.\n", hnfo->ln_str,
286 hnfo->lname);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800287 return -1;
288 }
289 aux += hnfo->ln_strlen;
290
291 *matchlen = hnfo->match_len(ct, aux, limit, &shift);
292 if (!*matchlen)
293 return -1;
294
295 *matchoff = (aux - k) + shift;
296
Patrick McHardy0d537782007-07-07 22:39:38 -0700297 pr_debug("%s match succeeded! - len: %u\n", hnfo->lname,
298 *matchlen);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800299 return 1;
300 }
Patrick McHardy0d537782007-07-07 22:39:38 -0700301 pr_debug("%s header not found.\n", hnfo->lname);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800302 return 0;
303}
304EXPORT_SYMBOL_GPL(ct_sip_get_info);
305
Patrick McHardy3e9b4600b2008-03-25 20:17:55 -0700306/* SDP header parsing: a SDP session description contains an ordered set of
307 * headers, starting with a section containing general session parameters,
308 * optionally followed by multiple media descriptions.
309 *
310 * SDP headers always start at the beginning of a line. According to RFC 2327:
311 * "The sequence CRLF (0x0d0a) is used to end a record, although parsers should
312 * be tolerant and also accept records terminated with a single newline
313 * character". We handle both cases.
314 */
315static const struct sip_header ct_sdp_hdrs[] = {
316 [SDP_HDR_VERSION] = SDP_HDR("v=", NULL, digits_len),
317 [SDP_HDR_OWNER_IP4] = SDP_HDR("o=", "IN IP4 ", epaddr_len),
318 [SDP_HDR_CONNECTION_IP4] = SDP_HDR("c=", "IN IP4 ", epaddr_len),
319 [SDP_HDR_OWNER_IP6] = SDP_HDR("o=", "IN IP6 ", epaddr_len),
320 [SDP_HDR_CONNECTION_IP6] = SDP_HDR("c=", "IN IP6 ", epaddr_len),
321 [SDP_HDR_MEDIA] = SDP_HDR("m=", "audio ", digits_len),
322};
323
324/* Linear string search within SDP header values */
325static const char *ct_sdp_header_search(const char *dptr, const char *limit,
326 const char *needle, unsigned int len)
327{
328 for (limit -= len; dptr < limit; dptr++) {
329 if (*dptr == '\r' || *dptr == '\n')
330 break;
331 if (strncmp(dptr, needle, len) == 0)
332 return dptr;
333 }
334 return NULL;
335}
336
337/* Locate a SDP header (optionally a substring within the header value),
338 * optionally stopping at the first occurence of the term header, parse
339 * it and return the offset and length of the data we're interested in.
340 */
341int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr,
342 unsigned int dataoff, unsigned int datalen,
343 enum sdp_header_types type,
344 enum sdp_header_types term,
345 unsigned int *matchoff, unsigned int *matchlen)
346{
347 const struct sip_header *hdr = &ct_sdp_hdrs[type];
348 const struct sip_header *thdr = &ct_sdp_hdrs[term];
349 const char *start = dptr, *limit = dptr + datalen;
350 int shift = 0;
351
352 for (dptr += dataoff; dptr < limit; dptr++) {
353 /* Find beginning of line */
354 if (*dptr != '\r' && *dptr != '\n')
355 continue;
356 if (++dptr >= limit)
357 break;
358 if (*(dptr - 1) == '\r' && *dptr == '\n') {
359 if (++dptr >= limit)
360 break;
361 }
362
363 if (term != SDP_HDR_UNSPEC &&
364 limit - dptr >= thdr->len &&
365 strnicmp(dptr, thdr->name, thdr->len) == 0)
366 break;
367 else if (limit - dptr >= hdr->len &&
368 strnicmp(dptr, hdr->name, hdr->len) == 0)
369 dptr += hdr->len;
370 else
371 continue;
372
373 *matchoff = dptr - start;
374 if (hdr->search) {
375 dptr = ct_sdp_header_search(dptr, limit, hdr->search,
376 hdr->slen);
377 if (!dptr)
378 return -1;
379 dptr += hdr->slen;
380 }
381
382 *matchlen = hdr->match_len(ct, dptr, limit, &shift);
383 if (!*matchlen)
384 return -1;
385 *matchoff = dptr - start + shift;
386 return 1;
387 }
388 return 0;
389}
390EXPORT_SYMBOL_GPL(ct_sip_get_sdp_header);
391
Herbert Xu3db05fe2007-10-15 00:53:15 -0700392static int set_expected_rtp(struct sk_buff *skb,
Patrick McHardy212440a2008-03-25 20:17:13 -0700393 const char **dptr, unsigned int *datalen,
394 union nf_inet_addr *addr, __be16 port)
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800395{
396 struct nf_conntrack_expect *exp;
Patrick McHardy212440a2008-03-25 20:17:13 -0700397 enum ip_conntrack_info ctinfo;
398 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800399 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
400 int family = ct->tuplehash[!dir].tuple.src.l3num;
401 int ret;
402 typeof(nf_nat_sdp_hook) nf_nat_sdp;
403
Patrick McHardy68236452007-07-07 22:30:49 -0700404 exp = nf_ct_expect_alloc(ct);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800405 if (exp == NULL)
406 return NF_DROP;
Patrick McHardy6002f262008-03-25 20:09:15 -0700407 nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, family,
Patrick McHardy68236452007-07-07 22:30:49 -0700408 &ct->tuplehash[!dir].tuple.src.u3, addr,
409 IPPROTO_UDP, NULL, &port);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800410
411 nf_nat_sdp = rcu_dereference(nf_nat_sdp_hook);
412 if (nf_nat_sdp && ct->status & IPS_NAT_MASK)
Patrick McHardy212440a2008-03-25 20:17:13 -0700413 ret = nf_nat_sdp(skb, dptr, datalen, exp);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800414 else {
Patrick McHardy68236452007-07-07 22:30:49 -0700415 if (nf_ct_expect_related(exp) != 0)
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800416 ret = NF_DROP;
417 else
418 ret = NF_ACCEPT;
419 }
Patrick McHardy68236452007-07-07 22:30:49 -0700420 nf_ct_expect_put(exp);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800421
422 return ret;
423}
424
Herbert Xu3db05fe2007-10-15 00:53:15 -0700425static int sip_help(struct sk_buff *skb,
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800426 unsigned int protoff,
427 struct nf_conn *ct,
428 enum ip_conntrack_info ctinfo)
429{
430 int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
Jan Engelhardt643a2c12007-12-17 22:43:50 -0800431 union nf_inet_addr addr;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800432 unsigned int dataoff, datalen;
433 const char *dptr;
434 int ret = NF_ACCEPT;
Stephen Hemminger2f0d2f12008-01-31 04:08:10 -0800435 unsigned int matchoff, matchlen;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800436 u_int16_t port;
Patrick McHardy3e9b4600b2008-03-25 20:17:55 -0700437 enum sdp_header_types type;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800438 typeof(nf_nat_sip_hook) nf_nat_sip;
439
440 /* No Data ? */
441 dataoff = protoff + sizeof(struct udphdr);
Herbert Xu3db05fe2007-10-15 00:53:15 -0700442 if (dataoff >= skb->len)
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800443 return NF_ACCEPT;
444
Herbert Xu3db05fe2007-10-15 00:53:15 -0700445 nf_ct_refresh(ct, skb, sip_timeout * HZ);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800446
Herbert Xu3db05fe2007-10-15 00:53:15 -0700447 if (!skb_is_nonlinear(skb))
448 dptr = skb->data + dataoff;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800449 else {
Patrick McHardy0d537782007-07-07 22:39:38 -0700450 pr_debug("Copy of skbuff not supported yet.\n");
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800451 goto out;
452 }
453
454 nf_nat_sip = rcu_dereference(nf_nat_sip_hook);
455 if (nf_nat_sip && ct->status & IPS_NAT_MASK) {
Patrick McHardy212440a2008-03-25 20:17:13 -0700456 if (!nf_nat_sip(skb, &dptr, &datalen)) {
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800457 ret = NF_DROP;
458 goto out;
459 }
460 }
461
Herbert Xu3db05fe2007-10-15 00:53:15 -0700462 datalen = skb->len - dataoff;
Patrick McHardy779382e2008-03-25 20:17:36 -0700463 if (datalen < strlen("SIP/2.0 200"))
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800464 goto out;
465
466 /* RTP info only in some SDP pkts */
Patrick McHardy779382e2008-03-25 20:17:36 -0700467 if (strnicmp(dptr, "INVITE", strlen("INVITE")) != 0 &&
468 strnicmp(dptr, "UPDATE", strlen("UPDATE")) != 0 &&
469 strnicmp(dptr, "SIP/2.0 180", strlen("SIP/2.0 180")) != 0 &&
470 strnicmp(dptr, "SIP/2.0 183", strlen("SIP/2.0 183")) != 0 &&
471 strnicmp(dptr, "SIP/2.0 200", strlen("SIP/2.0 200")) != 0) {
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800472 goto out;
473 }
474 /* Get address and port from SDP packet. */
Patrick McHardy3e9b4600b2008-03-25 20:17:55 -0700475 type = family == AF_INET ? SDP_HDR_CONNECTION_IP4 :
476 SDP_HDR_CONNECTION_IP6;
477 if (ct_sip_get_sdp_header(ct, dptr, 0, datalen, type, SDP_HDR_UNSPEC,
478 &matchoff, &matchlen) > 0) {
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800479
480 /* We'll drop only if there are parse problems. */
481 if (!parse_addr(ct, dptr + matchoff, NULL, &addr,
YOSHIFUJI Hideaki601e68e2007-02-12 11:15:49 -0800482 dptr + datalen)) {
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800483 ret = NF_DROP;
484 goto out;
485 }
Patrick McHardy3e9b4600b2008-03-25 20:17:55 -0700486 if (ct_sip_get_sdp_header(ct, dptr, 0, datalen,
487 SDP_HDR_MEDIA, SDP_HDR_UNSPEC,
488 &matchoff, &matchlen) > 0) {
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800489
490 port = simple_strtoul(dptr + matchoff, NULL, 10);
491 if (port < 1024) {
492 ret = NF_DROP;
493 goto out;
494 }
Patrick McHardy212440a2008-03-25 20:17:13 -0700495 ret = set_expected_rtp(skb, &dptr, &datalen,
496 &addr, htons(port));
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800497 }
498 }
499out:
500 return ret;
501}
502
503static struct nf_conntrack_helper sip[MAX_PORTS][2] __read_mostly;
504static char sip_names[MAX_PORTS][2][sizeof("sip-65535")] __read_mostly;
505
Patrick McHardy6002f262008-03-25 20:09:15 -0700506static const struct nf_conntrack_expect_policy sip_exp_policy = {
507 .max_expected = 2,
508 .timeout = 3 * 60,
509};
510
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800511static void nf_conntrack_sip_fini(void)
512{
513 int i, j;
514
515 for (i = 0; i < ports_c; i++) {
516 for (j = 0; j < 2; j++) {
517 if (sip[i][j].me == NULL)
518 continue;
519 nf_conntrack_helper_unregister(&sip[i][j]);
520 }
521 }
522}
523
524static int __init nf_conntrack_sip_init(void)
525{
526 int i, j, ret;
527 char *tmpname;
528
529 if (ports_c == 0)
530 ports[ports_c++] = SIP_PORT;
531
532 for (i = 0; i < ports_c; i++) {
533 memset(&sip[i], 0, sizeof(sip[i]));
534
535 sip[i][0].tuple.src.l3num = AF_INET;
536 sip[i][1].tuple.src.l3num = AF_INET6;
537 for (j = 0; j < 2; j++) {
538 sip[i][j].tuple.dst.protonum = IPPROTO_UDP;
539 sip[i][j].tuple.src.u.udp.port = htons(ports[i]);
Patrick McHardy6002f262008-03-25 20:09:15 -0700540 sip[i][j].expect_policy = &sip_exp_policy;
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800541 sip[i][j].me = THIS_MODULE;
542 sip[i][j].help = sip_help;
543
544 tmpname = &sip_names[i][j][0];
545 if (ports[i] == SIP_PORT)
546 sprintf(tmpname, "sip");
547 else
548 sprintf(tmpname, "sip-%u", i);
549 sip[i][j].name = tmpname;
550
Patrick McHardy0d537782007-07-07 22:39:38 -0700551 pr_debug("port #%u: %u\n", i, ports[i]);
Patrick McHardy9fafcd72006-12-02 22:09:57 -0800552
553 ret = nf_conntrack_helper_register(&sip[i][j]);
554 if (ret) {
555 printk("nf_ct_sip: failed to register helper "
556 "for pf: %u port: %u\n",
557 sip[i][j].tuple.src.l3num, ports[i]);
558 nf_conntrack_sip_fini();
559 return ret;
560 }
561 }
562 }
563 return 0;
564}
565
566module_init(nf_conntrack_sip_init);
567module_exit(nf_conntrack_sip_fini);