19fafcd7bSPatrick McHardy /* SIP extension for IP connection tracking. 29fafcd7bSPatrick McHardy * 39fafcd7bSPatrick McHardy * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar> 49fafcd7bSPatrick McHardy * based on RR's ip_conntrack_ftp.c and other modules. 59fafcd7bSPatrick McHardy * 69fafcd7bSPatrick McHardy * This program is free software; you can redistribute it and/or modify 79fafcd7bSPatrick McHardy * it under the terms of the GNU General Public License version 2 as 89fafcd7bSPatrick McHardy * published by the Free Software Foundation. 99fafcd7bSPatrick McHardy */ 109fafcd7bSPatrick McHardy 119fafcd7bSPatrick McHardy #include <linux/module.h> 129fafcd7bSPatrick McHardy #include <linux/ctype.h> 139fafcd7bSPatrick McHardy #include <linux/skbuff.h> 149fafcd7bSPatrick McHardy #include <linux/inet.h> 159fafcd7bSPatrick McHardy #include <linux/in.h> 169fafcd7bSPatrick McHardy #include <linux/udp.h> 171863f096SYasuyuki Kozakai #include <linux/netfilter.h> 189fafcd7bSPatrick McHardy 199fafcd7bSPatrick McHardy #include <net/netfilter/nf_conntrack.h> 209fafcd7bSPatrick McHardy #include <net/netfilter/nf_conntrack_expect.h> 219fafcd7bSPatrick McHardy #include <net/netfilter/nf_conntrack_helper.h> 229fafcd7bSPatrick McHardy #include <linux/netfilter/nf_conntrack_sip.h> 239fafcd7bSPatrick McHardy 249fafcd7bSPatrick McHardy MODULE_LICENSE("GPL"); 259fafcd7bSPatrick McHardy MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>"); 269fafcd7bSPatrick McHardy MODULE_DESCRIPTION("SIP connection tracking helper"); 279fafcd7bSPatrick McHardy MODULE_ALIAS("ip_conntrack_sip"); 289fafcd7bSPatrick McHardy 299fafcd7bSPatrick McHardy #define MAX_PORTS 8 309fafcd7bSPatrick McHardy static unsigned short ports[MAX_PORTS]; 312f0d2f10SStephen Hemminger static unsigned int ports_c; 329fafcd7bSPatrick McHardy module_param_array(ports, ushort, &ports_c, 0400); 339fafcd7bSPatrick McHardy MODULE_PARM_DESC(ports, "port numbers of SIP servers"); 349fafcd7bSPatrick McHardy 359fafcd7bSPatrick McHardy static unsigned int sip_timeout __read_mostly = SIP_TIMEOUT; 369fafcd7bSPatrick McHardy module_param(sip_timeout, uint, 0600); 379fafcd7bSPatrick McHardy MODULE_PARM_DESC(sip_timeout, "timeout for the master SIP session"); 389fafcd7bSPatrick McHardy 393db05feaSHerbert Xu unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb, 402a6cfb22SPatrick McHardy const char **dptr, 412a6cfb22SPatrick McHardy unsigned int *datalen) __read_mostly; 429fafcd7bSPatrick McHardy EXPORT_SYMBOL_GPL(nf_nat_sip_hook); 439fafcd7bSPatrick McHardy 443db05feaSHerbert Xu unsigned int (*nf_nat_sdp_hook)(struct sk_buff *skb, 452a6cfb22SPatrick McHardy const char **dptr, 46212440a7SPatrick McHardy unsigned int *datalen, 47212440a7SPatrick McHardy struct nf_conntrack_expect *exp) __read_mostly; 489fafcd7bSPatrick McHardy EXPORT_SYMBOL_GPL(nf_nat_sdp_hook); 499fafcd7bSPatrick McHardy 50ac367740SPatrick McHardy static int string_len(const struct nf_conn *ct, const char *dptr, 51ac367740SPatrick McHardy const char *limit, int *shift) 52ac367740SPatrick McHardy { 53ac367740SPatrick McHardy int len = 0; 54ac367740SPatrick McHardy 55ac367740SPatrick McHardy while (dptr < limit && isalpha(*dptr)) { 56ac367740SPatrick McHardy dptr++; 57ac367740SPatrick McHardy len++; 58ac367740SPatrick McHardy } 59ac367740SPatrick McHardy return len; 60ac367740SPatrick McHardy } 61ac367740SPatrick McHardy 6213f7d63cSJan Engelhardt static int digits_len(const struct nf_conn *ct, const char *dptr, 639fafcd7bSPatrick McHardy const char *limit, int *shift) 649fafcd7bSPatrick McHardy { 659fafcd7bSPatrick McHardy int len = 0; 66b1ec488bSPatrick McHardy while (dptr < limit && isdigit(*dptr)) { 679fafcd7bSPatrick McHardy dptr++; 689fafcd7bSPatrick McHardy len++; 699fafcd7bSPatrick McHardy } 709fafcd7bSPatrick McHardy return len; 719fafcd7bSPatrick McHardy } 729fafcd7bSPatrick McHardy 7313f7d63cSJan Engelhardt static int parse_addr(const struct nf_conn *ct, const char *cp, 7413f7d63cSJan Engelhardt const char **endp, union nf_inet_addr *addr, 7513f7d63cSJan Engelhardt const char *limit) 769fafcd7bSPatrick McHardy { 779fafcd7bSPatrick McHardy const char *end; 789fafcd7bSPatrick McHardy int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num; 799fafcd7bSPatrick McHardy int ret = 0; 809fafcd7bSPatrick McHardy 819fafcd7bSPatrick McHardy switch (family) { 829fafcd7bSPatrick McHardy case AF_INET: 839fafcd7bSPatrick McHardy ret = in4_pton(cp, limit - cp, (u8 *)&addr->ip, -1, &end); 849fafcd7bSPatrick McHardy break; 859fafcd7bSPatrick McHardy case AF_INET6: 869fafcd7bSPatrick McHardy ret = in6_pton(cp, limit - cp, (u8 *)&addr->ip6, -1, &end); 879fafcd7bSPatrick McHardy break; 889fafcd7bSPatrick McHardy default: 899fafcd7bSPatrick McHardy BUG(); 909fafcd7bSPatrick McHardy } 919fafcd7bSPatrick McHardy 929fafcd7bSPatrick McHardy if (ret == 0 || end == cp) 939fafcd7bSPatrick McHardy return 0; 949fafcd7bSPatrick McHardy if (endp) 959fafcd7bSPatrick McHardy *endp = end; 969fafcd7bSPatrick McHardy return 1; 979fafcd7bSPatrick McHardy } 989fafcd7bSPatrick McHardy 999fafcd7bSPatrick McHardy /* skip ip address. returns its length. */ 10013f7d63cSJan Engelhardt static int epaddr_len(const struct nf_conn *ct, const char *dptr, 1019fafcd7bSPatrick McHardy const char *limit, int *shift) 1029fafcd7bSPatrick McHardy { 103643a2c15SJan Engelhardt union nf_inet_addr addr; 1049fafcd7bSPatrick McHardy const char *aux = dptr; 1059fafcd7bSPatrick McHardy 1069fafcd7bSPatrick McHardy if (!parse_addr(ct, dptr, &dptr, &addr, limit)) { 1070d53778eSPatrick McHardy pr_debug("ip: %s parse failed.!\n", dptr); 1089fafcd7bSPatrick McHardy return 0; 1099fafcd7bSPatrick McHardy } 1109fafcd7bSPatrick McHardy 1119fafcd7bSPatrick McHardy /* Port number */ 1129fafcd7bSPatrick McHardy if (*dptr == ':') { 1139fafcd7bSPatrick McHardy dptr++; 1149fafcd7bSPatrick McHardy dptr += digits_len(ct, dptr, limit, shift); 1159fafcd7bSPatrick McHardy } 1169fafcd7bSPatrick McHardy return dptr - aux; 1179fafcd7bSPatrick McHardy } 1189fafcd7bSPatrick McHardy 1199fafcd7bSPatrick McHardy /* get address length, skiping user info. */ 12013f7d63cSJan Engelhardt static int skp_epaddr_len(const struct nf_conn *ct, const char *dptr, 1219fafcd7bSPatrick McHardy const char *limit, int *shift) 1229fafcd7bSPatrick McHardy { 123aa584edaSPatrick McHardy const char *start = dptr; 1249fafcd7bSPatrick McHardy int s = *shift; 1259fafcd7bSPatrick McHardy 1267da5bfbbSLars Immisch /* Search for @, but stop at the end of the line. 1277da5bfbbSLars Immisch * We are inside a sip: URI, so we don't need to worry about 1287da5bfbbSLars Immisch * continuation lines. */ 129b1ec488bSPatrick McHardy while (dptr < limit && 1307da5bfbbSLars Immisch *dptr != '@' && *dptr != '\r' && *dptr != '\n') { 1319fafcd7bSPatrick McHardy (*shift)++; 1327da5bfbbSLars Immisch dptr++; 1337da5bfbbSLars Immisch } 1349fafcd7bSPatrick McHardy 135b1ec488bSPatrick McHardy if (dptr < limit && *dptr == '@') { 1369fafcd7bSPatrick McHardy dptr++; 1379fafcd7bSPatrick McHardy (*shift)++; 138aa584edaSPatrick McHardy } else { 139aa584edaSPatrick McHardy dptr = start; 1409fafcd7bSPatrick McHardy *shift = s; 141aa584edaSPatrick McHardy } 1429fafcd7bSPatrick McHardy 1439fafcd7bSPatrick McHardy return epaddr_len(ct, dptr, limit, shift); 1449fafcd7bSPatrick McHardy } 1459fafcd7bSPatrick McHardy 146ac367740SPatrick McHardy /* Parse a SIP request line of the form: 147ac367740SPatrick McHardy * 148ac367740SPatrick McHardy * Request-Line = Method SP Request-URI SP SIP-Version CRLF 149ac367740SPatrick McHardy * 150ac367740SPatrick McHardy * and return the offset and length of the address contained in the Request-URI. 151ac367740SPatrick McHardy */ 152ac367740SPatrick McHardy int ct_sip_parse_request(const struct nf_conn *ct, 153ac367740SPatrick McHardy const char *dptr, unsigned int datalen, 154624f8b7bSPatrick McHardy unsigned int *matchoff, unsigned int *matchlen, 155624f8b7bSPatrick McHardy union nf_inet_addr *addr, __be16 *port) 156ac367740SPatrick McHardy { 157624f8b7bSPatrick McHardy const char *start = dptr, *limit = dptr + datalen, *end; 158ac367740SPatrick McHardy unsigned int mlen; 159624f8b7bSPatrick McHardy unsigned int p; 160ac367740SPatrick McHardy int shift = 0; 161ac367740SPatrick McHardy 162ac367740SPatrick McHardy /* Skip method and following whitespace */ 163ac367740SPatrick McHardy mlen = string_len(ct, dptr, limit, NULL); 164ac367740SPatrick McHardy if (!mlen) 165ac367740SPatrick McHardy return 0; 166ac367740SPatrick McHardy dptr += mlen; 167ac367740SPatrick McHardy if (++dptr >= limit) 168ac367740SPatrick McHardy return 0; 169ac367740SPatrick McHardy 170ac367740SPatrick McHardy /* Find SIP URI */ 171ac367740SPatrick McHardy limit -= strlen("sip:"); 172ac367740SPatrick McHardy for (; dptr < limit; dptr++) { 173ac367740SPatrick McHardy if (*dptr == '\r' || *dptr == '\n') 174ac367740SPatrick McHardy return -1; 175ac367740SPatrick McHardy if (strnicmp(dptr, "sip:", strlen("sip:")) == 0) 176ac367740SPatrick McHardy break; 177ac367740SPatrick McHardy } 178624f8b7bSPatrick McHardy if (!skp_epaddr_len(ct, dptr, limit, &shift)) 179ac367740SPatrick McHardy return 0; 180624f8b7bSPatrick McHardy dptr += shift; 181624f8b7bSPatrick McHardy 182624f8b7bSPatrick McHardy if (!parse_addr(ct, dptr, &end, addr, limit)) 183624f8b7bSPatrick McHardy return -1; 184624f8b7bSPatrick McHardy if (end < limit && *end == ':') { 185624f8b7bSPatrick McHardy end++; 186624f8b7bSPatrick McHardy p = simple_strtoul(end, (char **)&end, 10); 187624f8b7bSPatrick McHardy if (p < 1024 || p > 65535) 188624f8b7bSPatrick McHardy return -1; 189624f8b7bSPatrick McHardy *port = htons(p); 190624f8b7bSPatrick McHardy } else 191624f8b7bSPatrick McHardy *port = htons(SIP_PORT); 192624f8b7bSPatrick McHardy 193624f8b7bSPatrick McHardy if (end == dptr) 194624f8b7bSPatrick McHardy return 0; 195624f8b7bSPatrick McHardy *matchoff = dptr - start; 196624f8b7bSPatrick McHardy *matchlen = end - dptr; 197ac367740SPatrick McHardy return 1; 198ac367740SPatrick McHardy } 199ac367740SPatrick McHardy EXPORT_SYMBOL_GPL(ct_sip_parse_request); 200ac367740SPatrick McHardy 201ea45f12aSPatrick McHardy /* SIP header parsing: SIP headers are located at the beginning of a line, but 202ea45f12aSPatrick McHardy * may span several lines, in which case the continuation lines begin with a 203ea45f12aSPatrick McHardy * whitespace character. RFC 2543 allows lines to be terminated with CR, LF or 204ea45f12aSPatrick McHardy * CRLF, RFC 3261 allows only CRLF, we support both. 205ea45f12aSPatrick McHardy * 206ea45f12aSPatrick McHardy * Headers are followed by (optionally) whitespace, a colon, again (optionally) 207ea45f12aSPatrick McHardy * whitespace and the values. Whitespace in this context means any amount of 208ea45f12aSPatrick McHardy * tabs, spaces and continuation lines, which are treated as a single whitespace 209ea45f12aSPatrick McHardy * character. 21005e3ced2SPatrick McHardy * 21105e3ced2SPatrick McHardy * Some headers may appear multiple times. A comma seperated list of values is 21205e3ced2SPatrick McHardy * equivalent to multiple headers. 213ea45f12aSPatrick McHardy */ 214ea45f12aSPatrick McHardy static const struct sip_header ct_sip_hdrs[] = { 21530f33e6dSPatrick McHardy [SIP_HDR_CSEQ] = SIP_HDR("CSeq", NULL, NULL, digits_len), 216ea45f12aSPatrick McHardy [SIP_HDR_FROM] = SIP_HDR("From", "f", "sip:", skp_epaddr_len), 217ea45f12aSPatrick McHardy [SIP_HDR_TO] = SIP_HDR("To", "t", "sip:", skp_epaddr_len), 218ea45f12aSPatrick McHardy [SIP_HDR_CONTACT] = SIP_HDR("Contact", "m", "sip:", skp_epaddr_len), 219ea45f12aSPatrick McHardy [SIP_HDR_VIA] = SIP_HDR("Via", "v", "UDP ", epaddr_len), 220ea45f12aSPatrick McHardy [SIP_HDR_CONTENT_LENGTH] = SIP_HDR("Content-Length", "l", NULL, digits_len), 221ea45f12aSPatrick McHardy }; 222ea45f12aSPatrick McHardy 223ea45f12aSPatrick McHardy static const char *sip_follow_continuation(const char *dptr, const char *limit) 2249fafcd7bSPatrick McHardy { 225ea45f12aSPatrick McHardy /* Walk past newline */ 226ea45f12aSPatrick McHardy if (++dptr >= limit) 227ea45f12aSPatrick McHardy return NULL; 2289fafcd7bSPatrick McHardy 229ea45f12aSPatrick McHardy /* Skip '\n' in CR LF */ 230ea45f12aSPatrick McHardy if (*(dptr - 1) == '\r' && *dptr == '\n') { 231ea45f12aSPatrick McHardy if (++dptr >= limit) 232ea45f12aSPatrick McHardy return NULL; 233ea45f12aSPatrick McHardy } 2349fafcd7bSPatrick McHardy 235ea45f12aSPatrick McHardy /* Continuation line? */ 236ea45f12aSPatrick McHardy if (*dptr != ' ' && *dptr != '\t') 237ea45f12aSPatrick McHardy return NULL; 238ea45f12aSPatrick McHardy 239ea45f12aSPatrick McHardy /* skip leading whitespace */ 240ea45f12aSPatrick McHardy for (; dptr < limit; dptr++) { 241ea45f12aSPatrick McHardy if (*dptr != ' ' && *dptr != '\t') 242ea45f12aSPatrick McHardy break; 243ea45f12aSPatrick McHardy } 244ea45f12aSPatrick McHardy return dptr; 245ea45f12aSPatrick McHardy } 246ea45f12aSPatrick McHardy 247ea45f12aSPatrick McHardy static const char *sip_skip_whitespace(const char *dptr, const char *limit) 248ea45f12aSPatrick McHardy { 249ea45f12aSPatrick McHardy for (; dptr < limit; dptr++) { 250ea45f12aSPatrick McHardy if (*dptr == ' ') 251ea45f12aSPatrick McHardy continue; 252ea45f12aSPatrick McHardy if (*dptr != '\r' && *dptr != '\n') 253ea45f12aSPatrick McHardy break; 254ea45f12aSPatrick McHardy dptr = sip_follow_continuation(dptr, limit); 255ea45f12aSPatrick McHardy if (dptr == NULL) 256ea45f12aSPatrick McHardy return NULL; 257ea45f12aSPatrick McHardy } 258ea45f12aSPatrick McHardy return dptr; 259ea45f12aSPatrick McHardy } 260ea45f12aSPatrick McHardy 261ea45f12aSPatrick McHardy /* Search within a SIP header value, dealing with continuation lines */ 262ea45f12aSPatrick McHardy static const char *ct_sip_header_search(const char *dptr, const char *limit, 263ea45f12aSPatrick McHardy const char *needle, unsigned int len) 264ea45f12aSPatrick McHardy { 265ea45f12aSPatrick McHardy for (limit -= len; dptr < limit; dptr++) { 266ea45f12aSPatrick McHardy if (*dptr == '\r' || *dptr == '\n') { 267ea45f12aSPatrick McHardy dptr = sip_follow_continuation(dptr, limit); 268ea45f12aSPatrick McHardy if (dptr == NULL) 269ea45f12aSPatrick McHardy break; 2709fafcd7bSPatrick McHardy continue; 2719fafcd7bSPatrick McHardy } 2729fafcd7bSPatrick McHardy 273ea45f12aSPatrick McHardy if (strnicmp(dptr, needle, len) == 0) 274ea45f12aSPatrick McHardy return dptr; 275ea45f12aSPatrick McHardy } 276ea45f12aSPatrick McHardy return NULL; 277ea45f12aSPatrick McHardy } 278ea45f12aSPatrick McHardy 279ea45f12aSPatrick McHardy int ct_sip_get_header(const struct nf_conn *ct, const char *dptr, 280ea45f12aSPatrick McHardy unsigned int dataoff, unsigned int datalen, 281ea45f12aSPatrick McHardy enum sip_header_types type, 282ea45f12aSPatrick McHardy unsigned int *matchoff, unsigned int *matchlen) 283ea45f12aSPatrick McHardy { 284ea45f12aSPatrick McHardy const struct sip_header *hdr = &ct_sip_hdrs[type]; 285ea45f12aSPatrick McHardy const char *start = dptr, *limit = dptr + datalen; 286ea45f12aSPatrick McHardy int shift = 0; 287ea45f12aSPatrick McHardy 288ea45f12aSPatrick McHardy for (dptr += dataoff; dptr < limit; dptr++) { 289ea45f12aSPatrick McHardy /* Find beginning of line */ 290ea45f12aSPatrick McHardy if (*dptr != '\r' && *dptr != '\n') 291ea45f12aSPatrick McHardy continue; 292ea45f12aSPatrick McHardy if (++dptr >= limit) 293ea45f12aSPatrick McHardy break; 294ea45f12aSPatrick McHardy if (*(dptr - 1) == '\r' && *dptr == '\n') { 295ea45f12aSPatrick McHardy if (++dptr >= limit) 296ea45f12aSPatrick McHardy break; 297ea45f12aSPatrick McHardy } 298ea45f12aSPatrick McHardy 299ea45f12aSPatrick McHardy /* Skip continuation lines */ 300ea45f12aSPatrick McHardy if (*dptr == ' ' || *dptr == '\t') 301ea45f12aSPatrick McHardy continue; 302ea45f12aSPatrick McHardy 303ea45f12aSPatrick McHardy /* Find header. Compact headers must be followed by a 304ea45f12aSPatrick McHardy * non-alphabetic character to avoid mismatches. */ 305ea45f12aSPatrick McHardy if (limit - dptr >= hdr->len && 306ea45f12aSPatrick McHardy strnicmp(dptr, hdr->name, hdr->len) == 0) 307ea45f12aSPatrick McHardy dptr += hdr->len; 308ea45f12aSPatrick McHardy else if (hdr->cname && limit - dptr >= hdr->clen + 1 && 309ea45f12aSPatrick McHardy strnicmp(dptr, hdr->cname, hdr->clen) == 0 && 310ea45f12aSPatrick McHardy !isalpha(*(dptr + hdr->clen + 1))) 311ea45f12aSPatrick McHardy dptr += hdr->clen; 312ea45f12aSPatrick McHardy else 313ea45f12aSPatrick McHardy continue; 314ea45f12aSPatrick McHardy 315ea45f12aSPatrick McHardy /* Find and skip colon */ 316ea45f12aSPatrick McHardy dptr = sip_skip_whitespace(dptr, limit); 317ea45f12aSPatrick McHardy if (dptr == NULL) 318ea45f12aSPatrick McHardy break; 319ea45f12aSPatrick McHardy if (*dptr != ':' || ++dptr >= limit) 320ea45f12aSPatrick McHardy break; 321ea45f12aSPatrick McHardy 322ea45f12aSPatrick McHardy /* Skip whitespace after colon */ 323ea45f12aSPatrick McHardy dptr = sip_skip_whitespace(dptr, limit); 324ea45f12aSPatrick McHardy if (dptr == NULL) 325ea45f12aSPatrick McHardy break; 326ea45f12aSPatrick McHardy 327ea45f12aSPatrick McHardy *matchoff = dptr - start; 328ea45f12aSPatrick McHardy if (hdr->search) { 329ea45f12aSPatrick McHardy dptr = ct_sip_header_search(dptr, limit, hdr->search, 330ea45f12aSPatrick McHardy hdr->slen); 331ea45f12aSPatrick McHardy if (!dptr) 332ea45f12aSPatrick McHardy return -1; 333ea45f12aSPatrick McHardy dptr += hdr->slen; 334ea45f12aSPatrick McHardy } 335ea45f12aSPatrick McHardy 336ea45f12aSPatrick McHardy *matchlen = hdr->match_len(ct, dptr, limit, &shift); 3379fafcd7bSPatrick McHardy if (!*matchlen) 3389fafcd7bSPatrick McHardy return -1; 339ea45f12aSPatrick McHardy *matchoff = dptr - start + shift; 3409fafcd7bSPatrick McHardy return 1; 3419fafcd7bSPatrick McHardy } 3429fafcd7bSPatrick McHardy return 0; 3439fafcd7bSPatrick McHardy } 344ea45f12aSPatrick McHardy EXPORT_SYMBOL_GPL(ct_sip_get_header); 3459fafcd7bSPatrick McHardy 34605e3ced2SPatrick McHardy /* Get next header field in a list of comma seperated values */ 34705e3ced2SPatrick McHardy static int ct_sip_next_header(const struct nf_conn *ct, const char *dptr, 34805e3ced2SPatrick McHardy unsigned int dataoff, unsigned int datalen, 34905e3ced2SPatrick McHardy enum sip_header_types type, 35005e3ced2SPatrick McHardy unsigned int *matchoff, unsigned int *matchlen) 35105e3ced2SPatrick McHardy { 35205e3ced2SPatrick McHardy const struct sip_header *hdr = &ct_sip_hdrs[type]; 35305e3ced2SPatrick McHardy const char *start = dptr, *limit = dptr + datalen; 35405e3ced2SPatrick McHardy int shift = 0; 35505e3ced2SPatrick McHardy 35605e3ced2SPatrick McHardy dptr += dataoff; 35705e3ced2SPatrick McHardy 35805e3ced2SPatrick McHardy dptr = ct_sip_header_search(dptr, limit, ",", strlen(",")); 35905e3ced2SPatrick McHardy if (!dptr) 36005e3ced2SPatrick McHardy return 0; 36105e3ced2SPatrick McHardy 36205e3ced2SPatrick McHardy dptr = ct_sip_header_search(dptr, limit, hdr->search, hdr->slen); 36305e3ced2SPatrick McHardy if (!dptr) 36405e3ced2SPatrick McHardy return 0; 36505e3ced2SPatrick McHardy dptr += hdr->slen; 36605e3ced2SPatrick McHardy 36705e3ced2SPatrick McHardy *matchoff = dptr - start; 36805e3ced2SPatrick McHardy *matchlen = hdr->match_len(ct, dptr, limit, &shift); 36905e3ced2SPatrick McHardy if (!*matchlen) 37005e3ced2SPatrick McHardy return -1; 37105e3ced2SPatrick McHardy *matchoff += shift; 37205e3ced2SPatrick McHardy return 1; 37305e3ced2SPatrick McHardy } 37405e3ced2SPatrick McHardy 37505e3ced2SPatrick McHardy /* Walk through headers until a parsable one is found or no header of the 37605e3ced2SPatrick McHardy * given type is left. */ 37705e3ced2SPatrick McHardy static int ct_sip_walk_headers(const struct nf_conn *ct, const char *dptr, 37805e3ced2SPatrick McHardy unsigned int dataoff, unsigned int datalen, 37905e3ced2SPatrick McHardy enum sip_header_types type, int *in_header, 38005e3ced2SPatrick McHardy unsigned int *matchoff, unsigned int *matchlen) 38105e3ced2SPatrick McHardy { 38205e3ced2SPatrick McHardy int ret; 38305e3ced2SPatrick McHardy 38405e3ced2SPatrick McHardy if (in_header && *in_header) { 38505e3ced2SPatrick McHardy while (1) { 38605e3ced2SPatrick McHardy ret = ct_sip_next_header(ct, dptr, dataoff, datalen, 38705e3ced2SPatrick McHardy type, matchoff, matchlen); 38805e3ced2SPatrick McHardy if (ret > 0) 38905e3ced2SPatrick McHardy return ret; 39005e3ced2SPatrick McHardy if (ret == 0) 39105e3ced2SPatrick McHardy break; 39205e3ced2SPatrick McHardy dataoff += *matchoff; 39305e3ced2SPatrick McHardy } 39405e3ced2SPatrick McHardy *in_header = 0; 39505e3ced2SPatrick McHardy } 39605e3ced2SPatrick McHardy 39705e3ced2SPatrick McHardy while (1) { 39805e3ced2SPatrick McHardy ret = ct_sip_get_header(ct, dptr, dataoff, datalen, 39905e3ced2SPatrick McHardy type, matchoff, matchlen); 40005e3ced2SPatrick McHardy if (ret > 0) 40105e3ced2SPatrick McHardy break; 40205e3ced2SPatrick McHardy if (ret == 0) 40305e3ced2SPatrick McHardy return ret; 40405e3ced2SPatrick McHardy dataoff += *matchoff; 40505e3ced2SPatrick McHardy } 40605e3ced2SPatrick McHardy 40705e3ced2SPatrick McHardy if (in_header) 40805e3ced2SPatrick McHardy *in_header = 1; 40905e3ced2SPatrick McHardy return 1; 41005e3ced2SPatrick McHardy } 41105e3ced2SPatrick McHardy 41205e3ced2SPatrick McHardy /* Locate a SIP header, parse the URI and return the offset and length of 41305e3ced2SPatrick McHardy * the address as well as the address and port themselves. A stream of 41405e3ced2SPatrick McHardy * headers can be parsed by handing in a non-NULL datalen and in_header 41505e3ced2SPatrick McHardy * pointer. 41605e3ced2SPatrick McHardy */ 41705e3ced2SPatrick McHardy int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr, 41805e3ced2SPatrick McHardy unsigned int *dataoff, unsigned int datalen, 41905e3ced2SPatrick McHardy enum sip_header_types type, int *in_header, 42005e3ced2SPatrick McHardy unsigned int *matchoff, unsigned int *matchlen, 42105e3ced2SPatrick McHardy union nf_inet_addr *addr, __be16 *port) 42205e3ced2SPatrick McHardy { 42305e3ced2SPatrick McHardy const char *c, *limit = dptr + datalen; 42405e3ced2SPatrick McHardy unsigned int p; 42505e3ced2SPatrick McHardy int ret; 42605e3ced2SPatrick McHardy 42705e3ced2SPatrick McHardy ret = ct_sip_walk_headers(ct, dptr, dataoff ? *dataoff : 0, datalen, 42805e3ced2SPatrick McHardy type, in_header, matchoff, matchlen); 42905e3ced2SPatrick McHardy WARN_ON(ret < 0); 43005e3ced2SPatrick McHardy if (ret == 0) 43105e3ced2SPatrick McHardy return ret; 43205e3ced2SPatrick McHardy 43305e3ced2SPatrick McHardy if (!parse_addr(ct, dptr + *matchoff, &c, addr, limit)) 43405e3ced2SPatrick McHardy return -1; 43505e3ced2SPatrick McHardy if (*c == ':') { 43605e3ced2SPatrick McHardy c++; 43705e3ced2SPatrick McHardy p = simple_strtoul(c, (char **)&c, 10); 43805e3ced2SPatrick McHardy if (p < 1024 || p > 65535) 43905e3ced2SPatrick McHardy return -1; 44005e3ced2SPatrick McHardy *port = htons(p); 44105e3ced2SPatrick McHardy } else 44205e3ced2SPatrick McHardy *port = htons(SIP_PORT); 44305e3ced2SPatrick McHardy 44405e3ced2SPatrick McHardy if (dataoff) 44505e3ced2SPatrick McHardy *dataoff = c - dptr; 44605e3ced2SPatrick McHardy return 1; 44705e3ced2SPatrick McHardy } 44805e3ced2SPatrick McHardy EXPORT_SYMBOL_GPL(ct_sip_parse_header_uri); 44905e3ced2SPatrick McHardy 4503e9b4600SPatrick McHardy /* SDP header parsing: a SDP session description contains an ordered set of 4513e9b4600SPatrick McHardy * headers, starting with a section containing general session parameters, 4523e9b4600SPatrick McHardy * optionally followed by multiple media descriptions. 4533e9b4600SPatrick McHardy * 4543e9b4600SPatrick McHardy * SDP headers always start at the beginning of a line. According to RFC 2327: 4553e9b4600SPatrick McHardy * "The sequence CRLF (0x0d0a) is used to end a record, although parsers should 4563e9b4600SPatrick McHardy * be tolerant and also accept records terminated with a single newline 4573e9b4600SPatrick McHardy * character". We handle both cases. 4583e9b4600SPatrick McHardy */ 4593e9b4600SPatrick McHardy static const struct sip_header ct_sdp_hdrs[] = { 4603e9b4600SPatrick McHardy [SDP_HDR_VERSION] = SDP_HDR("v=", NULL, digits_len), 4613e9b4600SPatrick McHardy [SDP_HDR_OWNER_IP4] = SDP_HDR("o=", "IN IP4 ", epaddr_len), 4623e9b4600SPatrick McHardy [SDP_HDR_CONNECTION_IP4] = SDP_HDR("c=", "IN IP4 ", epaddr_len), 4633e9b4600SPatrick McHardy [SDP_HDR_OWNER_IP6] = SDP_HDR("o=", "IN IP6 ", epaddr_len), 4643e9b4600SPatrick McHardy [SDP_HDR_CONNECTION_IP6] = SDP_HDR("c=", "IN IP6 ", epaddr_len), 4653e9b4600SPatrick McHardy [SDP_HDR_MEDIA] = SDP_HDR("m=", "audio ", digits_len), 4663e9b4600SPatrick McHardy }; 4673e9b4600SPatrick McHardy 4683e9b4600SPatrick McHardy /* Linear string search within SDP header values */ 4693e9b4600SPatrick McHardy static const char *ct_sdp_header_search(const char *dptr, const char *limit, 4703e9b4600SPatrick McHardy const char *needle, unsigned int len) 4713e9b4600SPatrick McHardy { 4723e9b4600SPatrick McHardy for (limit -= len; dptr < limit; dptr++) { 4733e9b4600SPatrick McHardy if (*dptr == '\r' || *dptr == '\n') 4743e9b4600SPatrick McHardy break; 4753e9b4600SPatrick McHardy if (strncmp(dptr, needle, len) == 0) 4763e9b4600SPatrick McHardy return dptr; 4773e9b4600SPatrick McHardy } 4783e9b4600SPatrick McHardy return NULL; 4793e9b4600SPatrick McHardy } 4803e9b4600SPatrick McHardy 4813e9b4600SPatrick McHardy /* Locate a SDP header (optionally a substring within the header value), 4823e9b4600SPatrick McHardy * optionally stopping at the first occurence of the term header, parse 4833e9b4600SPatrick McHardy * it and return the offset and length of the data we're interested in. 4843e9b4600SPatrick McHardy */ 4853e9b4600SPatrick McHardy int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr, 4863e9b4600SPatrick McHardy unsigned int dataoff, unsigned int datalen, 4873e9b4600SPatrick McHardy enum sdp_header_types type, 4883e9b4600SPatrick McHardy enum sdp_header_types term, 4893e9b4600SPatrick McHardy unsigned int *matchoff, unsigned int *matchlen) 4903e9b4600SPatrick McHardy { 4913e9b4600SPatrick McHardy const struct sip_header *hdr = &ct_sdp_hdrs[type]; 4923e9b4600SPatrick McHardy const struct sip_header *thdr = &ct_sdp_hdrs[term]; 4933e9b4600SPatrick McHardy const char *start = dptr, *limit = dptr + datalen; 4943e9b4600SPatrick McHardy int shift = 0; 4953e9b4600SPatrick McHardy 4963e9b4600SPatrick McHardy for (dptr += dataoff; dptr < limit; dptr++) { 4973e9b4600SPatrick McHardy /* Find beginning of line */ 4983e9b4600SPatrick McHardy if (*dptr != '\r' && *dptr != '\n') 4993e9b4600SPatrick McHardy continue; 5003e9b4600SPatrick McHardy if (++dptr >= limit) 5013e9b4600SPatrick McHardy break; 5023e9b4600SPatrick McHardy if (*(dptr - 1) == '\r' && *dptr == '\n') { 5033e9b4600SPatrick McHardy if (++dptr >= limit) 5043e9b4600SPatrick McHardy break; 5053e9b4600SPatrick McHardy } 5063e9b4600SPatrick McHardy 5073e9b4600SPatrick McHardy if (term != SDP_HDR_UNSPEC && 5083e9b4600SPatrick McHardy limit - dptr >= thdr->len && 5093e9b4600SPatrick McHardy strnicmp(dptr, thdr->name, thdr->len) == 0) 5103e9b4600SPatrick McHardy break; 5113e9b4600SPatrick McHardy else if (limit - dptr >= hdr->len && 5123e9b4600SPatrick McHardy strnicmp(dptr, hdr->name, hdr->len) == 0) 5133e9b4600SPatrick McHardy dptr += hdr->len; 5143e9b4600SPatrick McHardy else 5153e9b4600SPatrick McHardy continue; 5163e9b4600SPatrick McHardy 5173e9b4600SPatrick McHardy *matchoff = dptr - start; 5183e9b4600SPatrick McHardy if (hdr->search) { 5193e9b4600SPatrick McHardy dptr = ct_sdp_header_search(dptr, limit, hdr->search, 5203e9b4600SPatrick McHardy hdr->slen); 5213e9b4600SPatrick McHardy if (!dptr) 5223e9b4600SPatrick McHardy return -1; 5233e9b4600SPatrick McHardy dptr += hdr->slen; 5243e9b4600SPatrick McHardy } 5253e9b4600SPatrick McHardy 5263e9b4600SPatrick McHardy *matchlen = hdr->match_len(ct, dptr, limit, &shift); 5273e9b4600SPatrick McHardy if (!*matchlen) 5283e9b4600SPatrick McHardy return -1; 5293e9b4600SPatrick McHardy *matchoff = dptr - start + shift; 5303e9b4600SPatrick McHardy return 1; 5313e9b4600SPatrick McHardy } 5323e9b4600SPatrick McHardy return 0; 5333e9b4600SPatrick McHardy } 5343e9b4600SPatrick McHardy EXPORT_SYMBOL_GPL(ct_sip_get_sdp_header); 5353e9b4600SPatrick McHardy 5363db05feaSHerbert Xu static int set_expected_rtp(struct sk_buff *skb, 537212440a7SPatrick McHardy const char **dptr, unsigned int *datalen, 538212440a7SPatrick McHardy union nf_inet_addr *addr, __be16 port) 5399fafcd7bSPatrick McHardy { 5409fafcd7bSPatrick McHardy struct nf_conntrack_expect *exp; 541212440a7SPatrick McHardy enum ip_conntrack_info ctinfo; 542212440a7SPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 5439fafcd7bSPatrick McHardy enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); 5449fafcd7bSPatrick McHardy int family = ct->tuplehash[!dir].tuple.src.l3num; 5459fafcd7bSPatrick McHardy int ret; 5469fafcd7bSPatrick McHardy typeof(nf_nat_sdp_hook) nf_nat_sdp; 5479fafcd7bSPatrick McHardy 5486823645dSPatrick McHardy exp = nf_ct_expect_alloc(ct); 5499fafcd7bSPatrick McHardy if (exp == NULL) 5509fafcd7bSPatrick McHardy return NF_DROP; 5516002f266SPatrick McHardy nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, family, 5529fafcd7bSPatrick McHardy &ct->tuplehash[!dir].tuple.src.u3, addr, 5539fafcd7bSPatrick McHardy IPPROTO_UDP, NULL, &port); 5549fafcd7bSPatrick McHardy 5559fafcd7bSPatrick McHardy nf_nat_sdp = rcu_dereference(nf_nat_sdp_hook); 5569fafcd7bSPatrick McHardy if (nf_nat_sdp && ct->status & IPS_NAT_MASK) 557212440a7SPatrick McHardy ret = nf_nat_sdp(skb, dptr, datalen, exp); 5589fafcd7bSPatrick McHardy else { 5596823645dSPatrick McHardy if (nf_ct_expect_related(exp) != 0) 5609fafcd7bSPatrick McHardy ret = NF_DROP; 5619fafcd7bSPatrick McHardy else 5629fafcd7bSPatrick McHardy ret = NF_ACCEPT; 5639fafcd7bSPatrick McHardy } 5646823645dSPatrick McHardy nf_ct_expect_put(exp); 5659fafcd7bSPatrick McHardy 5669fafcd7bSPatrick McHardy return ret; 5679fafcd7bSPatrick McHardy } 5689fafcd7bSPatrick McHardy 5697d3dd043SPatrick McHardy static int process_sdp(struct sk_buff *skb, 57030f33e6dSPatrick McHardy const char **dptr, unsigned int *datalen, 57130f33e6dSPatrick McHardy unsigned int cseq) 5727d3dd043SPatrick McHardy { 5737d3dd043SPatrick McHardy enum ip_conntrack_info ctinfo; 5747d3dd043SPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 5757d3dd043SPatrick McHardy int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num; 5767d3dd043SPatrick McHardy unsigned int matchoff, matchlen; 5777d3dd043SPatrick McHardy union nf_inet_addr addr; 5787d3dd043SPatrick McHardy unsigned int port; 5797d3dd043SPatrick McHardy enum sdp_header_types type; 5807d3dd043SPatrick McHardy 5817d3dd043SPatrick McHardy /* Get address and port from SDP packet. */ 5827d3dd043SPatrick McHardy type = family == AF_INET ? SDP_HDR_CONNECTION_IP4 : 5837d3dd043SPatrick McHardy SDP_HDR_CONNECTION_IP6; 5847d3dd043SPatrick McHardy 5857d3dd043SPatrick McHardy if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen, 5867d3dd043SPatrick McHardy type, SDP_HDR_UNSPEC, 5877d3dd043SPatrick McHardy &matchoff, &matchlen) <= 0) 5887d3dd043SPatrick McHardy return NF_ACCEPT; 5897d3dd043SPatrick McHardy 5907d3dd043SPatrick McHardy /* We'll drop only if there are parse problems. */ 5917d3dd043SPatrick McHardy if (!parse_addr(ct, *dptr + matchoff, NULL, &addr, *dptr + *datalen)) 5927d3dd043SPatrick McHardy return NF_DROP; 5937d3dd043SPatrick McHardy 5947d3dd043SPatrick McHardy if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen, 5957d3dd043SPatrick McHardy SDP_HDR_MEDIA, SDP_HDR_UNSPEC, 5967d3dd043SPatrick McHardy &matchoff, &matchlen) <= 0) 5977d3dd043SPatrick McHardy return NF_ACCEPT; 5987d3dd043SPatrick McHardy 5997d3dd043SPatrick McHardy port = simple_strtoul(*dptr + matchoff, NULL, 10); 6007d3dd043SPatrick McHardy if (port < 1024 || port > 65535) 6017d3dd043SPatrick McHardy return NF_DROP; 6027d3dd043SPatrick McHardy 6037d3dd043SPatrick McHardy return set_expected_rtp(skb, dptr, datalen, &addr, htons(port)); 6047d3dd043SPatrick McHardy } 60530f33e6dSPatrick McHardy static int process_invite_response(struct sk_buff *skb, 60630f33e6dSPatrick McHardy const char **dptr, unsigned int *datalen, 60730f33e6dSPatrick McHardy unsigned int cseq, unsigned int code) 60830f33e6dSPatrick McHardy { 60930f33e6dSPatrick McHardy if ((code >= 100 && code <= 199) || 61030f33e6dSPatrick McHardy (code >= 200 && code <= 299)) 61130f33e6dSPatrick McHardy return process_sdp(skb, dptr, datalen, cseq); 61230f33e6dSPatrick McHardy 61330f33e6dSPatrick McHardy return NF_ACCEPT; 61430f33e6dSPatrick McHardy } 61530f33e6dSPatrick McHardy 61630f33e6dSPatrick McHardy static int process_update_response(struct sk_buff *skb, 61730f33e6dSPatrick McHardy const char **dptr, unsigned int *datalen, 61830f33e6dSPatrick McHardy unsigned int cseq, unsigned int code) 61930f33e6dSPatrick McHardy { 62030f33e6dSPatrick McHardy if ((code >= 100 && code <= 199) || 62130f33e6dSPatrick McHardy (code >= 200 && code <= 299)) 62230f33e6dSPatrick McHardy return process_sdp(skb, dptr, datalen, cseq); 62330f33e6dSPatrick McHardy 62430f33e6dSPatrick McHardy return NF_ACCEPT; 62530f33e6dSPatrick McHardy } 62630f33e6dSPatrick McHardy 627595a8ecbSPatrick McHardy static int process_prack_response(struct sk_buff *skb, 628595a8ecbSPatrick McHardy const char **dptr, unsigned int *datalen, 629595a8ecbSPatrick McHardy unsigned int cseq, unsigned int code) 630595a8ecbSPatrick McHardy { 631595a8ecbSPatrick McHardy if ((code >= 100 && code <= 199) || 632595a8ecbSPatrick McHardy (code >= 200 && code <= 299)) 633595a8ecbSPatrick McHardy return process_sdp(skb, dptr, datalen, cseq); 634595a8ecbSPatrick McHardy 635595a8ecbSPatrick McHardy return NF_ACCEPT; 636595a8ecbSPatrick McHardy } 637595a8ecbSPatrick McHardy 63830f33e6dSPatrick McHardy static const struct sip_handler sip_handlers[] = { 63930f33e6dSPatrick McHardy SIP_HANDLER("INVITE", process_sdp, process_invite_response), 64030f33e6dSPatrick McHardy SIP_HANDLER("UPDATE", process_sdp, process_update_response), 641595a8ecbSPatrick McHardy SIP_HANDLER("ACK", process_sdp, NULL), 642595a8ecbSPatrick McHardy SIP_HANDLER("PRACK", process_sdp, process_prack_response), 64330f33e6dSPatrick McHardy }; 64430f33e6dSPatrick McHardy 64530f33e6dSPatrick McHardy static int process_sip_response(struct sk_buff *skb, 64630f33e6dSPatrick McHardy const char **dptr, unsigned int *datalen) 64730f33e6dSPatrick McHardy { 64830f33e6dSPatrick McHardy static const struct sip_handler *handler; 64930f33e6dSPatrick McHardy enum ip_conntrack_info ctinfo; 65030f33e6dSPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 65130f33e6dSPatrick McHardy unsigned int matchoff, matchlen; 65230f33e6dSPatrick McHardy unsigned int code, cseq, dataoff, i; 65330f33e6dSPatrick McHardy 65430f33e6dSPatrick McHardy if (*datalen < strlen("SIP/2.0 200")) 65530f33e6dSPatrick McHardy return NF_ACCEPT; 65630f33e6dSPatrick McHardy code = simple_strtoul(*dptr + strlen("SIP/2.0 "), NULL, 10); 65730f33e6dSPatrick McHardy if (!code) 65830f33e6dSPatrick McHardy return NF_DROP; 65930f33e6dSPatrick McHardy 66030f33e6dSPatrick McHardy if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ, 66130f33e6dSPatrick McHardy &matchoff, &matchlen) <= 0) 66230f33e6dSPatrick McHardy return NF_DROP; 66330f33e6dSPatrick McHardy cseq = simple_strtoul(*dptr + matchoff, NULL, 10); 66430f33e6dSPatrick McHardy if (!cseq) 66530f33e6dSPatrick McHardy return NF_DROP; 66630f33e6dSPatrick McHardy dataoff = matchoff + matchlen + 1; 66730f33e6dSPatrick McHardy 66830f33e6dSPatrick McHardy for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) { 66930f33e6dSPatrick McHardy handler = &sip_handlers[i]; 67030f33e6dSPatrick McHardy if (handler->response == NULL) 67130f33e6dSPatrick McHardy continue; 67230f33e6dSPatrick McHardy if (*datalen < dataoff + handler->len || 67330f33e6dSPatrick McHardy strnicmp(*dptr + dataoff, handler->method, handler->len)) 67430f33e6dSPatrick McHardy continue; 67530f33e6dSPatrick McHardy return handler->response(skb, dptr, datalen, cseq, code); 67630f33e6dSPatrick McHardy } 67730f33e6dSPatrick McHardy return NF_ACCEPT; 67830f33e6dSPatrick McHardy } 67930f33e6dSPatrick McHardy 68030f33e6dSPatrick McHardy static int process_sip_request(struct sk_buff *skb, 68130f33e6dSPatrick McHardy const char **dptr, unsigned int *datalen) 68230f33e6dSPatrick McHardy { 68330f33e6dSPatrick McHardy static const struct sip_handler *handler; 68430f33e6dSPatrick McHardy enum ip_conntrack_info ctinfo; 68530f33e6dSPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 68630f33e6dSPatrick McHardy unsigned int matchoff, matchlen; 68730f33e6dSPatrick McHardy unsigned int cseq, i; 68830f33e6dSPatrick McHardy 68930f33e6dSPatrick McHardy for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) { 69030f33e6dSPatrick McHardy handler = &sip_handlers[i]; 69130f33e6dSPatrick McHardy if (handler->request == NULL) 69230f33e6dSPatrick McHardy continue; 69330f33e6dSPatrick McHardy if (*datalen < handler->len || 69430f33e6dSPatrick McHardy strnicmp(*dptr, handler->method, handler->len)) 69530f33e6dSPatrick McHardy continue; 69630f33e6dSPatrick McHardy 69730f33e6dSPatrick McHardy if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ, 69830f33e6dSPatrick McHardy &matchoff, &matchlen) <= 0) 69930f33e6dSPatrick McHardy return NF_DROP; 70030f33e6dSPatrick McHardy cseq = simple_strtoul(*dptr + matchoff, NULL, 10); 70130f33e6dSPatrick McHardy if (!cseq) 70230f33e6dSPatrick McHardy return NF_DROP; 70330f33e6dSPatrick McHardy 70430f33e6dSPatrick McHardy return handler->request(skb, dptr, datalen, cseq); 70530f33e6dSPatrick McHardy } 70630f33e6dSPatrick McHardy return NF_ACCEPT; 70730f33e6dSPatrick McHardy } 7087d3dd043SPatrick McHardy 7093db05feaSHerbert Xu static int sip_help(struct sk_buff *skb, 7109fafcd7bSPatrick McHardy unsigned int protoff, 7119fafcd7bSPatrick McHardy struct nf_conn *ct, 7129fafcd7bSPatrick McHardy enum ip_conntrack_info ctinfo) 7139fafcd7bSPatrick McHardy { 7149fafcd7bSPatrick McHardy unsigned int dataoff, datalen; 7159fafcd7bSPatrick McHardy const char *dptr; 71633cb1e9aSPatrick McHardy int ret; 7179fafcd7bSPatrick McHardy typeof(nf_nat_sip_hook) nf_nat_sip; 7189fafcd7bSPatrick McHardy 7199fafcd7bSPatrick McHardy /* No Data ? */ 7209fafcd7bSPatrick McHardy dataoff = protoff + sizeof(struct udphdr); 7213db05feaSHerbert Xu if (dataoff >= skb->len) 7229fafcd7bSPatrick McHardy return NF_ACCEPT; 7239fafcd7bSPatrick McHardy 7243db05feaSHerbert Xu nf_ct_refresh(ct, skb, sip_timeout * HZ); 7259fafcd7bSPatrick McHardy 7263db05feaSHerbert Xu if (!skb_is_nonlinear(skb)) 7273db05feaSHerbert Xu dptr = skb->data + dataoff; 7289fafcd7bSPatrick McHardy else { 7290d53778eSPatrick McHardy pr_debug("Copy of skbuff not supported yet.\n"); 7307d3dd043SPatrick McHardy return NF_ACCEPT; 7319fafcd7bSPatrick McHardy } 7329fafcd7bSPatrick McHardy 7333db05feaSHerbert Xu datalen = skb->len - dataoff; 734779382ebSPatrick McHardy if (datalen < strlen("SIP/2.0 200")) 7357d3dd043SPatrick McHardy return NF_ACCEPT; 7369fafcd7bSPatrick McHardy 73730f33e6dSPatrick McHardy if (strnicmp(dptr, "SIP/2.0 ", strlen("SIP/2.0 ")) != 0) 73833cb1e9aSPatrick McHardy ret = process_sip_request(skb, &dptr, &datalen); 73930f33e6dSPatrick McHardy else 74033cb1e9aSPatrick McHardy ret = process_sip_response(skb, &dptr, &datalen); 74133cb1e9aSPatrick McHardy 74233cb1e9aSPatrick McHardy if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) { 74333cb1e9aSPatrick McHardy nf_nat_sip = rcu_dereference(nf_nat_sip_hook); 74433cb1e9aSPatrick McHardy if (nf_nat_sip && !nf_nat_sip(skb, &dptr, &datalen)) 74533cb1e9aSPatrick McHardy ret = NF_DROP; 74633cb1e9aSPatrick McHardy } 74733cb1e9aSPatrick McHardy 74833cb1e9aSPatrick McHardy return ret; 7499fafcd7bSPatrick McHardy } 7509fafcd7bSPatrick McHardy 7519fafcd7bSPatrick McHardy static struct nf_conntrack_helper sip[MAX_PORTS][2] __read_mostly; 7529fafcd7bSPatrick McHardy static char sip_names[MAX_PORTS][2][sizeof("sip-65535")] __read_mostly; 7539fafcd7bSPatrick McHardy 7546002f266SPatrick McHardy static const struct nf_conntrack_expect_policy sip_exp_policy = { 7556002f266SPatrick McHardy .max_expected = 2, 7566002f266SPatrick McHardy .timeout = 3 * 60, 7576002f266SPatrick McHardy }; 7586002f266SPatrick McHardy 7599fafcd7bSPatrick McHardy static void nf_conntrack_sip_fini(void) 7609fafcd7bSPatrick McHardy { 7619fafcd7bSPatrick McHardy int i, j; 7629fafcd7bSPatrick McHardy 7639fafcd7bSPatrick McHardy for (i = 0; i < ports_c; i++) { 7649fafcd7bSPatrick McHardy for (j = 0; j < 2; j++) { 7659fafcd7bSPatrick McHardy if (sip[i][j].me == NULL) 7669fafcd7bSPatrick McHardy continue; 7679fafcd7bSPatrick McHardy nf_conntrack_helper_unregister(&sip[i][j]); 7689fafcd7bSPatrick McHardy } 7699fafcd7bSPatrick McHardy } 7709fafcd7bSPatrick McHardy } 7719fafcd7bSPatrick McHardy 7729fafcd7bSPatrick McHardy static int __init nf_conntrack_sip_init(void) 7739fafcd7bSPatrick McHardy { 7749fafcd7bSPatrick McHardy int i, j, ret; 7759fafcd7bSPatrick McHardy char *tmpname; 7769fafcd7bSPatrick McHardy 7779fafcd7bSPatrick McHardy if (ports_c == 0) 7789fafcd7bSPatrick McHardy ports[ports_c++] = SIP_PORT; 7799fafcd7bSPatrick McHardy 7809fafcd7bSPatrick McHardy for (i = 0; i < ports_c; i++) { 7819fafcd7bSPatrick McHardy memset(&sip[i], 0, sizeof(sip[i])); 7829fafcd7bSPatrick McHardy 7839fafcd7bSPatrick McHardy sip[i][0].tuple.src.l3num = AF_INET; 7849fafcd7bSPatrick McHardy sip[i][1].tuple.src.l3num = AF_INET6; 7859fafcd7bSPatrick McHardy for (j = 0; j < 2; j++) { 7869fafcd7bSPatrick McHardy sip[i][j].tuple.dst.protonum = IPPROTO_UDP; 7879fafcd7bSPatrick McHardy sip[i][j].tuple.src.u.udp.port = htons(ports[i]); 7886002f266SPatrick McHardy sip[i][j].expect_policy = &sip_exp_policy; 7899fafcd7bSPatrick McHardy sip[i][j].me = THIS_MODULE; 7909fafcd7bSPatrick McHardy sip[i][j].help = sip_help; 7919fafcd7bSPatrick McHardy 7929fafcd7bSPatrick McHardy tmpname = &sip_names[i][j][0]; 7939fafcd7bSPatrick McHardy if (ports[i] == SIP_PORT) 7949fafcd7bSPatrick McHardy sprintf(tmpname, "sip"); 7959fafcd7bSPatrick McHardy else 7969fafcd7bSPatrick McHardy sprintf(tmpname, "sip-%u", i); 7979fafcd7bSPatrick McHardy sip[i][j].name = tmpname; 7989fafcd7bSPatrick McHardy 7990d53778eSPatrick McHardy pr_debug("port #%u: %u\n", i, ports[i]); 8009fafcd7bSPatrick McHardy 8019fafcd7bSPatrick McHardy ret = nf_conntrack_helper_register(&sip[i][j]); 8029fafcd7bSPatrick McHardy if (ret) { 8039fafcd7bSPatrick McHardy printk("nf_ct_sip: failed to register helper " 8049fafcd7bSPatrick McHardy "for pf: %u port: %u\n", 8059fafcd7bSPatrick McHardy sip[i][j].tuple.src.l3num, ports[i]); 8069fafcd7bSPatrick McHardy nf_conntrack_sip_fini(); 8079fafcd7bSPatrick McHardy return ret; 8089fafcd7bSPatrick McHardy } 8099fafcd7bSPatrick McHardy } 8109fafcd7bSPatrick McHardy } 8119fafcd7bSPatrick McHardy return 0; 8129fafcd7bSPatrick McHardy } 8139fafcd7bSPatrick McHardy 8149fafcd7bSPatrick McHardy module_init(nf_conntrack_sip_init); 8159fafcd7bSPatrick McHardy module_exit(nf_conntrack_sip_fini); 816