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