1 /* SIP extension for IP connection tracking.
3 * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar>
4 * based on RR's ip_conntrack_ftp.c and other modules.
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.
11 #include <linux/module.h>
12 #include <linux/ctype.h>
13 #include <linux/skbuff.h>
14 #include <linux/inet.h>
16 #include <linux/udp.h>
17 #include <linux/netfilter.h>
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>
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");
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");
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");
39 unsigned int (*nf_nat_sip_hook
)(struct sk_buff
*skb
,
41 unsigned int *datalen
) __read_mostly
;
42 EXPORT_SYMBOL_GPL(nf_nat_sip_hook
);
44 unsigned int (*nf_nat_sdp_hook
)(struct sk_buff
*skb
,
46 unsigned int *datalen
,
47 struct nf_conntrack_expect
*exp
) __read_mostly
;
48 EXPORT_SYMBOL_GPL(nf_nat_sdp_hook
);
50 static int string_len(const struct nf_conn
*ct
, const char *dptr
,
51 const char *limit
, int *shift
)
55 while (dptr
< limit
&& isalpha(*dptr
)) {
62 static int digits_len(const struct nf_conn
*ct
, const char *dptr
,
63 const char *limit
, int *shift
)
66 while (dptr
< limit
&& isdigit(*dptr
)) {
73 static int parse_addr(const struct nf_conn
*ct
, const char *cp
,
74 const char **endp
, union nf_inet_addr
*addr
,
78 int family
= ct
->tuplehash
[IP_CT_DIR_ORIGINAL
].tuple
.src
.l3num
;
83 ret
= in4_pton(cp
, limit
- cp
, (u8
*)&addr
->ip
, -1, &end
);
86 ret
= in6_pton(cp
, limit
- cp
, (u8
*)&addr
->ip6
, -1, &end
);
92 if (ret
== 0 || end
== cp
)
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
)
103 union nf_inet_addr addr
;
104 const char *aux
= dptr
;
106 if (!parse_addr(ct
, dptr
, &dptr
, &addr
, limit
)) {
107 pr_debug("ip: %s parse failed.!\n", dptr
);
114 dptr
+= digits_len(ct
, dptr
, limit
, shift
);
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
)
123 const char *start
= dptr
;
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') {
135 if (dptr
< limit
&& *dptr
== '@') {
143 return epaddr_len(ct
, dptr
, limit
, shift
);
146 /* Parse a SIP request line of the form:
148 * Request-Line = Method SP Request-URI SP SIP-Version CRLF
150 * and return the offset and length of the address contained in the Request-URI.
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
)
157 const char *start
= dptr
, *limit
= dptr
+ datalen
, *end
;
162 /* Skip method and following whitespace */
163 mlen
= string_len(ct
, dptr
, limit
, NULL
);
171 limit
-= strlen("sip:");
172 for (; dptr
< limit
; dptr
++) {
173 if (*dptr
== '\r' || *dptr
== '\n')
175 if (strnicmp(dptr
, "sip:", strlen("sip:")) == 0)
178 if (!skp_epaddr_len(ct
, dptr
, limit
, &shift
))
182 if (!parse_addr(ct
, dptr
, &end
, addr
, limit
))
184 if (end
< limit
&& *end
== ':') {
186 p
= simple_strtoul(end
, (char **)&end
, 10);
187 if (p
< 1024 || p
> 65535)
191 *port
= htons(SIP_PORT
);
195 *matchoff
= dptr
- start
;
196 *matchlen
= end
- dptr
;
199 EXPORT_SYMBOL_GPL(ct_sip_parse_request
);
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.
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
211 * Some headers may appear multiple times. A comma seperated list of values is
212 * equivalent to multiple headers.
214 static const struct sip_header ct_sip_hdrs
[] = {
215 [SIP_HDR_CSEQ
] = SIP_HDR("CSeq", NULL
, NULL
, digits_len
),
216 [SIP_HDR_FROM
] = SIP_HDR("From", "f", "sip:", skp_epaddr_len
),
217 [SIP_HDR_TO
] = SIP_HDR("To", "t", "sip:", skp_epaddr_len
),
218 [SIP_HDR_CONTACT
] = SIP_HDR("Contact", "m", "sip:", skp_epaddr_len
),
219 [SIP_HDR_VIA
] = SIP_HDR("Via", "v", "UDP ", epaddr_len
),
220 [SIP_HDR_CONTENT_LENGTH
] = SIP_HDR("Content-Length", "l", NULL
, digits_len
),
223 static const char *sip_follow_continuation(const char *dptr
, const char *limit
)
225 /* Walk past newline */
229 /* Skip '\n' in CR LF */
230 if (*(dptr
- 1) == '\r' && *dptr
== '\n') {
235 /* Continuation line? */
236 if (*dptr
!= ' ' && *dptr
!= '\t')
239 /* skip leading whitespace */
240 for (; dptr
< limit
; dptr
++) {
241 if (*dptr
!= ' ' && *dptr
!= '\t')
247 static const char *sip_skip_whitespace(const char *dptr
, const char *limit
)
249 for (; dptr
< limit
; dptr
++) {
252 if (*dptr
!= '\r' && *dptr
!= '\n')
254 dptr
= sip_follow_continuation(dptr
, limit
);
261 /* Search within a SIP header value, dealing with continuation lines */
262 static const char *ct_sip_header_search(const char *dptr
, const char *limit
,
263 const char *needle
, unsigned int len
)
265 for (limit
-= len
; dptr
< limit
; dptr
++) {
266 if (*dptr
== '\r' || *dptr
== '\n') {
267 dptr
= sip_follow_continuation(dptr
, limit
);
273 if (strnicmp(dptr
, needle
, len
) == 0)
279 int ct_sip_get_header(const struct nf_conn
*ct
, const char *dptr
,
280 unsigned int dataoff
, unsigned int datalen
,
281 enum sip_header_types type
,
282 unsigned int *matchoff
, unsigned int *matchlen
)
284 const struct sip_header
*hdr
= &ct_sip_hdrs
[type
];
285 const char *start
= dptr
, *limit
= dptr
+ datalen
;
288 for (dptr
+= dataoff
; dptr
< limit
; dptr
++) {
289 /* Find beginning of line */
290 if (*dptr
!= '\r' && *dptr
!= '\n')
294 if (*(dptr
- 1) == '\r' && *dptr
== '\n') {
299 /* Skip continuation lines */
300 if (*dptr
== ' ' || *dptr
== '\t')
303 /* Find header. Compact headers must be followed by a
304 * non-alphabetic character to avoid mismatches. */
305 if (limit
- dptr
>= hdr
->len
&&
306 strnicmp(dptr
, hdr
->name
, hdr
->len
) == 0)
308 else if (hdr
->cname
&& limit
- dptr
>= hdr
->clen
+ 1 &&
309 strnicmp(dptr
, hdr
->cname
, hdr
->clen
) == 0 &&
310 !isalpha(*(dptr
+ hdr
->clen
+ 1)))
315 /* Find and skip colon */
316 dptr
= sip_skip_whitespace(dptr
, limit
);
319 if (*dptr
!= ':' || ++dptr
>= limit
)
322 /* Skip whitespace after colon */
323 dptr
= sip_skip_whitespace(dptr
, limit
);
327 *matchoff
= dptr
- start
;
329 dptr
= ct_sip_header_search(dptr
, limit
, hdr
->search
,
336 *matchlen
= hdr
->match_len(ct
, dptr
, limit
, &shift
);
339 *matchoff
= dptr
- start
+ shift
;
344 EXPORT_SYMBOL_GPL(ct_sip_get_header
);
346 /* Get next header field in a list of comma seperated values */
347 static int ct_sip_next_header(const struct nf_conn
*ct
, const char *dptr
,
348 unsigned int dataoff
, unsigned int datalen
,
349 enum sip_header_types type
,
350 unsigned int *matchoff
, unsigned int *matchlen
)
352 const struct sip_header
*hdr
= &ct_sip_hdrs
[type
];
353 const char *start
= dptr
, *limit
= dptr
+ datalen
;
358 dptr
= ct_sip_header_search(dptr
, limit
, ",", strlen(","));
362 dptr
= ct_sip_header_search(dptr
, limit
, hdr
->search
, hdr
->slen
);
367 *matchoff
= dptr
- start
;
368 *matchlen
= hdr
->match_len(ct
, dptr
, limit
, &shift
);
375 /* Walk through headers until a parsable one is found or no header of the
376 * given type is left. */
377 static int ct_sip_walk_headers(const struct nf_conn
*ct
, const char *dptr
,
378 unsigned int dataoff
, unsigned int datalen
,
379 enum sip_header_types type
, int *in_header
,
380 unsigned int *matchoff
, unsigned int *matchlen
)
384 if (in_header
&& *in_header
) {
386 ret
= ct_sip_next_header(ct
, dptr
, dataoff
, datalen
,
387 type
, matchoff
, matchlen
);
392 dataoff
+= *matchoff
;
398 ret
= ct_sip_get_header(ct
, dptr
, dataoff
, datalen
,
399 type
, matchoff
, matchlen
);
404 dataoff
+= *matchoff
;
412 /* Locate a SIP header, parse the URI and return the offset and length of
413 * the address as well as the address and port themselves. A stream of
414 * headers can be parsed by handing in a non-NULL datalen and in_header
417 int ct_sip_parse_header_uri(const struct nf_conn
*ct
, const char *dptr
,
418 unsigned int *dataoff
, unsigned int datalen
,
419 enum sip_header_types type
, int *in_header
,
420 unsigned int *matchoff
, unsigned int *matchlen
,
421 union nf_inet_addr
*addr
, __be16
*port
)
423 const char *c
, *limit
= dptr
+ datalen
;
427 ret
= ct_sip_walk_headers(ct
, dptr
, dataoff
? *dataoff
: 0, datalen
,
428 type
, in_header
, matchoff
, matchlen
);
433 if (!parse_addr(ct
, dptr
+ *matchoff
, &c
, addr
, limit
))
437 p
= simple_strtoul(c
, (char **)&c
, 10);
438 if (p
< 1024 || p
> 65535)
442 *port
= htons(SIP_PORT
);
448 EXPORT_SYMBOL_GPL(ct_sip_parse_header_uri
);
450 /* SDP header parsing: a SDP session description contains an ordered set of
451 * headers, starting with a section containing general session parameters,
452 * optionally followed by multiple media descriptions.
454 * SDP headers always start at the beginning of a line. According to RFC 2327:
455 * "The sequence CRLF (0x0d0a) is used to end a record, although parsers should
456 * be tolerant and also accept records terminated with a single newline
457 * character". We handle both cases.
459 static const struct sip_header ct_sdp_hdrs
[] = {
460 [SDP_HDR_VERSION
] = SDP_HDR("v=", NULL
, digits_len
),
461 [SDP_HDR_OWNER_IP4
] = SDP_HDR("o=", "IN IP4 ", epaddr_len
),
462 [SDP_HDR_CONNECTION_IP4
] = SDP_HDR("c=", "IN IP4 ", epaddr_len
),
463 [SDP_HDR_OWNER_IP6
] = SDP_HDR("o=", "IN IP6 ", epaddr_len
),
464 [SDP_HDR_CONNECTION_IP6
] = SDP_HDR("c=", "IN IP6 ", epaddr_len
),
465 [SDP_HDR_MEDIA
] = SDP_HDR("m=", "audio ", digits_len
),
468 /* Linear string search within SDP header values */
469 static const char *ct_sdp_header_search(const char *dptr
, const char *limit
,
470 const char *needle
, unsigned int len
)
472 for (limit
-= len
; dptr
< limit
; dptr
++) {
473 if (*dptr
== '\r' || *dptr
== '\n')
475 if (strncmp(dptr
, needle
, len
) == 0)
481 /* Locate a SDP header (optionally a substring within the header value),
482 * optionally stopping at the first occurence of the term header, parse
483 * it and return the offset and length of the data we're interested in.
485 int ct_sip_get_sdp_header(const struct nf_conn
*ct
, const char *dptr
,
486 unsigned int dataoff
, unsigned int datalen
,
487 enum sdp_header_types type
,
488 enum sdp_header_types term
,
489 unsigned int *matchoff
, unsigned int *matchlen
)
491 const struct sip_header
*hdr
= &ct_sdp_hdrs
[type
];
492 const struct sip_header
*thdr
= &ct_sdp_hdrs
[term
];
493 const char *start
= dptr
, *limit
= dptr
+ datalen
;
496 for (dptr
+= dataoff
; dptr
< limit
; dptr
++) {
497 /* Find beginning of line */
498 if (*dptr
!= '\r' && *dptr
!= '\n')
502 if (*(dptr
- 1) == '\r' && *dptr
== '\n') {
507 if (term
!= SDP_HDR_UNSPEC
&&
508 limit
- dptr
>= thdr
->len
&&
509 strnicmp(dptr
, thdr
->name
, thdr
->len
) == 0)
511 else if (limit
- dptr
>= hdr
->len
&&
512 strnicmp(dptr
, hdr
->name
, hdr
->len
) == 0)
517 *matchoff
= dptr
- start
;
519 dptr
= ct_sdp_header_search(dptr
, limit
, hdr
->search
,
526 *matchlen
= hdr
->match_len(ct
, dptr
, limit
, &shift
);
529 *matchoff
= dptr
- start
+ shift
;
534 EXPORT_SYMBOL_GPL(ct_sip_get_sdp_header
);
536 static int set_expected_rtp(struct sk_buff
*skb
,
537 const char **dptr
, unsigned int *datalen
,
538 union nf_inet_addr
*addr
, __be16 port
)
540 struct nf_conntrack_expect
*exp
;
541 enum ip_conntrack_info ctinfo
;
542 struct nf_conn
*ct
= nf_ct_get(skb
, &ctinfo
);
543 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
544 int family
= ct
->tuplehash
[!dir
].tuple
.src
.l3num
;
546 typeof(nf_nat_sdp_hook
) nf_nat_sdp
;
548 exp
= nf_ct_expect_alloc(ct
);
551 nf_ct_expect_init(exp
, NF_CT_EXPECT_CLASS_DEFAULT
, family
,
552 &ct
->tuplehash
[!dir
].tuple
.src
.u3
, addr
,
553 IPPROTO_UDP
, NULL
, &port
);
555 nf_nat_sdp
= rcu_dereference(nf_nat_sdp_hook
);
556 if (nf_nat_sdp
&& ct
->status
& IPS_NAT_MASK
)
557 ret
= nf_nat_sdp(skb
, dptr
, datalen
, exp
);
559 if (nf_ct_expect_related(exp
) != 0)
564 nf_ct_expect_put(exp
);
569 static int process_sdp(struct sk_buff
*skb
,
570 const char **dptr
, unsigned int *datalen
,
573 enum ip_conntrack_info ctinfo
;
574 struct nf_conn
*ct
= nf_ct_get(skb
, &ctinfo
);
575 int family
= ct
->tuplehash
[IP_CT_DIR_ORIGINAL
].tuple
.src
.l3num
;
576 unsigned int matchoff
, matchlen
;
577 union nf_inet_addr addr
;
579 enum sdp_header_types type
;
581 /* Get address and port from SDP packet. */
582 type
= family
== AF_INET
? SDP_HDR_CONNECTION_IP4
:
583 SDP_HDR_CONNECTION_IP6
;
585 if (ct_sip_get_sdp_header(ct
, *dptr
, 0, *datalen
,
586 type
, SDP_HDR_UNSPEC
,
587 &matchoff
, &matchlen
) <= 0)
590 /* We'll drop only if there are parse problems. */
591 if (!parse_addr(ct
, *dptr
+ matchoff
, NULL
, &addr
, *dptr
+ *datalen
))
594 if (ct_sip_get_sdp_header(ct
, *dptr
, 0, *datalen
,
595 SDP_HDR_MEDIA
, SDP_HDR_UNSPEC
,
596 &matchoff
, &matchlen
) <= 0)
599 port
= simple_strtoul(*dptr
+ matchoff
, NULL
, 10);
600 if (port
< 1024 || port
> 65535)
603 return set_expected_rtp(skb
, dptr
, datalen
, &addr
, htons(port
));
605 static int process_invite_response(struct sk_buff
*skb
,
606 const char **dptr
, unsigned int *datalen
,
607 unsigned int cseq
, unsigned int code
)
609 if ((code
>= 100 && code
<= 199) ||
610 (code
>= 200 && code
<= 299))
611 return process_sdp(skb
, dptr
, datalen
, cseq
);
616 static int process_update_response(struct sk_buff
*skb
,
617 const char **dptr
, unsigned int *datalen
,
618 unsigned int cseq
, unsigned int code
)
620 if ((code
>= 100 && code
<= 199) ||
621 (code
>= 200 && code
<= 299))
622 return process_sdp(skb
, dptr
, datalen
, cseq
);
627 static const struct sip_handler sip_handlers
[] = {
628 SIP_HANDLER("INVITE", process_sdp
, process_invite_response
),
629 SIP_HANDLER("UPDATE", process_sdp
, process_update_response
),
632 static int process_sip_response(struct sk_buff
*skb
,
633 const char **dptr
, unsigned int *datalen
)
635 static const struct sip_handler
*handler
;
636 enum ip_conntrack_info ctinfo
;
637 struct nf_conn
*ct
= nf_ct_get(skb
, &ctinfo
);
638 unsigned int matchoff
, matchlen
;
639 unsigned int code
, cseq
, dataoff
, i
;
641 if (*datalen
< strlen("SIP/2.0 200"))
643 code
= simple_strtoul(*dptr
+ strlen("SIP/2.0 "), NULL
, 10);
647 if (ct_sip_get_header(ct
, *dptr
, 0, *datalen
, SIP_HDR_CSEQ
,
648 &matchoff
, &matchlen
) <= 0)
650 cseq
= simple_strtoul(*dptr
+ matchoff
, NULL
, 10);
653 dataoff
= matchoff
+ matchlen
+ 1;
655 for (i
= 0; i
< ARRAY_SIZE(sip_handlers
); i
++) {
656 handler
= &sip_handlers
[i
];
657 if (handler
->response
== NULL
)
659 if (*datalen
< dataoff
+ handler
->len
||
660 strnicmp(*dptr
+ dataoff
, handler
->method
, handler
->len
))
662 return handler
->response(skb
, dptr
, datalen
, cseq
, code
);
667 static int process_sip_request(struct sk_buff
*skb
,
668 const char **dptr
, unsigned int *datalen
)
670 static const struct sip_handler
*handler
;
671 enum ip_conntrack_info ctinfo
;
672 struct nf_conn
*ct
= nf_ct_get(skb
, &ctinfo
);
673 unsigned int matchoff
, matchlen
;
674 unsigned int cseq
, i
;
676 for (i
= 0; i
< ARRAY_SIZE(sip_handlers
); i
++) {
677 handler
= &sip_handlers
[i
];
678 if (handler
->request
== NULL
)
680 if (*datalen
< handler
->len
||
681 strnicmp(*dptr
, handler
->method
, handler
->len
))
684 if (ct_sip_get_header(ct
, *dptr
, 0, *datalen
, SIP_HDR_CSEQ
,
685 &matchoff
, &matchlen
) <= 0)
687 cseq
= simple_strtoul(*dptr
+ matchoff
, NULL
, 10);
691 return handler
->request(skb
, dptr
, datalen
, cseq
);
696 static int sip_help(struct sk_buff
*skb
,
697 unsigned int protoff
,
699 enum ip_conntrack_info ctinfo
)
701 unsigned int dataoff
, datalen
;
704 typeof(nf_nat_sip_hook
) nf_nat_sip
;
707 dataoff
= protoff
+ sizeof(struct udphdr
);
708 if (dataoff
>= skb
->len
)
711 nf_ct_refresh(ct
, skb
, sip_timeout
* HZ
);
713 if (!skb_is_nonlinear(skb
))
714 dptr
= skb
->data
+ dataoff
;
716 pr_debug("Copy of skbuff not supported yet.\n");
720 datalen
= skb
->len
- dataoff
;
721 if (datalen
< strlen("SIP/2.0 200"))
724 if (strnicmp(dptr
, "SIP/2.0 ", strlen("SIP/2.0 ")) != 0)
725 ret
= process_sip_request(skb
, &dptr
, &datalen
);
727 ret
= process_sip_response(skb
, &dptr
, &datalen
);
729 if (ret
== NF_ACCEPT
&& ct
->status
& IPS_NAT_MASK
) {
730 nf_nat_sip
= rcu_dereference(nf_nat_sip_hook
);
731 if (nf_nat_sip
&& !nf_nat_sip(skb
, &dptr
, &datalen
))
738 static struct nf_conntrack_helper sip
[MAX_PORTS
][2] __read_mostly
;
739 static char sip_names
[MAX_PORTS
][2][sizeof("sip-65535")] __read_mostly
;
741 static const struct nf_conntrack_expect_policy sip_exp_policy
= {
746 static void nf_conntrack_sip_fini(void)
750 for (i
= 0; i
< ports_c
; i
++) {
751 for (j
= 0; j
< 2; j
++) {
752 if (sip
[i
][j
].me
== NULL
)
754 nf_conntrack_helper_unregister(&sip
[i
][j
]);
759 static int __init
nf_conntrack_sip_init(void)
765 ports
[ports_c
++] = SIP_PORT
;
767 for (i
= 0; i
< ports_c
; i
++) {
768 memset(&sip
[i
], 0, sizeof(sip
[i
]));
770 sip
[i
][0].tuple
.src
.l3num
= AF_INET
;
771 sip
[i
][1].tuple
.src
.l3num
= AF_INET6
;
772 for (j
= 0; j
< 2; j
++) {
773 sip
[i
][j
].tuple
.dst
.protonum
= IPPROTO_UDP
;
774 sip
[i
][j
].tuple
.src
.u
.udp
.port
= htons(ports
[i
]);
775 sip
[i
][j
].expect_policy
= &sip_exp_policy
;
776 sip
[i
][j
].me
= THIS_MODULE
;
777 sip
[i
][j
].help
= sip_help
;
779 tmpname
= &sip_names
[i
][j
][0];
780 if (ports
[i
] == SIP_PORT
)
781 sprintf(tmpname
, "sip");
783 sprintf(tmpname
, "sip-%u", i
);
784 sip
[i
][j
].name
= tmpname
;
786 pr_debug("port #%u: %u\n", i
, ports
[i
]);
788 ret
= nf_conntrack_helper_register(&sip
[i
][j
]);
790 printk("nf_ct_sip: failed to register helper "
791 "for pf: %u port: %u\n",
792 sip
[i
][j
].tuple
.src
.l3num
, ports
[i
]);
793 nf_conntrack_sip_fini();
801 module_init(nf_conntrack_sip_init
);
802 module_exit(nf_conntrack_sip_fini
);