[NETFILTER]: nf_conntrack_sip: move SDP parsing to seperate function
[safe/jmp/linux-2.6] / net / netfilter / nf_conntrack_sip.c
1 /* SIP extension for IP connection tracking.
2  *
3  * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar>
4  * based on RR's ip_conntrack_ftp.c and other modules.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10
11 #include <linux/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>
17 #include <linux/netfilter.h>
18
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
24 MODULE_LICENSE("GPL");
25 MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>");
26 MODULE_DESCRIPTION("SIP connection tracking helper");
27 MODULE_ALIAS("ip_conntrack_sip");
28
29 #define MAX_PORTS       8
30 static unsigned short ports[MAX_PORTS];
31 static unsigned int ports_c;
32 module_param_array(ports, ushort, &ports_c, 0400);
33 MODULE_PARM_DESC(ports, "port numbers of SIP servers");
34
35 static unsigned int sip_timeout __read_mostly = SIP_TIMEOUT;
36 module_param(sip_timeout, uint, 0600);
37 MODULE_PARM_DESC(sip_timeout, "timeout for the master SIP session");
38
39 unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb,
40                                 const char **dptr,
41                                 unsigned int *datalen) __read_mostly;
42 EXPORT_SYMBOL_GPL(nf_nat_sip_hook);
43
44 unsigned int (*nf_nat_sdp_hook)(struct sk_buff *skb,
45                                 const char **dptr,
46                                 unsigned int *datalen,
47                                 struct nf_conntrack_expect *exp) __read_mostly;
48 EXPORT_SYMBOL_GPL(nf_nat_sdp_hook);
49
50 static int string_len(const struct nf_conn *ct, const char *dptr,
51                       const char *limit, int *shift)
52 {
53         int len = 0;
54
55         while (dptr < limit && isalpha(*dptr)) {
56                 dptr++;
57                 len++;
58         }
59         return len;
60 }
61
62 static int digits_len(const struct nf_conn *ct, const char *dptr,
63                       const char *limit, int *shift)
64 {
65         int len = 0;
66         while (dptr < limit && isdigit(*dptr)) {
67                 dptr++;
68                 len++;
69         }
70         return len;
71 }
72
73 static int parse_addr(const struct nf_conn *ct, const char *cp,
74                       const char **endp, union nf_inet_addr *addr,
75                       const char *limit)
76 {
77         const char *end;
78         int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
79         int ret = 0;
80
81         switch (family) {
82         case AF_INET:
83                 ret = in4_pton(cp, limit - cp, (u8 *)&addr->ip, -1, &end);
84                 break;
85         case AF_INET6:
86                 ret = in6_pton(cp, limit - cp, (u8 *)&addr->ip6, -1, &end);
87                 break;
88         default:
89                 BUG();
90         }
91
92         if (ret == 0 || end == cp)
93                 return 0;
94         if (endp)
95                 *endp = end;
96         return 1;
97 }
98
99 /* skip ip address. returns its length. */
100 static int epaddr_len(const struct nf_conn *ct, const char *dptr,
101                       const char *limit, int *shift)
102 {
103         union nf_inet_addr addr;
104         const char *aux = dptr;
105
106         if (!parse_addr(ct, dptr, &dptr, &addr, limit)) {
107                 pr_debug("ip: %s parse failed.!\n", dptr);
108                 return 0;
109         }
110
111         /* Port number */
112         if (*dptr == ':') {
113                 dptr++;
114                 dptr += digits_len(ct, dptr, limit, shift);
115         }
116         return dptr - aux;
117 }
118
119 /* get address length, skiping user info. */
120 static int skp_epaddr_len(const struct nf_conn *ct, const char *dptr,
121                           const char *limit, int *shift)
122 {
123         const char *start = dptr;
124         int s = *shift;
125
126         /* Search for @, but stop at the end of the line.
127          * We are inside a sip: URI, so we don't need to worry about
128          * continuation lines. */
129         while (dptr < limit &&
130                *dptr != '@' && *dptr != '\r' && *dptr != '\n') {
131                 (*shift)++;
132                 dptr++;
133         }
134
135         if (dptr < limit && *dptr == '@') {
136                 dptr++;
137                 (*shift)++;
138         } else {
139                 dptr = start;
140                 *shift = s;
141         }
142
143         return epaddr_len(ct, dptr, limit, shift);
144 }
145
146 /* Parse a SIP request line of the form:
147  *
148  * Request-Line = Method SP Request-URI SP SIP-Version CRLF
149  *
150  * and return the offset and length of the address contained in the Request-URI.
151  */
152 int ct_sip_parse_request(const struct nf_conn *ct,
153                          const char *dptr, unsigned int datalen,
154                          unsigned int *matchoff, unsigned int *matchlen,
155                          union nf_inet_addr *addr, __be16 *port)
156 {
157         const char *start = dptr, *limit = dptr + datalen, *end;
158         unsigned int mlen;
159         unsigned int p;
160         int shift = 0;
161
162         /* Skip method and following whitespace */
163         mlen = string_len(ct, dptr, limit, NULL);
164         if (!mlen)
165                 return 0;
166         dptr += mlen;
167         if (++dptr >= limit)
168                 return 0;
169
170         /* Find SIP URI */
171         limit -= strlen("sip:");
172         for (; dptr < limit; dptr++) {
173                 if (*dptr == '\r' || *dptr == '\n')
174                         return -1;
175                 if (strnicmp(dptr, "sip:", strlen("sip:")) == 0)
176                         break;
177         }
178         if (!skp_epaddr_len(ct, dptr, limit, &shift))
179                 return 0;
180         dptr += shift;
181
182         if (!parse_addr(ct, dptr, &end, addr, limit))
183                 return -1;
184         if (end < limit && *end == ':') {
185                 end++;
186                 p = simple_strtoul(end, (char **)&end, 10);
187                 if (p < 1024 || p > 65535)
188                         return -1;
189                 *port = htons(p);
190         } else
191                 *port = htons(SIP_PORT);
192
193         if (end == dptr)
194                 return 0;
195         *matchoff = dptr - start;
196         *matchlen = end - dptr;
197         return 1;
198 }
199 EXPORT_SYMBOL_GPL(ct_sip_parse_request);
200
201 /* SIP header parsing: SIP headers are located at the beginning of a line, but
202  * may span several lines, in which case the continuation lines begin with a
203  * whitespace character. RFC 2543 allows lines to be terminated with CR, LF or
204  * CRLF, RFC 3261 allows only CRLF, we support both.
205  *
206  * Headers are followed by (optionally) whitespace, a colon, again (optionally)
207  * whitespace and the values. Whitespace in this context means any amount of
208  * tabs, spaces and continuation lines, which are treated as a single whitespace
209  * character.
210  *
211  * Some headers may appear multiple times. A comma seperated list of values is
212  * equivalent to multiple headers.
213  */
214 static const struct sip_header ct_sip_hdrs[] = {
215         [SIP_HDR_FROM]                  = SIP_HDR("From", "f", "sip:", skp_epaddr_len),
216         [SIP_HDR_TO]                    = SIP_HDR("To", "t", "sip:", skp_epaddr_len),
217         [SIP_HDR_CONTACT]               = SIP_HDR("Contact", "m", "sip:", skp_epaddr_len),
218         [SIP_HDR_VIA]                   = SIP_HDR("Via", "v", "UDP ", epaddr_len),
219         [SIP_HDR_CONTENT_LENGTH]        = SIP_HDR("Content-Length", "l", NULL, digits_len),
220 };
221
222 static const char *sip_follow_continuation(const char *dptr, const char *limit)
223 {
224         /* Walk past newline */
225         if (++dptr >= limit)
226                 return NULL;
227
228         /* Skip '\n' in CR LF */
229         if (*(dptr - 1) == '\r' && *dptr == '\n') {
230                 if (++dptr >= limit)
231                         return NULL;
232         }
233
234         /* Continuation line? */
235         if (*dptr != ' ' && *dptr != '\t')
236                 return NULL;
237
238         /* skip leading whitespace */
239         for (; dptr < limit; dptr++) {
240                 if (*dptr != ' ' && *dptr != '\t')
241                         break;
242         }
243         return dptr;
244 }
245
246 static const char *sip_skip_whitespace(const char *dptr, const char *limit)
247 {
248         for (; dptr < limit; dptr++) {
249                 if (*dptr == ' ')
250                         continue;
251                 if (*dptr != '\r' && *dptr != '\n')
252                         break;
253                 dptr = sip_follow_continuation(dptr, limit);
254                 if (dptr == NULL)
255                         return NULL;
256         }
257         return dptr;
258 }
259
260 /* Search within a SIP header value, dealing with continuation lines */
261 static const char *ct_sip_header_search(const char *dptr, const char *limit,
262                                         const char *needle, unsigned int len)
263 {
264         for (limit -= len; dptr < limit; dptr++) {
265                 if (*dptr == '\r' || *dptr == '\n') {
266                         dptr = sip_follow_continuation(dptr, limit);
267                         if (dptr == NULL)
268                                 break;
269                         continue;
270                 }
271
272                 if (strnicmp(dptr, needle, len) == 0)
273                         return dptr;
274         }
275         return NULL;
276 }
277
278 int ct_sip_get_header(const struct nf_conn *ct, const char *dptr,
279                       unsigned int dataoff, unsigned int datalen,
280                       enum sip_header_types type,
281                       unsigned int *matchoff, unsigned int *matchlen)
282 {
283         const struct sip_header *hdr = &ct_sip_hdrs[type];
284         const char *start = dptr, *limit = dptr + datalen;
285         int shift = 0;
286
287         for (dptr += dataoff; dptr < limit; dptr++) {
288                 /* Find beginning of line */
289                 if (*dptr != '\r' && *dptr != '\n')
290                         continue;
291                 if (++dptr >= limit)
292                         break;
293                 if (*(dptr - 1) == '\r' && *dptr == '\n') {
294                         if (++dptr >= limit)
295                                 break;
296                 }
297
298                 /* Skip continuation lines */
299                 if (*dptr == ' ' || *dptr == '\t')
300                         continue;
301
302                 /* Find header. Compact headers must be followed by a
303                  * non-alphabetic character to avoid mismatches. */
304                 if (limit - dptr >= hdr->len &&
305                     strnicmp(dptr, hdr->name, hdr->len) == 0)
306                         dptr += hdr->len;
307                 else if (hdr->cname && limit - dptr >= hdr->clen + 1 &&
308                          strnicmp(dptr, hdr->cname, hdr->clen) == 0 &&
309                          !isalpha(*(dptr + hdr->clen + 1)))
310                         dptr += hdr->clen;
311                 else
312                         continue;
313
314                 /* Find and skip colon */
315                 dptr = sip_skip_whitespace(dptr, limit);
316                 if (dptr == NULL)
317                         break;
318                 if (*dptr != ':' || ++dptr >= limit)
319                         break;
320
321                 /* Skip whitespace after colon */
322                 dptr = sip_skip_whitespace(dptr, limit);
323                 if (dptr == NULL)
324                         break;
325
326                 *matchoff = dptr - start;
327                 if (hdr->search) {
328                         dptr = ct_sip_header_search(dptr, limit, hdr->search,
329                                                     hdr->slen);
330                         if (!dptr)
331                                 return -1;
332                         dptr += hdr->slen;
333                 }
334
335                 *matchlen = hdr->match_len(ct, dptr, limit, &shift);
336                 if (!*matchlen)
337                         return -1;
338                 *matchoff = dptr - start + shift;
339                 return 1;
340         }
341         return 0;
342 }
343 EXPORT_SYMBOL_GPL(ct_sip_get_header);
344
345 /* Get next header field in a list of comma seperated values */
346 static int ct_sip_next_header(const struct nf_conn *ct, const char *dptr,
347                               unsigned int dataoff, unsigned int datalen,
348                               enum sip_header_types type,
349                               unsigned int *matchoff, unsigned int *matchlen)
350 {
351         const struct sip_header *hdr = &ct_sip_hdrs[type];
352         const char *start = dptr, *limit = dptr + datalen;
353         int shift = 0;
354
355         dptr += dataoff;
356
357         dptr = ct_sip_header_search(dptr, limit, ",", strlen(","));
358         if (!dptr)
359                 return 0;
360
361         dptr = ct_sip_header_search(dptr, limit, hdr->search, hdr->slen);
362         if (!dptr)
363                 return 0;
364         dptr += hdr->slen;
365
366         *matchoff = dptr - start;
367         *matchlen = hdr->match_len(ct, dptr, limit, &shift);
368         if (!*matchlen)
369                 return -1;
370         *matchoff += shift;
371         return 1;
372 }
373
374 /* Walk through headers until a parsable one is found or no header of the
375  * given type is left. */
376 static int ct_sip_walk_headers(const struct nf_conn *ct, const char *dptr,
377                                unsigned int dataoff, unsigned int datalen,
378                                enum sip_header_types type, int *in_header,
379                                unsigned int *matchoff, unsigned int *matchlen)
380 {
381         int ret;
382
383         if (in_header && *in_header) {
384                 while (1) {
385                         ret = ct_sip_next_header(ct, dptr, dataoff, datalen,
386                                                  type, matchoff, matchlen);
387                         if (ret > 0)
388                                 return ret;
389                         if (ret == 0)
390                                 break;
391                         dataoff += *matchoff;
392                 }
393                 *in_header = 0;
394         }
395
396         while (1) {
397                 ret = ct_sip_get_header(ct, dptr, dataoff, datalen,
398                                         type, matchoff, matchlen);
399                 if (ret > 0)
400                         break;
401                 if (ret == 0)
402                         return ret;
403                 dataoff += *matchoff;
404         }
405
406         if (in_header)
407                 *in_header = 1;
408         return 1;
409 }
410
411 /* Locate a SIP header, parse the URI and return the offset and length of
412  * the address as well as the address and port themselves. A stream of
413  * headers can be parsed by handing in a non-NULL datalen and in_header
414  * pointer.
415  */
416 int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr,
417                             unsigned int *dataoff, unsigned int datalen,
418                             enum sip_header_types type, int *in_header,
419                             unsigned int *matchoff, unsigned int *matchlen,
420                             union nf_inet_addr *addr, __be16 *port)
421 {
422         const char *c, *limit = dptr + datalen;
423         unsigned int p;
424         int ret;
425
426         ret = ct_sip_walk_headers(ct, dptr, dataoff ? *dataoff : 0, datalen,
427                                   type, in_header, matchoff, matchlen);
428         WARN_ON(ret < 0);
429         if (ret == 0)
430                 return ret;
431
432         if (!parse_addr(ct, dptr + *matchoff, &c, addr, limit))
433                 return -1;
434         if (*c == ':') {
435                 c++;
436                 p = simple_strtoul(c, (char **)&c, 10);
437                 if (p < 1024 || p > 65535)
438                         return -1;
439                 *port = htons(p);
440         } else
441                 *port = htons(SIP_PORT);
442
443         if (dataoff)
444                 *dataoff = c - dptr;
445         return 1;
446 }
447 EXPORT_SYMBOL_GPL(ct_sip_parse_header_uri);
448
449 /* SDP header parsing: a SDP session description contains an ordered set of
450  * headers, starting with a section containing general session parameters,
451  * optionally followed by multiple media descriptions.
452  *
453  * SDP headers always start at the beginning of a line. According to RFC 2327:
454  * "The sequence CRLF (0x0d0a) is used to end a record, although parsers should
455  * be tolerant and also accept records terminated with a single newline
456  * character". We handle both cases.
457  */
458 static const struct sip_header ct_sdp_hdrs[] = {
459         [SDP_HDR_VERSION]               = SDP_HDR("v=", NULL, digits_len),
460         [SDP_HDR_OWNER_IP4]             = SDP_HDR("o=", "IN IP4 ", epaddr_len),
461         [SDP_HDR_CONNECTION_IP4]        = SDP_HDR("c=", "IN IP4 ", epaddr_len),
462         [SDP_HDR_OWNER_IP6]             = SDP_HDR("o=", "IN IP6 ", epaddr_len),
463         [SDP_HDR_CONNECTION_IP6]        = SDP_HDR("c=", "IN IP6 ", epaddr_len),
464         [SDP_HDR_MEDIA]                 = SDP_HDR("m=", "audio ", digits_len),
465 };
466
467 /* Linear string search within SDP header values */
468 static const char *ct_sdp_header_search(const char *dptr, const char *limit,
469                                         const char *needle, unsigned int len)
470 {
471         for (limit -= len; dptr < limit; dptr++) {
472                 if (*dptr == '\r' || *dptr == '\n')
473                         break;
474                 if (strncmp(dptr, needle, len) == 0)
475                         return dptr;
476         }
477         return NULL;
478 }
479
480 /* Locate a SDP header (optionally a substring within the header value),
481  * optionally stopping at the first occurence of the term header, parse
482  * it and return the offset and length of the data we're interested in.
483  */
484 int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr,
485                           unsigned int dataoff, unsigned int datalen,
486                           enum sdp_header_types type,
487                           enum sdp_header_types term,
488                           unsigned int *matchoff, unsigned int *matchlen)
489 {
490         const struct sip_header *hdr = &ct_sdp_hdrs[type];
491         const struct sip_header *thdr = &ct_sdp_hdrs[term];
492         const char *start = dptr, *limit = dptr + datalen;
493         int shift = 0;
494
495         for (dptr += dataoff; dptr < limit; dptr++) {
496                 /* Find beginning of line */
497                 if (*dptr != '\r' && *dptr != '\n')
498                         continue;
499                 if (++dptr >= limit)
500                         break;
501                 if (*(dptr - 1) == '\r' && *dptr == '\n') {
502                         if (++dptr >= limit)
503                                 break;
504                 }
505
506                 if (term != SDP_HDR_UNSPEC &&
507                     limit - dptr >= thdr->len &&
508                     strnicmp(dptr, thdr->name, thdr->len) == 0)
509                         break;
510                 else if (limit - dptr >= hdr->len &&
511                          strnicmp(dptr, hdr->name, hdr->len) == 0)
512                         dptr += hdr->len;
513                 else
514                         continue;
515
516                 *matchoff = dptr - start;
517                 if (hdr->search) {
518                         dptr = ct_sdp_header_search(dptr, limit, hdr->search,
519                                                     hdr->slen);
520                         if (!dptr)
521                                 return -1;
522                         dptr += hdr->slen;
523                 }
524
525                 *matchlen = hdr->match_len(ct, dptr, limit, &shift);
526                 if (!*matchlen)
527                         return -1;
528                 *matchoff = dptr - start + shift;
529                 return 1;
530         }
531         return 0;
532 }
533 EXPORT_SYMBOL_GPL(ct_sip_get_sdp_header);
534
535 static int set_expected_rtp(struct sk_buff *skb,
536                             const char **dptr, unsigned int *datalen,
537                             union nf_inet_addr *addr, __be16 port)
538 {
539         struct nf_conntrack_expect *exp;
540         enum ip_conntrack_info ctinfo;
541         struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
542         enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
543         int family = ct->tuplehash[!dir].tuple.src.l3num;
544         int ret;
545         typeof(nf_nat_sdp_hook) nf_nat_sdp;
546
547         exp = nf_ct_expect_alloc(ct);
548         if (exp == NULL)
549                 return NF_DROP;
550         nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, family,
551                           &ct->tuplehash[!dir].tuple.src.u3, addr,
552                           IPPROTO_UDP, NULL, &port);
553
554         nf_nat_sdp = rcu_dereference(nf_nat_sdp_hook);
555         if (nf_nat_sdp && ct->status & IPS_NAT_MASK)
556                 ret = nf_nat_sdp(skb, dptr, datalen, exp);
557         else {
558                 if (nf_ct_expect_related(exp) != 0)
559                         ret = NF_DROP;
560                 else
561                         ret = NF_ACCEPT;
562         }
563         nf_ct_expect_put(exp);
564
565         return ret;
566 }
567
568 static int process_sdp(struct sk_buff *skb,
569                        const char **dptr, unsigned int *datalen)
570 {
571         enum ip_conntrack_info ctinfo;
572         struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
573         int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
574         unsigned int matchoff, matchlen;
575         union nf_inet_addr addr;
576         unsigned int port;
577         enum sdp_header_types type;
578
579         /* Get address and port from SDP packet. */
580         type = family == AF_INET ? SDP_HDR_CONNECTION_IP4 :
581                                    SDP_HDR_CONNECTION_IP6;
582
583         if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen,
584                                   type, SDP_HDR_UNSPEC,
585                                   &matchoff, &matchlen) <= 0)
586                 return NF_ACCEPT;
587
588         /* We'll drop only if there are parse problems. */
589         if (!parse_addr(ct, *dptr + matchoff, NULL, &addr, *dptr + *datalen))
590                 return NF_DROP;
591
592         if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen,
593                                   SDP_HDR_MEDIA, SDP_HDR_UNSPEC,
594                                   &matchoff, &matchlen) <= 0)
595                 return NF_ACCEPT;
596
597         port = simple_strtoul(*dptr + matchoff, NULL, 10);
598         if (port < 1024 || port > 65535)
599                 return NF_DROP;
600
601         return set_expected_rtp(skb, dptr, datalen, &addr, htons(port));
602 }
603
604 static int sip_help(struct sk_buff *skb,
605                     unsigned int protoff,
606                     struct nf_conn *ct,
607                     enum ip_conntrack_info ctinfo)
608 {
609         unsigned int dataoff, datalen;
610         const char *dptr;
611         typeof(nf_nat_sip_hook) nf_nat_sip;
612
613         /* No Data ? */
614         dataoff = protoff + sizeof(struct udphdr);
615         if (dataoff >= skb->len)
616                 return NF_ACCEPT;
617
618         nf_ct_refresh(ct, skb, sip_timeout * HZ);
619
620         if (!skb_is_nonlinear(skb))
621                 dptr = skb->data + dataoff;
622         else {
623                 pr_debug("Copy of skbuff not supported yet.\n");
624                 return NF_ACCEPT;
625         }
626
627         nf_nat_sip = rcu_dereference(nf_nat_sip_hook);
628         if (nf_nat_sip && ct->status & IPS_NAT_MASK) {
629                 if (!nf_nat_sip(skb, &dptr, &datalen))
630                         return NF_DROP;
631         }
632
633         datalen = skb->len - dataoff;
634         if (datalen < strlen("SIP/2.0 200"))
635                 return NF_ACCEPT;
636
637         /* RTP info only in some SDP pkts */
638         if (strnicmp(dptr, "INVITE", strlen("INVITE")) != 0 &&
639             strnicmp(dptr, "UPDATE", strlen("UPDATE")) != 0 &&
640             strnicmp(dptr, "SIP/2.0 180", strlen("SIP/2.0 180")) != 0 &&
641             strnicmp(dptr, "SIP/2.0 183", strlen("SIP/2.0 183")) != 0 &&
642             strnicmp(dptr, "SIP/2.0 200", strlen("SIP/2.0 200")) != 0)
643                 return NF_ACCEPT;
644
645         return process_sdp(skb, &dptr, &datalen);
646 }
647
648 static struct nf_conntrack_helper sip[MAX_PORTS][2] __read_mostly;
649 static char sip_names[MAX_PORTS][2][sizeof("sip-65535")] __read_mostly;
650
651 static const struct nf_conntrack_expect_policy sip_exp_policy = {
652         .max_expected   = 2,
653         .timeout        = 3 * 60,
654 };
655
656 static void nf_conntrack_sip_fini(void)
657 {
658         int i, j;
659
660         for (i = 0; i < ports_c; i++) {
661                 for (j = 0; j < 2; j++) {
662                         if (sip[i][j].me == NULL)
663                                 continue;
664                         nf_conntrack_helper_unregister(&sip[i][j]);
665                 }
666         }
667 }
668
669 static int __init nf_conntrack_sip_init(void)
670 {
671         int i, j, ret;
672         char *tmpname;
673
674         if (ports_c == 0)
675                 ports[ports_c++] = SIP_PORT;
676
677         for (i = 0; i < ports_c; i++) {
678                 memset(&sip[i], 0, sizeof(sip[i]));
679
680                 sip[i][0].tuple.src.l3num = AF_INET;
681                 sip[i][1].tuple.src.l3num = AF_INET6;
682                 for (j = 0; j < 2; j++) {
683                         sip[i][j].tuple.dst.protonum = IPPROTO_UDP;
684                         sip[i][j].tuple.src.u.udp.port = htons(ports[i]);
685                         sip[i][j].expect_policy = &sip_exp_policy;
686                         sip[i][j].me = THIS_MODULE;
687                         sip[i][j].help = sip_help;
688
689                         tmpname = &sip_names[i][j][0];
690                         if (ports[i] == SIP_PORT)
691                                 sprintf(tmpname, "sip");
692                         else
693                                 sprintf(tmpname, "sip-%u", i);
694                         sip[i][j].name = tmpname;
695
696                         pr_debug("port #%u: %u\n", i, ports[i]);
697
698                         ret = nf_conntrack_helper_register(&sip[i][j]);
699                         if (ret) {
700                                 printk("nf_ct_sip: failed to register helper "
701                                        "for pf: %u port: %u\n",
702                                        sip[i][j].tuple.src.l3num, ports[i]);
703                                 nf_conntrack_sip_fini();
704                                 return ret;
705                         }
706                 }
707         }
708         return 0;
709 }
710
711 module_init(nf_conntrack_sip_init);
712 module_exit(nf_conntrack_sip_fini);