1/* SIP extension for IP connection tracking. 2 * 3 * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar> 4 * based on RR's ip_conntrack_ftp.c and other modules. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 */ 10 11#include <linux/module.h> 12#include <linux/ctype.h> 13#include <linux/skbuff.h> 14#include <linux/inet.h> 15#include <linux/in.h> 16#include <linux/udp.h> 17#include <linux/netfilter.h> 18 19#include <net/netfilter/nf_conntrack.h> 20#include <net/netfilter/nf_conntrack_expect.h> 21#include <net/netfilter/nf_conntrack_helper.h> 22#include <linux/netfilter/nf_conntrack_sip.h> 23 24#define DEBUGP(format, args...) 25 26MODULE_LICENSE("GPL"); 27MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>"); 28MODULE_DESCRIPTION("SIP connection tracking helper"); 29MODULE_ALIAS("ip_conntrack_sip"); 30 31#define MAX_PORTS 8 32static unsigned short ports[MAX_PORTS]; 33static int ports_c; 34module_param_array(ports, ushort, &ports_c, 0400); 35MODULE_PARM_DESC(ports, "port numbers of SIP servers"); 36 37static unsigned int sip_timeout __read_mostly = SIP_TIMEOUT; 38module_param(sip_timeout, uint, 0600); 39MODULE_PARM_DESC(sip_timeout, "timeout for the master SIP session"); 40 41unsigned int (*nf_nat_sip_hook)(struct sk_buff **pskb, 42 enum ip_conntrack_info ctinfo, 43 struct nf_conn *ct, 44 const char **dptr) __read_mostly; 45EXPORT_SYMBOL_GPL(nf_nat_sip_hook); 46 47unsigned int (*nf_nat_sdp_hook)(struct sk_buff **pskb, 48 enum ip_conntrack_info ctinfo, 49 struct nf_conntrack_expect *exp, 50 const char *dptr) __read_mostly; 51EXPORT_SYMBOL_GPL(nf_nat_sdp_hook); 52 53static int digits_len(struct nf_conn *, const char *, const char *, int *); 54static int epaddr_len(struct nf_conn *, const char *, const char *, int *); 55static int skp_digits_len(struct nf_conn *, const char *, const char *, int *); 56static int skp_epaddr_len(struct nf_conn *, const char *, const char *, int *); 57 58struct sip_header_nfo { 59 const char *lname; 60 const char *sname; 61 const char *ln_str; 62 size_t lnlen; 63 size_t snlen; 64 size_t ln_strlen; 65 int case_sensitive; 66 int (*match_len)(struct nf_conn *, const char *, 67 const char *, int *); 68}; 69 70static const struct sip_header_nfo ct_sip_hdrs[] = { 71 [POS_REG_REQ_URI] = { /* SIP REGISTER request URI */ 72 .lname = "sip:", 73 .lnlen = sizeof("sip:") - 1, 74 .ln_str = ":", 75 .ln_strlen = sizeof(":") - 1, 76 .match_len = epaddr_len, 77 }, 78 [POS_REQ_URI] = { /* SIP request URI */ 79 .lname = "sip:", 80 .lnlen = sizeof("sip:") - 1, 81 .ln_str = "@", 82 .ln_strlen = sizeof("@") - 1, 83 .match_len = epaddr_len, 84 }, 85 [POS_FROM] = { /* SIP From header */ 86 .lname = "From:", 87 .lnlen = sizeof("From:") - 1, 88 .sname = "\r\nf:", 89 .snlen = sizeof("\r\nf:") - 1, 90 .ln_str = "sip:", 91 .ln_strlen = sizeof("sip:") - 1, 92 .match_len = skp_epaddr_len, 93 }, 94 [POS_TO] = { /* SIP To header */ 95 .lname = "To:", 96 .lnlen = sizeof("To:") - 1, 97 .sname = "\r\nt:", 98 .snlen = sizeof("\r\nt:") - 1, 99 .ln_str = "sip:", 100 .ln_strlen = sizeof("sip:") - 1, 101 .match_len = skp_epaddr_len 102 }, 103 [POS_VIA] = { /* SIP Via header */ 104 .lname = "Via:", 105 .lnlen = sizeof("Via:") - 1, 106 .sname = "\r\nv:", 107 .snlen = sizeof("\r\nv:") - 1, /* rfc3261 "\r\n" */ 108 .ln_str = "UDP ", 109 .ln_strlen = sizeof("UDP ") - 1, 110 .match_len = epaddr_len, 111 }, 112 [POS_CONTACT] = { /* SIP Contact header */ 113 .lname = "Contact:", 114 .lnlen = sizeof("Contact:") - 1, 115 .sname = "\r\nm:", 116 .snlen = sizeof("\r\nm:") - 1, 117 .ln_str = "sip:", 118 .ln_strlen = sizeof("sip:") - 1, 119 .match_len = skp_epaddr_len 120 }, 121 [POS_CONTENT] = { /* SIP Content length header */ 122 .lname = "Content-Length:", 123 .lnlen = sizeof("Content-Length:") - 1, 124 .sname = "\r\nl:", 125 .snlen = sizeof("\r\nl:") - 1, 126 .ln_str = ":", 127 .ln_strlen = sizeof(":") - 1, 128 .match_len = skp_digits_len 129 }, 130 [POS_MEDIA] = { /* SDP media info */ 131 .case_sensitive = 1, 132 .lname = "\nm=", 133 .lnlen = sizeof("\nm=") - 1, 134 .sname = "\rm=", 135 .snlen = sizeof("\rm=") - 1, 136 .ln_str = "audio ", 137 .ln_strlen = sizeof("audio ") - 1, 138 .match_len = digits_len 139 }, 140 [POS_OWNER_IP4] = { /* SDP owner address*/ 141 .case_sensitive = 1, 142 .lname = "\no=", 143 .lnlen = sizeof("\no=") - 1, 144 .sname = "\ro=", 145 .snlen = sizeof("\ro=") - 1, 146 .ln_str = "IN IP4 ", 147 .ln_strlen = sizeof("IN IP4 ") - 1, 148 .match_len = epaddr_len 149 }, 150 [POS_CONNECTION_IP4] = {/* SDP connection info */ 151 .case_sensitive = 1, 152 .lname = "\nc=", 153 .lnlen = sizeof("\nc=") - 1, 154 .sname = "\rc=", 155 .snlen = sizeof("\rc=") - 1, 156 .ln_str = "IN IP4 ", 157 .ln_strlen = sizeof("IN IP4 ") - 1, 158 .match_len = epaddr_len 159 }, 160 [POS_OWNER_IP6] = { /* SDP owner address*/ 161 .case_sensitive = 1, 162 .lname = "\no=", 163 .lnlen = sizeof("\no=") - 1, 164 .sname = "\ro=", 165 .snlen = sizeof("\ro=") - 1, 166 .ln_str = "IN IP6 ", 167 .ln_strlen = sizeof("IN IP6 ") - 1, 168 .match_len = epaddr_len 169 }, 170 [POS_CONNECTION_IP6] = {/* SDP connection info */ 171 .case_sensitive = 1, 172 .lname = "\nc=", 173 .lnlen = sizeof("\nc=") - 1, 174 .sname = "\rc=", 175 .snlen = sizeof("\rc=") - 1, 176 .ln_str = "IN IP6 ", 177 .ln_strlen = sizeof("IN IP6 ") - 1, 178 .match_len = epaddr_len 179 }, 180 [POS_SDP_HEADER] = { /* SDP version header */ 181 .case_sensitive = 1, 182 .lname = "\nv=", 183 .lnlen = sizeof("\nv=") - 1, 184 .sname = "\rv=", 185 .snlen = sizeof("\rv=") - 1, 186 .ln_str = "=", 187 .ln_strlen = sizeof("=") - 1, 188 .match_len = digits_len 189 } 190}; 191 192/* get line lenght until first CR or LF seen. */ 193int ct_sip_lnlen(const char *line, const char *limit) 194{ 195 const char *k = line; 196 197 while ((line <= limit) && (*line == '\r' || *line == '\n')) 198 line++; 199 200 while (line <= limit) { 201 if (*line == '\r' || *line == '\n') 202 break; 203 line++; 204 } 205 return line - k; 206} 207EXPORT_SYMBOL_GPL(ct_sip_lnlen); 208 209/* Linear string search, case sensitive. */ 210const char *ct_sip_search(const char *needle, const char *haystack, 211 size_t needle_len, size_t haystack_len, 212 int case_sensitive) 213{ 214 const char *limit = haystack + (haystack_len - needle_len); 215 216 while (haystack <= limit) { 217 if (case_sensitive) { 218 if (strncmp(haystack, needle, needle_len) == 0) 219 return haystack; 220 } else { 221 if (strnicmp(haystack, needle, needle_len) == 0) 222 return haystack; 223 } 224 haystack++; 225 } 226 return NULL; 227} 228EXPORT_SYMBOL_GPL(ct_sip_search); 229 230static int digits_len(struct nf_conn *ct, const char *dptr, 231 const char *limit, int *shift) 232{ 233 int len = 0; 234 while (dptr <= limit && isdigit(*dptr)) { 235 dptr++; 236 len++; 237 } 238 return len; 239} 240 241/* get digits lenght, skiping blank spaces. */ 242static int skp_digits_len(struct nf_conn *ct, const char *dptr, 243 const char *limit, int *shift) 244{ 245 for (; dptr <= limit && *dptr == ' '; dptr++) 246 (*shift)++; 247 248 return digits_len(ct, dptr, limit, shift); 249} 250 251static int parse_addr(struct nf_conn *ct, const char *cp, const char **endp, 252 union nf_conntrack_address *addr, const char *limit) 253{ 254 const char *end; 255 int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num; 256 int ret = 0; 257 258 switch (family) { 259 case AF_INET: 260 ret = in4_pton(cp, limit - cp, (u8 *)&addr->ip, -1, &end); 261 break; 262 case AF_INET6: 263 ret = in6_pton(cp, limit - cp, (u8 *)&addr->ip6, -1, &end); 264 break; 265 default: 266 BUG(); 267 } 268 269 if (ret == 0 || end == cp) 270 return 0; 271 if (endp) 272 *endp = end; 273 return 1; 274} 275 276/* skip ip address. returns its length. */ 277static int epaddr_len(struct nf_conn *ct, const char *dptr, 278 const char *limit, int *shift) 279{ 280 union nf_conntrack_address addr; 281 const char *aux = dptr; 282 283 if (!parse_addr(ct, dptr, &dptr, &addr, limit)) { 284 DEBUGP("ip: %s parse failed.!\n", dptr); 285 return 0; 286 } 287 288 /* Port number */ 289 if (*dptr == ':') { 290 dptr++; 291 dptr += digits_len(ct, dptr, limit, shift); 292 } 293 return dptr - aux; 294} 295 296/* get address length, skiping user info. */ 297static int skp_epaddr_len(struct nf_conn *ct, const char *dptr, 298 const char *limit, int *shift) 299{ 300 int s = *shift; 301 302 /* Search for @, but stop at the end of the line. 303 * We are inside a sip: URI, so we don't need to worry about 304 * continuation lines. */ 305 while (dptr <= limit && 306 *dptr != '@' && *dptr != '\r' && *dptr != '\n') { 307 (*shift)++; 308 dptr++; 309 } 310 311 if (dptr <= limit && *dptr == '@') { 312 dptr++; 313 (*shift)++; 314 } else 315 *shift = s; 316 317 return epaddr_len(ct, dptr, limit, shift); 318} 319 320/* Returns 0 if not found, -1 error parsing. */ 321int ct_sip_get_info(struct nf_conn *ct, 322 const char *dptr, size_t dlen, 323 unsigned int *matchoff, 324 unsigned int *matchlen, 325 enum sip_header_pos pos) 326{ 327 const struct sip_header_nfo *hnfo = &ct_sip_hdrs[pos]; 328 const char *limit, *aux, *k = dptr; 329 int shift = 0; 330 331 limit = dptr + (dlen - hnfo->lnlen); 332 333 while (dptr <= limit) { 334 if ((strncmp(dptr, hnfo->lname, hnfo->lnlen) != 0) && 335 (strncmp(dptr, hnfo->sname, hnfo->snlen) != 0)) { 336 dptr++; 337 continue; 338 } 339 aux = ct_sip_search(hnfo->ln_str, dptr, hnfo->ln_strlen, 340 ct_sip_lnlen(dptr, limit), 341 hnfo->case_sensitive); 342 if (!aux) { 343 DEBUGP("'%s' not found in '%s'.\n", hnfo->ln_str, 344 hnfo->lname); 345 return -1; 346 } 347 aux += hnfo->ln_strlen; 348 349 *matchlen = hnfo->match_len(ct, aux, limit, &shift); 350 if (!*matchlen) 351 return -1; 352 353 *matchoff = (aux - k) + shift; 354 355 DEBUGP("%s match succeeded! - len: %u\n", hnfo->lname, 356 *matchlen); 357 return 1; 358 } 359 DEBUGP("%s header not found.\n", hnfo->lname); 360 return 0; 361} 362EXPORT_SYMBOL_GPL(ct_sip_get_info); 363 364static int set_expected_rtp(struct sk_buff **pskb, 365 struct nf_conn *ct, 366 enum ip_conntrack_info ctinfo, 367 union nf_conntrack_address *addr, 368 __be16 port, 369 const char *dptr) 370{ 371 struct nf_conntrack_expect *exp; 372 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); 373 int family = ct->tuplehash[!dir].tuple.src.l3num; 374 int ret; 375 typeof(nf_nat_sdp_hook) nf_nat_sdp; 376 377 exp = nf_conntrack_expect_alloc(ct); 378 if (exp == NULL) 379 return NF_DROP; 380 nf_conntrack_expect_init(exp, family, 381 &ct->tuplehash[!dir].tuple.src.u3, addr, 382 IPPROTO_UDP, NULL, &port); 383 384 nf_nat_sdp = rcu_dereference(nf_nat_sdp_hook); 385 if (nf_nat_sdp && ct->status & IPS_NAT_MASK) 386 ret = nf_nat_sdp(pskb, ctinfo, exp, dptr); 387 else { 388 if (nf_conntrack_expect_related(exp) != 0) 389 ret = NF_DROP; 390 else 391 ret = NF_ACCEPT; 392 } 393 nf_conntrack_expect_put(exp); 394 395 return ret; 396} 397 398static int sip_help(struct sk_buff **pskb, 399 unsigned int protoff, 400 struct nf_conn *ct, 401 enum ip_conntrack_info ctinfo) 402{ 403 int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num; 404 union nf_conntrack_address addr; 405 unsigned int dataoff, datalen; 406 const char *dptr; 407 int ret = NF_ACCEPT; 408 int matchoff, matchlen; 409 u_int16_t port; 410 enum sip_header_pos pos; 411 typeof(nf_nat_sip_hook) nf_nat_sip; 412 413 /* No Data ? */ 414 dataoff = protoff + sizeof(struct udphdr); 415 if (dataoff >= (*pskb)->len) 416 return NF_ACCEPT; 417 418 nf_ct_refresh(ct, *pskb, sip_timeout * HZ); 419 420 if (!skb_is_nonlinear(*pskb)) 421 dptr = (*pskb)->data + dataoff; 422 else { 423 DEBUGP("Copy of skbuff not supported yet.\n"); 424 goto out; 425 } 426 427 nf_nat_sip = rcu_dereference(nf_nat_sip_hook); 428 if (nf_nat_sip && ct->status & IPS_NAT_MASK) { 429 if (!nf_nat_sip(pskb, ctinfo, ct, &dptr)) { 430 ret = NF_DROP; 431 goto out; 432 } 433 } 434 435 datalen = (*pskb)->len - dataoff; 436 if (datalen < sizeof("SIP/2.0 200") - 1) 437 goto out; 438 439 /* RTP info only in some SDP pkts */ 440 if (memcmp(dptr, "INVITE", sizeof("INVITE") - 1) != 0 && 441 memcmp(dptr, "UPDATE", sizeof("UPDATE") - 1) != 0 && 442 memcmp(dptr, "SIP/2.0 180", sizeof("SIP/2.0 180") - 1) != 0 && 443 memcmp(dptr, "SIP/2.0 183", sizeof("SIP/2.0 183") - 1) != 0 && 444 memcmp(dptr, "SIP/2.0 200", sizeof("SIP/2.0 200") - 1) != 0) { 445 goto out; 446 } 447 /* Get address and port from SDP packet. */ 448 pos = family == AF_INET ? POS_CONNECTION_IP4 : POS_CONNECTION_IP6; 449 if (ct_sip_get_info(ct, dptr, datalen, &matchoff, &matchlen, pos) > 0) { 450 451 /* We'll drop only if there are parse problems. */ 452 if (!parse_addr(ct, dptr + matchoff, NULL, &addr, 453 dptr + datalen)) { 454 ret = NF_DROP; 455 goto out; 456 } 457 if (ct_sip_get_info(ct, dptr, datalen, &matchoff, &matchlen, 458 POS_MEDIA) > 0) { 459 460 port = simple_strtoul(dptr + matchoff, NULL, 10); 461 if (port < 1024) { 462 ret = NF_DROP; 463 goto out; 464 } 465 ret = set_expected_rtp(pskb, ct, ctinfo, &addr, 466 htons(port), dptr); 467 } 468 } 469out: 470 return ret; 471} 472 473static struct nf_conntrack_helper sip[MAX_PORTS][2] __read_mostly; 474static char sip_names[MAX_PORTS][2][sizeof("sip-65535")] __read_mostly; 475 476static void nf_conntrack_sip_fini(void) 477{ 478 int i, j; 479 480 for (i = 0; i < ports_c; i++) { 481 for (j = 0; j < 2; j++) { 482 if (sip[i][j].me == NULL) 483 continue; 484 nf_conntrack_helper_unregister(&sip[i][j]); 485 } 486 } 487} 488 489static int __init nf_conntrack_sip_init(void) 490{ 491 int i, j, ret; 492 char *tmpname; 493 494 if (ports_c == 0) 495 ports[ports_c++] = SIP_PORT; 496 497 for (i = 0; i < ports_c; i++) { 498 memset(&sip[i], 0, sizeof(sip[i])); 499 500 sip[i][0].tuple.src.l3num = AF_INET; 501 sip[i][1].tuple.src.l3num = AF_INET6; 502 for (j = 0; j < 2; j++) { 503 sip[i][j].tuple.dst.protonum = IPPROTO_UDP; 504 sip[i][j].tuple.src.u.udp.port = htons(ports[i]); 505 sip[i][j].mask.src.l3num = 0xFFFF; 506 sip[i][j].mask.src.u.udp.port = htons(0xFFFF); 507 sip[i][j].mask.dst.protonum = 0xFF; 508 sip[i][j].max_expected = 2; 509 sip[i][j].timeout = 3 * 60; /* 3 minutes */ 510 sip[i][j].me = THIS_MODULE; 511 sip[i][j].help = sip_help; 512 513 tmpname = &sip_names[i][j][0]; 514 if (ports[i] == SIP_PORT) 515 sprintf(tmpname, "sip"); 516 else 517 sprintf(tmpname, "sip-%u", i); 518 sip[i][j].name = tmpname; 519 520 DEBUGP("port #%u: %u\n", i, ports[i]); 521 522 ret = nf_conntrack_helper_register(&sip[i][j]); 523 if (ret) { 524 printk("nf_ct_sip: failed to register helper " 525 "for pf: %u port: %u\n", 526 sip[i][j].tuple.src.l3num, ports[i]); 527 nf_conntrack_sip_fini(); 528 return ret; 529 } 530 } 531 } 532 return 0; 533} 534 535module_init(nf_conntrack_sip_init); 536module_exit(nf_conntrack_sip_fini); 537