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. 5f49e1aa1SPatrick McHardy * (C) 2007 United Security Providers 6f49e1aa1SPatrick McHardy * (C) 2007, 2008 Patrick McHardy <kaber@trash.net> 79fafcd7bSPatrick McHardy * 89fafcd7bSPatrick McHardy * This program is free software; you can redistribute it and/or modify 99fafcd7bSPatrick McHardy * it under the terms of the GNU General Public License version 2 as 109fafcd7bSPatrick McHardy * published by the Free Software Foundation. 119fafcd7bSPatrick McHardy */ 129fafcd7bSPatrick McHardy 139fafcd7bSPatrick McHardy #include <linux/module.h> 149fafcd7bSPatrick McHardy #include <linux/ctype.h> 159fafcd7bSPatrick McHardy #include <linux/skbuff.h> 169fafcd7bSPatrick McHardy #include <linux/inet.h> 179fafcd7bSPatrick McHardy #include <linux/in.h> 189fafcd7bSPatrick McHardy #include <linux/udp.h> 19f5b321bdSPatrick McHardy #include <linux/tcp.h> 201863f096SYasuyuki Kozakai #include <linux/netfilter.h> 219fafcd7bSPatrick McHardy 229fafcd7bSPatrick McHardy #include <net/netfilter/nf_conntrack.h> 239467ee38SPatrick McHardy #include <net/netfilter/nf_conntrack_core.h> 249fafcd7bSPatrick McHardy #include <net/netfilter/nf_conntrack_expect.h> 259fafcd7bSPatrick McHardy #include <net/netfilter/nf_conntrack_helper.h> 269fafcd7bSPatrick McHardy #include <linux/netfilter/nf_conntrack_sip.h> 279fafcd7bSPatrick McHardy 289fafcd7bSPatrick McHardy MODULE_LICENSE("GPL"); 299fafcd7bSPatrick McHardy MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>"); 309fafcd7bSPatrick McHardy MODULE_DESCRIPTION("SIP connection tracking helper"); 319fafcd7bSPatrick McHardy MODULE_ALIAS("ip_conntrack_sip"); 324dc06f96SPablo Neira Ayuso MODULE_ALIAS_NFCT_HELPER("sip"); 339fafcd7bSPatrick McHardy 349fafcd7bSPatrick McHardy #define MAX_PORTS 8 359fafcd7bSPatrick McHardy static unsigned short ports[MAX_PORTS]; 362f0d2f10SStephen Hemminger static unsigned int ports_c; 379fafcd7bSPatrick McHardy module_param_array(ports, ushort, &ports_c, 0400); 389fafcd7bSPatrick McHardy MODULE_PARM_DESC(ports, "port numbers of SIP servers"); 399fafcd7bSPatrick McHardy 409fafcd7bSPatrick McHardy static unsigned int sip_timeout __read_mostly = SIP_TIMEOUT; 419fafcd7bSPatrick McHardy module_param(sip_timeout, uint, 0600); 429fafcd7bSPatrick McHardy MODULE_PARM_DESC(sip_timeout, "timeout for the master SIP session"); 439fafcd7bSPatrick McHardy 440f32a40fSPatrick McHardy static int sip_direct_signalling __read_mostly = 1; 450f32a40fSPatrick McHardy module_param(sip_direct_signalling, int, 0600); 460f32a40fSPatrick McHardy MODULE_PARM_DESC(sip_direct_signalling, "expect incoming calls from registrar " 470f32a40fSPatrick McHardy "only (default 1)"); 480f32a40fSPatrick McHardy 49d901a936SPatrick McHardy static int sip_direct_media __read_mostly = 1; 50d901a936SPatrick McHardy module_param(sip_direct_media, int, 0600); 51d901a936SPatrick McHardy MODULE_PARM_DESC(sip_direct_media, "Expect Media streams between signalling " 52d901a936SPatrick McHardy "endpoints only (default 1)"); 53d901a936SPatrick McHardy 543b6b9fabSPatrick McHardy unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb, unsigned int dataoff, 552a6cfb22SPatrick McHardy const char **dptr, 562a6cfb22SPatrick McHardy unsigned int *datalen) __read_mostly; 579fafcd7bSPatrick McHardy EXPORT_SYMBOL_GPL(nf_nat_sip_hook); 589fafcd7bSPatrick McHardy 590f32a40fSPatrick McHardy unsigned int (*nf_nat_sip_expect_hook)(struct sk_buff *skb, 603b6b9fabSPatrick McHardy unsigned int dataoff, 610f32a40fSPatrick McHardy const char **dptr, 620f32a40fSPatrick McHardy unsigned int *datalen, 630f32a40fSPatrick McHardy struct nf_conntrack_expect *exp, 640f32a40fSPatrick McHardy unsigned int matchoff, 650f32a40fSPatrick McHardy unsigned int matchlen) __read_mostly; 660f32a40fSPatrick McHardy EXPORT_SYMBOL_GPL(nf_nat_sip_expect_hook); 670f32a40fSPatrick McHardy 683b6b9fabSPatrick McHardy unsigned int (*nf_nat_sdp_addr_hook)(struct sk_buff *skb, unsigned int dataoff, 694ab9e64eSPatrick McHardy const char **dptr, 704ab9e64eSPatrick McHardy unsigned int *datalen, 713b6b9fabSPatrick McHardy unsigned int sdpoff, 724ab9e64eSPatrick McHardy enum sdp_header_types type, 734ab9e64eSPatrick McHardy enum sdp_header_types term, 744ab9e64eSPatrick McHardy const union nf_inet_addr *addr) 754ab9e64eSPatrick McHardy __read_mostly; 764ab9e64eSPatrick McHardy EXPORT_SYMBOL_GPL(nf_nat_sdp_addr_hook); 774ab9e64eSPatrick McHardy 783b6b9fabSPatrick McHardy unsigned int (*nf_nat_sdp_port_hook)(struct sk_buff *skb, unsigned int dataoff, 79c7f485abSPatrick McHardy const char **dptr, 80c7f485abSPatrick McHardy unsigned int *datalen, 81c7f485abSPatrick McHardy unsigned int matchoff, 82c7f485abSPatrick McHardy unsigned int matchlen, 83c7f485abSPatrick McHardy u_int16_t port) __read_mostly; 84c7f485abSPatrick McHardy EXPORT_SYMBOL_GPL(nf_nat_sdp_port_hook); 85c7f485abSPatrick McHardy 864ab9e64eSPatrick McHardy unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb, 874ab9e64eSPatrick McHardy unsigned int dataoff, 883b6b9fabSPatrick McHardy const char **dptr, 894ab9e64eSPatrick McHardy unsigned int *datalen, 903b6b9fabSPatrick McHardy unsigned int sdpoff, 914ab9e64eSPatrick McHardy const union nf_inet_addr *addr) 924ab9e64eSPatrick McHardy __read_mostly; 934ab9e64eSPatrick McHardy EXPORT_SYMBOL_GPL(nf_nat_sdp_session_hook); 944ab9e64eSPatrick McHardy 953b6b9fabSPatrick McHardy unsigned int (*nf_nat_sdp_media_hook)(struct sk_buff *skb, unsigned int dataoff, 962a6cfb22SPatrick McHardy const char **dptr, 97212440a7SPatrick McHardy unsigned int *datalen, 98a9c1d359SPatrick McHardy struct nf_conntrack_expect *rtp_exp, 994ab9e64eSPatrick McHardy struct nf_conntrack_expect *rtcp_exp, 1004ab9e64eSPatrick McHardy unsigned int mediaoff, 1014ab9e64eSPatrick McHardy unsigned int medialen, 1024ab9e64eSPatrick McHardy union nf_inet_addr *rtp_addr) 103a9c1d359SPatrick McHardy __read_mostly; 1044ab9e64eSPatrick McHardy EXPORT_SYMBOL_GPL(nf_nat_sdp_media_hook); 1059fafcd7bSPatrick McHardy 106ac367740SPatrick McHardy static int string_len(const struct nf_conn *ct, const char *dptr, 107ac367740SPatrick McHardy const char *limit, int *shift) 108ac367740SPatrick McHardy { 109ac367740SPatrick McHardy int len = 0; 110ac367740SPatrick McHardy 111ac367740SPatrick McHardy while (dptr < limit && isalpha(*dptr)) { 112ac367740SPatrick McHardy dptr++; 113ac367740SPatrick McHardy len++; 114ac367740SPatrick McHardy } 115ac367740SPatrick McHardy return len; 116ac367740SPatrick McHardy } 117ac367740SPatrick McHardy 11813f7d63cSJan Engelhardt static int digits_len(const struct nf_conn *ct, const char *dptr, 1199fafcd7bSPatrick McHardy const char *limit, int *shift) 1209fafcd7bSPatrick McHardy { 1219fafcd7bSPatrick McHardy int len = 0; 122b1ec488bSPatrick McHardy while (dptr < limit && isdigit(*dptr)) { 1239fafcd7bSPatrick McHardy dptr++; 1249fafcd7bSPatrick McHardy len++; 1259fafcd7bSPatrick McHardy } 1269fafcd7bSPatrick McHardy return len; 1279fafcd7bSPatrick McHardy } 1289fafcd7bSPatrick McHardy 1290d0ab037SPatrick McHardy /* get media type + port length */ 1300d0ab037SPatrick McHardy static int media_len(const struct nf_conn *ct, const char *dptr, 1310d0ab037SPatrick McHardy const char *limit, int *shift) 1320d0ab037SPatrick McHardy { 1330d0ab037SPatrick McHardy int len = string_len(ct, dptr, limit, shift); 1340d0ab037SPatrick McHardy 1350d0ab037SPatrick McHardy dptr += len; 1360d0ab037SPatrick McHardy if (dptr >= limit || *dptr != ' ') 1370d0ab037SPatrick McHardy return 0; 1380d0ab037SPatrick McHardy len++; 1390d0ab037SPatrick McHardy dptr++; 1400d0ab037SPatrick McHardy 1410d0ab037SPatrick McHardy return len + digits_len(ct, dptr, limit, shift); 1420d0ab037SPatrick McHardy } 1430d0ab037SPatrick McHardy 14413f7d63cSJan Engelhardt static int parse_addr(const struct nf_conn *ct, const char *cp, 14513f7d63cSJan Engelhardt const char **endp, union nf_inet_addr *addr, 14613f7d63cSJan Engelhardt const char *limit) 1479fafcd7bSPatrick McHardy { 1489fafcd7bSPatrick McHardy const char *end; 1499fafcd7bSPatrick McHardy int ret = 0; 1509fafcd7bSPatrick McHardy 151fa913ddfSPatrick McHardy memset(addr, 0, sizeof(*addr)); 1525e8fbe2aSPatrick McHardy switch (nf_ct_l3num(ct)) { 1539fafcd7bSPatrick McHardy case AF_INET: 1549fafcd7bSPatrick McHardy ret = in4_pton(cp, limit - cp, (u8 *)&addr->ip, -1, &end); 1559fafcd7bSPatrick McHardy break; 1569fafcd7bSPatrick McHardy case AF_INET6: 1579fafcd7bSPatrick McHardy ret = in6_pton(cp, limit - cp, (u8 *)&addr->ip6, -1, &end); 1589fafcd7bSPatrick McHardy break; 1599fafcd7bSPatrick McHardy default: 1609fafcd7bSPatrick McHardy BUG(); 1619fafcd7bSPatrick McHardy } 1629fafcd7bSPatrick McHardy 1639fafcd7bSPatrick McHardy if (ret == 0 || end == cp) 1649fafcd7bSPatrick McHardy return 0; 1659fafcd7bSPatrick McHardy if (endp) 1669fafcd7bSPatrick McHardy *endp = end; 1679fafcd7bSPatrick McHardy return 1; 1689fafcd7bSPatrick McHardy } 1699fafcd7bSPatrick McHardy 1709fafcd7bSPatrick McHardy /* skip ip address. returns its length. */ 17113f7d63cSJan Engelhardt static int epaddr_len(const struct nf_conn *ct, const char *dptr, 1729fafcd7bSPatrick McHardy const char *limit, int *shift) 1739fafcd7bSPatrick McHardy { 174643a2c15SJan Engelhardt union nf_inet_addr addr; 1759fafcd7bSPatrick McHardy const char *aux = dptr; 1769fafcd7bSPatrick McHardy 1779fafcd7bSPatrick McHardy if (!parse_addr(ct, dptr, &dptr, &addr, limit)) { 1780d53778eSPatrick McHardy pr_debug("ip: %s parse failed.!\n", dptr); 1799fafcd7bSPatrick McHardy return 0; 1809fafcd7bSPatrick McHardy } 1819fafcd7bSPatrick McHardy 1829fafcd7bSPatrick McHardy /* Port number */ 1839fafcd7bSPatrick McHardy if (*dptr == ':') { 1849fafcd7bSPatrick McHardy dptr++; 1859fafcd7bSPatrick McHardy dptr += digits_len(ct, dptr, limit, shift); 1869fafcd7bSPatrick McHardy } 1879fafcd7bSPatrick McHardy return dptr - aux; 1889fafcd7bSPatrick McHardy } 1899fafcd7bSPatrick McHardy 1909fafcd7bSPatrick McHardy /* get address length, skiping user info. */ 19113f7d63cSJan Engelhardt static int skp_epaddr_len(const struct nf_conn *ct, const char *dptr, 1929fafcd7bSPatrick McHardy const char *limit, int *shift) 1939fafcd7bSPatrick McHardy { 194aa584edaSPatrick McHardy const char *start = dptr; 1959fafcd7bSPatrick McHardy int s = *shift; 1969fafcd7bSPatrick McHardy 1977da5bfbbSLars Immisch /* Search for @, but stop at the end of the line. 1987da5bfbbSLars Immisch * We are inside a sip: URI, so we don't need to worry about 1997da5bfbbSLars Immisch * continuation lines. */ 200b1ec488bSPatrick McHardy while (dptr < limit && 2017da5bfbbSLars Immisch *dptr != '@' && *dptr != '\r' && *dptr != '\n') { 2029fafcd7bSPatrick McHardy (*shift)++; 2037da5bfbbSLars Immisch dptr++; 2047da5bfbbSLars Immisch } 2059fafcd7bSPatrick McHardy 206b1ec488bSPatrick McHardy if (dptr < limit && *dptr == '@') { 2079fafcd7bSPatrick McHardy dptr++; 2089fafcd7bSPatrick McHardy (*shift)++; 209aa584edaSPatrick McHardy } else { 210aa584edaSPatrick McHardy dptr = start; 2119fafcd7bSPatrick McHardy *shift = s; 212aa584edaSPatrick McHardy } 2139fafcd7bSPatrick McHardy 2149fafcd7bSPatrick McHardy return epaddr_len(ct, dptr, limit, shift); 2159fafcd7bSPatrick McHardy } 2169fafcd7bSPatrick McHardy 217ac367740SPatrick McHardy /* Parse a SIP request line of the form: 218ac367740SPatrick McHardy * 219ac367740SPatrick McHardy * Request-Line = Method SP Request-URI SP SIP-Version CRLF 220ac367740SPatrick McHardy * 221ac367740SPatrick McHardy * and return the offset and length of the address contained in the Request-URI. 222ac367740SPatrick McHardy */ 223ac367740SPatrick McHardy int ct_sip_parse_request(const struct nf_conn *ct, 224ac367740SPatrick McHardy const char *dptr, unsigned int datalen, 225624f8b7bSPatrick McHardy unsigned int *matchoff, unsigned int *matchlen, 226624f8b7bSPatrick McHardy union nf_inet_addr *addr, __be16 *port) 227ac367740SPatrick McHardy { 228624f8b7bSPatrick McHardy const char *start = dptr, *limit = dptr + datalen, *end; 229ac367740SPatrick McHardy unsigned int mlen; 230624f8b7bSPatrick McHardy unsigned int p; 231ac367740SPatrick McHardy int shift = 0; 232ac367740SPatrick McHardy 233ac367740SPatrick McHardy /* Skip method and following whitespace */ 234ac367740SPatrick McHardy mlen = string_len(ct, dptr, limit, NULL); 235ac367740SPatrick McHardy if (!mlen) 236ac367740SPatrick McHardy return 0; 237ac367740SPatrick McHardy dptr += mlen; 238ac367740SPatrick McHardy if (++dptr >= limit) 239ac367740SPatrick McHardy return 0; 240ac367740SPatrick McHardy 241ac367740SPatrick McHardy /* Find SIP URI */ 24254101f4fSPatrick McHardy for (; dptr < limit - strlen("sip:"); dptr++) { 243ac367740SPatrick McHardy if (*dptr == '\r' || *dptr == '\n') 244ac367740SPatrick McHardy return -1; 24554101f4fSPatrick McHardy if (strnicmp(dptr, "sip:", strlen("sip:")) == 0) { 24654101f4fSPatrick McHardy dptr += strlen("sip:"); 247ac367740SPatrick McHardy break; 248ac367740SPatrick McHardy } 24954101f4fSPatrick McHardy } 250624f8b7bSPatrick McHardy if (!skp_epaddr_len(ct, dptr, limit, &shift)) 251ac367740SPatrick McHardy return 0; 252624f8b7bSPatrick McHardy dptr += shift; 253624f8b7bSPatrick McHardy 254624f8b7bSPatrick McHardy if (!parse_addr(ct, dptr, &end, addr, limit)) 255624f8b7bSPatrick McHardy return -1; 256624f8b7bSPatrick McHardy if (end < limit && *end == ':') { 257624f8b7bSPatrick McHardy end++; 258624f8b7bSPatrick McHardy p = simple_strtoul(end, (char **)&end, 10); 259624f8b7bSPatrick McHardy if (p < 1024 || p > 65535) 260624f8b7bSPatrick McHardy return -1; 261624f8b7bSPatrick McHardy *port = htons(p); 262624f8b7bSPatrick McHardy } else 263624f8b7bSPatrick McHardy *port = htons(SIP_PORT); 264624f8b7bSPatrick McHardy 265624f8b7bSPatrick McHardy if (end == dptr) 266624f8b7bSPatrick McHardy return 0; 267624f8b7bSPatrick McHardy *matchoff = dptr - start; 268624f8b7bSPatrick McHardy *matchlen = end - dptr; 269ac367740SPatrick McHardy return 1; 270ac367740SPatrick McHardy } 271ac367740SPatrick McHardy EXPORT_SYMBOL_GPL(ct_sip_parse_request); 272ac367740SPatrick McHardy 273ea45f12aSPatrick McHardy /* SIP header parsing: SIP headers are located at the beginning of a line, but 274ea45f12aSPatrick McHardy * may span several lines, in which case the continuation lines begin with a 275ea45f12aSPatrick McHardy * whitespace character. RFC 2543 allows lines to be terminated with CR, LF or 276ea45f12aSPatrick McHardy * CRLF, RFC 3261 allows only CRLF, we support both. 277ea45f12aSPatrick McHardy * 278ea45f12aSPatrick McHardy * Headers are followed by (optionally) whitespace, a colon, again (optionally) 279ea45f12aSPatrick McHardy * whitespace and the values. Whitespace in this context means any amount of 280ea45f12aSPatrick McHardy * tabs, spaces and continuation lines, which are treated as a single whitespace 281ea45f12aSPatrick McHardy * character. 28205e3ced2SPatrick McHardy * 28305e3ced2SPatrick McHardy * Some headers may appear multiple times. A comma seperated list of values is 28405e3ced2SPatrick McHardy * equivalent to multiple headers. 285ea45f12aSPatrick McHardy */ 286ea45f12aSPatrick McHardy static const struct sip_header ct_sip_hdrs[] = { 28730f33e6dSPatrick McHardy [SIP_HDR_CSEQ] = SIP_HDR("CSeq", NULL, NULL, digits_len), 288ea45f12aSPatrick McHardy [SIP_HDR_FROM] = SIP_HDR("From", "f", "sip:", skp_epaddr_len), 289ea45f12aSPatrick McHardy [SIP_HDR_TO] = SIP_HDR("To", "t", "sip:", skp_epaddr_len), 290ea45f12aSPatrick McHardy [SIP_HDR_CONTACT] = SIP_HDR("Contact", "m", "sip:", skp_epaddr_len), 291f5b321bdSPatrick McHardy [SIP_HDR_VIA_UDP] = SIP_HDR("Via", "v", "UDP ", epaddr_len), 292f5b321bdSPatrick McHardy [SIP_HDR_VIA_TCP] = SIP_HDR("Via", "v", "TCP ", epaddr_len), 2930f32a40fSPatrick McHardy [SIP_HDR_EXPIRES] = SIP_HDR("Expires", NULL, NULL, digits_len), 294ea45f12aSPatrick McHardy [SIP_HDR_CONTENT_LENGTH] = SIP_HDR("Content-Length", "l", NULL, digits_len), 295ea45f12aSPatrick McHardy }; 296ea45f12aSPatrick McHardy 297ea45f12aSPatrick McHardy static const char *sip_follow_continuation(const char *dptr, const char *limit) 2989fafcd7bSPatrick McHardy { 299ea45f12aSPatrick McHardy /* Walk past newline */ 300ea45f12aSPatrick McHardy if (++dptr >= limit) 301ea45f12aSPatrick McHardy return NULL; 3029fafcd7bSPatrick McHardy 303ea45f12aSPatrick McHardy /* Skip '\n' in CR LF */ 304ea45f12aSPatrick McHardy if (*(dptr - 1) == '\r' && *dptr == '\n') { 305ea45f12aSPatrick McHardy if (++dptr >= limit) 306ea45f12aSPatrick McHardy return NULL; 307ea45f12aSPatrick McHardy } 3089fafcd7bSPatrick McHardy 309ea45f12aSPatrick McHardy /* Continuation line? */ 310ea45f12aSPatrick McHardy if (*dptr != ' ' && *dptr != '\t') 311ea45f12aSPatrick McHardy return NULL; 312ea45f12aSPatrick McHardy 313ea45f12aSPatrick McHardy /* skip leading whitespace */ 314ea45f12aSPatrick McHardy for (; dptr < limit; dptr++) { 315ea45f12aSPatrick McHardy if (*dptr != ' ' && *dptr != '\t') 316ea45f12aSPatrick McHardy break; 317ea45f12aSPatrick McHardy } 318ea45f12aSPatrick McHardy return dptr; 319ea45f12aSPatrick McHardy } 320ea45f12aSPatrick McHardy 321ea45f12aSPatrick McHardy static const char *sip_skip_whitespace(const char *dptr, const char *limit) 322ea45f12aSPatrick McHardy { 323ea45f12aSPatrick McHardy for (; dptr < limit; dptr++) { 324ea45f12aSPatrick McHardy if (*dptr == ' ') 325ea45f12aSPatrick McHardy continue; 326ea45f12aSPatrick McHardy if (*dptr != '\r' && *dptr != '\n') 327ea45f12aSPatrick McHardy break; 328ea45f12aSPatrick McHardy dptr = sip_follow_continuation(dptr, limit); 329ea45f12aSPatrick McHardy if (dptr == NULL) 330ea45f12aSPatrick McHardy return NULL; 331ea45f12aSPatrick McHardy } 332ea45f12aSPatrick McHardy return dptr; 333ea45f12aSPatrick McHardy } 334ea45f12aSPatrick McHardy 335ea45f12aSPatrick McHardy /* Search within a SIP header value, dealing with continuation lines */ 336ea45f12aSPatrick McHardy static const char *ct_sip_header_search(const char *dptr, const char *limit, 337ea45f12aSPatrick McHardy const char *needle, unsigned int len) 338ea45f12aSPatrick McHardy { 339ea45f12aSPatrick McHardy for (limit -= len; dptr < limit; dptr++) { 340ea45f12aSPatrick McHardy if (*dptr == '\r' || *dptr == '\n') { 341ea45f12aSPatrick McHardy dptr = sip_follow_continuation(dptr, limit); 342ea45f12aSPatrick McHardy if (dptr == NULL) 343ea45f12aSPatrick McHardy break; 3449fafcd7bSPatrick McHardy continue; 3459fafcd7bSPatrick McHardy } 3469fafcd7bSPatrick McHardy 347ea45f12aSPatrick McHardy if (strnicmp(dptr, needle, len) == 0) 348ea45f12aSPatrick McHardy return dptr; 349ea45f12aSPatrick McHardy } 350ea45f12aSPatrick McHardy return NULL; 351ea45f12aSPatrick McHardy } 352ea45f12aSPatrick McHardy 353ea45f12aSPatrick McHardy int ct_sip_get_header(const struct nf_conn *ct, const char *dptr, 354ea45f12aSPatrick McHardy unsigned int dataoff, unsigned int datalen, 355ea45f12aSPatrick McHardy enum sip_header_types type, 356ea45f12aSPatrick McHardy unsigned int *matchoff, unsigned int *matchlen) 357ea45f12aSPatrick McHardy { 358ea45f12aSPatrick McHardy const struct sip_header *hdr = &ct_sip_hdrs[type]; 359ea45f12aSPatrick McHardy const char *start = dptr, *limit = dptr + datalen; 360ea45f12aSPatrick McHardy int shift = 0; 361ea45f12aSPatrick McHardy 362ea45f12aSPatrick McHardy for (dptr += dataoff; dptr < limit; dptr++) { 363ea45f12aSPatrick McHardy /* Find beginning of line */ 364ea45f12aSPatrick McHardy if (*dptr != '\r' && *dptr != '\n') 365ea45f12aSPatrick McHardy continue; 366ea45f12aSPatrick McHardy if (++dptr >= limit) 367ea45f12aSPatrick McHardy break; 368ea45f12aSPatrick McHardy if (*(dptr - 1) == '\r' && *dptr == '\n') { 369ea45f12aSPatrick McHardy if (++dptr >= limit) 370ea45f12aSPatrick McHardy break; 371ea45f12aSPatrick McHardy } 372ea45f12aSPatrick McHardy 373ea45f12aSPatrick McHardy /* Skip continuation lines */ 374ea45f12aSPatrick McHardy if (*dptr == ' ' || *dptr == '\t') 375ea45f12aSPatrick McHardy continue; 376ea45f12aSPatrick McHardy 377ea45f12aSPatrick McHardy /* Find header. Compact headers must be followed by a 378ea45f12aSPatrick McHardy * non-alphabetic character to avoid mismatches. */ 379ea45f12aSPatrick McHardy if (limit - dptr >= hdr->len && 380ea45f12aSPatrick McHardy strnicmp(dptr, hdr->name, hdr->len) == 0) 381ea45f12aSPatrick McHardy dptr += hdr->len; 382ea45f12aSPatrick McHardy else if (hdr->cname && limit - dptr >= hdr->clen + 1 && 383ea45f12aSPatrick McHardy strnicmp(dptr, hdr->cname, hdr->clen) == 0 && 384135d0189SPatrick McHardy !isalpha(*(dptr + hdr->clen))) 385ea45f12aSPatrick McHardy dptr += hdr->clen; 386ea45f12aSPatrick McHardy else 387ea45f12aSPatrick McHardy continue; 388ea45f12aSPatrick McHardy 389ea45f12aSPatrick McHardy /* Find and skip colon */ 390ea45f12aSPatrick McHardy dptr = sip_skip_whitespace(dptr, limit); 391ea45f12aSPatrick McHardy if (dptr == NULL) 392ea45f12aSPatrick McHardy break; 393ea45f12aSPatrick McHardy if (*dptr != ':' || ++dptr >= limit) 394ea45f12aSPatrick McHardy break; 395ea45f12aSPatrick McHardy 396ea45f12aSPatrick McHardy /* Skip whitespace after colon */ 397ea45f12aSPatrick McHardy dptr = sip_skip_whitespace(dptr, limit); 398ea45f12aSPatrick McHardy if (dptr == NULL) 399ea45f12aSPatrick McHardy break; 400ea45f12aSPatrick McHardy 401ea45f12aSPatrick McHardy *matchoff = dptr - start; 402ea45f12aSPatrick McHardy if (hdr->search) { 403ea45f12aSPatrick McHardy dptr = ct_sip_header_search(dptr, limit, hdr->search, 404ea45f12aSPatrick McHardy hdr->slen); 405ea45f12aSPatrick McHardy if (!dptr) 406ea45f12aSPatrick McHardy return -1; 407ea45f12aSPatrick McHardy dptr += hdr->slen; 408ea45f12aSPatrick McHardy } 409ea45f12aSPatrick McHardy 410ea45f12aSPatrick McHardy *matchlen = hdr->match_len(ct, dptr, limit, &shift); 4119fafcd7bSPatrick McHardy if (!*matchlen) 4129fafcd7bSPatrick McHardy return -1; 413ea45f12aSPatrick McHardy *matchoff = dptr - start + shift; 4149fafcd7bSPatrick McHardy return 1; 4159fafcd7bSPatrick McHardy } 4169fafcd7bSPatrick McHardy return 0; 4179fafcd7bSPatrick McHardy } 418ea45f12aSPatrick McHardy EXPORT_SYMBOL_GPL(ct_sip_get_header); 4199fafcd7bSPatrick McHardy 42005e3ced2SPatrick McHardy /* Get next header field in a list of comma seperated values */ 42105e3ced2SPatrick McHardy static int ct_sip_next_header(const struct nf_conn *ct, const char *dptr, 42205e3ced2SPatrick McHardy unsigned int dataoff, unsigned int datalen, 42305e3ced2SPatrick McHardy enum sip_header_types type, 42405e3ced2SPatrick McHardy unsigned int *matchoff, unsigned int *matchlen) 42505e3ced2SPatrick McHardy { 42605e3ced2SPatrick McHardy const struct sip_header *hdr = &ct_sip_hdrs[type]; 42705e3ced2SPatrick McHardy const char *start = dptr, *limit = dptr + datalen; 42805e3ced2SPatrick McHardy int shift = 0; 42905e3ced2SPatrick McHardy 43005e3ced2SPatrick McHardy dptr += dataoff; 43105e3ced2SPatrick McHardy 43205e3ced2SPatrick McHardy dptr = ct_sip_header_search(dptr, limit, ",", strlen(",")); 43305e3ced2SPatrick McHardy if (!dptr) 43405e3ced2SPatrick McHardy return 0; 43505e3ced2SPatrick McHardy 43605e3ced2SPatrick McHardy dptr = ct_sip_header_search(dptr, limit, hdr->search, hdr->slen); 43705e3ced2SPatrick McHardy if (!dptr) 43805e3ced2SPatrick McHardy return 0; 43905e3ced2SPatrick McHardy dptr += hdr->slen; 44005e3ced2SPatrick McHardy 44105e3ced2SPatrick McHardy *matchoff = dptr - start; 44205e3ced2SPatrick McHardy *matchlen = hdr->match_len(ct, dptr, limit, &shift); 44305e3ced2SPatrick McHardy if (!*matchlen) 44405e3ced2SPatrick McHardy return -1; 44505e3ced2SPatrick McHardy *matchoff += shift; 44605e3ced2SPatrick McHardy return 1; 44705e3ced2SPatrick McHardy } 44805e3ced2SPatrick McHardy 44905e3ced2SPatrick McHardy /* Walk through headers until a parsable one is found or no header of the 45005e3ced2SPatrick McHardy * given type is left. */ 45105e3ced2SPatrick McHardy static int ct_sip_walk_headers(const struct nf_conn *ct, const char *dptr, 45205e3ced2SPatrick McHardy unsigned int dataoff, unsigned int datalen, 45305e3ced2SPatrick McHardy enum sip_header_types type, int *in_header, 45405e3ced2SPatrick McHardy unsigned int *matchoff, unsigned int *matchlen) 45505e3ced2SPatrick McHardy { 45605e3ced2SPatrick McHardy int ret; 45705e3ced2SPatrick McHardy 45805e3ced2SPatrick McHardy if (in_header && *in_header) { 45905e3ced2SPatrick McHardy while (1) { 46005e3ced2SPatrick McHardy ret = ct_sip_next_header(ct, dptr, dataoff, datalen, 46105e3ced2SPatrick McHardy type, matchoff, matchlen); 46205e3ced2SPatrick McHardy if (ret > 0) 46305e3ced2SPatrick McHardy return ret; 46405e3ced2SPatrick McHardy if (ret == 0) 46505e3ced2SPatrick McHardy break; 46605e3ced2SPatrick McHardy dataoff += *matchoff; 46705e3ced2SPatrick McHardy } 46805e3ced2SPatrick McHardy *in_header = 0; 46905e3ced2SPatrick McHardy } 47005e3ced2SPatrick McHardy 47105e3ced2SPatrick McHardy while (1) { 47205e3ced2SPatrick McHardy ret = ct_sip_get_header(ct, dptr, dataoff, datalen, 47305e3ced2SPatrick McHardy type, matchoff, matchlen); 47405e3ced2SPatrick McHardy if (ret > 0) 47505e3ced2SPatrick McHardy break; 47605e3ced2SPatrick McHardy if (ret == 0) 47705e3ced2SPatrick McHardy return ret; 47805e3ced2SPatrick McHardy dataoff += *matchoff; 47905e3ced2SPatrick McHardy } 48005e3ced2SPatrick McHardy 48105e3ced2SPatrick McHardy if (in_header) 48205e3ced2SPatrick McHardy *in_header = 1; 48305e3ced2SPatrick McHardy return 1; 48405e3ced2SPatrick McHardy } 48505e3ced2SPatrick McHardy 48605e3ced2SPatrick McHardy /* Locate a SIP header, parse the URI and return the offset and length of 48705e3ced2SPatrick McHardy * the address as well as the address and port themselves. A stream of 48805e3ced2SPatrick McHardy * headers can be parsed by handing in a non-NULL datalen and in_header 48905e3ced2SPatrick McHardy * pointer. 49005e3ced2SPatrick McHardy */ 49105e3ced2SPatrick McHardy int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr, 49205e3ced2SPatrick McHardy unsigned int *dataoff, unsigned int datalen, 49305e3ced2SPatrick McHardy enum sip_header_types type, int *in_header, 49405e3ced2SPatrick McHardy unsigned int *matchoff, unsigned int *matchlen, 49505e3ced2SPatrick McHardy union nf_inet_addr *addr, __be16 *port) 49605e3ced2SPatrick McHardy { 49705e3ced2SPatrick McHardy const char *c, *limit = dptr + datalen; 49805e3ced2SPatrick McHardy unsigned int p; 49905e3ced2SPatrick McHardy int ret; 50005e3ced2SPatrick McHardy 50105e3ced2SPatrick McHardy ret = ct_sip_walk_headers(ct, dptr, dataoff ? *dataoff : 0, datalen, 50205e3ced2SPatrick McHardy type, in_header, matchoff, matchlen); 50305e3ced2SPatrick McHardy WARN_ON(ret < 0); 50405e3ced2SPatrick McHardy if (ret == 0) 50505e3ced2SPatrick McHardy return ret; 50605e3ced2SPatrick McHardy 50705e3ced2SPatrick McHardy if (!parse_addr(ct, dptr + *matchoff, &c, addr, limit)) 50805e3ced2SPatrick McHardy return -1; 50905e3ced2SPatrick McHardy if (*c == ':') { 51005e3ced2SPatrick McHardy c++; 51105e3ced2SPatrick McHardy p = simple_strtoul(c, (char **)&c, 10); 51205e3ced2SPatrick McHardy if (p < 1024 || p > 65535) 51305e3ced2SPatrick McHardy return -1; 51405e3ced2SPatrick McHardy *port = htons(p); 51505e3ced2SPatrick McHardy } else 51605e3ced2SPatrick McHardy *port = htons(SIP_PORT); 51705e3ced2SPatrick McHardy 51805e3ced2SPatrick McHardy if (dataoff) 51905e3ced2SPatrick McHardy *dataoff = c - dptr; 52005e3ced2SPatrick McHardy return 1; 52105e3ced2SPatrick McHardy } 52205e3ced2SPatrick McHardy EXPORT_SYMBOL_GPL(ct_sip_parse_header_uri); 52305e3ced2SPatrick McHardy 524f5b321bdSPatrick McHardy static int ct_sip_parse_param(const struct nf_conn *ct, const char *dptr, 525f5b321bdSPatrick McHardy unsigned int dataoff, unsigned int datalen, 526f5b321bdSPatrick McHardy const char *name, 527f5b321bdSPatrick McHardy unsigned int *matchoff, unsigned int *matchlen) 528f5b321bdSPatrick McHardy { 529f5b321bdSPatrick McHardy const char *limit = dptr + datalen; 530f5b321bdSPatrick McHardy const char *start; 531f5b321bdSPatrick McHardy const char *end; 532f5b321bdSPatrick McHardy 533f5b321bdSPatrick McHardy limit = ct_sip_header_search(dptr + dataoff, limit, ",", strlen(",")); 534f5b321bdSPatrick McHardy if (!limit) 535f5b321bdSPatrick McHardy limit = dptr + datalen; 536f5b321bdSPatrick McHardy 537f5b321bdSPatrick McHardy start = ct_sip_header_search(dptr + dataoff, limit, name, strlen(name)); 538f5b321bdSPatrick McHardy if (!start) 539f5b321bdSPatrick McHardy return 0; 540f5b321bdSPatrick McHardy start += strlen(name); 541f5b321bdSPatrick McHardy 542f5b321bdSPatrick McHardy end = ct_sip_header_search(start, limit, ";", strlen(";")); 543f5b321bdSPatrick McHardy if (!end) 544f5b321bdSPatrick McHardy end = limit; 545f5b321bdSPatrick McHardy 546f5b321bdSPatrick McHardy *matchoff = start - dptr; 547f5b321bdSPatrick McHardy *matchlen = end - start; 548f5b321bdSPatrick McHardy return 1; 549f5b321bdSPatrick McHardy } 550f5b321bdSPatrick McHardy 5512bbb2116SPatrick McHardy /* Parse address from header parameter and return address, offset and length */ 5522bbb2116SPatrick McHardy int ct_sip_parse_address_param(const struct nf_conn *ct, const char *dptr, 5532bbb2116SPatrick McHardy unsigned int dataoff, unsigned int datalen, 5542bbb2116SPatrick McHardy const char *name, 5552bbb2116SPatrick McHardy unsigned int *matchoff, unsigned int *matchlen, 5562bbb2116SPatrick McHardy union nf_inet_addr *addr) 5572bbb2116SPatrick McHardy { 5582bbb2116SPatrick McHardy const char *limit = dptr + datalen; 5592bbb2116SPatrick McHardy const char *start, *end; 5602bbb2116SPatrick McHardy 5612bbb2116SPatrick McHardy limit = ct_sip_header_search(dptr + dataoff, limit, ",", strlen(",")); 5622bbb2116SPatrick McHardy if (!limit) 5632bbb2116SPatrick McHardy limit = dptr + datalen; 5642bbb2116SPatrick McHardy 5652bbb2116SPatrick McHardy start = ct_sip_header_search(dptr + dataoff, limit, name, strlen(name)); 5662bbb2116SPatrick McHardy if (!start) 5672bbb2116SPatrick McHardy return 0; 5682bbb2116SPatrick McHardy 5692bbb2116SPatrick McHardy start += strlen(name); 5702bbb2116SPatrick McHardy if (!parse_addr(ct, start, &end, addr, limit)) 5712bbb2116SPatrick McHardy return 0; 5722bbb2116SPatrick McHardy *matchoff = start - dptr; 5732bbb2116SPatrick McHardy *matchlen = end - start; 5742bbb2116SPatrick McHardy return 1; 5752bbb2116SPatrick McHardy } 5762bbb2116SPatrick McHardy EXPORT_SYMBOL_GPL(ct_sip_parse_address_param); 5772bbb2116SPatrick McHardy 5782bbb2116SPatrick McHardy /* Parse numerical header parameter and return value, offset and length */ 5792bbb2116SPatrick McHardy int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr, 5802bbb2116SPatrick McHardy unsigned int dataoff, unsigned int datalen, 5812bbb2116SPatrick McHardy const char *name, 5822bbb2116SPatrick McHardy unsigned int *matchoff, unsigned int *matchlen, 5832bbb2116SPatrick McHardy unsigned int *val) 5842bbb2116SPatrick McHardy { 5852bbb2116SPatrick McHardy const char *limit = dptr + datalen; 5862bbb2116SPatrick McHardy const char *start; 5872bbb2116SPatrick McHardy char *end; 5882bbb2116SPatrick McHardy 5892bbb2116SPatrick McHardy limit = ct_sip_header_search(dptr + dataoff, limit, ",", strlen(",")); 5902bbb2116SPatrick McHardy if (!limit) 5912bbb2116SPatrick McHardy limit = dptr + datalen; 5922bbb2116SPatrick McHardy 5932bbb2116SPatrick McHardy start = ct_sip_header_search(dptr + dataoff, limit, name, strlen(name)); 5942bbb2116SPatrick McHardy if (!start) 5952bbb2116SPatrick McHardy return 0; 5962bbb2116SPatrick McHardy 5972bbb2116SPatrick McHardy start += strlen(name); 5982bbb2116SPatrick McHardy *val = simple_strtoul(start, &end, 0); 5992bbb2116SPatrick McHardy if (start == end) 6002bbb2116SPatrick McHardy return 0; 6012bbb2116SPatrick McHardy if (matchoff && matchlen) { 6022bbb2116SPatrick McHardy *matchoff = start - dptr; 6032bbb2116SPatrick McHardy *matchlen = end - start; 6042bbb2116SPatrick McHardy } 6052bbb2116SPatrick McHardy return 1; 6062bbb2116SPatrick McHardy } 6072bbb2116SPatrick McHardy EXPORT_SYMBOL_GPL(ct_sip_parse_numerical_param); 6082bbb2116SPatrick McHardy 609f5b321bdSPatrick McHardy static int ct_sip_parse_transport(struct nf_conn *ct, const char *dptr, 610f5b321bdSPatrick McHardy unsigned int dataoff, unsigned int datalen, 611f5b321bdSPatrick McHardy u8 *proto) 612f5b321bdSPatrick McHardy { 613f5b321bdSPatrick McHardy unsigned int matchoff, matchlen; 614f5b321bdSPatrick McHardy 615f5b321bdSPatrick McHardy if (ct_sip_parse_param(ct, dptr, dataoff, datalen, "transport=", 616f5b321bdSPatrick McHardy &matchoff, &matchlen)) { 617f5b321bdSPatrick McHardy if (!strnicmp(dptr + matchoff, "TCP", strlen("TCP"))) 618f5b321bdSPatrick McHardy *proto = IPPROTO_TCP; 619f5b321bdSPatrick McHardy else if (!strnicmp(dptr + matchoff, "UDP", strlen("UDP"))) 620f5b321bdSPatrick McHardy *proto = IPPROTO_UDP; 621f5b321bdSPatrick McHardy else 622f5b321bdSPatrick McHardy return 0; 623f5b321bdSPatrick McHardy 624f5b321bdSPatrick McHardy if (*proto != nf_ct_protonum(ct)) 625f5b321bdSPatrick McHardy return 0; 626f5b321bdSPatrick McHardy } else 627f5b321bdSPatrick McHardy *proto = nf_ct_protonum(ct); 628f5b321bdSPatrick McHardy 629f5b321bdSPatrick McHardy return 1; 630f5b321bdSPatrick McHardy } 631f5b321bdSPatrick McHardy 6323e9b4600SPatrick McHardy /* SDP header parsing: a SDP session description contains an ordered set of 6333e9b4600SPatrick McHardy * headers, starting with a section containing general session parameters, 6343e9b4600SPatrick McHardy * optionally followed by multiple media descriptions. 6353e9b4600SPatrick McHardy * 6363e9b4600SPatrick McHardy * SDP headers always start at the beginning of a line. According to RFC 2327: 6373e9b4600SPatrick McHardy * "The sequence CRLF (0x0d0a) is used to end a record, although parsers should 6383e9b4600SPatrick McHardy * be tolerant and also accept records terminated with a single newline 6393e9b4600SPatrick McHardy * character". We handle both cases. 6403e9b4600SPatrick McHardy */ 6413e9b4600SPatrick McHardy static const struct sip_header ct_sdp_hdrs[] = { 6423e9b4600SPatrick McHardy [SDP_HDR_VERSION] = SDP_HDR("v=", NULL, digits_len), 6433e9b4600SPatrick McHardy [SDP_HDR_OWNER_IP4] = SDP_HDR("o=", "IN IP4 ", epaddr_len), 6443e9b4600SPatrick McHardy [SDP_HDR_CONNECTION_IP4] = SDP_HDR("c=", "IN IP4 ", epaddr_len), 6453e9b4600SPatrick McHardy [SDP_HDR_OWNER_IP6] = SDP_HDR("o=", "IN IP6 ", epaddr_len), 6463e9b4600SPatrick McHardy [SDP_HDR_CONNECTION_IP6] = SDP_HDR("c=", "IN IP6 ", epaddr_len), 6470d0ab037SPatrick McHardy [SDP_HDR_MEDIA] = SDP_HDR("m=", NULL, media_len), 6483e9b4600SPatrick McHardy }; 6493e9b4600SPatrick McHardy 6503e9b4600SPatrick McHardy /* Linear string search within SDP header values */ 6513e9b4600SPatrick McHardy static const char *ct_sdp_header_search(const char *dptr, const char *limit, 6523e9b4600SPatrick McHardy const char *needle, unsigned int len) 6533e9b4600SPatrick McHardy { 6543e9b4600SPatrick McHardy for (limit -= len; dptr < limit; dptr++) { 6553e9b4600SPatrick McHardy if (*dptr == '\r' || *dptr == '\n') 6563e9b4600SPatrick McHardy break; 6573e9b4600SPatrick McHardy if (strncmp(dptr, needle, len) == 0) 6583e9b4600SPatrick McHardy return dptr; 6593e9b4600SPatrick McHardy } 6603e9b4600SPatrick McHardy return NULL; 6613e9b4600SPatrick McHardy } 6623e9b4600SPatrick McHardy 6633e9b4600SPatrick McHardy /* Locate a SDP header (optionally a substring within the header value), 6643e9b4600SPatrick McHardy * optionally stopping at the first occurence of the term header, parse 6653e9b4600SPatrick McHardy * it and return the offset and length of the data we're interested in. 6663e9b4600SPatrick McHardy */ 6673e9b4600SPatrick McHardy int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr, 6683e9b4600SPatrick McHardy unsigned int dataoff, unsigned int datalen, 6693e9b4600SPatrick McHardy enum sdp_header_types type, 6703e9b4600SPatrick McHardy enum sdp_header_types term, 6713e9b4600SPatrick McHardy unsigned int *matchoff, unsigned int *matchlen) 6723e9b4600SPatrick McHardy { 6733e9b4600SPatrick McHardy const struct sip_header *hdr = &ct_sdp_hdrs[type]; 6743e9b4600SPatrick McHardy const struct sip_header *thdr = &ct_sdp_hdrs[term]; 6753e9b4600SPatrick McHardy const char *start = dptr, *limit = dptr + datalen; 6763e9b4600SPatrick McHardy int shift = 0; 6773e9b4600SPatrick McHardy 6783e9b4600SPatrick McHardy for (dptr += dataoff; dptr < limit; dptr++) { 6793e9b4600SPatrick McHardy /* Find beginning of line */ 6803e9b4600SPatrick McHardy if (*dptr != '\r' && *dptr != '\n') 6813e9b4600SPatrick McHardy continue; 6823e9b4600SPatrick McHardy if (++dptr >= limit) 6833e9b4600SPatrick McHardy break; 6843e9b4600SPatrick McHardy if (*(dptr - 1) == '\r' && *dptr == '\n') { 6853e9b4600SPatrick McHardy if (++dptr >= limit) 6863e9b4600SPatrick McHardy break; 6873e9b4600SPatrick McHardy } 6883e9b4600SPatrick McHardy 6893e9b4600SPatrick McHardy if (term != SDP_HDR_UNSPEC && 6903e9b4600SPatrick McHardy limit - dptr >= thdr->len && 6913e9b4600SPatrick McHardy strnicmp(dptr, thdr->name, thdr->len) == 0) 6923e9b4600SPatrick McHardy break; 6933e9b4600SPatrick McHardy else if (limit - dptr >= hdr->len && 6943e9b4600SPatrick McHardy strnicmp(dptr, hdr->name, hdr->len) == 0) 6953e9b4600SPatrick McHardy dptr += hdr->len; 6963e9b4600SPatrick McHardy else 6973e9b4600SPatrick McHardy continue; 6983e9b4600SPatrick McHardy 6993e9b4600SPatrick McHardy *matchoff = dptr - start; 7003e9b4600SPatrick McHardy if (hdr->search) { 7013e9b4600SPatrick McHardy dptr = ct_sdp_header_search(dptr, limit, hdr->search, 7023e9b4600SPatrick McHardy hdr->slen); 7033e9b4600SPatrick McHardy if (!dptr) 7043e9b4600SPatrick McHardy return -1; 7053e9b4600SPatrick McHardy dptr += hdr->slen; 7063e9b4600SPatrick McHardy } 7073e9b4600SPatrick McHardy 7083e9b4600SPatrick McHardy *matchlen = hdr->match_len(ct, dptr, limit, &shift); 7093e9b4600SPatrick McHardy if (!*matchlen) 7103e9b4600SPatrick McHardy return -1; 7113e9b4600SPatrick McHardy *matchoff = dptr - start + shift; 7123e9b4600SPatrick McHardy return 1; 7133e9b4600SPatrick McHardy } 7143e9b4600SPatrick McHardy return 0; 7153e9b4600SPatrick McHardy } 7163e9b4600SPatrick McHardy EXPORT_SYMBOL_GPL(ct_sip_get_sdp_header); 7173e9b4600SPatrick McHardy 7184ab9e64eSPatrick McHardy static int ct_sip_parse_sdp_addr(const struct nf_conn *ct, const char *dptr, 7194ab9e64eSPatrick McHardy unsigned int dataoff, unsigned int datalen, 7204ab9e64eSPatrick McHardy enum sdp_header_types type, 7214ab9e64eSPatrick McHardy enum sdp_header_types term, 7224ab9e64eSPatrick McHardy unsigned int *matchoff, unsigned int *matchlen, 7234ab9e64eSPatrick McHardy union nf_inet_addr *addr) 7244ab9e64eSPatrick McHardy { 7254ab9e64eSPatrick McHardy int ret; 7264ab9e64eSPatrick McHardy 7274ab9e64eSPatrick McHardy ret = ct_sip_get_sdp_header(ct, dptr, dataoff, datalen, type, term, 7284ab9e64eSPatrick McHardy matchoff, matchlen); 7294ab9e64eSPatrick McHardy if (ret <= 0) 7304ab9e64eSPatrick McHardy return ret; 7314ab9e64eSPatrick McHardy 7324ab9e64eSPatrick McHardy if (!parse_addr(ct, dptr + *matchoff, NULL, addr, 7334ab9e64eSPatrick McHardy dptr + *matchoff + *matchlen)) 7344ab9e64eSPatrick McHardy return -1; 7354ab9e64eSPatrick McHardy return 1; 7364ab9e64eSPatrick McHardy } 7374ab9e64eSPatrick McHardy 7380f32a40fSPatrick McHardy static int refresh_signalling_expectation(struct nf_conn *ct, 7390f32a40fSPatrick McHardy union nf_inet_addr *addr, 740f5b321bdSPatrick McHardy u8 proto, __be16 port, 7410f32a40fSPatrick McHardy unsigned int expires) 7420f32a40fSPatrick McHardy { 7430f32a40fSPatrick McHardy struct nf_conn_help *help = nfct_help(ct); 7440f32a40fSPatrick McHardy struct nf_conntrack_expect *exp; 7450f32a40fSPatrick McHardy struct hlist_node *n, *next; 7460f32a40fSPatrick McHardy int found = 0; 7470f32a40fSPatrick McHardy 7480f32a40fSPatrick McHardy spin_lock_bh(&nf_conntrack_lock); 7490f32a40fSPatrick McHardy hlist_for_each_entry_safe(exp, n, next, &help->expectations, lnode) { 7500f32a40fSPatrick McHardy if (exp->class != SIP_EXPECT_SIGNALLING || 7510f32a40fSPatrick McHardy !nf_inet_addr_cmp(&exp->tuple.dst.u3, addr) || 752f5b321bdSPatrick McHardy exp->tuple.dst.protonum != proto || 7530f32a40fSPatrick McHardy exp->tuple.dst.u.udp.port != port) 7540f32a40fSPatrick McHardy continue; 7550f32a40fSPatrick McHardy if (!del_timer(&exp->timeout)) 7560f32a40fSPatrick McHardy continue; 7570f32a40fSPatrick McHardy exp->flags &= ~NF_CT_EXPECT_INACTIVE; 7580f32a40fSPatrick McHardy exp->timeout.expires = jiffies + expires * HZ; 7590f32a40fSPatrick McHardy add_timer(&exp->timeout); 7600f32a40fSPatrick McHardy found = 1; 7610f32a40fSPatrick McHardy break; 7620f32a40fSPatrick McHardy } 7630f32a40fSPatrick McHardy spin_unlock_bh(&nf_conntrack_lock); 7640f32a40fSPatrick McHardy return found; 7650f32a40fSPatrick McHardy } 7660f32a40fSPatrick McHardy 7670f32a40fSPatrick McHardy static void flush_expectations(struct nf_conn *ct, bool media) 7689467ee38SPatrick McHardy { 7699467ee38SPatrick McHardy struct nf_conn_help *help = nfct_help(ct); 7709467ee38SPatrick McHardy struct nf_conntrack_expect *exp; 7719467ee38SPatrick McHardy struct hlist_node *n, *next; 7729467ee38SPatrick McHardy 7739467ee38SPatrick McHardy spin_lock_bh(&nf_conntrack_lock); 7749467ee38SPatrick McHardy hlist_for_each_entry_safe(exp, n, next, &help->expectations, lnode) { 7750f32a40fSPatrick McHardy if ((exp->class != SIP_EXPECT_SIGNALLING) ^ media) 7760f32a40fSPatrick McHardy continue; 7779467ee38SPatrick McHardy if (!del_timer(&exp->timeout)) 7789467ee38SPatrick McHardy continue; 7799467ee38SPatrick McHardy nf_ct_unlink_expect(exp); 7809467ee38SPatrick McHardy nf_ct_expect_put(exp); 7810f32a40fSPatrick McHardy if (!media) 7820f32a40fSPatrick McHardy break; 7839467ee38SPatrick McHardy } 7849467ee38SPatrick McHardy spin_unlock_bh(&nf_conntrack_lock); 7859467ee38SPatrick McHardy } 7869467ee38SPatrick McHardy 7873b6b9fabSPatrick McHardy static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int dataoff, 788212440a7SPatrick McHardy const char **dptr, unsigned int *datalen, 7894ab9e64eSPatrick McHardy union nf_inet_addr *daddr, __be16 port, 7900d0ab037SPatrick McHardy enum sip_expectation_classes class, 7914ab9e64eSPatrick McHardy unsigned int mediaoff, unsigned int medialen) 7929fafcd7bSPatrick McHardy { 793a9c1d359SPatrick McHardy struct nf_conntrack_expect *exp, *rtp_exp, *rtcp_exp; 794212440a7SPatrick McHardy enum ip_conntrack_info ctinfo; 795212440a7SPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 796a5c3a800SAlexey Dobriyan struct net *net = nf_ct_net(ct); 7979fafcd7bSPatrick McHardy enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); 798d901a936SPatrick McHardy union nf_inet_addr *saddr; 799d901a936SPatrick McHardy struct nf_conntrack_tuple tuple; 800c7f485abSPatrick McHardy int direct_rtp = 0, skip_expect = 0, ret = NF_DROP; 801a9c1d359SPatrick McHardy u_int16_t base_port; 802a9c1d359SPatrick McHardy __be16 rtp_port, rtcp_port; 803c7f485abSPatrick McHardy typeof(nf_nat_sdp_port_hook) nf_nat_sdp_port; 8044ab9e64eSPatrick McHardy typeof(nf_nat_sdp_media_hook) nf_nat_sdp_media; 8059fafcd7bSPatrick McHardy 806d901a936SPatrick McHardy saddr = NULL; 807d901a936SPatrick McHardy if (sip_direct_media) { 808d901a936SPatrick McHardy if (!nf_inet_addr_cmp(daddr, &ct->tuplehash[dir].tuple.src.u3)) 809d901a936SPatrick McHardy return NF_ACCEPT; 810d901a936SPatrick McHardy saddr = &ct->tuplehash[!dir].tuple.src.u3; 811d901a936SPatrick McHardy } 812d901a936SPatrick McHardy 813d901a936SPatrick McHardy /* We need to check whether the registration exists before attempting 814d901a936SPatrick McHardy * to register it since we can see the same media description multiple 815d901a936SPatrick McHardy * times on different connections in case multiple endpoints receive 816d901a936SPatrick McHardy * the same call. 817c7f485abSPatrick McHardy * 818c7f485abSPatrick McHardy * RTP optimization: if we find a matching media channel expectation 819c7f485abSPatrick McHardy * and both the expectation and this connection are SNATed, we assume 820c7f485abSPatrick McHardy * both sides can reach each other directly and use the final 821c7f485abSPatrick McHardy * destination address from the expectation. We still need to keep 822c7f485abSPatrick McHardy * the NATed expectations for media that might arrive from the 823c7f485abSPatrick McHardy * outside, and additionally need to expect the direct RTP stream 824c7f485abSPatrick McHardy * in case it passes through us even without NAT. 825d901a936SPatrick McHardy */ 826d901a936SPatrick McHardy memset(&tuple, 0, sizeof(tuple)); 827d901a936SPatrick McHardy if (saddr) 828d901a936SPatrick McHardy tuple.src.u3 = *saddr; 8295e8fbe2aSPatrick McHardy tuple.src.l3num = nf_ct_l3num(ct); 830d901a936SPatrick McHardy tuple.dst.protonum = IPPROTO_UDP; 831d901a936SPatrick McHardy tuple.dst.u3 = *daddr; 832d901a936SPatrick McHardy tuple.dst.u.udp.port = port; 833d901a936SPatrick McHardy 834d901a936SPatrick McHardy rcu_read_lock(); 835c7f485abSPatrick McHardy do { 836a5c3a800SAlexey Dobriyan exp = __nf_ct_expect_find(net, &tuple); 837d901a936SPatrick McHardy 838c7f485abSPatrick McHardy if (!exp || exp->master == ct || 839c7f485abSPatrick McHardy nfct_help(exp->master)->helper != nfct_help(ct)->helper || 840c7f485abSPatrick McHardy exp->class != class) 841c7f485abSPatrick McHardy break; 842e1f9a464SPatrick McHardy #ifdef CONFIG_NF_NAT_NEEDED 843c7f485abSPatrick McHardy if (exp->tuple.src.l3num == AF_INET && !direct_rtp && 844c7f485abSPatrick McHardy (exp->saved_ip != exp->tuple.dst.u3.ip || 845c7f485abSPatrick McHardy exp->saved_proto.udp.port != exp->tuple.dst.u.udp.port) && 846c7f485abSPatrick McHardy ct->status & IPS_NAT_MASK) { 847c7f485abSPatrick McHardy daddr->ip = exp->saved_ip; 848c7f485abSPatrick McHardy tuple.dst.u3.ip = exp->saved_ip; 849c7f485abSPatrick McHardy tuple.dst.u.udp.port = exp->saved_proto.udp.port; 850c7f485abSPatrick McHardy direct_rtp = 1; 851c7f485abSPatrick McHardy } else 852e1f9a464SPatrick McHardy #endif 853c7f485abSPatrick McHardy skip_expect = 1; 854c7f485abSPatrick McHardy } while (!skip_expect); 855c7f485abSPatrick McHardy rcu_read_unlock(); 856d901a936SPatrick McHardy 857a9c1d359SPatrick McHardy base_port = ntohs(tuple.dst.u.udp.port) & ~1; 858a9c1d359SPatrick McHardy rtp_port = htons(base_port); 859a9c1d359SPatrick McHardy rtcp_port = htons(base_port + 1); 860a9c1d359SPatrick McHardy 861c7f485abSPatrick McHardy if (direct_rtp) { 862c7f485abSPatrick McHardy nf_nat_sdp_port = rcu_dereference(nf_nat_sdp_port_hook); 863c7f485abSPatrick McHardy if (nf_nat_sdp_port && 8643b6b9fabSPatrick McHardy !nf_nat_sdp_port(skb, dataoff, dptr, datalen, 865c7f485abSPatrick McHardy mediaoff, medialen, ntohs(rtp_port))) 866c7f485abSPatrick McHardy goto err1; 867c7f485abSPatrick McHardy } 868c7f485abSPatrick McHardy 869c7f485abSPatrick McHardy if (skip_expect) 870c7f485abSPatrick McHardy return NF_ACCEPT; 871c7f485abSPatrick McHardy 872a9c1d359SPatrick McHardy rtp_exp = nf_ct_expect_alloc(ct); 873a9c1d359SPatrick McHardy if (rtp_exp == NULL) 874a9c1d359SPatrick McHardy goto err1; 8755e8fbe2aSPatrick McHardy nf_ct_expect_init(rtp_exp, class, nf_ct_l3num(ct), saddr, daddr, 876a9c1d359SPatrick McHardy IPPROTO_UDP, NULL, &rtp_port); 877a9c1d359SPatrick McHardy 878a9c1d359SPatrick McHardy rtcp_exp = nf_ct_expect_alloc(ct); 879a9c1d359SPatrick McHardy if (rtcp_exp == NULL) 880a9c1d359SPatrick McHardy goto err2; 8815e8fbe2aSPatrick McHardy nf_ct_expect_init(rtcp_exp, class, nf_ct_l3num(ct), saddr, daddr, 882a9c1d359SPatrick McHardy IPPROTO_UDP, NULL, &rtcp_port); 8839fafcd7bSPatrick McHardy 8844ab9e64eSPatrick McHardy nf_nat_sdp_media = rcu_dereference(nf_nat_sdp_media_hook); 885c7f485abSPatrick McHardy if (nf_nat_sdp_media && ct->status & IPS_NAT_MASK && !direct_rtp) 8863b6b9fabSPatrick McHardy ret = nf_nat_sdp_media(skb, dataoff, dptr, datalen, 8873b6b9fabSPatrick McHardy rtp_exp, rtcp_exp, 8884ab9e64eSPatrick McHardy mediaoff, medialen, daddr); 8899fafcd7bSPatrick McHardy else { 890a9c1d359SPatrick McHardy if (nf_ct_expect_related(rtp_exp) == 0) { 891a9c1d359SPatrick McHardy if (nf_ct_expect_related(rtcp_exp) != 0) 892a9c1d359SPatrick McHardy nf_ct_unexpect_related(rtp_exp); 8939fafcd7bSPatrick McHardy else 8949fafcd7bSPatrick McHardy ret = NF_ACCEPT; 8959fafcd7bSPatrick McHardy } 896a9c1d359SPatrick McHardy } 897a9c1d359SPatrick McHardy nf_ct_expect_put(rtcp_exp); 898a9c1d359SPatrick McHardy err2: 899a9c1d359SPatrick McHardy nf_ct_expect_put(rtp_exp); 900a9c1d359SPatrick McHardy err1: 9019fafcd7bSPatrick McHardy return ret; 9029fafcd7bSPatrick McHardy } 9039fafcd7bSPatrick McHardy 9040d0ab037SPatrick McHardy static const struct sdp_media_type sdp_media_types[] = { 9050d0ab037SPatrick McHardy SDP_MEDIA_TYPE("audio ", SIP_EXPECT_AUDIO), 9060d0ab037SPatrick McHardy SDP_MEDIA_TYPE("video ", SIP_EXPECT_VIDEO), 9070d0ab037SPatrick McHardy }; 9080d0ab037SPatrick McHardy 9090d0ab037SPatrick McHardy static const struct sdp_media_type *sdp_media_type(const char *dptr, 9100d0ab037SPatrick McHardy unsigned int matchoff, 9110d0ab037SPatrick McHardy unsigned int matchlen) 9120d0ab037SPatrick McHardy { 9130d0ab037SPatrick McHardy const struct sdp_media_type *t; 9140d0ab037SPatrick McHardy unsigned int i; 9150d0ab037SPatrick McHardy 9160d0ab037SPatrick McHardy for (i = 0; i < ARRAY_SIZE(sdp_media_types); i++) { 9170d0ab037SPatrick McHardy t = &sdp_media_types[i]; 9180d0ab037SPatrick McHardy if (matchlen < t->len || 9190d0ab037SPatrick McHardy strncmp(dptr + matchoff, t->name, t->len)) 9200d0ab037SPatrick McHardy continue; 9210d0ab037SPatrick McHardy return t; 9220d0ab037SPatrick McHardy } 9230d0ab037SPatrick McHardy return NULL; 9240d0ab037SPatrick McHardy } 9250d0ab037SPatrick McHardy 9263b6b9fabSPatrick McHardy static int process_sdp(struct sk_buff *skb, unsigned int dataoff, 92730f33e6dSPatrick McHardy const char **dptr, unsigned int *datalen, 92830f33e6dSPatrick McHardy unsigned int cseq) 9297d3dd043SPatrick McHardy { 9307d3dd043SPatrick McHardy enum ip_conntrack_info ctinfo; 9317d3dd043SPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 932ef75d49fSPatrick McHardy struct nf_conn_help *help = nfct_help(ct); 9337d3dd043SPatrick McHardy unsigned int matchoff, matchlen; 9344ab9e64eSPatrick McHardy unsigned int mediaoff, medialen; 9354ab9e64eSPatrick McHardy unsigned int sdpoff; 9364ab9e64eSPatrick McHardy unsigned int caddr_len, maddr_len; 9370d0ab037SPatrick McHardy unsigned int i; 9384ab9e64eSPatrick McHardy union nf_inet_addr caddr, maddr, rtp_addr; 9397d3dd043SPatrick McHardy unsigned int port; 9404ab9e64eSPatrick McHardy enum sdp_header_types c_hdr; 9410d0ab037SPatrick McHardy const struct sdp_media_type *t; 9420d0ab037SPatrick McHardy int ret = NF_ACCEPT; 9434ab9e64eSPatrick McHardy typeof(nf_nat_sdp_addr_hook) nf_nat_sdp_addr; 9444ab9e64eSPatrick McHardy typeof(nf_nat_sdp_session_hook) nf_nat_sdp_session; 9457d3dd043SPatrick McHardy 9460d0ab037SPatrick McHardy nf_nat_sdp_addr = rcu_dereference(nf_nat_sdp_addr_hook); 9475e8fbe2aSPatrick McHardy c_hdr = nf_ct_l3num(ct) == AF_INET ? SDP_HDR_CONNECTION_IP4 : 9487d3dd043SPatrick McHardy SDP_HDR_CONNECTION_IP6; 9497d3dd043SPatrick McHardy 9504ab9e64eSPatrick McHardy /* Find beginning of session description */ 9517d3dd043SPatrick McHardy if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen, 9524ab9e64eSPatrick McHardy SDP_HDR_VERSION, SDP_HDR_UNSPEC, 9537d3dd043SPatrick McHardy &matchoff, &matchlen) <= 0) 9547d3dd043SPatrick McHardy return NF_ACCEPT; 9554ab9e64eSPatrick McHardy sdpoff = matchoff; 9567d3dd043SPatrick McHardy 9574ab9e64eSPatrick McHardy /* The connection information is contained in the session description 9584ab9e64eSPatrick McHardy * and/or once per media description. The first media description marks 9594ab9e64eSPatrick McHardy * the end of the session description. */ 9604ab9e64eSPatrick McHardy caddr_len = 0; 9614ab9e64eSPatrick McHardy if (ct_sip_parse_sdp_addr(ct, *dptr, sdpoff, *datalen, 9624ab9e64eSPatrick McHardy c_hdr, SDP_HDR_MEDIA, 9634ab9e64eSPatrick McHardy &matchoff, &matchlen, &caddr) > 0) 9644ab9e64eSPatrick McHardy caddr_len = matchlen; 9657d3dd043SPatrick McHardy 9660d0ab037SPatrick McHardy mediaoff = sdpoff; 9670d0ab037SPatrick McHardy for (i = 0; i < ARRAY_SIZE(sdp_media_types); ) { 9680d0ab037SPatrick McHardy if (ct_sip_get_sdp_header(ct, *dptr, mediaoff, *datalen, 9697d3dd043SPatrick McHardy SDP_HDR_MEDIA, SDP_HDR_UNSPEC, 9704ab9e64eSPatrick McHardy &mediaoff, &medialen) <= 0) 9710d0ab037SPatrick McHardy break; 9720d0ab037SPatrick McHardy 9730d0ab037SPatrick McHardy /* Get media type and port number. A media port value of zero 9740d0ab037SPatrick McHardy * indicates an inactive stream. */ 9750d0ab037SPatrick McHardy t = sdp_media_type(*dptr, mediaoff, medialen); 9760d0ab037SPatrick McHardy if (!t) { 9770d0ab037SPatrick McHardy mediaoff += medialen; 9780d0ab037SPatrick McHardy continue; 9790d0ab037SPatrick McHardy } 9800d0ab037SPatrick McHardy mediaoff += t->len; 9810d0ab037SPatrick McHardy medialen -= t->len; 9827d3dd043SPatrick McHardy 9834ab9e64eSPatrick McHardy port = simple_strtoul(*dptr + mediaoff, NULL, 10); 9840d0ab037SPatrick McHardy if (port == 0) 9850d0ab037SPatrick McHardy continue; 9867d3dd043SPatrick McHardy if (port < 1024 || port > 65535) 9877d3dd043SPatrick McHardy return NF_DROP; 9887d3dd043SPatrick McHardy 9894ab9e64eSPatrick McHardy /* The media description overrides the session description. */ 9904ab9e64eSPatrick McHardy maddr_len = 0; 9914ab9e64eSPatrick McHardy if (ct_sip_parse_sdp_addr(ct, *dptr, mediaoff, *datalen, 9924ab9e64eSPatrick McHardy c_hdr, SDP_HDR_MEDIA, 9934ab9e64eSPatrick McHardy &matchoff, &matchlen, &maddr) > 0) { 9944ab9e64eSPatrick McHardy maddr_len = matchlen; 9954ab9e64eSPatrick McHardy memcpy(&rtp_addr, &maddr, sizeof(rtp_addr)); 9964ab9e64eSPatrick McHardy } else if (caddr_len) 9974ab9e64eSPatrick McHardy memcpy(&rtp_addr, &caddr, sizeof(rtp_addr)); 9984ab9e64eSPatrick McHardy else 9994ab9e64eSPatrick McHardy return NF_DROP; 10004ab9e64eSPatrick McHardy 10013b6b9fabSPatrick McHardy ret = set_expected_rtp_rtcp(skb, dataoff, dptr, datalen, 10020d0ab037SPatrick McHardy &rtp_addr, htons(port), t->class, 10034ab9e64eSPatrick McHardy mediaoff, medialen); 10044ab9e64eSPatrick McHardy if (ret != NF_ACCEPT) 10054ab9e64eSPatrick McHardy return ret; 10064ab9e64eSPatrick McHardy 10074ab9e64eSPatrick McHardy /* Update media connection address if present */ 10080d0ab037SPatrick McHardy if (maddr_len && nf_nat_sdp_addr && ct->status & IPS_NAT_MASK) { 10093b6b9fabSPatrick McHardy ret = nf_nat_sdp_addr(skb, dataoff, dptr, datalen, 10103b6b9fabSPatrick McHardy mediaoff, c_hdr, SDP_HDR_MEDIA, 10113b6b9fabSPatrick McHardy &rtp_addr); 10124ab9e64eSPatrick McHardy if (ret != NF_ACCEPT) 10134ab9e64eSPatrick McHardy return ret; 10144ab9e64eSPatrick McHardy } 10150d0ab037SPatrick McHardy i++; 10164ab9e64eSPatrick McHardy } 10174ab9e64eSPatrick McHardy 10184ab9e64eSPatrick McHardy /* Update session connection and owner addresses */ 10194ab9e64eSPatrick McHardy nf_nat_sdp_session = rcu_dereference(nf_nat_sdp_session_hook); 10204ab9e64eSPatrick McHardy if (nf_nat_sdp_session && ct->status & IPS_NAT_MASK) 10213b6b9fabSPatrick McHardy ret = nf_nat_sdp_session(skb, dataoff, dptr, datalen, sdpoff, 10223b6b9fabSPatrick McHardy &rtp_addr); 10234ab9e64eSPatrick McHardy 1024ef75d49fSPatrick McHardy if (ret == NF_ACCEPT && i > 0) 1025ef75d49fSPatrick McHardy help->help.ct_sip_info.invite_cseq = cseq; 1026ef75d49fSPatrick McHardy 10274ab9e64eSPatrick McHardy return ret; 10287d3dd043SPatrick McHardy } 10293b6b9fabSPatrick McHardy static int process_invite_response(struct sk_buff *skb, unsigned int dataoff, 103030f33e6dSPatrick McHardy const char **dptr, unsigned int *datalen, 103130f33e6dSPatrick McHardy unsigned int cseq, unsigned int code) 103230f33e6dSPatrick McHardy { 10339467ee38SPatrick McHardy enum ip_conntrack_info ctinfo; 10349467ee38SPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 1035ef75d49fSPatrick McHardy struct nf_conn_help *help = nfct_help(ct); 10369467ee38SPatrick McHardy 103730f33e6dSPatrick McHardy if ((code >= 100 && code <= 199) || 103830f33e6dSPatrick McHardy (code >= 200 && code <= 299)) 10393b6b9fabSPatrick McHardy return process_sdp(skb, dataoff, dptr, datalen, cseq); 1040ef75d49fSPatrick McHardy else if (help->help.ct_sip_info.invite_cseq == cseq) 10410f32a40fSPatrick McHardy flush_expectations(ct, true); 104230f33e6dSPatrick McHardy return NF_ACCEPT; 104330f33e6dSPatrick McHardy } 104430f33e6dSPatrick McHardy 10453b6b9fabSPatrick McHardy static int process_update_response(struct sk_buff *skb, unsigned int dataoff, 104630f33e6dSPatrick McHardy const char **dptr, unsigned int *datalen, 104730f33e6dSPatrick McHardy unsigned int cseq, unsigned int code) 104830f33e6dSPatrick McHardy { 10499467ee38SPatrick McHardy enum ip_conntrack_info ctinfo; 10509467ee38SPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 1051ef75d49fSPatrick McHardy struct nf_conn_help *help = nfct_help(ct); 10529467ee38SPatrick McHardy 105330f33e6dSPatrick McHardy if ((code >= 100 && code <= 199) || 105430f33e6dSPatrick McHardy (code >= 200 && code <= 299)) 10553b6b9fabSPatrick McHardy return process_sdp(skb, dataoff, dptr, datalen, cseq); 1056ef75d49fSPatrick McHardy else if (help->help.ct_sip_info.invite_cseq == cseq) 10570f32a40fSPatrick McHardy flush_expectations(ct, true); 105830f33e6dSPatrick McHardy return NF_ACCEPT; 105930f33e6dSPatrick McHardy } 106030f33e6dSPatrick McHardy 10613b6b9fabSPatrick McHardy static int process_prack_response(struct sk_buff *skb, unsigned int dataoff, 1062595a8ecbSPatrick McHardy const char **dptr, unsigned int *datalen, 1063595a8ecbSPatrick McHardy unsigned int cseq, unsigned int code) 1064595a8ecbSPatrick McHardy { 10659467ee38SPatrick McHardy enum ip_conntrack_info ctinfo; 10669467ee38SPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 1067ef75d49fSPatrick McHardy struct nf_conn_help *help = nfct_help(ct); 10689467ee38SPatrick McHardy 1069595a8ecbSPatrick McHardy if ((code >= 100 && code <= 199) || 1070595a8ecbSPatrick McHardy (code >= 200 && code <= 299)) 10713b6b9fabSPatrick McHardy return process_sdp(skb, dataoff, dptr, datalen, cseq); 1072ef75d49fSPatrick McHardy else if (help->help.ct_sip_info.invite_cseq == cseq) 10730f32a40fSPatrick McHardy flush_expectations(ct, true); 10749467ee38SPatrick McHardy return NF_ACCEPT; 10759467ee38SPatrick McHardy } 1076595a8ecbSPatrick McHardy 10773b6b9fabSPatrick McHardy static int process_bye_request(struct sk_buff *skb, unsigned int dataoff, 10789467ee38SPatrick McHardy const char **dptr, unsigned int *datalen, 10799467ee38SPatrick McHardy unsigned int cseq) 10809467ee38SPatrick McHardy { 10819467ee38SPatrick McHardy enum ip_conntrack_info ctinfo; 10829467ee38SPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 10839467ee38SPatrick McHardy 10840f32a40fSPatrick McHardy flush_expectations(ct, true); 10850f32a40fSPatrick McHardy return NF_ACCEPT; 10860f32a40fSPatrick McHardy } 10870f32a40fSPatrick McHardy 10880f32a40fSPatrick McHardy /* Parse a REGISTER request and create a permanent expectation for incoming 10890f32a40fSPatrick McHardy * signalling connections. The expectation is marked inactive and is activated 10900f32a40fSPatrick McHardy * when receiving a response indicating success from the registrar. 10910f32a40fSPatrick McHardy */ 10923b6b9fabSPatrick McHardy static int process_register_request(struct sk_buff *skb, unsigned int dataoff, 10930f32a40fSPatrick McHardy const char **dptr, unsigned int *datalen, 10940f32a40fSPatrick McHardy unsigned int cseq) 10950f32a40fSPatrick McHardy { 10960f32a40fSPatrick McHardy enum ip_conntrack_info ctinfo; 10970f32a40fSPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 10980f32a40fSPatrick McHardy struct nf_conn_help *help = nfct_help(ct); 10990f32a40fSPatrick McHardy enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); 11000f32a40fSPatrick McHardy unsigned int matchoff, matchlen; 11010f32a40fSPatrick McHardy struct nf_conntrack_expect *exp; 11020f32a40fSPatrick McHardy union nf_inet_addr *saddr, daddr; 11030f32a40fSPatrick McHardy __be16 port; 1104f5b321bdSPatrick McHardy u8 proto; 11050f32a40fSPatrick McHardy unsigned int expires = 0; 11060f32a40fSPatrick McHardy int ret; 11070f32a40fSPatrick McHardy typeof(nf_nat_sip_expect_hook) nf_nat_sip_expect; 11080f32a40fSPatrick McHardy 11090f32a40fSPatrick McHardy /* Expected connections can not register again. */ 11100f32a40fSPatrick McHardy if (ct->status & IPS_EXPECTED) 11110f32a40fSPatrick McHardy return NF_ACCEPT; 11120f32a40fSPatrick McHardy 11130f32a40fSPatrick McHardy /* We must check the expiration time: a value of zero signals the 11140f32a40fSPatrick McHardy * registrar to release the binding. We'll remove our expectation 11150f32a40fSPatrick McHardy * when receiving the new bindings in the response, but we don't 11160f32a40fSPatrick McHardy * want to create new ones. 11170f32a40fSPatrick McHardy * 11180f32a40fSPatrick McHardy * The expiration time may be contained in Expires: header, the 11190f32a40fSPatrick McHardy * Contact: header parameters or the URI parameters. 11200f32a40fSPatrick McHardy */ 11210f32a40fSPatrick McHardy if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_EXPIRES, 11220f32a40fSPatrick McHardy &matchoff, &matchlen) > 0) 11230f32a40fSPatrick McHardy expires = simple_strtoul(*dptr + matchoff, NULL, 10); 11240f32a40fSPatrick McHardy 11250f32a40fSPatrick McHardy ret = ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, 11260f32a40fSPatrick McHardy SIP_HDR_CONTACT, NULL, 11270f32a40fSPatrick McHardy &matchoff, &matchlen, &daddr, &port); 11280f32a40fSPatrick McHardy if (ret < 0) 11290f32a40fSPatrick McHardy return NF_DROP; 11300f32a40fSPatrick McHardy else if (ret == 0) 11310f32a40fSPatrick McHardy return NF_ACCEPT; 11320f32a40fSPatrick McHardy 11330f32a40fSPatrick McHardy /* We don't support third-party registrations */ 11340f32a40fSPatrick McHardy if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, &daddr)) 11350f32a40fSPatrick McHardy return NF_ACCEPT; 11360f32a40fSPatrick McHardy 1137f5b321bdSPatrick McHardy if (ct_sip_parse_transport(ct, *dptr, matchoff + matchlen, *datalen, 1138f5b321bdSPatrick McHardy &proto) == 0) 1139f5b321bdSPatrick McHardy return NF_ACCEPT; 1140f5b321bdSPatrick McHardy 11410f32a40fSPatrick McHardy if (ct_sip_parse_numerical_param(ct, *dptr, 11420f32a40fSPatrick McHardy matchoff + matchlen, *datalen, 11430f32a40fSPatrick McHardy "expires=", NULL, NULL, &expires) < 0) 11440f32a40fSPatrick McHardy return NF_DROP; 11450f32a40fSPatrick McHardy 11460f32a40fSPatrick McHardy if (expires == 0) { 11470f32a40fSPatrick McHardy ret = NF_ACCEPT; 11480f32a40fSPatrick McHardy goto store_cseq; 11490f32a40fSPatrick McHardy } 11500f32a40fSPatrick McHardy 11510f32a40fSPatrick McHardy exp = nf_ct_expect_alloc(ct); 11520f32a40fSPatrick McHardy if (!exp) 11530f32a40fSPatrick McHardy return NF_DROP; 11540f32a40fSPatrick McHardy 11550f32a40fSPatrick McHardy saddr = NULL; 11560f32a40fSPatrick McHardy if (sip_direct_signalling) 11570f32a40fSPatrick McHardy saddr = &ct->tuplehash[!dir].tuple.src.u3; 11580f32a40fSPatrick McHardy 11595e8fbe2aSPatrick McHardy nf_ct_expect_init(exp, SIP_EXPECT_SIGNALLING, nf_ct_l3num(ct), 1160f5b321bdSPatrick McHardy saddr, &daddr, proto, NULL, &port); 11610f32a40fSPatrick McHardy exp->timeout.expires = sip_timeout * HZ; 11620f32a40fSPatrick McHardy exp->helper = nfct_help(ct)->helper; 11630f32a40fSPatrick McHardy exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE; 11640f32a40fSPatrick McHardy 11650f32a40fSPatrick McHardy nf_nat_sip_expect = rcu_dereference(nf_nat_sip_expect_hook); 11660f32a40fSPatrick McHardy if (nf_nat_sip_expect && ct->status & IPS_NAT_MASK) 11673b6b9fabSPatrick McHardy ret = nf_nat_sip_expect(skb, dataoff, dptr, datalen, exp, 11680f32a40fSPatrick McHardy matchoff, matchlen); 11690f32a40fSPatrick McHardy else { 11700f32a40fSPatrick McHardy if (nf_ct_expect_related(exp) != 0) 11710f32a40fSPatrick McHardy ret = NF_DROP; 11720f32a40fSPatrick McHardy else 11730f32a40fSPatrick McHardy ret = NF_ACCEPT; 11740f32a40fSPatrick McHardy } 11750f32a40fSPatrick McHardy nf_ct_expect_put(exp); 11760f32a40fSPatrick McHardy 11770f32a40fSPatrick McHardy store_cseq: 11780f32a40fSPatrick McHardy if (ret == NF_ACCEPT) 11790f32a40fSPatrick McHardy help->help.ct_sip_info.register_cseq = cseq; 11800f32a40fSPatrick McHardy return ret; 11810f32a40fSPatrick McHardy } 11820f32a40fSPatrick McHardy 11833b6b9fabSPatrick McHardy static int process_register_response(struct sk_buff *skb, unsigned int dataoff, 11840f32a40fSPatrick McHardy const char **dptr, unsigned int *datalen, 11850f32a40fSPatrick McHardy unsigned int cseq, unsigned int code) 11860f32a40fSPatrick McHardy { 11870f32a40fSPatrick McHardy enum ip_conntrack_info ctinfo; 11880f32a40fSPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 11890f32a40fSPatrick McHardy struct nf_conn_help *help = nfct_help(ct); 11900f32a40fSPatrick McHardy enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); 11910f32a40fSPatrick McHardy union nf_inet_addr addr; 11920f32a40fSPatrick McHardy __be16 port; 1193f5b321bdSPatrick McHardy u8 proto; 11943b6b9fabSPatrick McHardy unsigned int matchoff, matchlen, coff = 0; 11950f32a40fSPatrick McHardy unsigned int expires = 0; 11960f32a40fSPatrick McHardy int in_contact = 0, ret; 11970f32a40fSPatrick McHardy 11980f32a40fSPatrick McHardy /* According to RFC 3261, "UAs MUST NOT send a new registration until 11990f32a40fSPatrick McHardy * they have received a final response from the registrar for the 12000f32a40fSPatrick McHardy * previous one or the previous REGISTER request has timed out". 12010f32a40fSPatrick McHardy * 12020f32a40fSPatrick McHardy * However, some servers fail to detect retransmissions and send late 12030f32a40fSPatrick McHardy * responses, so we store the sequence number of the last valid 12040f32a40fSPatrick McHardy * request and compare it here. 12050f32a40fSPatrick McHardy */ 12060f32a40fSPatrick McHardy if (help->help.ct_sip_info.register_cseq != cseq) 12070f32a40fSPatrick McHardy return NF_ACCEPT; 12080f32a40fSPatrick McHardy 12090f32a40fSPatrick McHardy if (code >= 100 && code <= 199) 12100f32a40fSPatrick McHardy return NF_ACCEPT; 12110f32a40fSPatrick McHardy if (code < 200 || code > 299) 12120f32a40fSPatrick McHardy goto flush; 12130f32a40fSPatrick McHardy 12140f32a40fSPatrick McHardy if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_EXPIRES, 12150f32a40fSPatrick McHardy &matchoff, &matchlen) > 0) 12160f32a40fSPatrick McHardy expires = simple_strtoul(*dptr + matchoff, NULL, 10); 12170f32a40fSPatrick McHardy 12180f32a40fSPatrick McHardy while (1) { 12190f32a40fSPatrick McHardy unsigned int c_expires = expires; 12200f32a40fSPatrick McHardy 12213b6b9fabSPatrick McHardy ret = ct_sip_parse_header_uri(ct, *dptr, &coff, *datalen, 12220f32a40fSPatrick McHardy SIP_HDR_CONTACT, &in_contact, 12230f32a40fSPatrick McHardy &matchoff, &matchlen, 12240f32a40fSPatrick McHardy &addr, &port); 12250f32a40fSPatrick McHardy if (ret < 0) 12260f32a40fSPatrick McHardy return NF_DROP; 12270f32a40fSPatrick McHardy else if (ret == 0) 12280f32a40fSPatrick McHardy break; 12290f32a40fSPatrick McHardy 12300f32a40fSPatrick McHardy /* We don't support third-party registrations */ 12310f32a40fSPatrick McHardy if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, &addr)) 12320f32a40fSPatrick McHardy continue; 12330f32a40fSPatrick McHardy 1234f5b321bdSPatrick McHardy if (ct_sip_parse_transport(ct, *dptr, matchoff + matchlen, 1235f5b321bdSPatrick McHardy *datalen, &proto) == 0) 1236f5b321bdSPatrick McHardy continue; 1237f5b321bdSPatrick McHardy 12380f32a40fSPatrick McHardy ret = ct_sip_parse_numerical_param(ct, *dptr, 12390f32a40fSPatrick McHardy matchoff + matchlen, 12400f32a40fSPatrick McHardy *datalen, "expires=", 12410f32a40fSPatrick McHardy NULL, NULL, &c_expires); 12420f32a40fSPatrick McHardy if (ret < 0) 12430f32a40fSPatrick McHardy return NF_DROP; 12440f32a40fSPatrick McHardy if (c_expires == 0) 12450f32a40fSPatrick McHardy break; 1246f5b321bdSPatrick McHardy if (refresh_signalling_expectation(ct, &addr, proto, port, 1247f5b321bdSPatrick McHardy c_expires)) 12480f32a40fSPatrick McHardy return NF_ACCEPT; 12490f32a40fSPatrick McHardy } 12500f32a40fSPatrick McHardy 12510f32a40fSPatrick McHardy flush: 12520f32a40fSPatrick McHardy flush_expectations(ct, false); 1253595a8ecbSPatrick McHardy return NF_ACCEPT; 1254595a8ecbSPatrick McHardy } 1255595a8ecbSPatrick McHardy 125630f33e6dSPatrick McHardy static const struct sip_handler sip_handlers[] = { 125730f33e6dSPatrick McHardy SIP_HANDLER("INVITE", process_sdp, process_invite_response), 125830f33e6dSPatrick McHardy SIP_HANDLER("UPDATE", process_sdp, process_update_response), 1259595a8ecbSPatrick McHardy SIP_HANDLER("ACK", process_sdp, NULL), 1260595a8ecbSPatrick McHardy SIP_HANDLER("PRACK", process_sdp, process_prack_response), 12619467ee38SPatrick McHardy SIP_HANDLER("BYE", process_bye_request, NULL), 12620f32a40fSPatrick McHardy SIP_HANDLER("REGISTER", process_register_request, process_register_response), 126330f33e6dSPatrick McHardy }; 126430f33e6dSPatrick McHardy 12653b6b9fabSPatrick McHardy static int process_sip_response(struct sk_buff *skb, unsigned int dataoff, 126630f33e6dSPatrick McHardy const char **dptr, unsigned int *datalen) 126730f33e6dSPatrick McHardy { 126830f33e6dSPatrick McHardy enum ip_conntrack_info ctinfo; 126930f33e6dSPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 12703b6b9fabSPatrick McHardy unsigned int matchoff, matchlen, matchend; 12713b6b9fabSPatrick McHardy unsigned int code, cseq, i; 127230f33e6dSPatrick McHardy 127330f33e6dSPatrick McHardy if (*datalen < strlen("SIP/2.0 200")) 127430f33e6dSPatrick McHardy return NF_ACCEPT; 127530f33e6dSPatrick McHardy code = simple_strtoul(*dptr + strlen("SIP/2.0 "), NULL, 10); 127630f33e6dSPatrick McHardy if (!code) 127730f33e6dSPatrick McHardy return NF_DROP; 127830f33e6dSPatrick McHardy 127930f33e6dSPatrick McHardy if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ, 128030f33e6dSPatrick McHardy &matchoff, &matchlen) <= 0) 128130f33e6dSPatrick McHardy return NF_DROP; 128230f33e6dSPatrick McHardy cseq = simple_strtoul(*dptr + matchoff, NULL, 10); 128330f33e6dSPatrick McHardy if (!cseq) 128430f33e6dSPatrick McHardy return NF_DROP; 12853b6b9fabSPatrick McHardy matchend = matchoff + matchlen + 1; 128630f33e6dSPatrick McHardy 128730f33e6dSPatrick McHardy for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) { 128866bf7918SAlexey Dobriyan const struct sip_handler *handler; 128966bf7918SAlexey Dobriyan 129030f33e6dSPatrick McHardy handler = &sip_handlers[i]; 129130f33e6dSPatrick McHardy if (handler->response == NULL) 129230f33e6dSPatrick McHardy continue; 12933b6b9fabSPatrick McHardy if (*datalen < matchend + handler->len || 12943b6b9fabSPatrick McHardy strnicmp(*dptr + matchend, handler->method, handler->len)) 129530f33e6dSPatrick McHardy continue; 12963b6b9fabSPatrick McHardy return handler->response(skb, dataoff, dptr, datalen, 12973b6b9fabSPatrick McHardy cseq, code); 129830f33e6dSPatrick McHardy } 129930f33e6dSPatrick McHardy return NF_ACCEPT; 130030f33e6dSPatrick McHardy } 130130f33e6dSPatrick McHardy 13023b6b9fabSPatrick McHardy static int process_sip_request(struct sk_buff *skb, unsigned int dataoff, 130330f33e6dSPatrick McHardy const char **dptr, unsigned int *datalen) 130430f33e6dSPatrick McHardy { 130530f33e6dSPatrick McHardy enum ip_conntrack_info ctinfo; 130630f33e6dSPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 130730f33e6dSPatrick McHardy unsigned int matchoff, matchlen; 130830f33e6dSPatrick McHardy unsigned int cseq, i; 130930f33e6dSPatrick McHardy 131030f33e6dSPatrick McHardy for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) { 131166bf7918SAlexey Dobriyan const struct sip_handler *handler; 131266bf7918SAlexey Dobriyan 131330f33e6dSPatrick McHardy handler = &sip_handlers[i]; 131430f33e6dSPatrick McHardy if (handler->request == NULL) 131530f33e6dSPatrick McHardy continue; 131630f33e6dSPatrick McHardy if (*datalen < handler->len || 131730f33e6dSPatrick McHardy strnicmp(*dptr, handler->method, handler->len)) 131830f33e6dSPatrick McHardy continue; 131930f33e6dSPatrick McHardy 132030f33e6dSPatrick McHardy if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ, 132130f33e6dSPatrick McHardy &matchoff, &matchlen) <= 0) 132230f33e6dSPatrick McHardy return NF_DROP; 132330f33e6dSPatrick McHardy cseq = simple_strtoul(*dptr + matchoff, NULL, 10); 132430f33e6dSPatrick McHardy if (!cseq) 132530f33e6dSPatrick McHardy return NF_DROP; 132630f33e6dSPatrick McHardy 13273b6b9fabSPatrick McHardy return handler->request(skb, dataoff, dptr, datalen, cseq); 132830f33e6dSPatrick McHardy } 132930f33e6dSPatrick McHardy return NF_ACCEPT; 133030f33e6dSPatrick McHardy } 13317d3dd043SPatrick McHardy 1332f5b321bdSPatrick McHardy static int process_sip_msg(struct sk_buff *skb, struct nf_conn *ct, 1333f5b321bdSPatrick McHardy unsigned int dataoff, const char **dptr, 1334f5b321bdSPatrick McHardy unsigned int *datalen) 1335f5b321bdSPatrick McHardy { 1336f5b321bdSPatrick McHardy typeof(nf_nat_sip_hook) nf_nat_sip; 1337f5b321bdSPatrick McHardy int ret; 1338f5b321bdSPatrick McHardy 1339f5b321bdSPatrick McHardy if (strnicmp(*dptr, "SIP/2.0 ", strlen("SIP/2.0 ")) != 0) 1340f5b321bdSPatrick McHardy ret = process_sip_request(skb, dataoff, dptr, datalen); 1341f5b321bdSPatrick McHardy else 1342f5b321bdSPatrick McHardy ret = process_sip_response(skb, dataoff, dptr, datalen); 1343f5b321bdSPatrick McHardy 1344f5b321bdSPatrick McHardy if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) { 1345f5b321bdSPatrick McHardy nf_nat_sip = rcu_dereference(nf_nat_sip_hook); 1346f5b321bdSPatrick McHardy if (nf_nat_sip && !nf_nat_sip(skb, dataoff, dptr, datalen)) 1347f5b321bdSPatrick McHardy ret = NF_DROP; 1348f5b321bdSPatrick McHardy } 1349f5b321bdSPatrick McHardy 1350f5b321bdSPatrick McHardy return ret; 1351f5b321bdSPatrick McHardy } 1352f5b321bdSPatrick McHardy 1353f5b321bdSPatrick McHardy static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, 1354f5b321bdSPatrick McHardy struct nf_conn *ct, enum ip_conntrack_info ctinfo) 1355f5b321bdSPatrick McHardy { 1356f5b321bdSPatrick McHardy struct tcphdr *th, _tcph; 1357f5b321bdSPatrick McHardy unsigned int dataoff, datalen; 1358f5b321bdSPatrick McHardy unsigned int matchoff, matchlen, clen; 1359f5b321bdSPatrick McHardy unsigned int msglen, origlen; 1360f5b321bdSPatrick McHardy const char *dptr, *end; 1361f5b321bdSPatrick McHardy s16 diff, tdiff = 0; 1362f5b321bdSPatrick McHardy int ret; 1363f5b321bdSPatrick McHardy 1364f5b321bdSPatrick McHardy if (ctinfo != IP_CT_ESTABLISHED && 1365f5b321bdSPatrick McHardy ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) 1366f5b321bdSPatrick McHardy return NF_ACCEPT; 1367f5b321bdSPatrick McHardy 1368f5b321bdSPatrick McHardy /* No Data ? */ 1369f5b321bdSPatrick McHardy th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph); 1370f5b321bdSPatrick McHardy if (th == NULL) 1371f5b321bdSPatrick McHardy return NF_ACCEPT; 1372f5b321bdSPatrick McHardy dataoff = protoff + th->doff * 4; 1373f5b321bdSPatrick McHardy if (dataoff >= skb->len) 1374f5b321bdSPatrick McHardy return NF_ACCEPT; 1375f5b321bdSPatrick McHardy 1376f5b321bdSPatrick McHardy nf_ct_refresh(ct, skb, sip_timeout * HZ); 1377f5b321bdSPatrick McHardy 1378f5b321bdSPatrick McHardy if (skb_is_nonlinear(skb)) { 1379f5b321bdSPatrick McHardy pr_debug("Copy of skbuff not supported yet.\n"); 1380f5b321bdSPatrick McHardy return NF_ACCEPT; 1381f5b321bdSPatrick McHardy } 1382f5b321bdSPatrick McHardy 1383f5b321bdSPatrick McHardy dptr = skb->data + dataoff; 1384f5b321bdSPatrick McHardy datalen = skb->len - dataoff; 1385f5b321bdSPatrick McHardy if (datalen < strlen("SIP/2.0 200")) 1386f5b321bdSPatrick McHardy return NF_ACCEPT; 1387f5b321bdSPatrick McHardy 1388f5b321bdSPatrick McHardy while (1) { 1389f5b321bdSPatrick McHardy if (ct_sip_get_header(ct, dptr, 0, datalen, 1390f5b321bdSPatrick McHardy SIP_HDR_CONTENT_LENGTH, 1391f5b321bdSPatrick McHardy &matchoff, &matchlen) <= 0) 1392f5b321bdSPatrick McHardy break; 1393f5b321bdSPatrick McHardy 1394f5b321bdSPatrick McHardy clen = simple_strtoul(dptr + matchoff, (char **)&end, 10); 1395f5b321bdSPatrick McHardy if (dptr + matchoff == end) 1396f5b321bdSPatrick McHardy break; 1397f5b321bdSPatrick McHardy 1398f5b321bdSPatrick McHardy if (end + strlen("\r\n\r\n") > dptr + datalen) 1399f5b321bdSPatrick McHardy break; 1400f5b321bdSPatrick McHardy if (end[0] != '\r' || end[1] != '\n' || 1401f5b321bdSPatrick McHardy end[2] != '\r' || end[3] != '\n') 1402f5b321bdSPatrick McHardy break; 1403f5b321bdSPatrick McHardy end += strlen("\r\n\r\n") + clen; 1404f5b321bdSPatrick McHardy 1405f5b321bdSPatrick McHardy msglen = origlen = end - dptr; 1406f5b321bdSPatrick McHardy 1407f5b321bdSPatrick McHardy ret = process_sip_msg(skb, ct, dataoff, &dptr, &msglen); 1408f5b321bdSPatrick McHardy if (ret != NF_ACCEPT) 1409f5b321bdSPatrick McHardy break; 1410f5b321bdSPatrick McHardy diff = msglen - origlen; 1411f5b321bdSPatrick McHardy tdiff += diff; 1412f5b321bdSPatrick McHardy 1413f5b321bdSPatrick McHardy dataoff += msglen; 1414f5b321bdSPatrick McHardy dptr += msglen; 1415f5b321bdSPatrick McHardy datalen = datalen + diff - msglen; 1416f5b321bdSPatrick McHardy } 1417f5b321bdSPatrick McHardy 1418f5b321bdSPatrick McHardy return ret; 1419f5b321bdSPatrick McHardy } 1420f5b321bdSPatrick McHardy 1421f5b321bdSPatrick McHardy static int sip_help_udp(struct sk_buff *skb, unsigned int protoff, 1422f5b321bdSPatrick McHardy struct nf_conn *ct, enum ip_conntrack_info ctinfo) 14239fafcd7bSPatrick McHardy { 14249fafcd7bSPatrick McHardy unsigned int dataoff, datalen; 14259fafcd7bSPatrick McHardy const char *dptr; 14269fafcd7bSPatrick McHardy 14279fafcd7bSPatrick McHardy /* No Data ? */ 14289fafcd7bSPatrick McHardy dataoff = protoff + sizeof(struct udphdr); 14293db05feaSHerbert Xu if (dataoff >= skb->len) 14309fafcd7bSPatrick McHardy return NF_ACCEPT; 14319fafcd7bSPatrick McHardy 14323db05feaSHerbert Xu nf_ct_refresh(ct, skb, sip_timeout * HZ); 14339fafcd7bSPatrick McHardy 1434f5b321bdSPatrick McHardy if (skb_is_nonlinear(skb)) { 14350d53778eSPatrick McHardy pr_debug("Copy of skbuff not supported yet.\n"); 14367d3dd043SPatrick McHardy return NF_ACCEPT; 14379fafcd7bSPatrick McHardy } 14389fafcd7bSPatrick McHardy 1439f5b321bdSPatrick McHardy dptr = skb->data + dataoff; 14403db05feaSHerbert Xu datalen = skb->len - dataoff; 1441779382ebSPatrick McHardy if (datalen < strlen("SIP/2.0 200")) 14427d3dd043SPatrick McHardy return NF_ACCEPT; 14439fafcd7bSPatrick McHardy 1444f5b321bdSPatrick McHardy return process_sip_msg(skb, ct, dataoff, &dptr, &datalen); 144533cb1e9aSPatrick McHardy } 144633cb1e9aSPatrick McHardy 1447f5b321bdSPatrick McHardy static struct nf_conntrack_helper sip[MAX_PORTS][4] __read_mostly; 1448f5b321bdSPatrick McHardy static char sip_names[MAX_PORTS][4][sizeof("sip-65535")] __read_mostly; 14499fafcd7bSPatrick McHardy 14500f32a40fSPatrick McHardy static const struct nf_conntrack_expect_policy sip_exp_policy[SIP_EXPECT_MAX + 1] = { 14510f32a40fSPatrick McHardy [SIP_EXPECT_SIGNALLING] = { 1452b87921bdSPatrick McHardy .name = "signalling", 14530f32a40fSPatrick McHardy .max_expected = 1, 14546002f266SPatrick McHardy .timeout = 3 * 60, 14550f32a40fSPatrick McHardy }, 14560f32a40fSPatrick McHardy [SIP_EXPECT_AUDIO] = { 1457b87921bdSPatrick McHardy .name = "audio", 1458a9c1d359SPatrick McHardy .max_expected = 2 * IP_CT_DIR_MAX, 14590f32a40fSPatrick McHardy .timeout = 3 * 60, 14600f32a40fSPatrick McHardy }, 14610d0ab037SPatrick McHardy [SIP_EXPECT_VIDEO] = { 1462b87921bdSPatrick McHardy .name = "video", 14630d0ab037SPatrick McHardy .max_expected = 2 * IP_CT_DIR_MAX, 14640d0ab037SPatrick McHardy .timeout = 3 * 60, 14650d0ab037SPatrick McHardy }, 14666002f266SPatrick McHardy }; 14676002f266SPatrick McHardy 14689fafcd7bSPatrick McHardy static void nf_conntrack_sip_fini(void) 14699fafcd7bSPatrick McHardy { 14709fafcd7bSPatrick McHardy int i, j; 14719fafcd7bSPatrick McHardy 14729fafcd7bSPatrick McHardy for (i = 0; i < ports_c; i++) { 1473f5b321bdSPatrick McHardy for (j = 0; j < ARRAY_SIZE(sip[i]); j++) { 14749fafcd7bSPatrick McHardy if (sip[i][j].me == NULL) 14759fafcd7bSPatrick McHardy continue; 14769fafcd7bSPatrick McHardy nf_conntrack_helper_unregister(&sip[i][j]); 14779fafcd7bSPatrick McHardy } 14789fafcd7bSPatrick McHardy } 14799fafcd7bSPatrick McHardy } 14809fafcd7bSPatrick McHardy 14819fafcd7bSPatrick McHardy static int __init nf_conntrack_sip_init(void) 14829fafcd7bSPatrick McHardy { 14839fafcd7bSPatrick McHardy int i, j, ret; 14849fafcd7bSPatrick McHardy char *tmpname; 14859fafcd7bSPatrick McHardy 14869fafcd7bSPatrick McHardy if (ports_c == 0) 14879fafcd7bSPatrick McHardy ports[ports_c++] = SIP_PORT; 14889fafcd7bSPatrick McHardy 14899fafcd7bSPatrick McHardy for (i = 0; i < ports_c; i++) { 14909fafcd7bSPatrick McHardy memset(&sip[i], 0, sizeof(sip[i])); 14919fafcd7bSPatrick McHardy 14929fafcd7bSPatrick McHardy sip[i][0].tuple.src.l3num = AF_INET; 1493f5b321bdSPatrick McHardy sip[i][0].tuple.dst.protonum = IPPROTO_UDP; 1494f5b321bdSPatrick McHardy sip[i][0].help = sip_help_udp; 1495f5b321bdSPatrick McHardy sip[i][1].tuple.src.l3num = AF_INET; 1496f5b321bdSPatrick McHardy sip[i][1].tuple.dst.protonum = IPPROTO_TCP; 1497f5b321bdSPatrick McHardy sip[i][1].help = sip_help_tcp; 1498f5b321bdSPatrick McHardy 1499f5b321bdSPatrick McHardy sip[i][2].tuple.src.l3num = AF_INET6; 1500f5b321bdSPatrick McHardy sip[i][2].tuple.dst.protonum = IPPROTO_UDP; 1501f5b321bdSPatrick McHardy sip[i][2].help = sip_help_udp; 1502f5b321bdSPatrick McHardy sip[i][3].tuple.src.l3num = AF_INET6; 1503f5b321bdSPatrick McHardy sip[i][3].tuple.dst.protonum = IPPROTO_TCP; 1504f5b321bdSPatrick McHardy sip[i][3].help = sip_help_tcp; 1505f5b321bdSPatrick McHardy 1506f5b321bdSPatrick McHardy for (j = 0; j < ARRAY_SIZE(sip[i]); j++) { 15079fafcd7bSPatrick McHardy sip[i][j].tuple.src.u.udp.port = htons(ports[i]); 15080f32a40fSPatrick McHardy sip[i][j].expect_policy = sip_exp_policy; 15090f32a40fSPatrick McHardy sip[i][j].expect_class_max = SIP_EXPECT_MAX; 15109fafcd7bSPatrick McHardy sip[i][j].me = THIS_MODULE; 15119fafcd7bSPatrick McHardy 15129fafcd7bSPatrick McHardy tmpname = &sip_names[i][j][0]; 15139fafcd7bSPatrick McHardy if (ports[i] == SIP_PORT) 15149fafcd7bSPatrick McHardy sprintf(tmpname, "sip"); 15159fafcd7bSPatrick McHardy else 15169fafcd7bSPatrick McHardy sprintf(tmpname, "sip-%u", i); 15179fafcd7bSPatrick McHardy sip[i][j].name = tmpname; 15189fafcd7bSPatrick McHardy 15190d53778eSPatrick McHardy pr_debug("port #%u: %u\n", i, ports[i]); 15209fafcd7bSPatrick McHardy 15219fafcd7bSPatrick McHardy ret = nf_conntrack_helper_register(&sip[i][j]); 15229fafcd7bSPatrick McHardy if (ret) { 15239fafcd7bSPatrick McHardy printk("nf_ct_sip: failed to register helper " 15249fafcd7bSPatrick McHardy "for pf: %u port: %u\n", 15259fafcd7bSPatrick McHardy sip[i][j].tuple.src.l3num, ports[i]); 15269fafcd7bSPatrick McHardy nf_conntrack_sip_fini(); 15279fafcd7bSPatrick McHardy return ret; 15289fafcd7bSPatrick McHardy } 15299fafcd7bSPatrick McHardy } 15309fafcd7bSPatrick McHardy } 15319fafcd7bSPatrick McHardy return 0; 15329fafcd7bSPatrick McHardy } 15339fafcd7bSPatrick McHardy 15349fafcd7bSPatrick McHardy module_init(nf_conntrack_sip_init); 15359fafcd7bSPatrick McHardy module_exit(nf_conntrack_sip_fini); 1536