]>
Commit | Line | Data |
---|---|---|
9fafcd7b PM |
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> | |
1863f096 | 17 | #include <linux/netfilter.h> |
9fafcd7b PM |
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 | ||
9fafcd7b PM |
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]; | |
2f0d2f10 | 31 | static unsigned int ports_c; |
9fafcd7b PM |
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 | ||
3db05fea | 39 | unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb, |
2a6cfb22 PM |
40 | const char **dptr, |
41 | unsigned int *datalen) __read_mostly; | |
9fafcd7b PM |
42 | EXPORT_SYMBOL_GPL(nf_nat_sip_hook); |
43 | ||
3db05fea | 44 | unsigned int (*nf_nat_sdp_hook)(struct sk_buff *skb, |
2a6cfb22 | 45 | const char **dptr, |
212440a7 PM |
46 | unsigned int *datalen, |
47 | struct nf_conntrack_expect *exp) __read_mostly; | |
9fafcd7b PM |
48 | EXPORT_SYMBOL_GPL(nf_nat_sdp_hook); |
49 | ||
13f7d63c JE |
50 | static int digits_len(const struct nf_conn *, const char *, const char *, int *); |
51 | static int epaddr_len(const struct nf_conn *, const char *, const char *, int *); | |
52 | static int skp_digits_len(const struct nf_conn *, const char *, const char *, int *); | |
53 | static int skp_epaddr_len(const struct nf_conn *, const char *, const char *, int *); | |
9fafcd7b PM |
54 | |
55 | struct 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; | |
13f7d63c | 63 | int (*match_len)(const struct nf_conn *, const char *, |
9fafcd7b PM |
64 | const char *, int *); |
65 | }; | |
66 | ||
67 | static 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 | }, | |
127 | [POS_MEDIA] = { /* SDP media info */ | |
128 | .case_sensitive = 1, | |
129 | .lname = "\nm=", | |
130 | .lnlen = sizeof("\nm=") - 1, | |
131 | .sname = "\rm=", | |
132 | .snlen = sizeof("\rm=") - 1, | |
133 | .ln_str = "audio ", | |
134 | .ln_strlen = sizeof("audio ") - 1, | |
135 | .match_len = digits_len | |
136 | }, | |
137 | [POS_OWNER_IP4] = { /* SDP owner address*/ | |
138 | .case_sensitive = 1, | |
139 | .lname = "\no=", | |
140 | .lnlen = sizeof("\no=") - 1, | |
141 | .sname = "\ro=", | |
142 | .snlen = sizeof("\ro=") - 1, | |
143 | .ln_str = "IN IP4 ", | |
144 | .ln_strlen = sizeof("IN IP4 ") - 1, | |
145 | .match_len = epaddr_len | |
146 | }, | |
147 | [POS_CONNECTION_IP4] = {/* SDP connection info */ | |
148 | .case_sensitive = 1, | |
149 | .lname = "\nc=", | |
150 | .lnlen = sizeof("\nc=") - 1, | |
151 | .sname = "\rc=", | |
152 | .snlen = sizeof("\rc=") - 1, | |
153 | .ln_str = "IN IP4 ", | |
154 | .ln_strlen = sizeof("IN IP4 ") - 1, | |
155 | .match_len = epaddr_len | |
156 | }, | |
157 | [POS_OWNER_IP6] = { /* SDP owner address*/ | |
158 | .case_sensitive = 1, | |
159 | .lname = "\no=", | |
160 | .lnlen = sizeof("\no=") - 1, | |
161 | .sname = "\ro=", | |
162 | .snlen = sizeof("\ro=") - 1, | |
163 | .ln_str = "IN IP6 ", | |
164 | .ln_strlen = sizeof("IN IP6 ") - 1, | |
165 | .match_len = epaddr_len | |
166 | }, | |
167 | [POS_CONNECTION_IP6] = {/* SDP connection info */ | |
168 | .case_sensitive = 1, | |
169 | .lname = "\nc=", | |
170 | .lnlen = sizeof("\nc=") - 1, | |
171 | .sname = "\rc=", | |
172 | .snlen = sizeof("\rc=") - 1, | |
173 | .ln_str = "IN IP6 ", | |
174 | .ln_strlen = sizeof("IN IP6 ") - 1, | |
175 | .match_len = epaddr_len | |
176 | }, | |
177 | [POS_SDP_HEADER] = { /* SDP version header */ | |
178 | .case_sensitive = 1, | |
179 | .lname = "\nv=", | |
180 | .lnlen = sizeof("\nv=") - 1, | |
181 | .sname = "\rv=", | |
182 | .snlen = sizeof("\rv=") - 1, | |
183 | .ln_str = "=", | |
184 | .ln_strlen = sizeof("=") - 1, | |
185 | .match_len = digits_len | |
186 | } | |
187 | }; | |
188 | ||
c8238177 | 189 | /* get line length until first CR or LF seen. */ |
9fafcd7b PM |
190 | int ct_sip_lnlen(const char *line, const char *limit) |
191 | { | |
192 | const char *k = line; | |
193 | ||
b1ec488b | 194 | while ((line < limit) && (*line == '\r' || *line == '\n')) |
9fafcd7b PM |
195 | line++; |
196 | ||
b1ec488b | 197 | while (line < limit) { |
9fafcd7b PM |
198 | if (*line == '\r' || *line == '\n') |
199 | break; | |
200 | line++; | |
201 | } | |
202 | return line - k; | |
203 | } | |
204 | EXPORT_SYMBOL_GPL(ct_sip_lnlen); | |
205 | ||
206 | /* Linear string search, case sensitive. */ | |
207 | const char *ct_sip_search(const char *needle, const char *haystack, | |
208 | size_t needle_len, size_t haystack_len, | |
209 | int case_sensitive) | |
210 | { | |
211 | const char *limit = haystack + (haystack_len - needle_len); | |
212 | ||
b1ec488b | 213 | while (haystack < limit) { |
9fafcd7b PM |
214 | if (case_sensitive) { |
215 | if (strncmp(haystack, needle, needle_len) == 0) | |
216 | return haystack; | |
217 | } else { | |
218 | if (strnicmp(haystack, needle, needle_len) == 0) | |
219 | return haystack; | |
220 | } | |
221 | haystack++; | |
222 | } | |
223 | return NULL; | |
224 | } | |
225 | EXPORT_SYMBOL_GPL(ct_sip_search); | |
226 | ||
13f7d63c | 227 | static int digits_len(const struct nf_conn *ct, const char *dptr, |
9fafcd7b PM |
228 | const char *limit, int *shift) |
229 | { | |
230 | int len = 0; | |
b1ec488b | 231 | while (dptr < limit && isdigit(*dptr)) { |
9fafcd7b PM |
232 | dptr++; |
233 | len++; | |
234 | } | |
235 | return len; | |
236 | } | |
237 | ||
c8238177 | 238 | /* get digits length, skipping blank spaces. */ |
13f7d63c | 239 | static int skp_digits_len(const struct nf_conn *ct, const char *dptr, |
9fafcd7b PM |
240 | const char *limit, int *shift) |
241 | { | |
b1ec488b | 242 | for (; dptr < limit && *dptr == ' '; dptr++) |
9fafcd7b PM |
243 | (*shift)++; |
244 | ||
245 | return digits_len(ct, dptr, limit, shift); | |
246 | } | |
247 | ||
13f7d63c JE |
248 | static int parse_addr(const struct nf_conn *ct, const char *cp, |
249 | const char **endp, union nf_inet_addr *addr, | |
250 | const char *limit) | |
9fafcd7b PM |
251 | { |
252 | const char *end; | |
253 | int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num; | |
254 | int ret = 0; | |
255 | ||
256 | switch (family) { | |
257 | case AF_INET: | |
258 | ret = in4_pton(cp, limit - cp, (u8 *)&addr->ip, -1, &end); | |
259 | break; | |
260 | case AF_INET6: | |
261 | ret = in6_pton(cp, limit - cp, (u8 *)&addr->ip6, -1, &end); | |
262 | break; | |
263 | default: | |
264 | BUG(); | |
265 | } | |
266 | ||
267 | if (ret == 0 || end == cp) | |
268 | return 0; | |
269 | if (endp) | |
270 | *endp = end; | |
271 | return 1; | |
272 | } | |
273 | ||
274 | /* skip ip address. returns its length. */ | |
13f7d63c | 275 | static int epaddr_len(const struct nf_conn *ct, const char *dptr, |
9fafcd7b PM |
276 | const char *limit, int *shift) |
277 | { | |
643a2c15 | 278 | union nf_inet_addr addr; |
9fafcd7b PM |
279 | const char *aux = dptr; |
280 | ||
281 | if (!parse_addr(ct, dptr, &dptr, &addr, limit)) { | |
0d53778e | 282 | pr_debug("ip: %s parse failed.!\n", dptr); |
9fafcd7b PM |
283 | return 0; |
284 | } | |
285 | ||
286 | /* Port number */ | |
287 | if (*dptr == ':') { | |
288 | dptr++; | |
289 | dptr += digits_len(ct, dptr, limit, shift); | |
290 | } | |
291 | return dptr - aux; | |
292 | } | |
293 | ||
294 | /* get address length, skiping user info. */ | |
13f7d63c | 295 | static int skp_epaddr_len(const struct nf_conn *ct, const char *dptr, |
9fafcd7b PM |
296 | const char *limit, int *shift) |
297 | { | |
aa584eda | 298 | const char *start = dptr; |
9fafcd7b PM |
299 | int s = *shift; |
300 | ||
7da5bfbb LI |
301 | /* Search for @, but stop at the end of the line. |
302 | * We are inside a sip: URI, so we don't need to worry about | |
303 | * continuation lines. */ | |
b1ec488b | 304 | while (dptr < limit && |
7da5bfbb | 305 | *dptr != '@' && *dptr != '\r' && *dptr != '\n') { |
9fafcd7b | 306 | (*shift)++; |
7da5bfbb LI |
307 | dptr++; |
308 | } | |
9fafcd7b | 309 | |
b1ec488b | 310 | if (dptr < limit && *dptr == '@') { |
9fafcd7b PM |
311 | dptr++; |
312 | (*shift)++; | |
aa584eda PM |
313 | } else { |
314 | dptr = start; | |
9fafcd7b | 315 | *shift = s; |
aa584eda | 316 | } |
9fafcd7b PM |
317 | |
318 | return epaddr_len(ct, dptr, limit, shift); | |
319 | } | |
320 | ||
321 | /* Returns 0 if not found, -1 error parsing. */ | |
13f7d63c | 322 | int ct_sip_get_info(const struct nf_conn *ct, |
9fafcd7b PM |
323 | const char *dptr, size_t dlen, |
324 | unsigned int *matchoff, | |
325 | unsigned int *matchlen, | |
326 | enum sip_header_pos pos) | |
327 | { | |
328 | const struct sip_header_nfo *hnfo = &ct_sip_hdrs[pos]; | |
329 | const char *limit, *aux, *k = dptr; | |
330 | int shift = 0; | |
331 | ||
332 | limit = dptr + (dlen - hnfo->lnlen); | |
333 | ||
b1ec488b | 334 | while (dptr < limit) { |
9fafcd7b | 335 | if ((strncmp(dptr, hnfo->lname, hnfo->lnlen) != 0) && |
465f90a4 PM |
336 | (hnfo->sname == NULL || |
337 | strncmp(dptr, hnfo->sname, hnfo->snlen) != 0)) { | |
9fafcd7b PM |
338 | dptr++; |
339 | continue; | |
340 | } | |
341 | aux = ct_sip_search(hnfo->ln_str, dptr, hnfo->ln_strlen, | |
601e68e1 | 342 | ct_sip_lnlen(dptr, limit), |
9fafcd7b PM |
343 | hnfo->case_sensitive); |
344 | if (!aux) { | |
0d53778e PM |
345 | pr_debug("'%s' not found in '%s'.\n", hnfo->ln_str, |
346 | hnfo->lname); | |
9fafcd7b PM |
347 | return -1; |
348 | } | |
349 | aux += hnfo->ln_strlen; | |
350 | ||
351 | *matchlen = hnfo->match_len(ct, aux, limit, &shift); | |
352 | if (!*matchlen) | |
353 | return -1; | |
354 | ||
355 | *matchoff = (aux - k) + shift; | |
356 | ||
0d53778e PM |
357 | pr_debug("%s match succeeded! - len: %u\n", hnfo->lname, |
358 | *matchlen); | |
9fafcd7b PM |
359 | return 1; |
360 | } | |
0d53778e | 361 | pr_debug("%s header not found.\n", hnfo->lname); |
9fafcd7b PM |
362 | return 0; |
363 | } | |
364 | EXPORT_SYMBOL_GPL(ct_sip_get_info); | |
365 | ||
3db05fea | 366 | static int set_expected_rtp(struct sk_buff *skb, |
212440a7 PM |
367 | const char **dptr, unsigned int *datalen, |
368 | union nf_inet_addr *addr, __be16 port) | |
9fafcd7b PM |
369 | { |
370 | struct nf_conntrack_expect *exp; | |
212440a7 PM |
371 | enum ip_conntrack_info ctinfo; |
372 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | |
9fafcd7b PM |
373 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); |
374 | int family = ct->tuplehash[!dir].tuple.src.l3num; | |
375 | int ret; | |
376 | typeof(nf_nat_sdp_hook) nf_nat_sdp; | |
377 | ||
6823645d | 378 | exp = nf_ct_expect_alloc(ct); |
9fafcd7b PM |
379 | if (exp == NULL) |
380 | return NF_DROP; | |
6002f266 | 381 | nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, family, |
6823645d PM |
382 | &ct->tuplehash[!dir].tuple.src.u3, addr, |
383 | IPPROTO_UDP, NULL, &port); | |
9fafcd7b PM |
384 | |
385 | nf_nat_sdp = rcu_dereference(nf_nat_sdp_hook); | |
386 | if (nf_nat_sdp && ct->status & IPS_NAT_MASK) | |
212440a7 | 387 | ret = nf_nat_sdp(skb, dptr, datalen, exp); |
9fafcd7b | 388 | else { |
6823645d | 389 | if (nf_ct_expect_related(exp) != 0) |
9fafcd7b PM |
390 | ret = NF_DROP; |
391 | else | |
392 | ret = NF_ACCEPT; | |
393 | } | |
6823645d | 394 | nf_ct_expect_put(exp); |
9fafcd7b PM |
395 | |
396 | return ret; | |
397 | } | |
398 | ||
3db05fea | 399 | static int sip_help(struct sk_buff *skb, |
9fafcd7b PM |
400 | unsigned int protoff, |
401 | struct nf_conn *ct, | |
402 | enum ip_conntrack_info ctinfo) | |
403 | { | |
404 | int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num; | |
643a2c15 | 405 | union nf_inet_addr addr; |
9fafcd7b PM |
406 | unsigned int dataoff, datalen; |
407 | const char *dptr; | |
408 | int ret = NF_ACCEPT; | |
2f0d2f10 | 409 | unsigned int matchoff, matchlen; |
9fafcd7b PM |
410 | u_int16_t port; |
411 | enum sip_header_pos pos; | |
412 | typeof(nf_nat_sip_hook) nf_nat_sip; | |
413 | ||
414 | /* No Data ? */ | |
415 | dataoff = protoff + sizeof(struct udphdr); | |
3db05fea | 416 | if (dataoff >= skb->len) |
9fafcd7b PM |
417 | return NF_ACCEPT; |
418 | ||
3db05fea | 419 | nf_ct_refresh(ct, skb, sip_timeout * HZ); |
9fafcd7b | 420 | |
3db05fea HX |
421 | if (!skb_is_nonlinear(skb)) |
422 | dptr = skb->data + dataoff; | |
9fafcd7b | 423 | else { |
0d53778e | 424 | pr_debug("Copy of skbuff not supported yet.\n"); |
9fafcd7b PM |
425 | goto out; |
426 | } | |
427 | ||
428 | nf_nat_sip = rcu_dereference(nf_nat_sip_hook); | |
429 | if (nf_nat_sip && ct->status & IPS_NAT_MASK) { | |
212440a7 | 430 | if (!nf_nat_sip(skb, &dptr, &datalen)) { |
9fafcd7b PM |
431 | ret = NF_DROP; |
432 | goto out; | |
433 | } | |
434 | } | |
435 | ||
3db05fea | 436 | datalen = skb->len - dataoff; |
9fafcd7b PM |
437 | if (datalen < sizeof("SIP/2.0 200") - 1) |
438 | goto out; | |
439 | ||
440 | /* RTP info only in some SDP pkts */ | |
441 | if (memcmp(dptr, "INVITE", sizeof("INVITE") - 1) != 0 && | |
d258131a JB |
442 | memcmp(dptr, "UPDATE", sizeof("UPDATE") - 1) != 0 && |
443 | memcmp(dptr, "SIP/2.0 180", sizeof("SIP/2.0 180") - 1) != 0 && | |
444 | memcmp(dptr, "SIP/2.0 183", sizeof("SIP/2.0 183") - 1) != 0 && | |
9fafcd7b PM |
445 | memcmp(dptr, "SIP/2.0 200", sizeof("SIP/2.0 200") - 1) != 0) { |
446 | goto out; | |
447 | } | |
448 | /* Get address and port from SDP packet. */ | |
449 | pos = family == AF_INET ? POS_CONNECTION_IP4 : POS_CONNECTION_IP6; | |
450 | if (ct_sip_get_info(ct, dptr, datalen, &matchoff, &matchlen, pos) > 0) { | |
451 | ||
452 | /* We'll drop only if there are parse problems. */ | |
453 | if (!parse_addr(ct, dptr + matchoff, NULL, &addr, | |
601e68e1 | 454 | dptr + datalen)) { |
9fafcd7b PM |
455 | ret = NF_DROP; |
456 | goto out; | |
457 | } | |
458 | if (ct_sip_get_info(ct, dptr, datalen, &matchoff, &matchlen, | |
601e68e1 | 459 | POS_MEDIA) > 0) { |
9fafcd7b PM |
460 | |
461 | port = simple_strtoul(dptr + matchoff, NULL, 10); | |
462 | if (port < 1024) { | |
463 | ret = NF_DROP; | |
464 | goto out; | |
465 | } | |
212440a7 PM |
466 | ret = set_expected_rtp(skb, &dptr, &datalen, |
467 | &addr, htons(port)); | |
9fafcd7b PM |
468 | } |
469 | } | |
470 | out: | |
471 | return ret; | |
472 | } | |
473 | ||
474 | static struct nf_conntrack_helper sip[MAX_PORTS][2] __read_mostly; | |
475 | static char sip_names[MAX_PORTS][2][sizeof("sip-65535")] __read_mostly; | |
476 | ||
6002f266 PM |
477 | static const struct nf_conntrack_expect_policy sip_exp_policy = { |
478 | .max_expected = 2, | |
479 | .timeout = 3 * 60, | |
480 | }; | |
481 | ||
9fafcd7b PM |
482 | static void nf_conntrack_sip_fini(void) |
483 | { | |
484 | int i, j; | |
485 | ||
486 | for (i = 0; i < ports_c; i++) { | |
487 | for (j = 0; j < 2; j++) { | |
488 | if (sip[i][j].me == NULL) | |
489 | continue; | |
490 | nf_conntrack_helper_unregister(&sip[i][j]); | |
491 | } | |
492 | } | |
493 | } | |
494 | ||
495 | static int __init nf_conntrack_sip_init(void) | |
496 | { | |
497 | int i, j, ret; | |
498 | char *tmpname; | |
499 | ||
500 | if (ports_c == 0) | |
501 | ports[ports_c++] = SIP_PORT; | |
502 | ||
503 | for (i = 0; i < ports_c; i++) { | |
504 | memset(&sip[i], 0, sizeof(sip[i])); | |
505 | ||
506 | sip[i][0].tuple.src.l3num = AF_INET; | |
507 | sip[i][1].tuple.src.l3num = AF_INET6; | |
508 | for (j = 0; j < 2; j++) { | |
509 | sip[i][j].tuple.dst.protonum = IPPROTO_UDP; | |
510 | sip[i][j].tuple.src.u.udp.port = htons(ports[i]); | |
6002f266 | 511 | sip[i][j].expect_policy = &sip_exp_policy; |
9fafcd7b PM |
512 | sip[i][j].me = THIS_MODULE; |
513 | sip[i][j].help = sip_help; | |
514 | ||
515 | tmpname = &sip_names[i][j][0]; | |
516 | if (ports[i] == SIP_PORT) | |
517 | sprintf(tmpname, "sip"); | |
518 | else | |
519 | sprintf(tmpname, "sip-%u", i); | |
520 | sip[i][j].name = tmpname; | |
521 | ||
0d53778e | 522 | pr_debug("port #%u: %u\n", i, ports[i]); |
9fafcd7b PM |
523 | |
524 | ret = nf_conntrack_helper_register(&sip[i][j]); | |
525 | if (ret) { | |
526 | printk("nf_ct_sip: failed to register helper " | |
527 | "for pf: %u port: %u\n", | |
528 | sip[i][j].tuple.src.l3num, ports[i]); | |
529 | nf_conntrack_sip_fini(); | |
530 | return ret; | |
531 | } | |
532 | } | |
533 | } | |
534 | return 0; | |
535 | } | |
536 | ||
537 | module_init(nf_conntrack_sip_init); | |
538 | module_exit(nf_conntrack_sip_fini); |