1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* SIP extension for IP connection tracking. |
3 | * |
4 | * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar> |
5 | * based on RR's ip_conntrack_ftp.c and other modules. |
6 | * (C) 2007 United Security Providers |
7 | * (C) 2007, 2008 Patrick McHardy <kaber@trash.net> |
8 | */ |
9 | |
10 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
11 | |
12 | #include <linux/module.h> |
13 | #include <linux/ctype.h> |
14 | #include <linux/skbuff.h> |
15 | #include <linux/inet.h> |
16 | #include <linux/in.h> |
17 | #include <linux/udp.h> |
18 | #include <linux/tcp.h> |
19 | #include <linux/netfilter.h> |
20 | #include <linux/netfilter_ipv4.h> |
21 | #include <linux/netfilter_ipv6.h> |
22 | |
23 | #include <net/netfilter/nf_conntrack.h> |
24 | #include <net/netfilter/nf_conntrack_core.h> |
25 | #include <net/netfilter/nf_conntrack_expect.h> |
26 | #include <net/netfilter/nf_conntrack_helper.h> |
27 | #include <net/netfilter/nf_conntrack_zones.h> |
28 | #include <linux/netfilter/nf_conntrack_sip.h> |
29 | |
30 | #define HELPER_NAME "sip" |
31 | |
32 | MODULE_LICENSE("GPL" ); |
33 | MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>" ); |
34 | MODULE_DESCRIPTION("SIP connection tracking helper" ); |
35 | MODULE_ALIAS("ip_conntrack_sip" ); |
36 | MODULE_ALIAS_NFCT_HELPER(HELPER_NAME); |
37 | |
38 | #define MAX_PORTS 8 |
39 | static unsigned short ports[MAX_PORTS]; |
40 | static unsigned int ports_c; |
41 | module_param_array(ports, ushort, &ports_c, 0400); |
42 | MODULE_PARM_DESC(ports, "port numbers of SIP servers" ); |
43 | |
44 | static unsigned int sip_timeout __read_mostly = SIP_TIMEOUT; |
45 | module_param(sip_timeout, uint, 0600); |
46 | MODULE_PARM_DESC(sip_timeout, "timeout for the master SIP session" ); |
47 | |
48 | static int sip_direct_signalling __read_mostly = 1; |
49 | module_param(sip_direct_signalling, int, 0600); |
50 | MODULE_PARM_DESC(sip_direct_signalling, "expect incoming calls from registrar " |
51 | "only (default 1)" ); |
52 | |
53 | static int sip_direct_media __read_mostly = 1; |
54 | module_param(sip_direct_media, int, 0600); |
55 | MODULE_PARM_DESC(sip_direct_media, "Expect Media streams between signalling " |
56 | "endpoints only (default 1)" ); |
57 | |
58 | static int sip_external_media __read_mostly = 0; |
59 | module_param(sip_external_media, int, 0600); |
60 | MODULE_PARM_DESC(sip_external_media, "Expect Media streams between external " |
61 | "endpoints (default 0)" ); |
62 | |
63 | const struct nf_nat_sip_hooks __rcu *nf_nat_sip_hooks; |
64 | EXPORT_SYMBOL_GPL(nf_nat_sip_hooks); |
65 | |
66 | static int string_len(const struct nf_conn *ct, const char *dptr, |
67 | const char *limit, int *shift) |
68 | { |
69 | int len = 0; |
70 | |
71 | while (dptr < limit && isalpha(*dptr)) { |
72 | dptr++; |
73 | len++; |
74 | } |
75 | return len; |
76 | } |
77 | |
78 | static int digits_len(const struct nf_conn *ct, const char *dptr, |
79 | const char *limit, int *shift) |
80 | { |
81 | int len = 0; |
82 | while (dptr < limit && isdigit(c: *dptr)) { |
83 | dptr++; |
84 | len++; |
85 | } |
86 | return len; |
87 | } |
88 | |
89 | static int iswordc(const char c) |
90 | { |
91 | if (isalnum(c) || c == '!' || c == '"' || c == '%' || |
92 | (c >= '(' && c <= '+') || c == ':' || c == '<' || c == '>' || |
93 | c == '?' || (c >= '[' && c <= ']') || c == '_' || c == '`' || |
94 | c == '{' || c == '}' || c == '~' || (c >= '-' && c <= '/') || |
95 | c == '\'') |
96 | return 1; |
97 | return 0; |
98 | } |
99 | |
100 | static int word_len(const char *dptr, const char *limit) |
101 | { |
102 | int len = 0; |
103 | while (dptr < limit && iswordc(c: *dptr)) { |
104 | dptr++; |
105 | len++; |
106 | } |
107 | return len; |
108 | } |
109 | |
110 | static int callid_len(const struct nf_conn *ct, const char *dptr, |
111 | const char *limit, int *shift) |
112 | { |
113 | int len, domain_len; |
114 | |
115 | len = word_len(dptr, limit); |
116 | dptr += len; |
117 | if (!len || dptr == limit || *dptr != '@') |
118 | return len; |
119 | dptr++; |
120 | len++; |
121 | |
122 | domain_len = word_len(dptr, limit); |
123 | if (!domain_len) |
124 | return 0; |
125 | return len + domain_len; |
126 | } |
127 | |
128 | /* get media type + port length */ |
129 | static int media_len(const struct nf_conn *ct, const char *dptr, |
130 | const char *limit, int *shift) |
131 | { |
132 | int len = string_len(ct, dptr, limit, shift); |
133 | |
134 | dptr += len; |
135 | if (dptr >= limit || *dptr != ' ') |
136 | return 0; |
137 | len++; |
138 | dptr++; |
139 | |
140 | return len + digits_len(ct, dptr, limit, shift); |
141 | } |
142 | |
143 | static int sip_parse_addr(const struct nf_conn *ct, const char *cp, |
144 | const char **endp, union nf_inet_addr *addr, |
145 | const char *limit, bool delim) |
146 | { |
147 | const char *end; |
148 | int ret; |
149 | |
150 | if (!ct) |
151 | return 0; |
152 | |
153 | memset(addr, 0, sizeof(*addr)); |
154 | switch (nf_ct_l3num(ct)) { |
155 | case AF_INET: |
156 | ret = in4_pton(src: cp, srclen: limit - cp, dst: (u8 *)&addr->ip, delim: -1, end: &end); |
157 | if (ret == 0) |
158 | return 0; |
159 | break; |
160 | case AF_INET6: |
161 | if (cp < limit && *cp == '[') |
162 | cp++; |
163 | else if (delim) |
164 | return 0; |
165 | |
166 | ret = in6_pton(src: cp, srclen: limit - cp, dst: (u8 *)&addr->ip6, delim: -1, end: &end); |
167 | if (ret == 0) |
168 | return 0; |
169 | |
170 | if (end < limit && *end == ']') |
171 | end++; |
172 | else if (delim) |
173 | return 0; |
174 | break; |
175 | default: |
176 | BUG(); |
177 | } |
178 | |
179 | if (endp) |
180 | *endp = end; |
181 | return 1; |
182 | } |
183 | |
184 | /* skip ip address. returns its length. */ |
185 | static int epaddr_len(const struct nf_conn *ct, const char *dptr, |
186 | const char *limit, int *shift) |
187 | { |
188 | union nf_inet_addr addr; |
189 | const char *aux = dptr; |
190 | |
191 | if (!sip_parse_addr(ct, cp: dptr, endp: &dptr, addr: &addr, limit, delim: true)) { |
192 | pr_debug("ip: %s parse failed.!\n" , dptr); |
193 | return 0; |
194 | } |
195 | |
196 | /* Port number */ |
197 | if (*dptr == ':') { |
198 | dptr++; |
199 | dptr += digits_len(ct, dptr, limit, shift); |
200 | } |
201 | return dptr - aux; |
202 | } |
203 | |
204 | /* get address length, skiping user info. */ |
205 | static int skp_epaddr_len(const struct nf_conn *ct, const char *dptr, |
206 | const char *limit, int *shift) |
207 | { |
208 | const char *start = dptr; |
209 | int s = *shift; |
210 | |
211 | /* Search for @, but stop at the end of the line. |
212 | * We are inside a sip: URI, so we don't need to worry about |
213 | * continuation lines. */ |
214 | while (dptr < limit && |
215 | *dptr != '@' && *dptr != '\r' && *dptr != '\n') { |
216 | (*shift)++; |
217 | dptr++; |
218 | } |
219 | |
220 | if (dptr < limit && *dptr == '@') { |
221 | dptr++; |
222 | (*shift)++; |
223 | } else { |
224 | dptr = start; |
225 | *shift = s; |
226 | } |
227 | |
228 | return epaddr_len(ct, dptr, limit, shift); |
229 | } |
230 | |
231 | /* Parse a SIP request line of the form: |
232 | * |
233 | * Request-Line = Method SP Request-URI SP SIP-Version CRLF |
234 | * |
235 | * and return the offset and length of the address contained in the Request-URI. |
236 | */ |
237 | int ct_sip_parse_request(const struct nf_conn *ct, |
238 | const char *dptr, unsigned int datalen, |
239 | unsigned int *matchoff, unsigned int *matchlen, |
240 | union nf_inet_addr *addr, __be16 *port) |
241 | { |
242 | const char *start = dptr, *limit = dptr + datalen, *end; |
243 | unsigned int mlen; |
244 | unsigned int p; |
245 | int shift = 0; |
246 | |
247 | /* Skip method and following whitespace */ |
248 | mlen = string_len(ct, dptr, limit, NULL); |
249 | if (!mlen) |
250 | return 0; |
251 | dptr += mlen; |
252 | if (++dptr >= limit) |
253 | return 0; |
254 | |
255 | /* Find SIP URI */ |
256 | for (; dptr < limit - strlen("sip:" ); dptr++) { |
257 | if (*dptr == '\r' || *dptr == '\n') |
258 | return -1; |
259 | if (strncasecmp(s1: dptr, s2: "sip:" , strlen("sip:" )) == 0) { |
260 | dptr += strlen("sip:" ); |
261 | break; |
262 | } |
263 | } |
264 | if (!skp_epaddr_len(ct, dptr, limit, shift: &shift)) |
265 | return 0; |
266 | dptr += shift; |
267 | |
268 | if (!sip_parse_addr(ct, cp: dptr, endp: &end, addr, limit, delim: true)) |
269 | return -1; |
270 | if (end < limit && *end == ':') { |
271 | end++; |
272 | p = simple_strtoul(end, (char **)&end, 10); |
273 | if (p < 1024 || p > 65535) |
274 | return -1; |
275 | *port = htons(p); |
276 | } else |
277 | *port = htons(SIP_PORT); |
278 | |
279 | if (end == dptr) |
280 | return 0; |
281 | *matchoff = dptr - start; |
282 | *matchlen = end - dptr; |
283 | return 1; |
284 | } |
285 | EXPORT_SYMBOL_GPL(ct_sip_parse_request); |
286 | |
287 | /* SIP header parsing: SIP headers are located at the beginning of a line, but |
288 | * may span several lines, in which case the continuation lines begin with a |
289 | * whitespace character. RFC 2543 allows lines to be terminated with CR, LF or |
290 | * CRLF, RFC 3261 allows only CRLF, we support both. |
291 | * |
292 | * Headers are followed by (optionally) whitespace, a colon, again (optionally) |
293 | * whitespace and the values. Whitespace in this context means any amount of |
294 | * tabs, spaces and continuation lines, which are treated as a single whitespace |
295 | * character. |
296 | * |
297 | * Some headers may appear multiple times. A comma separated list of values is |
298 | * equivalent to multiple headers. |
299 | */ |
300 | static const struct sip_header ct_sip_hdrs[] = { |
301 | [SIP_HDR_CSEQ] = SIP_HDR("CSeq" , NULL, NULL, digits_len), |
302 | [SIP_HDR_FROM] = SIP_HDR("From" , "f" , "sip:" , skp_epaddr_len), |
303 | [SIP_HDR_TO] = SIP_HDR("To" , "t" , "sip:" , skp_epaddr_len), |
304 | [SIP_HDR_CONTACT] = SIP_HDR("Contact" , "m" , "sip:" , skp_epaddr_len), |
305 | [SIP_HDR_VIA_UDP] = SIP_HDR("Via" , "v" , "UDP " , epaddr_len), |
306 | [SIP_HDR_VIA_TCP] = SIP_HDR("Via" , "v" , "TCP " , epaddr_len), |
307 | [SIP_HDR_EXPIRES] = SIP_HDR("Expires" , NULL, NULL, digits_len), |
308 | [SIP_HDR_CONTENT_LENGTH] = SIP_HDR("Content-Length" , "l" , NULL, digits_len), |
309 | [SIP_HDR_CALL_ID] = SIP_HDR("Call-Id" , "i" , NULL, callid_len), |
310 | }; |
311 | |
312 | static const char *sip_follow_continuation(const char *dptr, const char *limit) |
313 | { |
314 | /* Walk past newline */ |
315 | if (++dptr >= limit) |
316 | return NULL; |
317 | |
318 | /* Skip '\n' in CR LF */ |
319 | if (*(dptr - 1) == '\r' && *dptr == '\n') { |
320 | if (++dptr >= limit) |
321 | return NULL; |
322 | } |
323 | |
324 | /* Continuation line? */ |
325 | if (*dptr != ' ' && *dptr != '\t') |
326 | return NULL; |
327 | |
328 | /* skip leading whitespace */ |
329 | for (; dptr < limit; dptr++) { |
330 | if (*dptr != ' ' && *dptr != '\t') |
331 | break; |
332 | } |
333 | return dptr; |
334 | } |
335 | |
336 | static const char *sip_skip_whitespace(const char *dptr, const char *limit) |
337 | { |
338 | for (; dptr < limit; dptr++) { |
339 | if (*dptr == ' ' || *dptr == '\t') |
340 | continue; |
341 | if (*dptr != '\r' && *dptr != '\n') |
342 | break; |
343 | dptr = sip_follow_continuation(dptr, limit); |
344 | break; |
345 | } |
346 | return dptr; |
347 | } |
348 | |
349 | /* Search within a SIP header value, dealing with continuation lines */ |
350 | static const char *(const char *dptr, const char *limit, |
351 | const char *needle, unsigned int len) |
352 | { |
353 | for (limit -= len; dptr < limit; dptr++) { |
354 | if (*dptr == '\r' || *dptr == '\n') { |
355 | dptr = sip_follow_continuation(dptr, limit); |
356 | if (dptr == NULL) |
357 | break; |
358 | continue; |
359 | } |
360 | |
361 | if (strncasecmp(s1: dptr, s2: needle, n: len) == 0) |
362 | return dptr; |
363 | } |
364 | return NULL; |
365 | } |
366 | |
367 | int (const struct nf_conn *ct, const char *dptr, |
368 | unsigned int dataoff, unsigned int datalen, |
369 | enum sip_header_types type, |
370 | unsigned int *matchoff, unsigned int *matchlen) |
371 | { |
372 | const struct sip_header *hdr = &ct_sip_hdrs[type]; |
373 | const char *start = dptr, *limit = dptr + datalen; |
374 | int shift = 0; |
375 | |
376 | for (dptr += dataoff; dptr < limit; dptr++) { |
377 | /* Find beginning of line */ |
378 | if (*dptr != '\r' && *dptr != '\n') |
379 | continue; |
380 | if (++dptr >= limit) |
381 | break; |
382 | if (*(dptr - 1) == '\r' && *dptr == '\n') { |
383 | if (++dptr >= limit) |
384 | break; |
385 | } |
386 | |
387 | /* Skip continuation lines */ |
388 | if (*dptr == ' ' || *dptr == '\t') |
389 | continue; |
390 | |
391 | /* Find header. Compact headers must be followed by a |
392 | * non-alphabetic character to avoid mismatches. */ |
393 | if (limit - dptr >= hdr->len && |
394 | strncasecmp(s1: dptr, s2: hdr->name, n: hdr->len) == 0) |
395 | dptr += hdr->len; |
396 | else if (hdr->cname && limit - dptr >= hdr->clen + 1 && |
397 | strncasecmp(s1: dptr, s2: hdr->cname, n: hdr->clen) == 0 && |
398 | !isalpha(*(dptr + hdr->clen))) |
399 | dptr += hdr->clen; |
400 | else |
401 | continue; |
402 | |
403 | /* Find and skip colon */ |
404 | dptr = sip_skip_whitespace(dptr, limit); |
405 | if (dptr == NULL) |
406 | break; |
407 | if (*dptr != ':' || ++dptr >= limit) |
408 | break; |
409 | |
410 | /* Skip whitespace after colon */ |
411 | dptr = sip_skip_whitespace(dptr, limit); |
412 | if (dptr == NULL) |
413 | break; |
414 | |
415 | *matchoff = dptr - start; |
416 | if (hdr->search) { |
417 | dptr = ct_sip_header_search(dptr, limit, needle: hdr->search, |
418 | len: hdr->slen); |
419 | if (!dptr) |
420 | return -1; |
421 | dptr += hdr->slen; |
422 | } |
423 | |
424 | *matchlen = hdr->match_len(ct, dptr, limit, &shift); |
425 | if (!*matchlen) |
426 | return -1; |
427 | *matchoff = dptr - start + shift; |
428 | return 1; |
429 | } |
430 | return 0; |
431 | } |
432 | EXPORT_SYMBOL_GPL(ct_sip_get_header); |
433 | |
434 | /* Get next header field in a list of comma separated values */ |
435 | static int (const struct nf_conn *ct, const char *dptr, |
436 | unsigned int dataoff, unsigned int datalen, |
437 | enum sip_header_types type, |
438 | unsigned int *matchoff, unsigned int *matchlen) |
439 | { |
440 | const struct sip_header *hdr = &ct_sip_hdrs[type]; |
441 | const char *start = dptr, *limit = dptr + datalen; |
442 | int shift = 0; |
443 | |
444 | dptr += dataoff; |
445 | |
446 | dptr = ct_sip_header_search(dptr, limit, needle: "," , strlen("," )); |
447 | if (!dptr) |
448 | return 0; |
449 | |
450 | dptr = ct_sip_header_search(dptr, limit, needle: hdr->search, len: hdr->slen); |
451 | if (!dptr) |
452 | return 0; |
453 | dptr += hdr->slen; |
454 | |
455 | *matchoff = dptr - start; |
456 | *matchlen = hdr->match_len(ct, dptr, limit, &shift); |
457 | if (!*matchlen) |
458 | return -1; |
459 | *matchoff += shift; |
460 | return 1; |
461 | } |
462 | |
463 | /* Walk through headers until a parsable one is found or no header of the |
464 | * given type is left. */ |
465 | static int (const struct nf_conn *ct, const char *dptr, |
466 | unsigned int dataoff, unsigned int datalen, |
467 | enum sip_header_types type, int *, |
468 | unsigned int *matchoff, unsigned int *matchlen) |
469 | { |
470 | int ret; |
471 | |
472 | if (in_header && *in_header) { |
473 | while (1) { |
474 | ret = ct_sip_next_header(ct, dptr, dataoff, datalen, |
475 | type, matchoff, matchlen); |
476 | if (ret > 0) |
477 | return ret; |
478 | if (ret == 0) |
479 | break; |
480 | dataoff = *matchoff; |
481 | } |
482 | *in_header = 0; |
483 | } |
484 | |
485 | while (1) { |
486 | ret = ct_sip_get_header(ct, dptr, dataoff, datalen, |
487 | type, matchoff, matchlen); |
488 | if (ret > 0) |
489 | break; |
490 | if (ret == 0) |
491 | return ret; |
492 | dataoff = *matchoff; |
493 | } |
494 | |
495 | if (in_header) |
496 | *in_header = 1; |
497 | return 1; |
498 | } |
499 | |
500 | /* Locate a SIP header, parse the URI and return the offset and length of |
501 | * the address as well as the address and port themselves. A stream of |
502 | * headers can be parsed by handing in a non-NULL datalen and in_header |
503 | * pointer. |
504 | */ |
505 | int (const struct nf_conn *ct, const char *dptr, |
506 | unsigned int *dataoff, unsigned int datalen, |
507 | enum sip_header_types type, int *, |
508 | unsigned int *matchoff, unsigned int *matchlen, |
509 | union nf_inet_addr *addr, __be16 *port) |
510 | { |
511 | const char *c, *limit = dptr + datalen; |
512 | unsigned int p; |
513 | int ret; |
514 | |
515 | ret = ct_sip_walk_headers(ct, dptr, dataoff: dataoff ? *dataoff : 0, datalen, |
516 | type, in_header, matchoff, matchlen); |
517 | WARN_ON(ret < 0); |
518 | if (ret == 0) |
519 | return ret; |
520 | |
521 | if (!sip_parse_addr(ct, cp: dptr + *matchoff, endp: &c, addr, limit, delim: true)) |
522 | return -1; |
523 | if (*c == ':') { |
524 | c++; |
525 | p = simple_strtoul(c, (char **)&c, 10); |
526 | if (p < 1024 || p > 65535) |
527 | return -1; |
528 | *port = htons(p); |
529 | } else |
530 | *port = htons(SIP_PORT); |
531 | |
532 | if (dataoff) |
533 | *dataoff = c - dptr; |
534 | return 1; |
535 | } |
536 | EXPORT_SYMBOL_GPL(ct_sip_parse_header_uri); |
537 | |
538 | static int ct_sip_parse_param(const struct nf_conn *ct, const char *dptr, |
539 | unsigned int dataoff, unsigned int datalen, |
540 | const char *name, |
541 | unsigned int *matchoff, unsigned int *matchlen) |
542 | { |
543 | const char *limit = dptr + datalen; |
544 | const char *start; |
545 | const char *end; |
546 | |
547 | limit = ct_sip_header_search(dptr: dptr + dataoff, limit, needle: "," , strlen("," )); |
548 | if (!limit) |
549 | limit = dptr + datalen; |
550 | |
551 | start = ct_sip_header_search(dptr: dptr + dataoff, limit, needle: name, strlen(name)); |
552 | if (!start) |
553 | return 0; |
554 | start += strlen(name); |
555 | |
556 | end = ct_sip_header_search(dptr: start, limit, needle: ";" , strlen(";" )); |
557 | if (!end) |
558 | end = limit; |
559 | |
560 | *matchoff = start - dptr; |
561 | *matchlen = end - start; |
562 | return 1; |
563 | } |
564 | |
565 | /* Parse address from header parameter and return address, offset and length */ |
566 | int ct_sip_parse_address_param(const struct nf_conn *ct, const char *dptr, |
567 | unsigned int dataoff, unsigned int datalen, |
568 | const char *name, |
569 | unsigned int *matchoff, unsigned int *matchlen, |
570 | union nf_inet_addr *addr, bool delim) |
571 | { |
572 | const char *limit = dptr + datalen; |
573 | const char *start, *end; |
574 | |
575 | limit = ct_sip_header_search(dptr: dptr + dataoff, limit, needle: "," , strlen("," )); |
576 | if (!limit) |
577 | limit = dptr + datalen; |
578 | |
579 | start = ct_sip_header_search(dptr: dptr + dataoff, limit, needle: name, strlen(name)); |
580 | if (!start) |
581 | return 0; |
582 | |
583 | start += strlen(name); |
584 | if (!sip_parse_addr(ct, cp: start, endp: &end, addr, limit, delim)) |
585 | return 0; |
586 | *matchoff = start - dptr; |
587 | *matchlen = end - start; |
588 | return 1; |
589 | } |
590 | EXPORT_SYMBOL_GPL(ct_sip_parse_address_param); |
591 | |
592 | /* Parse numerical header parameter and return value, offset and length */ |
593 | int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr, |
594 | unsigned int dataoff, unsigned int datalen, |
595 | const char *name, |
596 | unsigned int *matchoff, unsigned int *matchlen, |
597 | unsigned int *val) |
598 | { |
599 | const char *limit = dptr + datalen; |
600 | const char *start; |
601 | char *end; |
602 | |
603 | limit = ct_sip_header_search(dptr: dptr + dataoff, limit, needle: "," , strlen("," )); |
604 | if (!limit) |
605 | limit = dptr + datalen; |
606 | |
607 | start = ct_sip_header_search(dptr: dptr + dataoff, limit, needle: name, strlen(name)); |
608 | if (!start) |
609 | return 0; |
610 | |
611 | start += strlen(name); |
612 | *val = simple_strtoul(start, &end, 0); |
613 | if (start == end) |
614 | return -1; |
615 | if (matchoff && matchlen) { |
616 | *matchoff = start - dptr; |
617 | *matchlen = end - start; |
618 | } |
619 | return 1; |
620 | } |
621 | EXPORT_SYMBOL_GPL(ct_sip_parse_numerical_param); |
622 | |
623 | static int ct_sip_parse_transport(struct nf_conn *ct, const char *dptr, |
624 | unsigned int dataoff, unsigned int datalen, |
625 | u8 *proto) |
626 | { |
627 | unsigned int matchoff, matchlen; |
628 | |
629 | if (ct_sip_parse_param(ct, dptr, dataoff, datalen, name: "transport=" , |
630 | matchoff: &matchoff, matchlen: &matchlen)) { |
631 | if (!strncasecmp(s1: dptr + matchoff, s2: "TCP" , strlen("TCP" ))) |
632 | *proto = IPPROTO_TCP; |
633 | else if (!strncasecmp(s1: dptr + matchoff, s2: "UDP" , strlen("UDP" ))) |
634 | *proto = IPPROTO_UDP; |
635 | else |
636 | return 0; |
637 | |
638 | if (*proto != nf_ct_protonum(ct)) |
639 | return 0; |
640 | } else |
641 | *proto = nf_ct_protonum(ct); |
642 | |
643 | return 1; |
644 | } |
645 | |
646 | static int sdp_parse_addr(const struct nf_conn *ct, const char *cp, |
647 | const char **endp, union nf_inet_addr *addr, |
648 | const char *limit) |
649 | { |
650 | const char *end; |
651 | int ret; |
652 | |
653 | memset(addr, 0, sizeof(*addr)); |
654 | switch (nf_ct_l3num(ct)) { |
655 | case AF_INET: |
656 | ret = in4_pton(src: cp, srclen: limit - cp, dst: (u8 *)&addr->ip, delim: -1, end: &end); |
657 | break; |
658 | case AF_INET6: |
659 | ret = in6_pton(src: cp, srclen: limit - cp, dst: (u8 *)&addr->ip6, delim: -1, end: &end); |
660 | break; |
661 | default: |
662 | BUG(); |
663 | } |
664 | |
665 | if (ret == 0) |
666 | return 0; |
667 | if (endp) |
668 | *endp = end; |
669 | return 1; |
670 | } |
671 | |
672 | /* skip ip address. returns its length. */ |
673 | static int sdp_addr_len(const struct nf_conn *ct, const char *dptr, |
674 | const char *limit, int *shift) |
675 | { |
676 | union nf_inet_addr addr; |
677 | const char *aux = dptr; |
678 | |
679 | if (!sdp_parse_addr(ct, cp: dptr, endp: &dptr, addr: &addr, limit)) { |
680 | pr_debug("ip: %s parse failed.!\n" , dptr); |
681 | return 0; |
682 | } |
683 | |
684 | return dptr - aux; |
685 | } |
686 | |
687 | /* SDP header parsing: a SDP session description contains an ordered set of |
688 | * headers, starting with a section containing general session parameters, |
689 | * optionally followed by multiple media descriptions. |
690 | * |
691 | * SDP headers always start at the beginning of a line. According to RFC 2327: |
692 | * "The sequence CRLF (0x0d0a) is used to end a record, although parsers should |
693 | * be tolerant and also accept records terminated with a single newline |
694 | * character". We handle both cases. |
695 | */ |
696 | static const struct sip_header ct_sdp_hdrs_v4[] = { |
697 | [SDP_HDR_VERSION] = SDP_HDR("v=" , NULL, digits_len), |
698 | [SDP_HDR_OWNER] = SDP_HDR("o=" , "IN IP4 " , sdp_addr_len), |
699 | [SDP_HDR_CONNECTION] = SDP_HDR("c=" , "IN IP4 " , sdp_addr_len), |
700 | [SDP_HDR_MEDIA] = SDP_HDR("m=" , NULL, media_len), |
701 | }; |
702 | |
703 | static const struct sip_header ct_sdp_hdrs_v6[] = { |
704 | [SDP_HDR_VERSION] = SDP_HDR("v=" , NULL, digits_len), |
705 | [SDP_HDR_OWNER] = SDP_HDR("o=" , "IN IP6 " , sdp_addr_len), |
706 | [SDP_HDR_CONNECTION] = SDP_HDR("c=" , "IN IP6 " , sdp_addr_len), |
707 | [SDP_HDR_MEDIA] = SDP_HDR("m=" , NULL, media_len), |
708 | }; |
709 | |
710 | /* Linear string search within SDP header values */ |
711 | static const char *(const char *dptr, const char *limit, |
712 | const char *needle, unsigned int len) |
713 | { |
714 | for (limit -= len; dptr < limit; dptr++) { |
715 | if (*dptr == '\r' || *dptr == '\n') |
716 | break; |
717 | if (strncmp(dptr, needle, len) == 0) |
718 | return dptr; |
719 | } |
720 | return NULL; |
721 | } |
722 | |
723 | /* Locate a SDP header (optionally a substring within the header value), |
724 | * optionally stopping at the first occurrence of the term header, parse |
725 | * it and return the offset and length of the data we're interested in. |
726 | */ |
727 | int (const struct nf_conn *ct, const char *dptr, |
728 | unsigned int dataoff, unsigned int datalen, |
729 | enum sdp_header_types type, |
730 | enum sdp_header_types term, |
731 | unsigned int *matchoff, unsigned int *matchlen) |
732 | { |
733 | const struct sip_header *hdrs, *hdr, *thdr; |
734 | const char *start = dptr, *limit = dptr + datalen; |
735 | int shift = 0; |
736 | |
737 | hdrs = nf_ct_l3num(ct) == NFPROTO_IPV4 ? ct_sdp_hdrs_v4 : ct_sdp_hdrs_v6; |
738 | hdr = &hdrs[type]; |
739 | thdr = &hdrs[term]; |
740 | |
741 | for (dptr += dataoff; dptr < limit; dptr++) { |
742 | /* Find beginning of line */ |
743 | if (*dptr != '\r' && *dptr != '\n') |
744 | continue; |
745 | if (++dptr >= limit) |
746 | break; |
747 | if (*(dptr - 1) == '\r' && *dptr == '\n') { |
748 | if (++dptr >= limit) |
749 | break; |
750 | } |
751 | |
752 | if (term != SDP_HDR_UNSPEC && |
753 | limit - dptr >= thdr->len && |
754 | strncasecmp(s1: dptr, s2: thdr->name, n: thdr->len) == 0) |
755 | break; |
756 | else if (limit - dptr >= hdr->len && |
757 | strncasecmp(s1: dptr, s2: hdr->name, n: hdr->len) == 0) |
758 | dptr += hdr->len; |
759 | else |
760 | continue; |
761 | |
762 | *matchoff = dptr - start; |
763 | if (hdr->search) { |
764 | dptr = ct_sdp_header_search(dptr, limit, needle: hdr->search, |
765 | len: hdr->slen); |
766 | if (!dptr) |
767 | return -1; |
768 | dptr += hdr->slen; |
769 | } |
770 | |
771 | *matchlen = hdr->match_len(ct, dptr, limit, &shift); |
772 | if (!*matchlen) |
773 | return -1; |
774 | *matchoff = dptr - start + shift; |
775 | return 1; |
776 | } |
777 | return 0; |
778 | } |
779 | EXPORT_SYMBOL_GPL(ct_sip_get_sdp_header); |
780 | |
781 | static int ct_sip_parse_sdp_addr(const struct nf_conn *ct, const char *dptr, |
782 | unsigned int dataoff, unsigned int datalen, |
783 | enum sdp_header_types type, |
784 | enum sdp_header_types term, |
785 | unsigned int *matchoff, unsigned int *matchlen, |
786 | union nf_inet_addr *addr) |
787 | { |
788 | int ret; |
789 | |
790 | ret = ct_sip_get_sdp_header(ct, dptr, dataoff, datalen, type, term, |
791 | matchoff, matchlen); |
792 | if (ret <= 0) |
793 | return ret; |
794 | |
795 | if (!sdp_parse_addr(ct, cp: dptr + *matchoff, NULL, addr, |
796 | limit: dptr + *matchoff + *matchlen)) |
797 | return -1; |
798 | return 1; |
799 | } |
800 | |
801 | static int refresh_signalling_expectation(struct nf_conn *ct, |
802 | union nf_inet_addr *addr, |
803 | u8 proto, __be16 port, |
804 | unsigned int expires) |
805 | { |
806 | struct nf_conn_help *help = nfct_help(ct); |
807 | struct nf_conntrack_expect *exp; |
808 | struct hlist_node *next; |
809 | int found = 0; |
810 | |
811 | spin_lock_bh(lock: &nf_conntrack_expect_lock); |
812 | hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) { |
813 | if (exp->class != SIP_EXPECT_SIGNALLING || |
814 | !nf_inet_addr_cmp(a1: &exp->tuple.dst.u3, a2: addr) || |
815 | exp->tuple.dst.protonum != proto || |
816 | exp->tuple.dst.u.udp.port != port) |
817 | continue; |
818 | if (mod_timer_pending(timer: &exp->timeout, expires: jiffies + expires * HZ)) { |
819 | exp->flags &= ~NF_CT_EXPECT_INACTIVE; |
820 | found = 1; |
821 | break; |
822 | } |
823 | } |
824 | spin_unlock_bh(lock: &nf_conntrack_expect_lock); |
825 | return found; |
826 | } |
827 | |
828 | static void flush_expectations(struct nf_conn *ct, bool media) |
829 | { |
830 | struct nf_conn_help *help = nfct_help(ct); |
831 | struct nf_conntrack_expect *exp; |
832 | struct hlist_node *next; |
833 | |
834 | spin_lock_bh(lock: &nf_conntrack_expect_lock); |
835 | hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) { |
836 | if ((exp->class != SIP_EXPECT_SIGNALLING) ^ media) |
837 | continue; |
838 | if (!nf_ct_remove_expect(exp)) |
839 | continue; |
840 | if (!media) |
841 | break; |
842 | } |
843 | spin_unlock_bh(lock: &nf_conntrack_expect_lock); |
844 | } |
845 | |
846 | static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, |
847 | unsigned int dataoff, |
848 | const char **dptr, unsigned int *datalen, |
849 | union nf_inet_addr *daddr, __be16 port, |
850 | enum sip_expectation_classes class, |
851 | unsigned int mediaoff, unsigned int medialen) |
852 | { |
853 | struct nf_conntrack_expect *exp, *rtp_exp, *rtcp_exp; |
854 | enum ip_conntrack_info ctinfo; |
855 | struct nf_conn *ct = nf_ct_get(skb, ctinfo: &ctinfo); |
856 | struct net *net = nf_ct_net(ct); |
857 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); |
858 | union nf_inet_addr *saddr; |
859 | struct nf_conntrack_tuple tuple; |
860 | int direct_rtp = 0, skip_expect = 0, ret = NF_DROP; |
861 | u_int16_t base_port; |
862 | __be16 rtp_port, rtcp_port; |
863 | const struct nf_nat_sip_hooks *hooks; |
864 | |
865 | saddr = NULL; |
866 | if (sip_direct_media) { |
867 | if (!nf_inet_addr_cmp(a1: daddr, a2: &ct->tuplehash[dir].tuple.src.u3)) |
868 | return NF_ACCEPT; |
869 | saddr = &ct->tuplehash[!dir].tuple.src.u3; |
870 | } else if (sip_external_media) { |
871 | struct net_device *dev = skb_dst(skb)->dev; |
872 | struct net *net = dev_net(dev); |
873 | struct flowi fl; |
874 | struct dst_entry *dst = NULL; |
875 | |
876 | memset(&fl, 0, sizeof(fl)); |
877 | |
878 | switch (nf_ct_l3num(ct)) { |
879 | case NFPROTO_IPV4: |
880 | fl.u.ip4.daddr = daddr->ip; |
881 | nf_ip_route(net, dst: &dst, fl: &fl, strict: false); |
882 | break; |
883 | |
884 | case NFPROTO_IPV6: |
885 | fl.u.ip6.daddr = daddr->in6; |
886 | nf_ip6_route(net, dst: &dst, fl: &fl, strict: false); |
887 | break; |
888 | } |
889 | |
890 | /* Don't predict any conntracks when media endpoint is reachable |
891 | * through the same interface as the signalling peer. |
892 | */ |
893 | if (dst) { |
894 | bool external_media = (dst->dev == dev); |
895 | |
896 | dst_release(dst); |
897 | if (external_media) |
898 | return NF_ACCEPT; |
899 | } |
900 | } |
901 | |
902 | /* We need to check whether the registration exists before attempting |
903 | * to register it since we can see the same media description multiple |
904 | * times on different connections in case multiple endpoints receive |
905 | * the same call. |
906 | * |
907 | * RTP optimization: if we find a matching media channel expectation |
908 | * and both the expectation and this connection are SNATed, we assume |
909 | * both sides can reach each other directly and use the final |
910 | * destination address from the expectation. We still need to keep |
911 | * the NATed expectations for media that might arrive from the |
912 | * outside, and additionally need to expect the direct RTP stream |
913 | * in case it passes through us even without NAT. |
914 | */ |
915 | memset(&tuple, 0, sizeof(tuple)); |
916 | if (saddr) |
917 | tuple.src.u3 = *saddr; |
918 | tuple.src.l3num = nf_ct_l3num(ct); |
919 | tuple.dst.protonum = IPPROTO_UDP; |
920 | tuple.dst.u3 = *daddr; |
921 | tuple.dst.u.udp.port = port; |
922 | |
923 | do { |
924 | exp = __nf_ct_expect_find(net, zone: nf_ct_zone(ct), tuple: &tuple); |
925 | |
926 | if (!exp || exp->master == ct || |
927 | nfct_help(ct: exp->master)->helper != nfct_help(ct)->helper || |
928 | exp->class != class) |
929 | break; |
930 | #if IS_ENABLED(CONFIG_NF_NAT) |
931 | if (!direct_rtp && |
932 | (!nf_inet_addr_cmp(a1: &exp->saved_addr, a2: &exp->tuple.dst.u3) || |
933 | exp->saved_proto.udp.port != exp->tuple.dst.u.udp.port) && |
934 | ct->status & IPS_NAT_MASK) { |
935 | *daddr = exp->saved_addr; |
936 | tuple.dst.u3 = exp->saved_addr; |
937 | tuple.dst.u.udp.port = exp->saved_proto.udp.port; |
938 | direct_rtp = 1; |
939 | } else |
940 | #endif |
941 | skip_expect = 1; |
942 | } while (!skip_expect); |
943 | |
944 | base_port = ntohs(tuple.dst.u.udp.port) & ~1; |
945 | rtp_port = htons(base_port); |
946 | rtcp_port = htons(base_port + 1); |
947 | |
948 | if (direct_rtp) { |
949 | hooks = rcu_dereference(nf_nat_sip_hooks); |
950 | if (hooks && |
951 | !hooks->sdp_port(skb, protoff, dataoff, dptr, datalen, |
952 | mediaoff, medialen, ntohs(rtp_port))) |
953 | goto err1; |
954 | } |
955 | |
956 | if (skip_expect) |
957 | return NF_ACCEPT; |
958 | |
959 | rtp_exp = nf_ct_expect_alloc(me: ct); |
960 | if (rtp_exp == NULL) |
961 | goto err1; |
962 | nf_ct_expect_init(rtp_exp, class, nf_ct_l3num(ct), saddr, daddr, |
963 | IPPROTO_UDP, NULL, &rtp_port); |
964 | |
965 | rtcp_exp = nf_ct_expect_alloc(me: ct); |
966 | if (rtcp_exp == NULL) |
967 | goto err2; |
968 | nf_ct_expect_init(rtcp_exp, class, nf_ct_l3num(ct), saddr, daddr, |
969 | IPPROTO_UDP, NULL, &rtcp_port); |
970 | |
971 | hooks = rcu_dereference(nf_nat_sip_hooks); |
972 | if (hooks && ct->status & IPS_NAT_MASK && !direct_rtp) |
973 | ret = hooks->sdp_media(skb, protoff, dataoff, dptr, |
974 | datalen, rtp_exp, rtcp_exp, |
975 | mediaoff, medialen, daddr); |
976 | else { |
977 | /* -EALREADY handling works around end-points that send |
978 | * SDP messages with identical port but different media type, |
979 | * we pretend expectation was set up. |
980 | * It also works in the case that SDP messages are sent with |
981 | * identical expect tuples but for different master conntracks. |
982 | */ |
983 | int errp = nf_ct_expect_related(expect: rtp_exp, |
984 | NF_CT_EXP_F_SKIP_MASTER); |
985 | |
986 | if (errp == 0 || errp == -EALREADY) { |
987 | int errcp = nf_ct_expect_related(expect: rtcp_exp, |
988 | NF_CT_EXP_F_SKIP_MASTER); |
989 | |
990 | if (errcp == 0 || errcp == -EALREADY) |
991 | ret = NF_ACCEPT; |
992 | else if (errp == 0) |
993 | nf_ct_unexpect_related(exp: rtp_exp); |
994 | } |
995 | } |
996 | nf_ct_expect_put(exp: rtcp_exp); |
997 | err2: |
998 | nf_ct_expect_put(exp: rtp_exp); |
999 | err1: |
1000 | return ret; |
1001 | } |
1002 | |
1003 | static const struct sdp_media_type sdp_media_types[] = { |
1004 | SDP_MEDIA_TYPE("audio " , SIP_EXPECT_AUDIO), |
1005 | SDP_MEDIA_TYPE("video " , SIP_EXPECT_VIDEO), |
1006 | SDP_MEDIA_TYPE("image " , SIP_EXPECT_IMAGE), |
1007 | }; |
1008 | |
1009 | static const struct sdp_media_type *sdp_media_type(const char *dptr, |
1010 | unsigned int matchoff, |
1011 | unsigned int matchlen) |
1012 | { |
1013 | const struct sdp_media_type *t; |
1014 | unsigned int i; |
1015 | |
1016 | for (i = 0; i < ARRAY_SIZE(sdp_media_types); i++) { |
1017 | t = &sdp_media_types[i]; |
1018 | if (matchlen < t->len || |
1019 | strncmp(dptr + matchoff, t->name, t->len)) |
1020 | continue; |
1021 | return t; |
1022 | } |
1023 | return NULL; |
1024 | } |
1025 | |
1026 | static int process_sdp(struct sk_buff *skb, unsigned int protoff, |
1027 | unsigned int dataoff, |
1028 | const char **dptr, unsigned int *datalen, |
1029 | unsigned int cseq) |
1030 | { |
1031 | enum ip_conntrack_info ctinfo; |
1032 | struct nf_conn *ct = nf_ct_get(skb, ctinfo: &ctinfo); |
1033 | unsigned int matchoff, matchlen; |
1034 | unsigned int mediaoff, medialen; |
1035 | unsigned int sdpoff; |
1036 | unsigned int caddr_len, maddr_len; |
1037 | unsigned int i; |
1038 | union nf_inet_addr caddr, maddr, rtp_addr; |
1039 | const struct nf_nat_sip_hooks *hooks; |
1040 | unsigned int port; |
1041 | const struct sdp_media_type *t; |
1042 | int ret = NF_ACCEPT; |
1043 | |
1044 | hooks = rcu_dereference(nf_nat_sip_hooks); |
1045 | |
1046 | /* Find beginning of session description */ |
1047 | if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen, |
1048 | SDP_HDR_VERSION, SDP_HDR_UNSPEC, |
1049 | &matchoff, &matchlen) <= 0) |
1050 | return NF_ACCEPT; |
1051 | sdpoff = matchoff; |
1052 | |
1053 | /* The connection information is contained in the session description |
1054 | * and/or once per media description. The first media description marks |
1055 | * the end of the session description. */ |
1056 | caddr_len = 0; |
1057 | if (ct_sip_parse_sdp_addr(ct, dptr: *dptr, dataoff: sdpoff, datalen: *datalen, |
1058 | type: SDP_HDR_CONNECTION, term: SDP_HDR_MEDIA, |
1059 | matchoff: &matchoff, matchlen: &matchlen, addr: &caddr) > 0) |
1060 | caddr_len = matchlen; |
1061 | |
1062 | mediaoff = sdpoff; |
1063 | for (i = 0; i < ARRAY_SIZE(sdp_media_types); ) { |
1064 | if (ct_sip_get_sdp_header(ct, *dptr, mediaoff, *datalen, |
1065 | SDP_HDR_MEDIA, SDP_HDR_UNSPEC, |
1066 | &mediaoff, &medialen) <= 0) |
1067 | break; |
1068 | |
1069 | /* Get media type and port number. A media port value of zero |
1070 | * indicates an inactive stream. */ |
1071 | t = sdp_media_type(dptr: *dptr, matchoff: mediaoff, matchlen: medialen); |
1072 | if (!t) { |
1073 | mediaoff += medialen; |
1074 | continue; |
1075 | } |
1076 | mediaoff += t->len; |
1077 | medialen -= t->len; |
1078 | |
1079 | port = simple_strtoul(*dptr + mediaoff, NULL, 10); |
1080 | if (port == 0) |
1081 | continue; |
1082 | if (port < 1024 || port > 65535) { |
1083 | nf_ct_helper_log(skb, ct, fmt: "wrong port %u" , port); |
1084 | return NF_DROP; |
1085 | } |
1086 | |
1087 | /* The media description overrides the session description. */ |
1088 | maddr_len = 0; |
1089 | if (ct_sip_parse_sdp_addr(ct, dptr: *dptr, dataoff: mediaoff, datalen: *datalen, |
1090 | type: SDP_HDR_CONNECTION, term: SDP_HDR_MEDIA, |
1091 | matchoff: &matchoff, matchlen: &matchlen, addr: &maddr) > 0) { |
1092 | maddr_len = matchlen; |
1093 | memcpy(&rtp_addr, &maddr, sizeof(rtp_addr)); |
1094 | } else if (caddr_len) |
1095 | memcpy(&rtp_addr, &caddr, sizeof(rtp_addr)); |
1096 | else { |
1097 | nf_ct_helper_log(skb, ct, fmt: "cannot parse SDP message" ); |
1098 | return NF_DROP; |
1099 | } |
1100 | |
1101 | ret = set_expected_rtp_rtcp(skb, protoff, dataoff, |
1102 | dptr, datalen, |
1103 | daddr: &rtp_addr, htons(port), class: t->class, |
1104 | mediaoff, medialen); |
1105 | if (ret != NF_ACCEPT) { |
1106 | nf_ct_helper_log(skb, ct, |
1107 | fmt: "cannot add expectation for voice" ); |
1108 | return ret; |
1109 | } |
1110 | |
1111 | /* Update media connection address if present */ |
1112 | if (maddr_len && hooks && ct->status & IPS_NAT_MASK) { |
1113 | ret = hooks->sdp_addr(skb, protoff, dataoff, |
1114 | dptr, datalen, mediaoff, |
1115 | SDP_HDR_CONNECTION, |
1116 | SDP_HDR_MEDIA, |
1117 | &rtp_addr); |
1118 | if (ret != NF_ACCEPT) { |
1119 | nf_ct_helper_log(skb, ct, fmt: "cannot mangle SDP" ); |
1120 | return ret; |
1121 | } |
1122 | } |
1123 | i++; |
1124 | } |
1125 | |
1126 | /* Update session connection and owner addresses */ |
1127 | hooks = rcu_dereference(nf_nat_sip_hooks); |
1128 | if (hooks && ct->status & IPS_NAT_MASK) |
1129 | ret = hooks->sdp_session(skb, protoff, dataoff, |
1130 | dptr, datalen, sdpoff, |
1131 | &rtp_addr); |
1132 | |
1133 | return ret; |
1134 | } |
1135 | static int process_invite_response(struct sk_buff *skb, unsigned int protoff, |
1136 | unsigned int dataoff, |
1137 | const char **dptr, unsigned int *datalen, |
1138 | unsigned int cseq, unsigned int code) |
1139 | { |
1140 | enum ip_conntrack_info ctinfo; |
1141 | struct nf_conn *ct = nf_ct_get(skb, ctinfo: &ctinfo); |
1142 | struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); |
1143 | |
1144 | if ((code >= 100 && code <= 199) || |
1145 | (code >= 200 && code <= 299)) |
1146 | return process_sdp(skb, protoff, dataoff, dptr, datalen, cseq); |
1147 | else if (ct_sip_info->invite_cseq == cseq) |
1148 | flush_expectations(ct, media: true); |
1149 | return NF_ACCEPT; |
1150 | } |
1151 | |
1152 | static int process_update_response(struct sk_buff *skb, unsigned int protoff, |
1153 | unsigned int dataoff, |
1154 | const char **dptr, unsigned int *datalen, |
1155 | unsigned int cseq, unsigned int code) |
1156 | { |
1157 | enum ip_conntrack_info ctinfo; |
1158 | struct nf_conn *ct = nf_ct_get(skb, ctinfo: &ctinfo); |
1159 | struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); |
1160 | |
1161 | if ((code >= 100 && code <= 199) || |
1162 | (code >= 200 && code <= 299)) |
1163 | return process_sdp(skb, protoff, dataoff, dptr, datalen, cseq); |
1164 | else if (ct_sip_info->invite_cseq == cseq) |
1165 | flush_expectations(ct, media: true); |
1166 | return NF_ACCEPT; |
1167 | } |
1168 | |
1169 | static int process_prack_response(struct sk_buff *skb, unsigned int protoff, |
1170 | unsigned int dataoff, |
1171 | const char **dptr, unsigned int *datalen, |
1172 | unsigned int cseq, unsigned int code) |
1173 | { |
1174 | enum ip_conntrack_info ctinfo; |
1175 | struct nf_conn *ct = nf_ct_get(skb, ctinfo: &ctinfo); |
1176 | struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); |
1177 | |
1178 | if ((code >= 100 && code <= 199) || |
1179 | (code >= 200 && code <= 299)) |
1180 | return process_sdp(skb, protoff, dataoff, dptr, datalen, cseq); |
1181 | else if (ct_sip_info->invite_cseq == cseq) |
1182 | flush_expectations(ct, media: true); |
1183 | return NF_ACCEPT; |
1184 | } |
1185 | |
1186 | static int process_invite_request(struct sk_buff *skb, unsigned int protoff, |
1187 | unsigned int dataoff, |
1188 | const char **dptr, unsigned int *datalen, |
1189 | unsigned int cseq) |
1190 | { |
1191 | enum ip_conntrack_info ctinfo; |
1192 | struct nf_conn *ct = nf_ct_get(skb, ctinfo: &ctinfo); |
1193 | struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); |
1194 | unsigned int ret; |
1195 | |
1196 | flush_expectations(ct, media: true); |
1197 | ret = process_sdp(skb, protoff, dataoff, dptr, datalen, cseq); |
1198 | if (ret == NF_ACCEPT) |
1199 | ct_sip_info->invite_cseq = cseq; |
1200 | return ret; |
1201 | } |
1202 | |
1203 | static int process_bye_request(struct sk_buff *skb, unsigned int protoff, |
1204 | unsigned int dataoff, |
1205 | const char **dptr, unsigned int *datalen, |
1206 | unsigned int cseq) |
1207 | { |
1208 | enum ip_conntrack_info ctinfo; |
1209 | struct nf_conn *ct = nf_ct_get(skb, ctinfo: &ctinfo); |
1210 | |
1211 | flush_expectations(ct, media: true); |
1212 | return NF_ACCEPT; |
1213 | } |
1214 | |
1215 | /* Parse a REGISTER request and create a permanent expectation for incoming |
1216 | * signalling connections. The expectation is marked inactive and is activated |
1217 | * when receiving a response indicating success from the registrar. |
1218 | */ |
1219 | static int process_register_request(struct sk_buff *skb, unsigned int protoff, |
1220 | unsigned int dataoff, |
1221 | const char **dptr, unsigned int *datalen, |
1222 | unsigned int cseq) |
1223 | { |
1224 | enum ip_conntrack_info ctinfo; |
1225 | struct nf_conn *ct = nf_ct_get(skb, ctinfo: &ctinfo); |
1226 | struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); |
1227 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); |
1228 | unsigned int matchoff, matchlen; |
1229 | struct nf_conntrack_expect *exp; |
1230 | union nf_inet_addr *saddr, daddr; |
1231 | const struct nf_nat_sip_hooks *hooks; |
1232 | struct nf_conntrack_helper *helper; |
1233 | __be16 port; |
1234 | u8 proto; |
1235 | unsigned int expires = 0; |
1236 | int ret; |
1237 | |
1238 | /* Expected connections can not register again. */ |
1239 | if (ct->status & IPS_EXPECTED) |
1240 | return NF_ACCEPT; |
1241 | |
1242 | /* We must check the expiration time: a value of zero signals the |
1243 | * registrar to release the binding. We'll remove our expectation |
1244 | * when receiving the new bindings in the response, but we don't |
1245 | * want to create new ones. |
1246 | * |
1247 | * The expiration time may be contained in Expires: header, the |
1248 | * Contact: header parameters or the URI parameters. |
1249 | */ |
1250 | if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_EXPIRES, |
1251 | &matchoff, &matchlen) > 0) |
1252 | expires = simple_strtoul(*dptr + matchoff, NULL, 10); |
1253 | |
1254 | ret = ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, |
1255 | SIP_HDR_CONTACT, NULL, |
1256 | &matchoff, &matchlen, &daddr, &port); |
1257 | if (ret < 0) { |
1258 | nf_ct_helper_log(skb, ct, fmt: "cannot parse contact" ); |
1259 | return NF_DROP; |
1260 | } else if (ret == 0) |
1261 | return NF_ACCEPT; |
1262 | |
1263 | /* We don't support third-party registrations */ |
1264 | if (!nf_inet_addr_cmp(a1: &ct->tuplehash[dir].tuple.src.u3, a2: &daddr)) |
1265 | return NF_ACCEPT; |
1266 | |
1267 | if (ct_sip_parse_transport(ct, dptr: *dptr, dataoff: matchoff + matchlen, datalen: *datalen, |
1268 | proto: &proto) == 0) |
1269 | return NF_ACCEPT; |
1270 | |
1271 | if (ct_sip_parse_numerical_param(ct, *dptr, |
1272 | matchoff + matchlen, *datalen, |
1273 | "expires=" , NULL, NULL, &expires) < 0) { |
1274 | nf_ct_helper_log(skb, ct, fmt: "cannot parse expires" ); |
1275 | return NF_DROP; |
1276 | } |
1277 | |
1278 | if (expires == 0) { |
1279 | ret = NF_ACCEPT; |
1280 | goto store_cseq; |
1281 | } |
1282 | |
1283 | exp = nf_ct_expect_alloc(me: ct); |
1284 | if (!exp) { |
1285 | nf_ct_helper_log(skb, ct, fmt: "cannot alloc expectation" ); |
1286 | return NF_DROP; |
1287 | } |
1288 | |
1289 | saddr = NULL; |
1290 | if (sip_direct_signalling) |
1291 | saddr = &ct->tuplehash[!dir].tuple.src.u3; |
1292 | |
1293 | helper = rcu_dereference(nfct_help(ct)->helper); |
1294 | if (!helper) |
1295 | return NF_DROP; |
1296 | |
1297 | nf_ct_expect_init(exp, SIP_EXPECT_SIGNALLING, nf_ct_l3num(ct), |
1298 | saddr, &daddr, proto, NULL, &port); |
1299 | exp->timeout.expires = sip_timeout * HZ; |
1300 | exp->helper = helper; |
1301 | exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE; |
1302 | |
1303 | hooks = rcu_dereference(nf_nat_sip_hooks); |
1304 | if (hooks && ct->status & IPS_NAT_MASK) |
1305 | ret = hooks->expect(skb, protoff, dataoff, dptr, datalen, |
1306 | exp, matchoff, matchlen); |
1307 | else { |
1308 | if (nf_ct_expect_related(expect: exp, flags: 0) != 0) { |
1309 | nf_ct_helper_log(skb, ct, fmt: "cannot add expectation" ); |
1310 | ret = NF_DROP; |
1311 | } else |
1312 | ret = NF_ACCEPT; |
1313 | } |
1314 | nf_ct_expect_put(exp); |
1315 | |
1316 | store_cseq: |
1317 | if (ret == NF_ACCEPT) |
1318 | ct_sip_info->register_cseq = cseq; |
1319 | return ret; |
1320 | } |
1321 | |
1322 | static int process_register_response(struct sk_buff *skb, unsigned int protoff, |
1323 | unsigned int dataoff, |
1324 | const char **dptr, unsigned int *datalen, |
1325 | unsigned int cseq, unsigned int code) |
1326 | { |
1327 | enum ip_conntrack_info ctinfo; |
1328 | struct nf_conn *ct = nf_ct_get(skb, ctinfo: &ctinfo); |
1329 | struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); |
1330 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); |
1331 | union nf_inet_addr addr; |
1332 | __be16 port; |
1333 | u8 proto; |
1334 | unsigned int matchoff, matchlen, coff = 0; |
1335 | unsigned int expires = 0; |
1336 | int in_contact = 0, ret; |
1337 | |
1338 | /* According to RFC 3261, "UAs MUST NOT send a new registration until |
1339 | * they have received a final response from the registrar for the |
1340 | * previous one or the previous REGISTER request has timed out". |
1341 | * |
1342 | * However, some servers fail to detect retransmissions and send late |
1343 | * responses, so we store the sequence number of the last valid |
1344 | * request and compare it here. |
1345 | */ |
1346 | if (ct_sip_info->register_cseq != cseq) |
1347 | return NF_ACCEPT; |
1348 | |
1349 | if (code >= 100 && code <= 199) |
1350 | return NF_ACCEPT; |
1351 | if (code < 200 || code > 299) |
1352 | goto flush; |
1353 | |
1354 | if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_EXPIRES, |
1355 | &matchoff, &matchlen) > 0) |
1356 | expires = simple_strtoul(*dptr + matchoff, NULL, 10); |
1357 | |
1358 | while (1) { |
1359 | unsigned int c_expires = expires; |
1360 | |
1361 | ret = ct_sip_parse_header_uri(ct, *dptr, &coff, *datalen, |
1362 | SIP_HDR_CONTACT, &in_contact, |
1363 | &matchoff, &matchlen, |
1364 | &addr, &port); |
1365 | if (ret < 0) { |
1366 | nf_ct_helper_log(skb, ct, fmt: "cannot parse contact" ); |
1367 | return NF_DROP; |
1368 | } else if (ret == 0) |
1369 | break; |
1370 | |
1371 | /* We don't support third-party registrations */ |
1372 | if (!nf_inet_addr_cmp(a1: &ct->tuplehash[dir].tuple.dst.u3, a2: &addr)) |
1373 | continue; |
1374 | |
1375 | if (ct_sip_parse_transport(ct, dptr: *dptr, dataoff: matchoff + matchlen, |
1376 | datalen: *datalen, proto: &proto) == 0) |
1377 | continue; |
1378 | |
1379 | ret = ct_sip_parse_numerical_param(ct, *dptr, |
1380 | matchoff + matchlen, |
1381 | *datalen, "expires=" , |
1382 | NULL, NULL, &c_expires); |
1383 | if (ret < 0) { |
1384 | nf_ct_helper_log(skb, ct, fmt: "cannot parse expires" ); |
1385 | return NF_DROP; |
1386 | } |
1387 | if (c_expires == 0) |
1388 | break; |
1389 | if (refresh_signalling_expectation(ct, addr: &addr, proto, port, |
1390 | expires: c_expires)) |
1391 | return NF_ACCEPT; |
1392 | } |
1393 | |
1394 | flush: |
1395 | flush_expectations(ct, media: false); |
1396 | return NF_ACCEPT; |
1397 | } |
1398 | |
1399 | static const struct sip_handler sip_handlers[] = { |
1400 | SIP_HANDLER("INVITE" , process_invite_request, process_invite_response), |
1401 | SIP_HANDLER("UPDATE" , process_sdp, process_update_response), |
1402 | SIP_HANDLER("ACK" , process_sdp, NULL), |
1403 | SIP_HANDLER("PRACK" , process_sdp, process_prack_response), |
1404 | SIP_HANDLER("BYE" , process_bye_request, NULL), |
1405 | SIP_HANDLER("REGISTER" , process_register_request, process_register_response), |
1406 | }; |
1407 | |
1408 | static int process_sip_response(struct sk_buff *skb, unsigned int protoff, |
1409 | unsigned int dataoff, |
1410 | const char **dptr, unsigned int *datalen) |
1411 | { |
1412 | enum ip_conntrack_info ctinfo; |
1413 | struct nf_conn *ct = nf_ct_get(skb, ctinfo: &ctinfo); |
1414 | unsigned int matchoff, matchlen, matchend; |
1415 | unsigned int code, cseq, i; |
1416 | |
1417 | if (*datalen < strlen("SIP/2.0 200" )) |
1418 | return NF_ACCEPT; |
1419 | code = simple_strtoul(*dptr + strlen("SIP/2.0 " ), NULL, 10); |
1420 | if (!code) { |
1421 | nf_ct_helper_log(skb, ct, fmt: "cannot get code" ); |
1422 | return NF_DROP; |
1423 | } |
1424 | |
1425 | if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ, |
1426 | &matchoff, &matchlen) <= 0) { |
1427 | nf_ct_helper_log(skb, ct, fmt: "cannot parse cseq" ); |
1428 | return NF_DROP; |
1429 | } |
1430 | cseq = simple_strtoul(*dptr + matchoff, NULL, 10); |
1431 | if (!cseq && *(*dptr + matchoff) != '0') { |
1432 | nf_ct_helper_log(skb, ct, fmt: "cannot get cseq" ); |
1433 | return NF_DROP; |
1434 | } |
1435 | matchend = matchoff + matchlen + 1; |
1436 | |
1437 | for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) { |
1438 | const struct sip_handler *handler; |
1439 | |
1440 | handler = &sip_handlers[i]; |
1441 | if (handler->response == NULL) |
1442 | continue; |
1443 | if (*datalen < matchend + handler->len || |
1444 | strncasecmp(s1: *dptr + matchend, s2: handler->method, n: handler->len)) |
1445 | continue; |
1446 | return handler->response(skb, protoff, dataoff, dptr, datalen, |
1447 | cseq, code); |
1448 | } |
1449 | return NF_ACCEPT; |
1450 | } |
1451 | |
1452 | static int process_sip_request(struct sk_buff *skb, unsigned int protoff, |
1453 | unsigned int dataoff, |
1454 | const char **dptr, unsigned int *datalen) |
1455 | { |
1456 | enum ip_conntrack_info ctinfo; |
1457 | struct nf_conn *ct = nf_ct_get(skb, ctinfo: &ctinfo); |
1458 | struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); |
1459 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); |
1460 | unsigned int matchoff, matchlen; |
1461 | unsigned int cseq, i; |
1462 | union nf_inet_addr addr; |
1463 | __be16 port; |
1464 | |
1465 | /* Many Cisco IP phones use a high source port for SIP requests, but |
1466 | * listen for the response on port 5060. If we are the local |
1467 | * router for one of these phones, save the port number from the |
1468 | * Via: header so that nf_nat_sip can redirect the responses to |
1469 | * the correct port. |
1470 | */ |
1471 | if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, |
1472 | SIP_HDR_VIA_UDP, NULL, &matchoff, |
1473 | &matchlen, &addr, &port) > 0 && |
1474 | port != ct->tuplehash[dir].tuple.src.u.udp.port && |
1475 | nf_inet_addr_cmp(a1: &addr, a2: &ct->tuplehash[dir].tuple.src.u3)) |
1476 | ct_sip_info->forced_dport = port; |
1477 | |
1478 | for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) { |
1479 | const struct sip_handler *handler; |
1480 | |
1481 | handler = &sip_handlers[i]; |
1482 | if (handler->request == NULL) |
1483 | continue; |
1484 | if (*datalen < handler->len + 2 || |
1485 | strncasecmp(s1: *dptr, s2: handler->method, n: handler->len)) |
1486 | continue; |
1487 | if ((*dptr)[handler->len] != ' ' || |
1488 | !isalpha((*dptr)[handler->len+1])) |
1489 | continue; |
1490 | |
1491 | if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ, |
1492 | &matchoff, &matchlen) <= 0) { |
1493 | nf_ct_helper_log(skb, ct, fmt: "cannot parse cseq" ); |
1494 | return NF_DROP; |
1495 | } |
1496 | cseq = simple_strtoul(*dptr + matchoff, NULL, 10); |
1497 | if (!cseq && *(*dptr + matchoff) != '0') { |
1498 | nf_ct_helper_log(skb, ct, fmt: "cannot get cseq" ); |
1499 | return NF_DROP; |
1500 | } |
1501 | |
1502 | return handler->request(skb, protoff, dataoff, dptr, datalen, |
1503 | cseq); |
1504 | } |
1505 | return NF_ACCEPT; |
1506 | } |
1507 | |
1508 | static int process_sip_msg(struct sk_buff *skb, struct nf_conn *ct, |
1509 | unsigned int protoff, unsigned int dataoff, |
1510 | const char **dptr, unsigned int *datalen) |
1511 | { |
1512 | const struct nf_nat_sip_hooks *hooks; |
1513 | int ret; |
1514 | |
1515 | if (strncasecmp(s1: *dptr, s2: "SIP/2.0 " , strlen("SIP/2.0 " )) != 0) |
1516 | ret = process_sip_request(skb, protoff, dataoff, dptr, datalen); |
1517 | else |
1518 | ret = process_sip_response(skb, protoff, dataoff, dptr, datalen); |
1519 | |
1520 | if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) { |
1521 | hooks = rcu_dereference(nf_nat_sip_hooks); |
1522 | if (hooks && !hooks->msg(skb, protoff, dataoff, |
1523 | dptr, datalen)) { |
1524 | nf_ct_helper_log(skb, ct, fmt: "cannot NAT SIP message" ); |
1525 | ret = NF_DROP; |
1526 | } |
1527 | } |
1528 | |
1529 | return ret; |
1530 | } |
1531 | |
1532 | static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, |
1533 | struct nf_conn *ct, enum ip_conntrack_info ctinfo) |
1534 | { |
1535 | struct tcphdr *th, _tcph; |
1536 | unsigned int dataoff, datalen; |
1537 | unsigned int matchoff, matchlen, clen; |
1538 | unsigned int msglen, origlen; |
1539 | const char *dptr, *end; |
1540 | s16 diff, tdiff = 0; |
1541 | int ret = NF_ACCEPT; |
1542 | bool term; |
1543 | |
1544 | if (ctinfo != IP_CT_ESTABLISHED && |
1545 | ctinfo != IP_CT_ESTABLISHED_REPLY) |
1546 | return NF_ACCEPT; |
1547 | |
1548 | /* No Data ? */ |
1549 | th = skb_header_pointer(skb, offset: protoff, len: sizeof(_tcph), buffer: &_tcph); |
1550 | if (th == NULL) |
1551 | return NF_ACCEPT; |
1552 | dataoff = protoff + th->doff * 4; |
1553 | if (dataoff >= skb->len) |
1554 | return NF_ACCEPT; |
1555 | |
1556 | nf_ct_refresh(ct, skb, extra_jiffies: sip_timeout * HZ); |
1557 | |
1558 | if (unlikely(skb_linearize(skb))) |
1559 | return NF_DROP; |
1560 | |
1561 | dptr = skb->data + dataoff; |
1562 | datalen = skb->len - dataoff; |
1563 | if (datalen < strlen("SIP/2.0 200" )) |
1564 | return NF_ACCEPT; |
1565 | |
1566 | while (1) { |
1567 | if (ct_sip_get_header(ct, dptr, 0, datalen, |
1568 | SIP_HDR_CONTENT_LENGTH, |
1569 | &matchoff, &matchlen) <= 0) |
1570 | break; |
1571 | |
1572 | clen = simple_strtoul(dptr + matchoff, (char **)&end, 10); |
1573 | if (dptr + matchoff == end) |
1574 | break; |
1575 | |
1576 | term = false; |
1577 | for (; end + strlen("\r\n\r\n" ) <= dptr + datalen; end++) { |
1578 | if (end[0] == '\r' && end[1] == '\n' && |
1579 | end[2] == '\r' && end[3] == '\n') { |
1580 | term = true; |
1581 | break; |
1582 | } |
1583 | } |
1584 | if (!term) |
1585 | break; |
1586 | end += strlen("\r\n\r\n" ) + clen; |
1587 | |
1588 | msglen = origlen = end - dptr; |
1589 | if (msglen > datalen) |
1590 | return NF_ACCEPT; |
1591 | |
1592 | ret = process_sip_msg(skb, ct, protoff, dataoff, |
1593 | dptr: &dptr, datalen: &msglen); |
1594 | /* process_sip_* functions report why this packet is dropped */ |
1595 | if (ret != NF_ACCEPT) |
1596 | break; |
1597 | diff = msglen - origlen; |
1598 | tdiff += diff; |
1599 | |
1600 | dataoff += msglen; |
1601 | dptr += msglen; |
1602 | datalen = datalen + diff - msglen; |
1603 | } |
1604 | |
1605 | if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) { |
1606 | const struct nf_nat_sip_hooks *hooks; |
1607 | |
1608 | hooks = rcu_dereference(nf_nat_sip_hooks); |
1609 | if (hooks) |
1610 | hooks->seq_adjust(skb, protoff, tdiff); |
1611 | } |
1612 | |
1613 | return ret; |
1614 | } |
1615 | |
1616 | static int sip_help_udp(struct sk_buff *skb, unsigned int protoff, |
1617 | struct nf_conn *ct, enum ip_conntrack_info ctinfo) |
1618 | { |
1619 | unsigned int dataoff, datalen; |
1620 | const char *dptr; |
1621 | |
1622 | /* No Data ? */ |
1623 | dataoff = protoff + sizeof(struct udphdr); |
1624 | if (dataoff >= skb->len) |
1625 | return NF_ACCEPT; |
1626 | |
1627 | nf_ct_refresh(ct, skb, extra_jiffies: sip_timeout * HZ); |
1628 | |
1629 | if (unlikely(skb_linearize(skb))) |
1630 | return NF_DROP; |
1631 | |
1632 | dptr = skb->data + dataoff; |
1633 | datalen = skb->len - dataoff; |
1634 | if (datalen < strlen("SIP/2.0 200" )) |
1635 | return NF_ACCEPT; |
1636 | |
1637 | return process_sip_msg(skb, ct, protoff, dataoff, dptr: &dptr, datalen: &datalen); |
1638 | } |
1639 | |
1640 | static struct nf_conntrack_helper sip[MAX_PORTS * 4] __read_mostly; |
1641 | |
1642 | static const struct nf_conntrack_expect_policy sip_exp_policy[SIP_EXPECT_MAX + 1] = { |
1643 | [SIP_EXPECT_SIGNALLING] = { |
1644 | .name = "signalling" , |
1645 | .max_expected = 1, |
1646 | .timeout = 3 * 60, |
1647 | }, |
1648 | [SIP_EXPECT_AUDIO] = { |
1649 | .name = "audio" , |
1650 | .max_expected = 2 * IP_CT_DIR_MAX, |
1651 | .timeout = 3 * 60, |
1652 | }, |
1653 | [SIP_EXPECT_VIDEO] = { |
1654 | .name = "video" , |
1655 | .max_expected = 2 * IP_CT_DIR_MAX, |
1656 | .timeout = 3 * 60, |
1657 | }, |
1658 | [SIP_EXPECT_IMAGE] = { |
1659 | .name = "image" , |
1660 | .max_expected = IP_CT_DIR_MAX, |
1661 | .timeout = 3 * 60, |
1662 | }, |
1663 | }; |
1664 | |
1665 | static void __exit nf_conntrack_sip_fini(void) |
1666 | { |
1667 | nf_conntrack_helpers_unregister(sip, ports_c * 4); |
1668 | } |
1669 | |
1670 | static int __init nf_conntrack_sip_init(void) |
1671 | { |
1672 | int i, ret; |
1673 | |
1674 | NF_CT_HELPER_BUILD_BUG_ON(sizeof(struct nf_ct_sip_master)); |
1675 | |
1676 | if (ports_c == 0) |
1677 | ports[ports_c++] = SIP_PORT; |
1678 | |
1679 | for (i = 0; i < ports_c; i++) { |
1680 | nf_ct_helper_init(helper: &sip[4 * i], AF_INET, IPPROTO_UDP, |
1681 | HELPER_NAME, SIP_PORT, spec_port: ports[i], id: i, |
1682 | exp_pol: sip_exp_policy, SIP_EXPECT_MAX, help: sip_help_udp, |
1683 | NULL, THIS_MODULE); |
1684 | nf_ct_helper_init(helper: &sip[4 * i + 1], AF_INET, IPPROTO_TCP, |
1685 | HELPER_NAME, SIP_PORT, spec_port: ports[i], id: i, |
1686 | exp_pol: sip_exp_policy, SIP_EXPECT_MAX, help: sip_help_tcp, |
1687 | NULL, THIS_MODULE); |
1688 | nf_ct_helper_init(helper: &sip[4 * i + 2], AF_INET6, IPPROTO_UDP, |
1689 | HELPER_NAME, SIP_PORT, spec_port: ports[i], id: i, |
1690 | exp_pol: sip_exp_policy, SIP_EXPECT_MAX, help: sip_help_udp, |
1691 | NULL, THIS_MODULE); |
1692 | nf_ct_helper_init(helper: &sip[4 * i + 3], AF_INET6, IPPROTO_TCP, |
1693 | HELPER_NAME, SIP_PORT, spec_port: ports[i], id: i, |
1694 | exp_pol: sip_exp_policy, SIP_EXPECT_MAX, help: sip_help_tcp, |
1695 | NULL, THIS_MODULE); |
1696 | } |
1697 | |
1698 | ret = nf_conntrack_helpers_register(sip, ports_c * 4); |
1699 | if (ret < 0) { |
1700 | pr_err("failed to register helpers\n" ); |
1701 | return ret; |
1702 | } |
1703 | return 0; |
1704 | } |
1705 | |
1706 | module_init(nf_conntrack_sip_init); |
1707 | module_exit(nf_conntrack_sip_fini); |
1708 | |