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> 265d0aa2ccSPatrick McHardy #include <net/netfilter/nf_conntrack_zones.h> 279fafcd7bSPatrick McHardy #include <linux/netfilter/nf_conntrack_sip.h> 289fafcd7bSPatrick McHardy 299fafcd7bSPatrick McHardy MODULE_LICENSE("GPL"); 309fafcd7bSPatrick McHardy MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>"); 319fafcd7bSPatrick McHardy MODULE_DESCRIPTION("SIP connection tracking helper"); 329fafcd7bSPatrick McHardy MODULE_ALIAS("ip_conntrack_sip"); 334dc06f96SPablo Neira Ayuso MODULE_ALIAS_NFCT_HELPER("sip"); 349fafcd7bSPatrick McHardy 359fafcd7bSPatrick McHardy #define MAX_PORTS 8 369fafcd7bSPatrick McHardy static unsigned short ports[MAX_PORTS]; 372f0d2f10SStephen Hemminger static unsigned int ports_c; 389fafcd7bSPatrick McHardy module_param_array(ports, ushort, &ports_c, 0400); 399fafcd7bSPatrick McHardy MODULE_PARM_DESC(ports, "port numbers of SIP servers"); 409fafcd7bSPatrick McHardy 419fafcd7bSPatrick McHardy static unsigned int sip_timeout __read_mostly = SIP_TIMEOUT; 429fafcd7bSPatrick McHardy module_param(sip_timeout, uint, 0600); 439fafcd7bSPatrick McHardy MODULE_PARM_DESC(sip_timeout, "timeout for the master SIP session"); 449fafcd7bSPatrick McHardy 450f32a40fSPatrick McHardy static int sip_direct_signalling __read_mostly = 1; 460f32a40fSPatrick McHardy module_param(sip_direct_signalling, int, 0600); 470f32a40fSPatrick McHardy MODULE_PARM_DESC(sip_direct_signalling, "expect incoming calls from registrar " 480f32a40fSPatrick McHardy "only (default 1)"); 490f32a40fSPatrick McHardy 50d901a936SPatrick McHardy static int sip_direct_media __read_mostly = 1; 51d901a936SPatrick McHardy module_param(sip_direct_media, int, 0600); 52d901a936SPatrick McHardy MODULE_PARM_DESC(sip_direct_media, "Expect Media streams between signalling " 53d901a936SPatrick McHardy "endpoints only (default 1)"); 54d901a936SPatrick McHardy 553b6b9fabSPatrick McHardy unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb, unsigned int dataoff, 562a6cfb22SPatrick McHardy const char **dptr, 572a6cfb22SPatrick McHardy unsigned int *datalen) __read_mostly; 589fafcd7bSPatrick McHardy EXPORT_SYMBOL_GPL(nf_nat_sip_hook); 599fafcd7bSPatrick McHardy 6048f8ac26SPatrick McHardy void (*nf_nat_sip_seq_adjust_hook)(struct sk_buff *skb, s16 off) __read_mostly; 6148f8ac26SPatrick McHardy EXPORT_SYMBOL_GPL(nf_nat_sip_seq_adjust_hook); 6248f8ac26SPatrick McHardy 630f32a40fSPatrick McHardy unsigned int (*nf_nat_sip_expect_hook)(struct sk_buff *skb, 643b6b9fabSPatrick McHardy unsigned int dataoff, 650f32a40fSPatrick McHardy const char **dptr, 660f32a40fSPatrick McHardy unsigned int *datalen, 670f32a40fSPatrick McHardy struct nf_conntrack_expect *exp, 680f32a40fSPatrick McHardy unsigned int matchoff, 690f32a40fSPatrick McHardy unsigned int matchlen) __read_mostly; 700f32a40fSPatrick McHardy EXPORT_SYMBOL_GPL(nf_nat_sip_expect_hook); 710f32a40fSPatrick McHardy 723b6b9fabSPatrick McHardy unsigned int (*nf_nat_sdp_addr_hook)(struct sk_buff *skb, unsigned int dataoff, 734ab9e64eSPatrick McHardy const char **dptr, 744ab9e64eSPatrick McHardy unsigned int *datalen, 753b6b9fabSPatrick McHardy unsigned int sdpoff, 764ab9e64eSPatrick McHardy enum sdp_header_types type, 774ab9e64eSPatrick McHardy enum sdp_header_types term, 784ab9e64eSPatrick McHardy const union nf_inet_addr *addr) 794ab9e64eSPatrick McHardy __read_mostly; 804ab9e64eSPatrick McHardy EXPORT_SYMBOL_GPL(nf_nat_sdp_addr_hook); 814ab9e64eSPatrick McHardy 823b6b9fabSPatrick McHardy unsigned int (*nf_nat_sdp_port_hook)(struct sk_buff *skb, unsigned int dataoff, 83c7f485abSPatrick McHardy const char **dptr, 84c7f485abSPatrick McHardy unsigned int *datalen, 85c7f485abSPatrick McHardy unsigned int matchoff, 86c7f485abSPatrick McHardy unsigned int matchlen, 87c7f485abSPatrick McHardy u_int16_t port) __read_mostly; 88c7f485abSPatrick McHardy EXPORT_SYMBOL_GPL(nf_nat_sdp_port_hook); 89c7f485abSPatrick McHardy 904ab9e64eSPatrick McHardy unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb, 914ab9e64eSPatrick McHardy unsigned int dataoff, 923b6b9fabSPatrick McHardy const char **dptr, 934ab9e64eSPatrick McHardy unsigned int *datalen, 943b6b9fabSPatrick McHardy unsigned int sdpoff, 954ab9e64eSPatrick McHardy const union nf_inet_addr *addr) 964ab9e64eSPatrick McHardy __read_mostly; 974ab9e64eSPatrick McHardy EXPORT_SYMBOL_GPL(nf_nat_sdp_session_hook); 984ab9e64eSPatrick McHardy 993b6b9fabSPatrick McHardy unsigned int (*nf_nat_sdp_media_hook)(struct sk_buff *skb, unsigned int dataoff, 1002a6cfb22SPatrick McHardy const char **dptr, 101212440a7SPatrick McHardy unsigned int *datalen, 102a9c1d359SPatrick McHardy struct nf_conntrack_expect *rtp_exp, 1034ab9e64eSPatrick McHardy struct nf_conntrack_expect *rtcp_exp, 1044ab9e64eSPatrick McHardy unsigned int mediaoff, 1054ab9e64eSPatrick McHardy unsigned int medialen, 1064ab9e64eSPatrick McHardy union nf_inet_addr *rtp_addr) 107a9c1d359SPatrick McHardy __read_mostly; 1084ab9e64eSPatrick McHardy EXPORT_SYMBOL_GPL(nf_nat_sdp_media_hook); 1099fafcd7bSPatrick McHardy 110ac367740SPatrick McHardy static int string_len(const struct nf_conn *ct, const char *dptr, 111ac367740SPatrick McHardy const char *limit, int *shift) 112ac367740SPatrick McHardy { 113ac367740SPatrick McHardy int len = 0; 114ac367740SPatrick McHardy 115ac367740SPatrick McHardy while (dptr < limit && isalpha(*dptr)) { 116ac367740SPatrick McHardy dptr++; 117ac367740SPatrick McHardy len++; 118ac367740SPatrick McHardy } 119ac367740SPatrick McHardy return len; 120ac367740SPatrick McHardy } 121ac367740SPatrick McHardy 12213f7d63cSJan Engelhardt static int digits_len(const struct nf_conn *ct, const char *dptr, 1239fafcd7bSPatrick McHardy const char *limit, int *shift) 1249fafcd7bSPatrick McHardy { 1259fafcd7bSPatrick McHardy int len = 0; 126b1ec488bSPatrick McHardy while (dptr < limit && isdigit(*dptr)) { 1279fafcd7bSPatrick McHardy dptr++; 1289fafcd7bSPatrick McHardy len++; 1299fafcd7bSPatrick McHardy } 1309fafcd7bSPatrick McHardy return len; 1319fafcd7bSPatrick McHardy } 1329fafcd7bSPatrick McHardy 1330d0ab037SPatrick McHardy /* get media type + port length */ 1340d0ab037SPatrick McHardy static int media_len(const struct nf_conn *ct, const char *dptr, 1350d0ab037SPatrick McHardy const char *limit, int *shift) 1360d0ab037SPatrick McHardy { 1370d0ab037SPatrick McHardy int len = string_len(ct, dptr, limit, shift); 1380d0ab037SPatrick McHardy 1390d0ab037SPatrick McHardy dptr += len; 1400d0ab037SPatrick McHardy if (dptr >= limit || *dptr != ' ') 1410d0ab037SPatrick McHardy return 0; 1420d0ab037SPatrick McHardy len++; 1430d0ab037SPatrick McHardy dptr++; 1440d0ab037SPatrick McHardy 1450d0ab037SPatrick McHardy return len + digits_len(ct, dptr, limit, shift); 1460d0ab037SPatrick McHardy } 1470d0ab037SPatrick McHardy 14813f7d63cSJan Engelhardt static int parse_addr(const struct nf_conn *ct, const char *cp, 14913f7d63cSJan Engelhardt const char **endp, union nf_inet_addr *addr, 15013f7d63cSJan Engelhardt const char *limit) 1519fafcd7bSPatrick McHardy { 1529fafcd7bSPatrick McHardy const char *end; 1539fafcd7bSPatrick McHardy int ret = 0; 1549fafcd7bSPatrick McHardy 155fa913ddfSPatrick McHardy memset(addr, 0, sizeof(*addr)); 1565e8fbe2aSPatrick McHardy switch (nf_ct_l3num(ct)) { 1579fafcd7bSPatrick McHardy case AF_INET: 1589fafcd7bSPatrick McHardy ret = in4_pton(cp, limit - cp, (u8 *)&addr->ip, -1, &end); 1599fafcd7bSPatrick McHardy break; 1609fafcd7bSPatrick McHardy case AF_INET6: 1619fafcd7bSPatrick McHardy ret = in6_pton(cp, limit - cp, (u8 *)&addr->ip6, -1, &end); 1629fafcd7bSPatrick McHardy break; 1639fafcd7bSPatrick McHardy default: 1649fafcd7bSPatrick McHardy BUG(); 1659fafcd7bSPatrick McHardy } 1669fafcd7bSPatrick McHardy 1679fafcd7bSPatrick McHardy if (ret == 0 || end == cp) 1689fafcd7bSPatrick McHardy return 0; 1699fafcd7bSPatrick McHardy if (endp) 1709fafcd7bSPatrick McHardy *endp = end; 1719fafcd7bSPatrick McHardy return 1; 1729fafcd7bSPatrick McHardy } 1739fafcd7bSPatrick McHardy 1749fafcd7bSPatrick McHardy /* skip ip address. returns its length. */ 17513f7d63cSJan Engelhardt static int epaddr_len(const struct nf_conn *ct, const char *dptr, 1769fafcd7bSPatrick McHardy const char *limit, int *shift) 1779fafcd7bSPatrick McHardy { 178643a2c15SJan Engelhardt union nf_inet_addr addr; 1799fafcd7bSPatrick McHardy const char *aux = dptr; 1809fafcd7bSPatrick McHardy 1819fafcd7bSPatrick McHardy if (!parse_addr(ct, dptr, &dptr, &addr, limit)) { 1820d53778eSPatrick McHardy pr_debug("ip: %s parse failed.!\n", dptr); 1839fafcd7bSPatrick McHardy return 0; 1849fafcd7bSPatrick McHardy } 1859fafcd7bSPatrick McHardy 1869fafcd7bSPatrick McHardy /* Port number */ 1879fafcd7bSPatrick McHardy if (*dptr == ':') { 1889fafcd7bSPatrick McHardy dptr++; 1899fafcd7bSPatrick McHardy dptr += digits_len(ct, dptr, limit, shift); 1909fafcd7bSPatrick McHardy } 1919fafcd7bSPatrick McHardy return dptr - aux; 1929fafcd7bSPatrick McHardy } 1939fafcd7bSPatrick McHardy 1949fafcd7bSPatrick McHardy /* get address length, skiping user info. */ 19513f7d63cSJan Engelhardt static int skp_epaddr_len(const struct nf_conn *ct, const char *dptr, 1969fafcd7bSPatrick McHardy const char *limit, int *shift) 1979fafcd7bSPatrick McHardy { 198aa584edaSPatrick McHardy const char *start = dptr; 1999fafcd7bSPatrick McHardy int s = *shift; 2009fafcd7bSPatrick McHardy 2017da5bfbbSLars Immisch /* Search for @, but stop at the end of the line. 2027da5bfbbSLars Immisch * We are inside a sip: URI, so we don't need to worry about 2037da5bfbbSLars Immisch * continuation lines. */ 204b1ec488bSPatrick McHardy while (dptr < limit && 2057da5bfbbSLars Immisch *dptr != '@' && *dptr != '\r' && *dptr != '\n') { 2069fafcd7bSPatrick McHardy (*shift)++; 2077da5bfbbSLars Immisch dptr++; 2087da5bfbbSLars Immisch } 2099fafcd7bSPatrick McHardy 210b1ec488bSPatrick McHardy if (dptr < limit && *dptr == '@') { 2119fafcd7bSPatrick McHardy dptr++; 2129fafcd7bSPatrick McHardy (*shift)++; 213aa584edaSPatrick McHardy } else { 214aa584edaSPatrick McHardy dptr = start; 2159fafcd7bSPatrick McHardy *shift = s; 216aa584edaSPatrick McHardy } 2179fafcd7bSPatrick McHardy 2189fafcd7bSPatrick McHardy return epaddr_len(ct, dptr, limit, shift); 2199fafcd7bSPatrick McHardy } 2209fafcd7bSPatrick McHardy 221ac367740SPatrick McHardy /* Parse a SIP request line of the form: 222ac367740SPatrick McHardy * 223ac367740SPatrick McHardy * Request-Line = Method SP Request-URI SP SIP-Version CRLF 224ac367740SPatrick McHardy * 225ac367740SPatrick McHardy * and return the offset and length of the address contained in the Request-URI. 226ac367740SPatrick McHardy */ 227ac367740SPatrick McHardy int ct_sip_parse_request(const struct nf_conn *ct, 228ac367740SPatrick McHardy const char *dptr, unsigned int datalen, 229624f8b7bSPatrick McHardy unsigned int *matchoff, unsigned int *matchlen, 230624f8b7bSPatrick McHardy union nf_inet_addr *addr, __be16 *port) 231ac367740SPatrick McHardy { 232624f8b7bSPatrick McHardy const char *start = dptr, *limit = dptr + datalen, *end; 233ac367740SPatrick McHardy unsigned int mlen; 234624f8b7bSPatrick McHardy unsigned int p; 235ac367740SPatrick McHardy int shift = 0; 236ac367740SPatrick McHardy 237ac367740SPatrick McHardy /* Skip method and following whitespace */ 238ac367740SPatrick McHardy mlen = string_len(ct, dptr, limit, NULL); 239ac367740SPatrick McHardy if (!mlen) 240ac367740SPatrick McHardy return 0; 241ac367740SPatrick McHardy dptr += mlen; 242ac367740SPatrick McHardy if (++dptr >= limit) 243ac367740SPatrick McHardy return 0; 244ac367740SPatrick McHardy 245ac367740SPatrick McHardy /* Find SIP URI */ 24654101f4fSPatrick McHardy for (; dptr < limit - strlen("sip:"); dptr++) { 247ac367740SPatrick McHardy if (*dptr == '\r' || *dptr == '\n') 248ac367740SPatrick McHardy return -1; 24954101f4fSPatrick McHardy if (strnicmp(dptr, "sip:", strlen("sip:")) == 0) { 25054101f4fSPatrick McHardy dptr += strlen("sip:"); 251ac367740SPatrick McHardy break; 252ac367740SPatrick McHardy } 25354101f4fSPatrick McHardy } 254624f8b7bSPatrick McHardy if (!skp_epaddr_len(ct, dptr, limit, &shift)) 255ac367740SPatrick McHardy return 0; 256624f8b7bSPatrick McHardy dptr += shift; 257624f8b7bSPatrick McHardy 258624f8b7bSPatrick McHardy if (!parse_addr(ct, dptr, &end, addr, limit)) 259624f8b7bSPatrick McHardy return -1; 260624f8b7bSPatrick McHardy if (end < limit && *end == ':') { 261624f8b7bSPatrick McHardy end++; 262624f8b7bSPatrick McHardy p = simple_strtoul(end, (char **)&end, 10); 263624f8b7bSPatrick McHardy if (p < 1024 || p > 65535) 264624f8b7bSPatrick McHardy return -1; 265624f8b7bSPatrick McHardy *port = htons(p); 266624f8b7bSPatrick McHardy } else 267624f8b7bSPatrick McHardy *port = htons(SIP_PORT); 268624f8b7bSPatrick McHardy 269624f8b7bSPatrick McHardy if (end == dptr) 270624f8b7bSPatrick McHardy return 0; 271624f8b7bSPatrick McHardy *matchoff = dptr - start; 272624f8b7bSPatrick McHardy *matchlen = end - dptr; 273ac367740SPatrick McHardy return 1; 274ac367740SPatrick McHardy } 275ac367740SPatrick McHardy EXPORT_SYMBOL_GPL(ct_sip_parse_request); 276ac367740SPatrick McHardy 277ea45f12aSPatrick McHardy /* SIP header parsing: SIP headers are located at the beginning of a line, but 278ea45f12aSPatrick McHardy * may span several lines, in which case the continuation lines begin with a 279ea45f12aSPatrick McHardy * whitespace character. RFC 2543 allows lines to be terminated with CR, LF or 280ea45f12aSPatrick McHardy * CRLF, RFC 3261 allows only CRLF, we support both. 281ea45f12aSPatrick McHardy * 282ea45f12aSPatrick McHardy * Headers are followed by (optionally) whitespace, a colon, again (optionally) 283ea45f12aSPatrick McHardy * whitespace and the values. Whitespace in this context means any amount of 284ea45f12aSPatrick McHardy * tabs, spaces and continuation lines, which are treated as a single whitespace 285ea45f12aSPatrick McHardy * character. 28605e3ced2SPatrick McHardy * 2873ad2f3fbSDaniel Mack * Some headers may appear multiple times. A comma separated list of values is 28805e3ced2SPatrick McHardy * equivalent to multiple headers. 289ea45f12aSPatrick McHardy */ 290ea45f12aSPatrick McHardy static const struct sip_header ct_sip_hdrs[] = { 29130f33e6dSPatrick McHardy [SIP_HDR_CSEQ] = SIP_HDR("CSeq", NULL, NULL, digits_len), 292ea45f12aSPatrick McHardy [SIP_HDR_FROM] = SIP_HDR("From", "f", "sip:", skp_epaddr_len), 293ea45f12aSPatrick McHardy [SIP_HDR_TO] = SIP_HDR("To", "t", "sip:", skp_epaddr_len), 294ea45f12aSPatrick McHardy [SIP_HDR_CONTACT] = SIP_HDR("Contact", "m", "sip:", skp_epaddr_len), 295f5b321bdSPatrick McHardy [SIP_HDR_VIA_UDP] = SIP_HDR("Via", "v", "UDP ", epaddr_len), 296f5b321bdSPatrick McHardy [SIP_HDR_VIA_TCP] = SIP_HDR("Via", "v", "TCP ", epaddr_len), 2970f32a40fSPatrick McHardy [SIP_HDR_EXPIRES] = SIP_HDR("Expires", NULL, NULL, digits_len), 298ea45f12aSPatrick McHardy [SIP_HDR_CONTENT_LENGTH] = SIP_HDR("Content-Length", "l", NULL, digits_len), 299ea45f12aSPatrick McHardy }; 300ea45f12aSPatrick McHardy 301ea45f12aSPatrick McHardy static const char *sip_follow_continuation(const char *dptr, const char *limit) 3029fafcd7bSPatrick McHardy { 303ea45f12aSPatrick McHardy /* Walk past newline */ 304ea45f12aSPatrick McHardy if (++dptr >= limit) 305ea45f12aSPatrick McHardy return NULL; 3069fafcd7bSPatrick McHardy 307ea45f12aSPatrick McHardy /* Skip '\n' in CR LF */ 308ea45f12aSPatrick McHardy if (*(dptr - 1) == '\r' && *dptr == '\n') { 309ea45f12aSPatrick McHardy if (++dptr >= limit) 310ea45f12aSPatrick McHardy return NULL; 311ea45f12aSPatrick McHardy } 3129fafcd7bSPatrick McHardy 313ea45f12aSPatrick McHardy /* Continuation line? */ 314ea45f12aSPatrick McHardy if (*dptr != ' ' && *dptr != '\t') 315ea45f12aSPatrick McHardy return NULL; 316ea45f12aSPatrick McHardy 317ea45f12aSPatrick McHardy /* skip leading whitespace */ 318ea45f12aSPatrick McHardy for (; dptr < limit; dptr++) { 319ea45f12aSPatrick McHardy if (*dptr != ' ' && *dptr != '\t') 320ea45f12aSPatrick McHardy break; 321ea45f12aSPatrick McHardy } 322ea45f12aSPatrick McHardy return dptr; 323ea45f12aSPatrick McHardy } 324ea45f12aSPatrick McHardy 325ea45f12aSPatrick McHardy static const char *sip_skip_whitespace(const char *dptr, const char *limit) 326ea45f12aSPatrick McHardy { 327ea45f12aSPatrick McHardy for (; dptr < limit; dptr++) { 328ea45f12aSPatrick McHardy if (*dptr == ' ') 329ea45f12aSPatrick McHardy continue; 330ea45f12aSPatrick McHardy if (*dptr != '\r' && *dptr != '\n') 331ea45f12aSPatrick McHardy break; 332ea45f12aSPatrick McHardy dptr = sip_follow_continuation(dptr, limit); 333ea45f12aSPatrick McHardy if (dptr == NULL) 334ea45f12aSPatrick McHardy return NULL; 335ea45f12aSPatrick McHardy } 336ea45f12aSPatrick McHardy return dptr; 337ea45f12aSPatrick McHardy } 338ea45f12aSPatrick McHardy 339ea45f12aSPatrick McHardy /* Search within a SIP header value, dealing with continuation lines */ 340ea45f12aSPatrick McHardy static const char *ct_sip_header_search(const char *dptr, const char *limit, 341ea45f12aSPatrick McHardy const char *needle, unsigned int len) 342ea45f12aSPatrick McHardy { 343ea45f12aSPatrick McHardy for (limit -= len; dptr < limit; dptr++) { 344ea45f12aSPatrick McHardy if (*dptr == '\r' || *dptr == '\n') { 345ea45f12aSPatrick McHardy dptr = sip_follow_continuation(dptr, limit); 346ea45f12aSPatrick McHardy if (dptr == NULL) 347ea45f12aSPatrick McHardy break; 3489fafcd7bSPatrick McHardy continue; 3499fafcd7bSPatrick McHardy } 3509fafcd7bSPatrick McHardy 351ea45f12aSPatrick McHardy if (strnicmp(dptr, needle, len) == 0) 352ea45f12aSPatrick McHardy return dptr; 353ea45f12aSPatrick McHardy } 354ea45f12aSPatrick McHardy return NULL; 355ea45f12aSPatrick McHardy } 356ea45f12aSPatrick McHardy 357ea45f12aSPatrick McHardy int ct_sip_get_header(const struct nf_conn *ct, const char *dptr, 358ea45f12aSPatrick McHardy unsigned int dataoff, unsigned int datalen, 359ea45f12aSPatrick McHardy enum sip_header_types type, 360ea45f12aSPatrick McHardy unsigned int *matchoff, unsigned int *matchlen) 361ea45f12aSPatrick McHardy { 362ea45f12aSPatrick McHardy const struct sip_header *hdr = &ct_sip_hdrs[type]; 363ea45f12aSPatrick McHardy const char *start = dptr, *limit = dptr + datalen; 364ea45f12aSPatrick McHardy int shift = 0; 365ea45f12aSPatrick McHardy 366ea45f12aSPatrick McHardy for (dptr += dataoff; dptr < limit; dptr++) { 367ea45f12aSPatrick McHardy /* Find beginning of line */ 368ea45f12aSPatrick McHardy if (*dptr != '\r' && *dptr != '\n') 369ea45f12aSPatrick McHardy continue; 370ea45f12aSPatrick McHardy if (++dptr >= limit) 371ea45f12aSPatrick McHardy break; 372ea45f12aSPatrick McHardy if (*(dptr - 1) == '\r' && *dptr == '\n') { 373ea45f12aSPatrick McHardy if (++dptr >= limit) 374ea45f12aSPatrick McHardy break; 375ea45f12aSPatrick McHardy } 376ea45f12aSPatrick McHardy 377ea45f12aSPatrick McHardy /* Skip continuation lines */ 378ea45f12aSPatrick McHardy if (*dptr == ' ' || *dptr == '\t') 379ea45f12aSPatrick McHardy continue; 380ea45f12aSPatrick McHardy 381ea45f12aSPatrick McHardy /* Find header. Compact headers must be followed by a 382ea45f12aSPatrick McHardy * non-alphabetic character to avoid mismatches. */ 383ea45f12aSPatrick McHardy if (limit - dptr >= hdr->len && 384ea45f12aSPatrick McHardy strnicmp(dptr, hdr->name, hdr->len) == 0) 385ea45f12aSPatrick McHardy dptr += hdr->len; 386ea45f12aSPatrick McHardy else if (hdr->cname && limit - dptr >= hdr->clen + 1 && 387ea45f12aSPatrick McHardy strnicmp(dptr, hdr->cname, hdr->clen) == 0 && 388135d0189SPatrick McHardy !isalpha(*(dptr + hdr->clen))) 389ea45f12aSPatrick McHardy dptr += hdr->clen; 390ea45f12aSPatrick McHardy else 391ea45f12aSPatrick McHardy continue; 392ea45f12aSPatrick McHardy 393ea45f12aSPatrick McHardy /* Find and skip colon */ 394ea45f12aSPatrick McHardy dptr = sip_skip_whitespace(dptr, limit); 395ea45f12aSPatrick McHardy if (dptr == NULL) 396ea45f12aSPatrick McHardy break; 397ea45f12aSPatrick McHardy if (*dptr != ':' || ++dptr >= limit) 398ea45f12aSPatrick McHardy break; 399ea45f12aSPatrick McHardy 400ea45f12aSPatrick McHardy /* Skip whitespace after colon */ 401ea45f12aSPatrick McHardy dptr = sip_skip_whitespace(dptr, limit); 402ea45f12aSPatrick McHardy if (dptr == NULL) 403ea45f12aSPatrick McHardy break; 404ea45f12aSPatrick McHardy 405ea45f12aSPatrick McHardy *matchoff = dptr - start; 406ea45f12aSPatrick McHardy if (hdr->search) { 407ea45f12aSPatrick McHardy dptr = ct_sip_header_search(dptr, limit, hdr->search, 408ea45f12aSPatrick McHardy hdr->slen); 409ea45f12aSPatrick McHardy if (!dptr) 410ea45f12aSPatrick McHardy return -1; 411ea45f12aSPatrick McHardy dptr += hdr->slen; 412ea45f12aSPatrick McHardy } 413ea45f12aSPatrick McHardy 414ea45f12aSPatrick McHardy *matchlen = hdr->match_len(ct, dptr, limit, &shift); 4159fafcd7bSPatrick McHardy if (!*matchlen) 4169fafcd7bSPatrick McHardy return -1; 417ea45f12aSPatrick McHardy *matchoff = dptr - start + shift; 4189fafcd7bSPatrick McHardy return 1; 4199fafcd7bSPatrick McHardy } 4209fafcd7bSPatrick McHardy return 0; 4219fafcd7bSPatrick McHardy } 422ea45f12aSPatrick McHardy EXPORT_SYMBOL_GPL(ct_sip_get_header); 4239fafcd7bSPatrick McHardy 4243ad2f3fbSDaniel Mack /* Get next header field in a list of comma separated values */ 42505e3ced2SPatrick McHardy static int ct_sip_next_header(const struct nf_conn *ct, const char *dptr, 42605e3ced2SPatrick McHardy unsigned int dataoff, unsigned int datalen, 42705e3ced2SPatrick McHardy enum sip_header_types type, 42805e3ced2SPatrick McHardy unsigned int *matchoff, unsigned int *matchlen) 42905e3ced2SPatrick McHardy { 43005e3ced2SPatrick McHardy const struct sip_header *hdr = &ct_sip_hdrs[type]; 43105e3ced2SPatrick McHardy const char *start = dptr, *limit = dptr + datalen; 43205e3ced2SPatrick McHardy int shift = 0; 43305e3ced2SPatrick McHardy 43405e3ced2SPatrick McHardy dptr += dataoff; 43505e3ced2SPatrick McHardy 43605e3ced2SPatrick McHardy dptr = ct_sip_header_search(dptr, limit, ",", strlen(",")); 43705e3ced2SPatrick McHardy if (!dptr) 43805e3ced2SPatrick McHardy return 0; 43905e3ced2SPatrick McHardy 44005e3ced2SPatrick McHardy dptr = ct_sip_header_search(dptr, limit, hdr->search, hdr->slen); 44105e3ced2SPatrick McHardy if (!dptr) 44205e3ced2SPatrick McHardy return 0; 44305e3ced2SPatrick McHardy dptr += hdr->slen; 44405e3ced2SPatrick McHardy 44505e3ced2SPatrick McHardy *matchoff = dptr - start; 44605e3ced2SPatrick McHardy *matchlen = hdr->match_len(ct, dptr, limit, &shift); 44705e3ced2SPatrick McHardy if (!*matchlen) 44805e3ced2SPatrick McHardy return -1; 44905e3ced2SPatrick McHardy *matchoff += shift; 45005e3ced2SPatrick McHardy return 1; 45105e3ced2SPatrick McHardy } 45205e3ced2SPatrick McHardy 45305e3ced2SPatrick McHardy /* Walk through headers until a parsable one is found or no header of the 45405e3ced2SPatrick McHardy * given type is left. */ 45505e3ced2SPatrick McHardy static int ct_sip_walk_headers(const struct nf_conn *ct, const char *dptr, 45605e3ced2SPatrick McHardy unsigned int dataoff, unsigned int datalen, 45705e3ced2SPatrick McHardy enum sip_header_types type, int *in_header, 45805e3ced2SPatrick McHardy unsigned int *matchoff, unsigned int *matchlen) 45905e3ced2SPatrick McHardy { 46005e3ced2SPatrick McHardy int ret; 46105e3ced2SPatrick McHardy 46205e3ced2SPatrick McHardy if (in_header && *in_header) { 46305e3ced2SPatrick McHardy while (1) { 46405e3ced2SPatrick McHardy ret = ct_sip_next_header(ct, dptr, dataoff, datalen, 46505e3ced2SPatrick McHardy type, matchoff, matchlen); 46605e3ced2SPatrick McHardy if (ret > 0) 46705e3ced2SPatrick McHardy return ret; 46805e3ced2SPatrick McHardy if (ret == 0) 46905e3ced2SPatrick McHardy break; 47005e3ced2SPatrick McHardy dataoff += *matchoff; 47105e3ced2SPatrick McHardy } 47205e3ced2SPatrick McHardy *in_header = 0; 47305e3ced2SPatrick McHardy } 47405e3ced2SPatrick McHardy 47505e3ced2SPatrick McHardy while (1) { 47605e3ced2SPatrick McHardy ret = ct_sip_get_header(ct, dptr, dataoff, datalen, 47705e3ced2SPatrick McHardy type, matchoff, matchlen); 47805e3ced2SPatrick McHardy if (ret > 0) 47905e3ced2SPatrick McHardy break; 48005e3ced2SPatrick McHardy if (ret == 0) 48105e3ced2SPatrick McHardy return ret; 48205e3ced2SPatrick McHardy dataoff += *matchoff; 48305e3ced2SPatrick McHardy } 48405e3ced2SPatrick McHardy 48505e3ced2SPatrick McHardy if (in_header) 48605e3ced2SPatrick McHardy *in_header = 1; 48705e3ced2SPatrick McHardy return 1; 48805e3ced2SPatrick McHardy } 48905e3ced2SPatrick McHardy 49005e3ced2SPatrick McHardy /* Locate a SIP header, parse the URI and return the offset and length of 49105e3ced2SPatrick McHardy * the address as well as the address and port themselves. A stream of 49205e3ced2SPatrick McHardy * headers can be parsed by handing in a non-NULL datalen and in_header 49305e3ced2SPatrick McHardy * pointer. 49405e3ced2SPatrick McHardy */ 49505e3ced2SPatrick McHardy int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr, 49605e3ced2SPatrick McHardy unsigned int *dataoff, unsigned int datalen, 49705e3ced2SPatrick McHardy enum sip_header_types type, int *in_header, 49805e3ced2SPatrick McHardy unsigned int *matchoff, unsigned int *matchlen, 49905e3ced2SPatrick McHardy union nf_inet_addr *addr, __be16 *port) 50005e3ced2SPatrick McHardy { 50105e3ced2SPatrick McHardy const char *c, *limit = dptr + datalen; 50205e3ced2SPatrick McHardy unsigned int p; 50305e3ced2SPatrick McHardy int ret; 50405e3ced2SPatrick McHardy 50505e3ced2SPatrick McHardy ret = ct_sip_walk_headers(ct, dptr, dataoff ? *dataoff : 0, datalen, 50605e3ced2SPatrick McHardy type, in_header, matchoff, matchlen); 50705e3ced2SPatrick McHardy WARN_ON(ret < 0); 50805e3ced2SPatrick McHardy if (ret == 0) 50905e3ced2SPatrick McHardy return ret; 51005e3ced2SPatrick McHardy 51105e3ced2SPatrick McHardy if (!parse_addr(ct, dptr + *matchoff, &c, addr, limit)) 51205e3ced2SPatrick McHardy return -1; 51305e3ced2SPatrick McHardy if (*c == ':') { 51405e3ced2SPatrick McHardy c++; 51505e3ced2SPatrick McHardy p = simple_strtoul(c, (char **)&c, 10); 51605e3ced2SPatrick McHardy if (p < 1024 || p > 65535) 51705e3ced2SPatrick McHardy return -1; 51805e3ced2SPatrick McHardy *port = htons(p); 51905e3ced2SPatrick McHardy } else 52005e3ced2SPatrick McHardy *port = htons(SIP_PORT); 52105e3ced2SPatrick McHardy 52205e3ced2SPatrick McHardy if (dataoff) 52305e3ced2SPatrick McHardy *dataoff = c - dptr; 52405e3ced2SPatrick McHardy return 1; 52505e3ced2SPatrick McHardy } 52605e3ced2SPatrick McHardy EXPORT_SYMBOL_GPL(ct_sip_parse_header_uri); 52705e3ced2SPatrick McHardy 528f5b321bdSPatrick McHardy static int ct_sip_parse_param(const struct nf_conn *ct, const char *dptr, 529f5b321bdSPatrick McHardy unsigned int dataoff, unsigned int datalen, 530f5b321bdSPatrick McHardy const char *name, 531f5b321bdSPatrick McHardy unsigned int *matchoff, unsigned int *matchlen) 532f5b321bdSPatrick McHardy { 533f5b321bdSPatrick McHardy const char *limit = dptr + datalen; 534f5b321bdSPatrick McHardy const char *start; 535f5b321bdSPatrick McHardy const char *end; 536f5b321bdSPatrick McHardy 537f5b321bdSPatrick McHardy limit = ct_sip_header_search(dptr + dataoff, limit, ",", strlen(",")); 538f5b321bdSPatrick McHardy if (!limit) 539f5b321bdSPatrick McHardy limit = dptr + datalen; 540f5b321bdSPatrick McHardy 541f5b321bdSPatrick McHardy start = ct_sip_header_search(dptr + dataoff, limit, name, strlen(name)); 542f5b321bdSPatrick McHardy if (!start) 543f5b321bdSPatrick McHardy return 0; 544f5b321bdSPatrick McHardy start += strlen(name); 545f5b321bdSPatrick McHardy 546f5b321bdSPatrick McHardy end = ct_sip_header_search(start, limit, ";", strlen(";")); 547f5b321bdSPatrick McHardy if (!end) 548f5b321bdSPatrick McHardy end = limit; 549f5b321bdSPatrick McHardy 550f5b321bdSPatrick McHardy *matchoff = start - dptr; 551f5b321bdSPatrick McHardy *matchlen = end - start; 552f5b321bdSPatrick McHardy return 1; 553f5b321bdSPatrick McHardy } 554f5b321bdSPatrick McHardy 5552bbb2116SPatrick McHardy /* Parse address from header parameter and return address, offset and length */ 5562bbb2116SPatrick McHardy int ct_sip_parse_address_param(const struct nf_conn *ct, const char *dptr, 5572bbb2116SPatrick McHardy unsigned int dataoff, unsigned int datalen, 5582bbb2116SPatrick McHardy const char *name, 5592bbb2116SPatrick McHardy unsigned int *matchoff, unsigned int *matchlen, 5602bbb2116SPatrick McHardy union nf_inet_addr *addr) 5612bbb2116SPatrick McHardy { 5622bbb2116SPatrick McHardy const char *limit = dptr + datalen; 5632bbb2116SPatrick McHardy const char *start, *end; 5642bbb2116SPatrick McHardy 5652bbb2116SPatrick McHardy limit = ct_sip_header_search(dptr + dataoff, limit, ",", strlen(",")); 5662bbb2116SPatrick McHardy if (!limit) 5672bbb2116SPatrick McHardy limit = dptr + datalen; 5682bbb2116SPatrick McHardy 5692bbb2116SPatrick McHardy start = ct_sip_header_search(dptr + dataoff, limit, name, strlen(name)); 5702bbb2116SPatrick McHardy if (!start) 5712bbb2116SPatrick McHardy return 0; 5722bbb2116SPatrick McHardy 5732bbb2116SPatrick McHardy start += strlen(name); 5742bbb2116SPatrick McHardy if (!parse_addr(ct, start, &end, addr, limit)) 5752bbb2116SPatrick McHardy return 0; 5762bbb2116SPatrick McHardy *matchoff = start - dptr; 5772bbb2116SPatrick McHardy *matchlen = end - start; 5782bbb2116SPatrick McHardy return 1; 5792bbb2116SPatrick McHardy } 5802bbb2116SPatrick McHardy EXPORT_SYMBOL_GPL(ct_sip_parse_address_param); 5812bbb2116SPatrick McHardy 5822bbb2116SPatrick McHardy /* Parse numerical header parameter and return value, offset and length */ 5832bbb2116SPatrick McHardy int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr, 5842bbb2116SPatrick McHardy unsigned int dataoff, unsigned int datalen, 5852bbb2116SPatrick McHardy const char *name, 5862bbb2116SPatrick McHardy unsigned int *matchoff, unsigned int *matchlen, 5872bbb2116SPatrick McHardy unsigned int *val) 5882bbb2116SPatrick McHardy { 5892bbb2116SPatrick McHardy const char *limit = dptr + datalen; 5902bbb2116SPatrick McHardy const char *start; 5912bbb2116SPatrick McHardy char *end; 5922bbb2116SPatrick McHardy 5932bbb2116SPatrick McHardy limit = ct_sip_header_search(dptr + dataoff, limit, ",", strlen(",")); 5942bbb2116SPatrick McHardy if (!limit) 5952bbb2116SPatrick McHardy limit = dptr + datalen; 5962bbb2116SPatrick McHardy 5972bbb2116SPatrick McHardy start = ct_sip_header_search(dptr + dataoff, limit, name, strlen(name)); 5982bbb2116SPatrick McHardy if (!start) 5992bbb2116SPatrick McHardy return 0; 6002bbb2116SPatrick McHardy 6012bbb2116SPatrick McHardy start += strlen(name); 6022bbb2116SPatrick McHardy *val = simple_strtoul(start, &end, 0); 6032bbb2116SPatrick McHardy if (start == end) 6042bbb2116SPatrick McHardy return 0; 6052bbb2116SPatrick McHardy if (matchoff && matchlen) { 6062bbb2116SPatrick McHardy *matchoff = start - dptr; 6072bbb2116SPatrick McHardy *matchlen = end - start; 6082bbb2116SPatrick McHardy } 6092bbb2116SPatrick McHardy return 1; 6102bbb2116SPatrick McHardy } 6112bbb2116SPatrick McHardy EXPORT_SYMBOL_GPL(ct_sip_parse_numerical_param); 6122bbb2116SPatrick McHardy 613f5b321bdSPatrick McHardy static int ct_sip_parse_transport(struct nf_conn *ct, const char *dptr, 614f5b321bdSPatrick McHardy unsigned int dataoff, unsigned int datalen, 615f5b321bdSPatrick McHardy u8 *proto) 616f5b321bdSPatrick McHardy { 617f5b321bdSPatrick McHardy unsigned int matchoff, matchlen; 618f5b321bdSPatrick McHardy 619f5b321bdSPatrick McHardy if (ct_sip_parse_param(ct, dptr, dataoff, datalen, "transport=", 620f5b321bdSPatrick McHardy &matchoff, &matchlen)) { 621f5b321bdSPatrick McHardy if (!strnicmp(dptr + matchoff, "TCP", strlen("TCP"))) 622f5b321bdSPatrick McHardy *proto = IPPROTO_TCP; 623f5b321bdSPatrick McHardy else if (!strnicmp(dptr + matchoff, "UDP", strlen("UDP"))) 624f5b321bdSPatrick McHardy *proto = IPPROTO_UDP; 625f5b321bdSPatrick McHardy else 626f5b321bdSPatrick McHardy return 0; 627f5b321bdSPatrick McHardy 628f5b321bdSPatrick McHardy if (*proto != nf_ct_protonum(ct)) 629f5b321bdSPatrick McHardy return 0; 630f5b321bdSPatrick McHardy } else 631f5b321bdSPatrick McHardy *proto = nf_ct_protonum(ct); 632f5b321bdSPatrick McHardy 633f5b321bdSPatrick McHardy return 1; 634f5b321bdSPatrick McHardy } 635f5b321bdSPatrick McHardy 6363e9b4600SPatrick McHardy /* SDP header parsing: a SDP session description contains an ordered set of 6373e9b4600SPatrick McHardy * headers, starting with a section containing general session parameters, 6383e9b4600SPatrick McHardy * optionally followed by multiple media descriptions. 6393e9b4600SPatrick McHardy * 6403e9b4600SPatrick McHardy * SDP headers always start at the beginning of a line. According to RFC 2327: 6413e9b4600SPatrick McHardy * "The sequence CRLF (0x0d0a) is used to end a record, although parsers should 6423e9b4600SPatrick McHardy * be tolerant and also accept records terminated with a single newline 6433e9b4600SPatrick McHardy * character". We handle both cases. 6443e9b4600SPatrick McHardy */ 6453e9b4600SPatrick McHardy static const struct sip_header ct_sdp_hdrs[] = { 6463e9b4600SPatrick McHardy [SDP_HDR_VERSION] = SDP_HDR("v=", NULL, digits_len), 6473e9b4600SPatrick McHardy [SDP_HDR_OWNER_IP4] = SDP_HDR("o=", "IN IP4 ", epaddr_len), 6483e9b4600SPatrick McHardy [SDP_HDR_CONNECTION_IP4] = SDP_HDR("c=", "IN IP4 ", epaddr_len), 6493e9b4600SPatrick McHardy [SDP_HDR_OWNER_IP6] = SDP_HDR("o=", "IN IP6 ", epaddr_len), 6503e9b4600SPatrick McHardy [SDP_HDR_CONNECTION_IP6] = SDP_HDR("c=", "IN IP6 ", epaddr_len), 6510d0ab037SPatrick McHardy [SDP_HDR_MEDIA] = SDP_HDR("m=", NULL, media_len), 6523e9b4600SPatrick McHardy }; 6533e9b4600SPatrick McHardy 6543e9b4600SPatrick McHardy /* Linear string search within SDP header values */ 6553e9b4600SPatrick McHardy static const char *ct_sdp_header_search(const char *dptr, const char *limit, 6563e9b4600SPatrick McHardy const char *needle, unsigned int len) 6573e9b4600SPatrick McHardy { 6583e9b4600SPatrick McHardy for (limit -= len; dptr < limit; dptr++) { 6593e9b4600SPatrick McHardy if (*dptr == '\r' || *dptr == '\n') 6603e9b4600SPatrick McHardy break; 6613e9b4600SPatrick McHardy if (strncmp(dptr, needle, len) == 0) 6623e9b4600SPatrick McHardy return dptr; 6633e9b4600SPatrick McHardy } 6643e9b4600SPatrick McHardy return NULL; 6653e9b4600SPatrick McHardy } 6663e9b4600SPatrick McHardy 6673e9b4600SPatrick McHardy /* Locate a SDP header (optionally a substring within the header value), 6683e9b4600SPatrick McHardy * optionally stopping at the first occurence of the term header, parse 6693e9b4600SPatrick McHardy * it and return the offset and length of the data we're interested in. 6703e9b4600SPatrick McHardy */ 6713e9b4600SPatrick McHardy int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr, 6723e9b4600SPatrick McHardy unsigned int dataoff, unsigned int datalen, 6733e9b4600SPatrick McHardy enum sdp_header_types type, 6743e9b4600SPatrick McHardy enum sdp_header_types term, 6753e9b4600SPatrick McHardy unsigned int *matchoff, unsigned int *matchlen) 6763e9b4600SPatrick McHardy { 6773e9b4600SPatrick McHardy const struct sip_header *hdr = &ct_sdp_hdrs[type]; 6783e9b4600SPatrick McHardy const struct sip_header *thdr = &ct_sdp_hdrs[term]; 6793e9b4600SPatrick McHardy const char *start = dptr, *limit = dptr + datalen; 6803e9b4600SPatrick McHardy int shift = 0; 6813e9b4600SPatrick McHardy 6823e9b4600SPatrick McHardy for (dptr += dataoff; dptr < limit; dptr++) { 6833e9b4600SPatrick McHardy /* Find beginning of line */ 6843e9b4600SPatrick McHardy if (*dptr != '\r' && *dptr != '\n') 6853e9b4600SPatrick McHardy continue; 6863e9b4600SPatrick McHardy if (++dptr >= limit) 6873e9b4600SPatrick McHardy break; 6883e9b4600SPatrick McHardy if (*(dptr - 1) == '\r' && *dptr == '\n') { 6893e9b4600SPatrick McHardy if (++dptr >= limit) 6903e9b4600SPatrick McHardy break; 6913e9b4600SPatrick McHardy } 6923e9b4600SPatrick McHardy 6933e9b4600SPatrick McHardy if (term != SDP_HDR_UNSPEC && 6943e9b4600SPatrick McHardy limit - dptr >= thdr->len && 6953e9b4600SPatrick McHardy strnicmp(dptr, thdr->name, thdr->len) == 0) 6963e9b4600SPatrick McHardy break; 6973e9b4600SPatrick McHardy else if (limit - dptr >= hdr->len && 6983e9b4600SPatrick McHardy strnicmp(dptr, hdr->name, hdr->len) == 0) 6993e9b4600SPatrick McHardy dptr += hdr->len; 7003e9b4600SPatrick McHardy else 7013e9b4600SPatrick McHardy continue; 7023e9b4600SPatrick McHardy 7033e9b4600SPatrick McHardy *matchoff = dptr - start; 7043e9b4600SPatrick McHardy if (hdr->search) { 7053e9b4600SPatrick McHardy dptr = ct_sdp_header_search(dptr, limit, hdr->search, 7063e9b4600SPatrick McHardy hdr->slen); 7073e9b4600SPatrick McHardy if (!dptr) 7083e9b4600SPatrick McHardy return -1; 7093e9b4600SPatrick McHardy dptr += hdr->slen; 7103e9b4600SPatrick McHardy } 7113e9b4600SPatrick McHardy 7123e9b4600SPatrick McHardy *matchlen = hdr->match_len(ct, dptr, limit, &shift); 7133e9b4600SPatrick McHardy if (!*matchlen) 7143e9b4600SPatrick McHardy return -1; 7153e9b4600SPatrick McHardy *matchoff = dptr - start + shift; 7163e9b4600SPatrick McHardy return 1; 7173e9b4600SPatrick McHardy } 7183e9b4600SPatrick McHardy return 0; 7193e9b4600SPatrick McHardy } 7203e9b4600SPatrick McHardy EXPORT_SYMBOL_GPL(ct_sip_get_sdp_header); 7213e9b4600SPatrick McHardy 7224ab9e64eSPatrick McHardy static int ct_sip_parse_sdp_addr(const struct nf_conn *ct, const char *dptr, 7234ab9e64eSPatrick McHardy unsigned int dataoff, unsigned int datalen, 7244ab9e64eSPatrick McHardy enum sdp_header_types type, 7254ab9e64eSPatrick McHardy enum sdp_header_types term, 7264ab9e64eSPatrick McHardy unsigned int *matchoff, unsigned int *matchlen, 7274ab9e64eSPatrick McHardy union nf_inet_addr *addr) 7284ab9e64eSPatrick McHardy { 7294ab9e64eSPatrick McHardy int ret; 7304ab9e64eSPatrick McHardy 7314ab9e64eSPatrick McHardy ret = ct_sip_get_sdp_header(ct, dptr, dataoff, datalen, type, term, 7324ab9e64eSPatrick McHardy matchoff, matchlen); 7334ab9e64eSPatrick McHardy if (ret <= 0) 7344ab9e64eSPatrick McHardy return ret; 7354ab9e64eSPatrick McHardy 7364ab9e64eSPatrick McHardy if (!parse_addr(ct, dptr + *matchoff, NULL, addr, 7374ab9e64eSPatrick McHardy dptr + *matchoff + *matchlen)) 7384ab9e64eSPatrick McHardy return -1; 7394ab9e64eSPatrick McHardy return 1; 7404ab9e64eSPatrick McHardy } 7414ab9e64eSPatrick McHardy 7420f32a40fSPatrick McHardy static int refresh_signalling_expectation(struct nf_conn *ct, 7430f32a40fSPatrick McHardy union nf_inet_addr *addr, 744f5b321bdSPatrick McHardy u8 proto, __be16 port, 7450f32a40fSPatrick McHardy unsigned int expires) 7460f32a40fSPatrick McHardy { 7470f32a40fSPatrick McHardy struct nf_conn_help *help = nfct_help(ct); 7480f32a40fSPatrick McHardy struct nf_conntrack_expect *exp; 7490f32a40fSPatrick McHardy struct hlist_node *n, *next; 7500f32a40fSPatrick McHardy int found = 0; 7510f32a40fSPatrick McHardy 7520f32a40fSPatrick McHardy spin_lock_bh(&nf_conntrack_lock); 7530f32a40fSPatrick McHardy hlist_for_each_entry_safe(exp, n, next, &help->expectations, lnode) { 7540f32a40fSPatrick McHardy if (exp->class != SIP_EXPECT_SIGNALLING || 7550f32a40fSPatrick McHardy !nf_inet_addr_cmp(&exp->tuple.dst.u3, addr) || 756f5b321bdSPatrick McHardy exp->tuple.dst.protonum != proto || 7570f32a40fSPatrick McHardy exp->tuple.dst.u.udp.port != port) 7580f32a40fSPatrick McHardy continue; 7590f32a40fSPatrick McHardy if (!del_timer(&exp->timeout)) 7600f32a40fSPatrick McHardy continue; 7610f32a40fSPatrick McHardy exp->flags &= ~NF_CT_EXPECT_INACTIVE; 7620f32a40fSPatrick McHardy exp->timeout.expires = jiffies + expires * HZ; 7630f32a40fSPatrick McHardy add_timer(&exp->timeout); 7640f32a40fSPatrick McHardy found = 1; 7650f32a40fSPatrick McHardy break; 7660f32a40fSPatrick McHardy } 7670f32a40fSPatrick McHardy spin_unlock_bh(&nf_conntrack_lock); 7680f32a40fSPatrick McHardy return found; 7690f32a40fSPatrick McHardy } 7700f32a40fSPatrick McHardy 7710f32a40fSPatrick McHardy static void flush_expectations(struct nf_conn *ct, bool media) 7729467ee38SPatrick McHardy { 7739467ee38SPatrick McHardy struct nf_conn_help *help = nfct_help(ct); 7749467ee38SPatrick McHardy struct nf_conntrack_expect *exp; 7759467ee38SPatrick McHardy struct hlist_node *n, *next; 7769467ee38SPatrick McHardy 7779467ee38SPatrick McHardy spin_lock_bh(&nf_conntrack_lock); 7789467ee38SPatrick McHardy hlist_for_each_entry_safe(exp, n, next, &help->expectations, lnode) { 7790f32a40fSPatrick McHardy if ((exp->class != SIP_EXPECT_SIGNALLING) ^ media) 7800f32a40fSPatrick McHardy continue; 7819467ee38SPatrick McHardy if (!del_timer(&exp->timeout)) 7829467ee38SPatrick McHardy continue; 7839467ee38SPatrick McHardy nf_ct_unlink_expect(exp); 7849467ee38SPatrick McHardy nf_ct_expect_put(exp); 7850f32a40fSPatrick McHardy if (!media) 7860f32a40fSPatrick McHardy break; 7879467ee38SPatrick McHardy } 7889467ee38SPatrick McHardy spin_unlock_bh(&nf_conntrack_lock); 7899467ee38SPatrick McHardy } 7909467ee38SPatrick McHardy 7913b6b9fabSPatrick McHardy static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int dataoff, 792212440a7SPatrick McHardy const char **dptr, unsigned int *datalen, 7934ab9e64eSPatrick McHardy union nf_inet_addr *daddr, __be16 port, 7940d0ab037SPatrick McHardy enum sip_expectation_classes class, 7954ab9e64eSPatrick McHardy unsigned int mediaoff, unsigned int medialen) 7969fafcd7bSPatrick McHardy { 797a9c1d359SPatrick McHardy struct nf_conntrack_expect *exp, *rtp_exp, *rtcp_exp; 798212440a7SPatrick McHardy enum ip_conntrack_info ctinfo; 799212440a7SPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 800a5c3a800SAlexey Dobriyan struct net *net = nf_ct_net(ct); 8019fafcd7bSPatrick McHardy enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); 802d901a936SPatrick McHardy union nf_inet_addr *saddr; 803d901a936SPatrick McHardy struct nf_conntrack_tuple tuple; 804c7f485abSPatrick McHardy int direct_rtp = 0, skip_expect = 0, ret = NF_DROP; 805a9c1d359SPatrick McHardy u_int16_t base_port; 806a9c1d359SPatrick McHardy __be16 rtp_port, rtcp_port; 807c7f485abSPatrick McHardy typeof(nf_nat_sdp_port_hook) nf_nat_sdp_port; 8084ab9e64eSPatrick McHardy typeof(nf_nat_sdp_media_hook) nf_nat_sdp_media; 8099fafcd7bSPatrick McHardy 810d901a936SPatrick McHardy saddr = NULL; 811d901a936SPatrick McHardy if (sip_direct_media) { 812d901a936SPatrick McHardy if (!nf_inet_addr_cmp(daddr, &ct->tuplehash[dir].tuple.src.u3)) 813d901a936SPatrick McHardy return NF_ACCEPT; 814d901a936SPatrick McHardy saddr = &ct->tuplehash[!dir].tuple.src.u3; 815d901a936SPatrick McHardy } 816d901a936SPatrick McHardy 817d901a936SPatrick McHardy /* We need to check whether the registration exists before attempting 818d901a936SPatrick McHardy * to register it since we can see the same media description multiple 819d901a936SPatrick McHardy * times on different connections in case multiple endpoints receive 820d901a936SPatrick McHardy * the same call. 821c7f485abSPatrick McHardy * 822c7f485abSPatrick McHardy * RTP optimization: if we find a matching media channel expectation 823c7f485abSPatrick McHardy * and both the expectation and this connection are SNATed, we assume 824c7f485abSPatrick McHardy * both sides can reach each other directly and use the final 825c7f485abSPatrick McHardy * destination address from the expectation. We still need to keep 826c7f485abSPatrick McHardy * the NATed expectations for media that might arrive from the 827c7f485abSPatrick McHardy * outside, and additionally need to expect the direct RTP stream 828c7f485abSPatrick McHardy * in case it passes through us even without NAT. 829d901a936SPatrick McHardy */ 830d901a936SPatrick McHardy memset(&tuple, 0, sizeof(tuple)); 831d901a936SPatrick McHardy if (saddr) 832d901a936SPatrick McHardy tuple.src.u3 = *saddr; 8335e8fbe2aSPatrick McHardy tuple.src.l3num = nf_ct_l3num(ct); 834d901a936SPatrick McHardy tuple.dst.protonum = IPPROTO_UDP; 835d901a936SPatrick McHardy tuple.dst.u3 = *daddr; 836d901a936SPatrick McHardy tuple.dst.u.udp.port = port; 837d901a936SPatrick McHardy 838d901a936SPatrick McHardy rcu_read_lock(); 839c7f485abSPatrick McHardy do { 8405d0aa2ccSPatrick McHardy exp = __nf_ct_expect_find(net, nf_ct_zone(ct), &tuple); 841d901a936SPatrick McHardy 842c7f485abSPatrick McHardy if (!exp || exp->master == ct || 843c7f485abSPatrick McHardy nfct_help(exp->master)->helper != nfct_help(ct)->helper || 844c7f485abSPatrick McHardy exp->class != class) 845c7f485abSPatrick McHardy break; 846e1f9a464SPatrick McHardy #ifdef CONFIG_NF_NAT_NEEDED 847c7f485abSPatrick McHardy if (exp->tuple.src.l3num == AF_INET && !direct_rtp && 848c7f485abSPatrick McHardy (exp->saved_ip != exp->tuple.dst.u3.ip || 849c7f485abSPatrick McHardy exp->saved_proto.udp.port != exp->tuple.dst.u.udp.port) && 850c7f485abSPatrick McHardy ct->status & IPS_NAT_MASK) { 851c7f485abSPatrick McHardy daddr->ip = exp->saved_ip; 852c7f485abSPatrick McHardy tuple.dst.u3.ip = exp->saved_ip; 853c7f485abSPatrick McHardy tuple.dst.u.udp.port = exp->saved_proto.udp.port; 854c7f485abSPatrick McHardy direct_rtp = 1; 855c7f485abSPatrick McHardy } else 856e1f9a464SPatrick McHardy #endif 857c7f485abSPatrick McHardy skip_expect = 1; 858c7f485abSPatrick McHardy } while (!skip_expect); 859c7f485abSPatrick McHardy rcu_read_unlock(); 860d901a936SPatrick McHardy 861a9c1d359SPatrick McHardy base_port = ntohs(tuple.dst.u.udp.port) & ~1; 862a9c1d359SPatrick McHardy rtp_port = htons(base_port); 863a9c1d359SPatrick McHardy rtcp_port = htons(base_port + 1); 864a9c1d359SPatrick McHardy 865c7f485abSPatrick McHardy if (direct_rtp) { 866c7f485abSPatrick McHardy nf_nat_sdp_port = rcu_dereference(nf_nat_sdp_port_hook); 867c7f485abSPatrick McHardy if (nf_nat_sdp_port && 8683b6b9fabSPatrick McHardy !nf_nat_sdp_port(skb, dataoff, dptr, datalen, 869c7f485abSPatrick McHardy mediaoff, medialen, ntohs(rtp_port))) 870c7f485abSPatrick McHardy goto err1; 871c7f485abSPatrick McHardy } 872c7f485abSPatrick McHardy 873c7f485abSPatrick McHardy if (skip_expect) 874c7f485abSPatrick McHardy return NF_ACCEPT; 875c7f485abSPatrick McHardy 876a9c1d359SPatrick McHardy rtp_exp = nf_ct_expect_alloc(ct); 877a9c1d359SPatrick McHardy if (rtp_exp == NULL) 878a9c1d359SPatrick McHardy goto err1; 8795e8fbe2aSPatrick McHardy nf_ct_expect_init(rtp_exp, class, nf_ct_l3num(ct), saddr, daddr, 880a9c1d359SPatrick McHardy IPPROTO_UDP, NULL, &rtp_port); 881a9c1d359SPatrick McHardy 882a9c1d359SPatrick McHardy rtcp_exp = nf_ct_expect_alloc(ct); 883a9c1d359SPatrick McHardy if (rtcp_exp == NULL) 884a9c1d359SPatrick McHardy goto err2; 8855e8fbe2aSPatrick McHardy nf_ct_expect_init(rtcp_exp, class, nf_ct_l3num(ct), saddr, daddr, 886a9c1d359SPatrick McHardy IPPROTO_UDP, NULL, &rtcp_port); 8879fafcd7bSPatrick McHardy 8884ab9e64eSPatrick McHardy nf_nat_sdp_media = rcu_dereference(nf_nat_sdp_media_hook); 889c7f485abSPatrick McHardy if (nf_nat_sdp_media && ct->status & IPS_NAT_MASK && !direct_rtp) 8903b6b9fabSPatrick McHardy ret = nf_nat_sdp_media(skb, dataoff, dptr, datalen, 8913b6b9fabSPatrick McHardy rtp_exp, rtcp_exp, 8924ab9e64eSPatrick McHardy mediaoff, medialen, daddr); 8939fafcd7bSPatrick McHardy else { 894a9c1d359SPatrick McHardy if (nf_ct_expect_related(rtp_exp) == 0) { 895a9c1d359SPatrick McHardy if (nf_ct_expect_related(rtcp_exp) != 0) 896a9c1d359SPatrick McHardy nf_ct_unexpect_related(rtp_exp); 8979fafcd7bSPatrick McHardy else 8989fafcd7bSPatrick McHardy ret = NF_ACCEPT; 8999fafcd7bSPatrick McHardy } 900a9c1d359SPatrick McHardy } 901a9c1d359SPatrick McHardy nf_ct_expect_put(rtcp_exp); 902a9c1d359SPatrick McHardy err2: 903a9c1d359SPatrick McHardy nf_ct_expect_put(rtp_exp); 904a9c1d359SPatrick McHardy err1: 9059fafcd7bSPatrick McHardy return ret; 9069fafcd7bSPatrick McHardy } 9079fafcd7bSPatrick McHardy 9080d0ab037SPatrick McHardy static const struct sdp_media_type sdp_media_types[] = { 9090d0ab037SPatrick McHardy SDP_MEDIA_TYPE("audio ", SIP_EXPECT_AUDIO), 9100d0ab037SPatrick McHardy SDP_MEDIA_TYPE("video ", SIP_EXPECT_VIDEO), 9119d288dffSPatrick McHardy SDP_MEDIA_TYPE("image ", SIP_EXPECT_IMAGE), 9120d0ab037SPatrick McHardy }; 9130d0ab037SPatrick McHardy 9140d0ab037SPatrick McHardy static const struct sdp_media_type *sdp_media_type(const char *dptr, 9150d0ab037SPatrick McHardy unsigned int matchoff, 9160d0ab037SPatrick McHardy unsigned int matchlen) 9170d0ab037SPatrick McHardy { 9180d0ab037SPatrick McHardy const struct sdp_media_type *t; 9190d0ab037SPatrick McHardy unsigned int i; 9200d0ab037SPatrick McHardy 9210d0ab037SPatrick McHardy for (i = 0; i < ARRAY_SIZE(sdp_media_types); i++) { 9220d0ab037SPatrick McHardy t = &sdp_media_types[i]; 9230d0ab037SPatrick McHardy if (matchlen < t->len || 9240d0ab037SPatrick McHardy strncmp(dptr + matchoff, t->name, t->len)) 9250d0ab037SPatrick McHardy continue; 9260d0ab037SPatrick McHardy return t; 9270d0ab037SPatrick McHardy } 9280d0ab037SPatrick McHardy return NULL; 9290d0ab037SPatrick McHardy } 9300d0ab037SPatrick McHardy 9313b6b9fabSPatrick McHardy static int process_sdp(struct sk_buff *skb, unsigned int dataoff, 93230f33e6dSPatrick McHardy const char **dptr, unsigned int *datalen, 93330f33e6dSPatrick McHardy unsigned int cseq) 9347d3dd043SPatrick McHardy { 9357d3dd043SPatrick McHardy enum ip_conntrack_info ctinfo; 9367d3dd043SPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 9377d3dd043SPatrick McHardy unsigned int matchoff, matchlen; 9384ab9e64eSPatrick McHardy unsigned int mediaoff, medialen; 9394ab9e64eSPatrick McHardy unsigned int sdpoff; 9404ab9e64eSPatrick McHardy unsigned int caddr_len, maddr_len; 9410d0ab037SPatrick McHardy unsigned int i; 9424ab9e64eSPatrick McHardy union nf_inet_addr caddr, maddr, rtp_addr; 9437d3dd043SPatrick McHardy unsigned int port; 9444ab9e64eSPatrick McHardy enum sdp_header_types c_hdr; 9450d0ab037SPatrick McHardy const struct sdp_media_type *t; 9460d0ab037SPatrick McHardy int ret = NF_ACCEPT; 9474ab9e64eSPatrick McHardy typeof(nf_nat_sdp_addr_hook) nf_nat_sdp_addr; 9484ab9e64eSPatrick McHardy typeof(nf_nat_sdp_session_hook) nf_nat_sdp_session; 9497d3dd043SPatrick McHardy 9500d0ab037SPatrick McHardy nf_nat_sdp_addr = rcu_dereference(nf_nat_sdp_addr_hook); 9515e8fbe2aSPatrick McHardy c_hdr = nf_ct_l3num(ct) == AF_INET ? SDP_HDR_CONNECTION_IP4 : 9527d3dd043SPatrick McHardy SDP_HDR_CONNECTION_IP6; 9537d3dd043SPatrick McHardy 9544ab9e64eSPatrick McHardy /* Find beginning of session description */ 9557d3dd043SPatrick McHardy if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen, 9564ab9e64eSPatrick McHardy SDP_HDR_VERSION, SDP_HDR_UNSPEC, 9577d3dd043SPatrick McHardy &matchoff, &matchlen) <= 0) 9587d3dd043SPatrick McHardy return NF_ACCEPT; 9594ab9e64eSPatrick McHardy sdpoff = matchoff; 9607d3dd043SPatrick McHardy 9614ab9e64eSPatrick McHardy /* The connection information is contained in the session description 9624ab9e64eSPatrick McHardy * and/or once per media description. The first media description marks 9634ab9e64eSPatrick McHardy * the end of the session description. */ 9644ab9e64eSPatrick McHardy caddr_len = 0; 9654ab9e64eSPatrick McHardy if (ct_sip_parse_sdp_addr(ct, *dptr, sdpoff, *datalen, 9664ab9e64eSPatrick McHardy c_hdr, SDP_HDR_MEDIA, 9674ab9e64eSPatrick McHardy &matchoff, &matchlen, &caddr) > 0) 9684ab9e64eSPatrick McHardy caddr_len = matchlen; 9697d3dd043SPatrick McHardy 9700d0ab037SPatrick McHardy mediaoff = sdpoff; 9710d0ab037SPatrick McHardy for (i = 0; i < ARRAY_SIZE(sdp_media_types); ) { 9720d0ab037SPatrick McHardy if (ct_sip_get_sdp_header(ct, *dptr, mediaoff, *datalen, 9737d3dd043SPatrick McHardy SDP_HDR_MEDIA, SDP_HDR_UNSPEC, 9744ab9e64eSPatrick McHardy &mediaoff, &medialen) <= 0) 9750d0ab037SPatrick McHardy break; 9760d0ab037SPatrick McHardy 9770d0ab037SPatrick McHardy /* Get media type and port number. A media port value of zero 9780d0ab037SPatrick McHardy * indicates an inactive stream. */ 9790d0ab037SPatrick McHardy t = sdp_media_type(*dptr, mediaoff, medialen); 9800d0ab037SPatrick McHardy if (!t) { 9810d0ab037SPatrick McHardy mediaoff += medialen; 9820d0ab037SPatrick McHardy continue; 9830d0ab037SPatrick McHardy } 9840d0ab037SPatrick McHardy mediaoff += t->len; 9850d0ab037SPatrick McHardy medialen -= t->len; 9867d3dd043SPatrick McHardy 9874ab9e64eSPatrick McHardy port = simple_strtoul(*dptr + mediaoff, NULL, 10); 9880d0ab037SPatrick McHardy if (port == 0) 9890d0ab037SPatrick McHardy continue; 9907d3dd043SPatrick McHardy if (port < 1024 || port > 65535) 9917d3dd043SPatrick McHardy return NF_DROP; 9927d3dd043SPatrick McHardy 9934ab9e64eSPatrick McHardy /* The media description overrides the session description. */ 9944ab9e64eSPatrick McHardy maddr_len = 0; 9954ab9e64eSPatrick McHardy if (ct_sip_parse_sdp_addr(ct, *dptr, mediaoff, *datalen, 9964ab9e64eSPatrick McHardy c_hdr, SDP_HDR_MEDIA, 9974ab9e64eSPatrick McHardy &matchoff, &matchlen, &maddr) > 0) { 9984ab9e64eSPatrick McHardy maddr_len = matchlen; 9994ab9e64eSPatrick McHardy memcpy(&rtp_addr, &maddr, sizeof(rtp_addr)); 10004ab9e64eSPatrick McHardy } else if (caddr_len) 10014ab9e64eSPatrick McHardy memcpy(&rtp_addr, &caddr, sizeof(rtp_addr)); 10024ab9e64eSPatrick McHardy else 10034ab9e64eSPatrick McHardy return NF_DROP; 10044ab9e64eSPatrick McHardy 10053b6b9fabSPatrick McHardy ret = set_expected_rtp_rtcp(skb, dataoff, dptr, datalen, 10060d0ab037SPatrick McHardy &rtp_addr, htons(port), t->class, 10074ab9e64eSPatrick McHardy mediaoff, medialen); 10084ab9e64eSPatrick McHardy if (ret != NF_ACCEPT) 10094ab9e64eSPatrick McHardy return ret; 10104ab9e64eSPatrick McHardy 10114ab9e64eSPatrick McHardy /* Update media connection address if present */ 10120d0ab037SPatrick McHardy if (maddr_len && nf_nat_sdp_addr && ct->status & IPS_NAT_MASK) { 10133b6b9fabSPatrick McHardy ret = nf_nat_sdp_addr(skb, dataoff, dptr, datalen, 10143b6b9fabSPatrick McHardy mediaoff, c_hdr, SDP_HDR_MEDIA, 10153b6b9fabSPatrick McHardy &rtp_addr); 10164ab9e64eSPatrick McHardy if (ret != NF_ACCEPT) 10174ab9e64eSPatrick McHardy return ret; 10184ab9e64eSPatrick McHardy } 10190d0ab037SPatrick McHardy i++; 10204ab9e64eSPatrick McHardy } 10214ab9e64eSPatrick McHardy 10224ab9e64eSPatrick McHardy /* Update session connection and owner addresses */ 10234ab9e64eSPatrick McHardy nf_nat_sdp_session = rcu_dereference(nf_nat_sdp_session_hook); 10244ab9e64eSPatrick McHardy if (nf_nat_sdp_session && ct->status & IPS_NAT_MASK) 10253b6b9fabSPatrick McHardy ret = nf_nat_sdp_session(skb, dataoff, dptr, datalen, sdpoff, 10263b6b9fabSPatrick McHardy &rtp_addr); 10274ab9e64eSPatrick McHardy 10284ab9e64eSPatrick McHardy return ret; 10297d3dd043SPatrick McHardy } 10303b6b9fabSPatrick McHardy static int process_invite_response(struct sk_buff *skb, unsigned int dataoff, 103130f33e6dSPatrick McHardy const char **dptr, unsigned int *datalen, 103230f33e6dSPatrick McHardy unsigned int cseq, unsigned int code) 103330f33e6dSPatrick McHardy { 10349467ee38SPatrick McHardy enum ip_conntrack_info ctinfo; 10359467ee38SPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 1036ef75d49fSPatrick McHardy struct nf_conn_help *help = nfct_help(ct); 10379467ee38SPatrick McHardy 103830f33e6dSPatrick McHardy if ((code >= 100 && code <= 199) || 103930f33e6dSPatrick McHardy (code >= 200 && code <= 299)) 10403b6b9fabSPatrick McHardy return process_sdp(skb, dataoff, dptr, datalen, cseq); 1041ef75d49fSPatrick McHardy else if (help->help.ct_sip_info.invite_cseq == cseq) 10420f32a40fSPatrick McHardy flush_expectations(ct, true); 104330f33e6dSPatrick McHardy return NF_ACCEPT; 104430f33e6dSPatrick McHardy } 104530f33e6dSPatrick McHardy 10463b6b9fabSPatrick McHardy static int process_update_response(struct sk_buff *skb, unsigned int dataoff, 104730f33e6dSPatrick McHardy const char **dptr, unsigned int *datalen, 104830f33e6dSPatrick McHardy unsigned int cseq, unsigned int code) 104930f33e6dSPatrick McHardy { 10509467ee38SPatrick McHardy enum ip_conntrack_info ctinfo; 10519467ee38SPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 1052ef75d49fSPatrick McHardy struct nf_conn_help *help = nfct_help(ct); 10539467ee38SPatrick McHardy 105430f33e6dSPatrick McHardy if ((code >= 100 && code <= 199) || 105530f33e6dSPatrick McHardy (code >= 200 && code <= 299)) 10563b6b9fabSPatrick McHardy return process_sdp(skb, dataoff, dptr, datalen, cseq); 1057ef75d49fSPatrick McHardy else if (help->help.ct_sip_info.invite_cseq == cseq) 10580f32a40fSPatrick McHardy flush_expectations(ct, true); 105930f33e6dSPatrick McHardy return NF_ACCEPT; 106030f33e6dSPatrick McHardy } 106130f33e6dSPatrick McHardy 10623b6b9fabSPatrick McHardy static int process_prack_response(struct sk_buff *skb, unsigned int dataoff, 1063595a8ecbSPatrick McHardy const char **dptr, unsigned int *datalen, 1064595a8ecbSPatrick McHardy unsigned int cseq, unsigned int code) 1065595a8ecbSPatrick McHardy { 10669467ee38SPatrick McHardy enum ip_conntrack_info ctinfo; 10679467ee38SPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 1068ef75d49fSPatrick McHardy struct nf_conn_help *help = nfct_help(ct); 10699467ee38SPatrick McHardy 1070595a8ecbSPatrick McHardy if ((code >= 100 && code <= 199) || 1071595a8ecbSPatrick McHardy (code >= 200 && code <= 299)) 10723b6b9fabSPatrick McHardy return process_sdp(skb, dataoff, dptr, datalen, cseq); 1073ef75d49fSPatrick McHardy else if (help->help.ct_sip_info.invite_cseq == cseq) 10740f32a40fSPatrick McHardy flush_expectations(ct, true); 10759467ee38SPatrick McHardy return NF_ACCEPT; 10769467ee38SPatrick McHardy } 1077595a8ecbSPatrick McHardy 10789d288dffSPatrick McHardy static int process_invite_request(struct sk_buff *skb, unsigned int dataoff, 10799d288dffSPatrick McHardy const char **dptr, unsigned int *datalen, 10809d288dffSPatrick McHardy unsigned int cseq) 10819d288dffSPatrick McHardy { 10829d288dffSPatrick McHardy enum ip_conntrack_info ctinfo; 10839d288dffSPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 10849d288dffSPatrick McHardy struct nf_conn_help *help = nfct_help(ct); 10859d288dffSPatrick McHardy unsigned int ret; 10869d288dffSPatrick McHardy 10879d288dffSPatrick McHardy flush_expectations(ct, true); 10889d288dffSPatrick McHardy ret = process_sdp(skb, dataoff, dptr, datalen, cseq); 10899d288dffSPatrick McHardy if (ret == NF_ACCEPT) 10909d288dffSPatrick McHardy help->help.ct_sip_info.invite_cseq = cseq; 10919d288dffSPatrick McHardy return ret; 10929d288dffSPatrick McHardy } 10939d288dffSPatrick McHardy 10943b6b9fabSPatrick McHardy static int process_bye_request(struct sk_buff *skb, unsigned int dataoff, 10959467ee38SPatrick McHardy const char **dptr, unsigned int *datalen, 10969467ee38SPatrick McHardy unsigned int cseq) 10979467ee38SPatrick McHardy { 10989467ee38SPatrick McHardy enum ip_conntrack_info ctinfo; 10999467ee38SPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 11009467ee38SPatrick McHardy 11010f32a40fSPatrick McHardy flush_expectations(ct, true); 11020f32a40fSPatrick McHardy return NF_ACCEPT; 11030f32a40fSPatrick McHardy } 11040f32a40fSPatrick McHardy 11050f32a40fSPatrick McHardy /* Parse a REGISTER request and create a permanent expectation for incoming 11060f32a40fSPatrick McHardy * signalling connections. The expectation is marked inactive and is activated 11070f32a40fSPatrick McHardy * when receiving a response indicating success from the registrar. 11080f32a40fSPatrick McHardy */ 11093b6b9fabSPatrick McHardy static int process_register_request(struct sk_buff *skb, unsigned int dataoff, 11100f32a40fSPatrick McHardy const char **dptr, unsigned int *datalen, 11110f32a40fSPatrick McHardy unsigned int cseq) 11120f32a40fSPatrick McHardy { 11130f32a40fSPatrick McHardy enum ip_conntrack_info ctinfo; 11140f32a40fSPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 11150f32a40fSPatrick McHardy struct nf_conn_help *help = nfct_help(ct); 11160f32a40fSPatrick McHardy enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); 11170f32a40fSPatrick McHardy unsigned int matchoff, matchlen; 11180f32a40fSPatrick McHardy struct nf_conntrack_expect *exp; 11190f32a40fSPatrick McHardy union nf_inet_addr *saddr, daddr; 11200f32a40fSPatrick McHardy __be16 port; 1121f5b321bdSPatrick McHardy u8 proto; 11220f32a40fSPatrick McHardy unsigned int expires = 0; 11230f32a40fSPatrick McHardy int ret; 11240f32a40fSPatrick McHardy typeof(nf_nat_sip_expect_hook) nf_nat_sip_expect; 11250f32a40fSPatrick McHardy 11260f32a40fSPatrick McHardy /* Expected connections can not register again. */ 11270f32a40fSPatrick McHardy if (ct->status & IPS_EXPECTED) 11280f32a40fSPatrick McHardy return NF_ACCEPT; 11290f32a40fSPatrick McHardy 11300f32a40fSPatrick McHardy /* We must check the expiration time: a value of zero signals the 11310f32a40fSPatrick McHardy * registrar to release the binding. We'll remove our expectation 11320f32a40fSPatrick McHardy * when receiving the new bindings in the response, but we don't 11330f32a40fSPatrick McHardy * want to create new ones. 11340f32a40fSPatrick McHardy * 11350f32a40fSPatrick McHardy * The expiration time may be contained in Expires: header, the 11360f32a40fSPatrick McHardy * Contact: header parameters or the URI parameters. 11370f32a40fSPatrick McHardy */ 11380f32a40fSPatrick McHardy if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_EXPIRES, 11390f32a40fSPatrick McHardy &matchoff, &matchlen) > 0) 11400f32a40fSPatrick McHardy expires = simple_strtoul(*dptr + matchoff, NULL, 10); 11410f32a40fSPatrick McHardy 11420f32a40fSPatrick McHardy ret = ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, 11430f32a40fSPatrick McHardy SIP_HDR_CONTACT, NULL, 11440f32a40fSPatrick McHardy &matchoff, &matchlen, &daddr, &port); 11450f32a40fSPatrick McHardy if (ret < 0) 11460f32a40fSPatrick McHardy return NF_DROP; 11470f32a40fSPatrick McHardy else if (ret == 0) 11480f32a40fSPatrick McHardy return NF_ACCEPT; 11490f32a40fSPatrick McHardy 11500f32a40fSPatrick McHardy /* We don't support third-party registrations */ 11510f32a40fSPatrick McHardy if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, &daddr)) 11520f32a40fSPatrick McHardy return NF_ACCEPT; 11530f32a40fSPatrick McHardy 1154f5b321bdSPatrick McHardy if (ct_sip_parse_transport(ct, *dptr, matchoff + matchlen, *datalen, 1155f5b321bdSPatrick McHardy &proto) == 0) 1156f5b321bdSPatrick McHardy return NF_ACCEPT; 1157f5b321bdSPatrick McHardy 11580f32a40fSPatrick McHardy if (ct_sip_parse_numerical_param(ct, *dptr, 11590f32a40fSPatrick McHardy matchoff + matchlen, *datalen, 11600f32a40fSPatrick McHardy "expires=", NULL, NULL, &expires) < 0) 11610f32a40fSPatrick McHardy return NF_DROP; 11620f32a40fSPatrick McHardy 11630f32a40fSPatrick McHardy if (expires == 0) { 11640f32a40fSPatrick McHardy ret = NF_ACCEPT; 11650f32a40fSPatrick McHardy goto store_cseq; 11660f32a40fSPatrick McHardy } 11670f32a40fSPatrick McHardy 11680f32a40fSPatrick McHardy exp = nf_ct_expect_alloc(ct); 11690f32a40fSPatrick McHardy if (!exp) 11700f32a40fSPatrick McHardy return NF_DROP; 11710f32a40fSPatrick McHardy 11720f32a40fSPatrick McHardy saddr = NULL; 11730f32a40fSPatrick McHardy if (sip_direct_signalling) 11740f32a40fSPatrick McHardy saddr = &ct->tuplehash[!dir].tuple.src.u3; 11750f32a40fSPatrick McHardy 11765e8fbe2aSPatrick McHardy nf_ct_expect_init(exp, SIP_EXPECT_SIGNALLING, nf_ct_l3num(ct), 1177f5b321bdSPatrick McHardy saddr, &daddr, proto, NULL, &port); 11780f32a40fSPatrick McHardy exp->timeout.expires = sip_timeout * HZ; 11790f32a40fSPatrick McHardy exp->helper = nfct_help(ct)->helper; 11800f32a40fSPatrick McHardy exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE; 11810f32a40fSPatrick McHardy 11820f32a40fSPatrick McHardy nf_nat_sip_expect = rcu_dereference(nf_nat_sip_expect_hook); 11830f32a40fSPatrick McHardy if (nf_nat_sip_expect && ct->status & IPS_NAT_MASK) 11843b6b9fabSPatrick McHardy ret = nf_nat_sip_expect(skb, dataoff, dptr, datalen, exp, 11850f32a40fSPatrick McHardy matchoff, matchlen); 11860f32a40fSPatrick McHardy else { 11870f32a40fSPatrick McHardy if (nf_ct_expect_related(exp) != 0) 11880f32a40fSPatrick McHardy ret = NF_DROP; 11890f32a40fSPatrick McHardy else 11900f32a40fSPatrick McHardy ret = NF_ACCEPT; 11910f32a40fSPatrick McHardy } 11920f32a40fSPatrick McHardy nf_ct_expect_put(exp); 11930f32a40fSPatrick McHardy 11940f32a40fSPatrick McHardy store_cseq: 11950f32a40fSPatrick McHardy if (ret == NF_ACCEPT) 11960f32a40fSPatrick McHardy help->help.ct_sip_info.register_cseq = cseq; 11970f32a40fSPatrick McHardy return ret; 11980f32a40fSPatrick McHardy } 11990f32a40fSPatrick McHardy 12003b6b9fabSPatrick McHardy static int process_register_response(struct sk_buff *skb, unsigned int dataoff, 12010f32a40fSPatrick McHardy const char **dptr, unsigned int *datalen, 12020f32a40fSPatrick McHardy unsigned int cseq, unsigned int code) 12030f32a40fSPatrick McHardy { 12040f32a40fSPatrick McHardy enum ip_conntrack_info ctinfo; 12050f32a40fSPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 12060f32a40fSPatrick McHardy struct nf_conn_help *help = nfct_help(ct); 12070f32a40fSPatrick McHardy enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); 12080f32a40fSPatrick McHardy union nf_inet_addr addr; 12090f32a40fSPatrick McHardy __be16 port; 1210f5b321bdSPatrick McHardy u8 proto; 12113b6b9fabSPatrick McHardy unsigned int matchoff, matchlen, coff = 0; 12120f32a40fSPatrick McHardy unsigned int expires = 0; 12130f32a40fSPatrick McHardy int in_contact = 0, ret; 12140f32a40fSPatrick McHardy 12150f32a40fSPatrick McHardy /* According to RFC 3261, "UAs MUST NOT send a new registration until 12160f32a40fSPatrick McHardy * they have received a final response from the registrar for the 12170f32a40fSPatrick McHardy * previous one or the previous REGISTER request has timed out". 12180f32a40fSPatrick McHardy * 12190f32a40fSPatrick McHardy * However, some servers fail to detect retransmissions and send late 12200f32a40fSPatrick McHardy * responses, so we store the sequence number of the last valid 12210f32a40fSPatrick McHardy * request and compare it here. 12220f32a40fSPatrick McHardy */ 12230f32a40fSPatrick McHardy if (help->help.ct_sip_info.register_cseq != cseq) 12240f32a40fSPatrick McHardy return NF_ACCEPT; 12250f32a40fSPatrick McHardy 12260f32a40fSPatrick McHardy if (code >= 100 && code <= 199) 12270f32a40fSPatrick McHardy return NF_ACCEPT; 12280f32a40fSPatrick McHardy if (code < 200 || code > 299) 12290f32a40fSPatrick McHardy goto flush; 12300f32a40fSPatrick McHardy 12310f32a40fSPatrick McHardy if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_EXPIRES, 12320f32a40fSPatrick McHardy &matchoff, &matchlen) > 0) 12330f32a40fSPatrick McHardy expires = simple_strtoul(*dptr + matchoff, NULL, 10); 12340f32a40fSPatrick McHardy 12350f32a40fSPatrick McHardy while (1) { 12360f32a40fSPatrick McHardy unsigned int c_expires = expires; 12370f32a40fSPatrick McHardy 12383b6b9fabSPatrick McHardy ret = ct_sip_parse_header_uri(ct, *dptr, &coff, *datalen, 12390f32a40fSPatrick McHardy SIP_HDR_CONTACT, &in_contact, 12400f32a40fSPatrick McHardy &matchoff, &matchlen, 12410f32a40fSPatrick McHardy &addr, &port); 12420f32a40fSPatrick McHardy if (ret < 0) 12430f32a40fSPatrick McHardy return NF_DROP; 12440f32a40fSPatrick McHardy else if (ret == 0) 12450f32a40fSPatrick McHardy break; 12460f32a40fSPatrick McHardy 12470f32a40fSPatrick McHardy /* We don't support third-party registrations */ 12480f32a40fSPatrick McHardy if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, &addr)) 12490f32a40fSPatrick McHardy continue; 12500f32a40fSPatrick McHardy 1251f5b321bdSPatrick McHardy if (ct_sip_parse_transport(ct, *dptr, matchoff + matchlen, 1252f5b321bdSPatrick McHardy *datalen, &proto) == 0) 1253f5b321bdSPatrick McHardy continue; 1254f5b321bdSPatrick McHardy 12550f32a40fSPatrick McHardy ret = ct_sip_parse_numerical_param(ct, *dptr, 12560f32a40fSPatrick McHardy matchoff + matchlen, 12570f32a40fSPatrick McHardy *datalen, "expires=", 12580f32a40fSPatrick McHardy NULL, NULL, &c_expires); 12590f32a40fSPatrick McHardy if (ret < 0) 12600f32a40fSPatrick McHardy return NF_DROP; 12610f32a40fSPatrick McHardy if (c_expires == 0) 12620f32a40fSPatrick McHardy break; 1263f5b321bdSPatrick McHardy if (refresh_signalling_expectation(ct, &addr, proto, port, 1264f5b321bdSPatrick McHardy c_expires)) 12650f32a40fSPatrick McHardy return NF_ACCEPT; 12660f32a40fSPatrick McHardy } 12670f32a40fSPatrick McHardy 12680f32a40fSPatrick McHardy flush: 12690f32a40fSPatrick McHardy flush_expectations(ct, false); 1270595a8ecbSPatrick McHardy return NF_ACCEPT; 1271595a8ecbSPatrick McHardy } 1272595a8ecbSPatrick McHardy 127330f33e6dSPatrick McHardy static const struct sip_handler sip_handlers[] = { 12749d288dffSPatrick McHardy SIP_HANDLER("INVITE", process_invite_request, process_invite_response), 127530f33e6dSPatrick McHardy SIP_HANDLER("UPDATE", process_sdp, process_update_response), 1276595a8ecbSPatrick McHardy SIP_HANDLER("ACK", process_sdp, NULL), 1277595a8ecbSPatrick McHardy SIP_HANDLER("PRACK", process_sdp, process_prack_response), 12789467ee38SPatrick McHardy SIP_HANDLER("BYE", process_bye_request, NULL), 12790f32a40fSPatrick McHardy SIP_HANDLER("REGISTER", process_register_request, process_register_response), 128030f33e6dSPatrick McHardy }; 128130f33e6dSPatrick McHardy 12823b6b9fabSPatrick McHardy static int process_sip_response(struct sk_buff *skb, unsigned int dataoff, 128330f33e6dSPatrick McHardy const char **dptr, unsigned int *datalen) 128430f33e6dSPatrick McHardy { 128530f33e6dSPatrick McHardy enum ip_conntrack_info ctinfo; 128630f33e6dSPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 12873b6b9fabSPatrick McHardy unsigned int matchoff, matchlen, matchend; 12883b6b9fabSPatrick McHardy unsigned int code, cseq, i; 128930f33e6dSPatrick McHardy 129030f33e6dSPatrick McHardy if (*datalen < strlen("SIP/2.0 200")) 129130f33e6dSPatrick McHardy return NF_ACCEPT; 129230f33e6dSPatrick McHardy code = simple_strtoul(*dptr + strlen("SIP/2.0 "), NULL, 10); 129330f33e6dSPatrick McHardy if (!code) 129430f33e6dSPatrick McHardy return NF_DROP; 129530f33e6dSPatrick McHardy 129630f33e6dSPatrick McHardy if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ, 129730f33e6dSPatrick McHardy &matchoff, &matchlen) <= 0) 129830f33e6dSPatrick McHardy return NF_DROP; 129930f33e6dSPatrick McHardy cseq = simple_strtoul(*dptr + matchoff, NULL, 10); 130030f33e6dSPatrick McHardy if (!cseq) 130130f33e6dSPatrick McHardy return NF_DROP; 13023b6b9fabSPatrick McHardy matchend = matchoff + matchlen + 1; 130330f33e6dSPatrick McHardy 130430f33e6dSPatrick McHardy for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) { 130566bf7918SAlexey Dobriyan const struct sip_handler *handler; 130666bf7918SAlexey Dobriyan 130730f33e6dSPatrick McHardy handler = &sip_handlers[i]; 130830f33e6dSPatrick McHardy if (handler->response == NULL) 130930f33e6dSPatrick McHardy continue; 13103b6b9fabSPatrick McHardy if (*datalen < matchend + handler->len || 13113b6b9fabSPatrick McHardy strnicmp(*dptr + matchend, handler->method, handler->len)) 131230f33e6dSPatrick McHardy continue; 13133b6b9fabSPatrick McHardy return handler->response(skb, dataoff, dptr, datalen, 13143b6b9fabSPatrick McHardy cseq, code); 131530f33e6dSPatrick McHardy } 131630f33e6dSPatrick McHardy return NF_ACCEPT; 131730f33e6dSPatrick McHardy } 131830f33e6dSPatrick McHardy 13193b6b9fabSPatrick McHardy static int process_sip_request(struct sk_buff *skb, unsigned int dataoff, 132030f33e6dSPatrick McHardy const char **dptr, unsigned int *datalen) 132130f33e6dSPatrick McHardy { 132230f33e6dSPatrick McHardy enum ip_conntrack_info ctinfo; 132330f33e6dSPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 132430f33e6dSPatrick McHardy unsigned int matchoff, matchlen; 132530f33e6dSPatrick McHardy unsigned int cseq, i; 132630f33e6dSPatrick McHardy 132730f33e6dSPatrick McHardy for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) { 132866bf7918SAlexey Dobriyan const struct sip_handler *handler; 132966bf7918SAlexey Dobriyan 133030f33e6dSPatrick McHardy handler = &sip_handlers[i]; 133130f33e6dSPatrick McHardy if (handler->request == NULL) 133230f33e6dSPatrick McHardy continue; 133330f33e6dSPatrick McHardy if (*datalen < handler->len || 133430f33e6dSPatrick McHardy strnicmp(*dptr, handler->method, handler->len)) 133530f33e6dSPatrick McHardy continue; 133630f33e6dSPatrick McHardy 133730f33e6dSPatrick McHardy if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ, 133830f33e6dSPatrick McHardy &matchoff, &matchlen) <= 0) 133930f33e6dSPatrick McHardy return NF_DROP; 134030f33e6dSPatrick McHardy cseq = simple_strtoul(*dptr + matchoff, NULL, 10); 134130f33e6dSPatrick McHardy if (!cseq) 134230f33e6dSPatrick McHardy return NF_DROP; 134330f33e6dSPatrick McHardy 13443b6b9fabSPatrick McHardy return handler->request(skb, dataoff, dptr, datalen, cseq); 134530f33e6dSPatrick McHardy } 134630f33e6dSPatrick McHardy return NF_ACCEPT; 134730f33e6dSPatrick McHardy } 13487d3dd043SPatrick McHardy 1349f5b321bdSPatrick McHardy static int process_sip_msg(struct sk_buff *skb, struct nf_conn *ct, 1350f5b321bdSPatrick McHardy unsigned int dataoff, const char **dptr, 1351f5b321bdSPatrick McHardy unsigned int *datalen) 1352f5b321bdSPatrick McHardy { 1353f5b321bdSPatrick McHardy typeof(nf_nat_sip_hook) nf_nat_sip; 1354f5b321bdSPatrick McHardy int ret; 1355f5b321bdSPatrick McHardy 1356f5b321bdSPatrick McHardy if (strnicmp(*dptr, "SIP/2.0 ", strlen("SIP/2.0 ")) != 0) 1357f5b321bdSPatrick McHardy ret = process_sip_request(skb, dataoff, dptr, datalen); 1358f5b321bdSPatrick McHardy else 1359f5b321bdSPatrick McHardy ret = process_sip_response(skb, dataoff, dptr, datalen); 1360f5b321bdSPatrick McHardy 1361f5b321bdSPatrick McHardy if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) { 1362f5b321bdSPatrick McHardy nf_nat_sip = rcu_dereference(nf_nat_sip_hook); 1363f5b321bdSPatrick McHardy if (nf_nat_sip && !nf_nat_sip(skb, dataoff, dptr, datalen)) 1364f5b321bdSPatrick McHardy ret = NF_DROP; 1365f5b321bdSPatrick McHardy } 1366f5b321bdSPatrick McHardy 1367f5b321bdSPatrick McHardy return ret; 1368f5b321bdSPatrick McHardy } 1369f5b321bdSPatrick McHardy 1370f5b321bdSPatrick McHardy static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, 1371f5b321bdSPatrick McHardy struct nf_conn *ct, enum ip_conntrack_info ctinfo) 1372f5b321bdSPatrick McHardy { 1373f5b321bdSPatrick McHardy struct tcphdr *th, _tcph; 1374f5b321bdSPatrick McHardy unsigned int dataoff, datalen; 1375f5b321bdSPatrick McHardy unsigned int matchoff, matchlen, clen; 1376f5b321bdSPatrick McHardy unsigned int msglen, origlen; 1377f5b321bdSPatrick McHardy const char *dptr, *end; 1378f5b321bdSPatrick McHardy s16 diff, tdiff = 0; 1379f5b321bdSPatrick McHardy int ret; 138048f8ac26SPatrick McHardy typeof(nf_nat_sip_seq_adjust_hook) nf_nat_sip_seq_adjust; 1381f5b321bdSPatrick McHardy 1382f5b321bdSPatrick McHardy if (ctinfo != IP_CT_ESTABLISHED && 1383f5b321bdSPatrick McHardy ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) 1384f5b321bdSPatrick McHardy return NF_ACCEPT; 1385f5b321bdSPatrick McHardy 1386f5b321bdSPatrick McHardy /* No Data ? */ 1387f5b321bdSPatrick McHardy th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph); 1388f5b321bdSPatrick McHardy if (th == NULL) 1389f5b321bdSPatrick McHardy return NF_ACCEPT; 1390f5b321bdSPatrick McHardy dataoff = protoff + th->doff * 4; 1391f5b321bdSPatrick McHardy if (dataoff >= skb->len) 1392f5b321bdSPatrick McHardy return NF_ACCEPT; 1393f5b321bdSPatrick McHardy 1394f5b321bdSPatrick McHardy nf_ct_refresh(ct, skb, sip_timeout * HZ); 1395f5b321bdSPatrick McHardy 1396f5b321bdSPatrick McHardy if (skb_is_nonlinear(skb)) { 1397f5b321bdSPatrick McHardy pr_debug("Copy of skbuff not supported yet.\n"); 1398f5b321bdSPatrick McHardy return NF_ACCEPT; 1399f5b321bdSPatrick McHardy } 1400f5b321bdSPatrick McHardy 1401f5b321bdSPatrick McHardy dptr = skb->data + dataoff; 1402f5b321bdSPatrick McHardy datalen = skb->len - dataoff; 1403f5b321bdSPatrick McHardy if (datalen < strlen("SIP/2.0 200")) 1404f5b321bdSPatrick McHardy return NF_ACCEPT; 1405f5b321bdSPatrick McHardy 1406f5b321bdSPatrick McHardy while (1) { 1407f5b321bdSPatrick McHardy if (ct_sip_get_header(ct, dptr, 0, datalen, 1408f5b321bdSPatrick McHardy SIP_HDR_CONTENT_LENGTH, 1409f5b321bdSPatrick McHardy &matchoff, &matchlen) <= 0) 1410f5b321bdSPatrick McHardy break; 1411f5b321bdSPatrick McHardy 1412f5b321bdSPatrick McHardy clen = simple_strtoul(dptr + matchoff, (char **)&end, 10); 1413f5b321bdSPatrick McHardy if (dptr + matchoff == end) 1414f5b321bdSPatrick McHardy break; 1415f5b321bdSPatrick McHardy 1416f5b321bdSPatrick McHardy if (end + strlen("\r\n\r\n") > dptr + datalen) 1417f5b321bdSPatrick McHardy break; 1418f5b321bdSPatrick McHardy if (end[0] != '\r' || end[1] != '\n' || 1419f5b321bdSPatrick McHardy end[2] != '\r' || end[3] != '\n') 1420f5b321bdSPatrick McHardy break; 1421f5b321bdSPatrick McHardy end += strlen("\r\n\r\n") + clen; 1422f5b321bdSPatrick McHardy 1423f5b321bdSPatrick McHardy msglen = origlen = end - dptr; 1424f5b321bdSPatrick McHardy 1425f5b321bdSPatrick McHardy ret = process_sip_msg(skb, ct, dataoff, &dptr, &msglen); 1426f5b321bdSPatrick McHardy if (ret != NF_ACCEPT) 1427f5b321bdSPatrick McHardy break; 1428f5b321bdSPatrick McHardy diff = msglen - origlen; 1429f5b321bdSPatrick McHardy tdiff += diff; 1430f5b321bdSPatrick McHardy 1431f5b321bdSPatrick McHardy dataoff += msglen; 1432f5b321bdSPatrick McHardy dptr += msglen; 1433f5b321bdSPatrick McHardy datalen = datalen + diff - msglen; 1434f5b321bdSPatrick McHardy } 1435f5b321bdSPatrick McHardy 143648f8ac26SPatrick McHardy if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) { 143748f8ac26SPatrick McHardy nf_nat_sip_seq_adjust = rcu_dereference(nf_nat_sip_seq_adjust_hook); 143848f8ac26SPatrick McHardy if (nf_nat_sip_seq_adjust) 143948f8ac26SPatrick McHardy nf_nat_sip_seq_adjust(skb, tdiff); 144048f8ac26SPatrick McHardy } 144148f8ac26SPatrick McHardy 1442f5b321bdSPatrick McHardy return ret; 1443f5b321bdSPatrick McHardy } 1444f5b321bdSPatrick McHardy 1445f5b321bdSPatrick McHardy static int sip_help_udp(struct sk_buff *skb, unsigned int protoff, 1446f5b321bdSPatrick McHardy struct nf_conn *ct, enum ip_conntrack_info ctinfo) 14479fafcd7bSPatrick McHardy { 14489fafcd7bSPatrick McHardy unsigned int dataoff, datalen; 14499fafcd7bSPatrick McHardy const char *dptr; 14509fafcd7bSPatrick McHardy 14519fafcd7bSPatrick McHardy /* No Data ? */ 14529fafcd7bSPatrick McHardy dataoff = protoff + sizeof(struct udphdr); 14533db05feaSHerbert Xu if (dataoff >= skb->len) 14549fafcd7bSPatrick McHardy return NF_ACCEPT; 14559fafcd7bSPatrick McHardy 14563db05feaSHerbert Xu nf_ct_refresh(ct, skb, sip_timeout * HZ); 14579fafcd7bSPatrick McHardy 1458f5b321bdSPatrick McHardy if (skb_is_nonlinear(skb)) { 14590d53778eSPatrick McHardy pr_debug("Copy of skbuff not supported yet.\n"); 14607d3dd043SPatrick McHardy return NF_ACCEPT; 14619fafcd7bSPatrick McHardy } 14629fafcd7bSPatrick McHardy 1463f5b321bdSPatrick McHardy dptr = skb->data + dataoff; 14643db05feaSHerbert Xu datalen = skb->len - dataoff; 1465779382ebSPatrick McHardy if (datalen < strlen("SIP/2.0 200")) 14667d3dd043SPatrick McHardy return NF_ACCEPT; 14679fafcd7bSPatrick McHardy 1468f5b321bdSPatrick McHardy return process_sip_msg(skb, ct, dataoff, &dptr, &datalen); 146933cb1e9aSPatrick McHardy } 147033cb1e9aSPatrick McHardy 1471f5b321bdSPatrick McHardy static struct nf_conntrack_helper sip[MAX_PORTS][4] __read_mostly; 1472f5b321bdSPatrick McHardy static char sip_names[MAX_PORTS][4][sizeof("sip-65535")] __read_mostly; 14739fafcd7bSPatrick McHardy 14740f32a40fSPatrick McHardy static const struct nf_conntrack_expect_policy sip_exp_policy[SIP_EXPECT_MAX + 1] = { 14750f32a40fSPatrick McHardy [SIP_EXPECT_SIGNALLING] = { 1476b87921bdSPatrick McHardy .name = "signalling", 14770f32a40fSPatrick McHardy .max_expected = 1, 14786002f266SPatrick McHardy .timeout = 3 * 60, 14790f32a40fSPatrick McHardy }, 14800f32a40fSPatrick McHardy [SIP_EXPECT_AUDIO] = { 1481b87921bdSPatrick McHardy .name = "audio", 1482a9c1d359SPatrick McHardy .max_expected = 2 * IP_CT_DIR_MAX, 14830f32a40fSPatrick McHardy .timeout = 3 * 60, 14840f32a40fSPatrick McHardy }, 14850d0ab037SPatrick McHardy [SIP_EXPECT_VIDEO] = { 1486b87921bdSPatrick McHardy .name = "video", 14870d0ab037SPatrick McHardy .max_expected = 2 * IP_CT_DIR_MAX, 14880d0ab037SPatrick McHardy .timeout = 3 * 60, 14890d0ab037SPatrick McHardy }, 14909d288dffSPatrick McHardy [SIP_EXPECT_IMAGE] = { 14919d288dffSPatrick McHardy .name = "image", 14929d288dffSPatrick McHardy .max_expected = IP_CT_DIR_MAX, 14939d288dffSPatrick McHardy .timeout = 3 * 60, 14949d288dffSPatrick McHardy }, 14956002f266SPatrick McHardy }; 14966002f266SPatrick McHardy 14979fafcd7bSPatrick McHardy static void nf_conntrack_sip_fini(void) 14989fafcd7bSPatrick McHardy { 14999fafcd7bSPatrick McHardy int i, j; 15009fafcd7bSPatrick McHardy 15019fafcd7bSPatrick McHardy for (i = 0; i < ports_c; i++) { 1502f5b321bdSPatrick McHardy for (j = 0; j < ARRAY_SIZE(sip[i]); j++) { 15039fafcd7bSPatrick McHardy if (sip[i][j].me == NULL) 15049fafcd7bSPatrick McHardy continue; 15059fafcd7bSPatrick McHardy nf_conntrack_helper_unregister(&sip[i][j]); 15069fafcd7bSPatrick McHardy } 15079fafcd7bSPatrick McHardy } 15089fafcd7bSPatrick McHardy } 15099fafcd7bSPatrick McHardy 15109fafcd7bSPatrick McHardy static int __init nf_conntrack_sip_init(void) 15119fafcd7bSPatrick McHardy { 15129fafcd7bSPatrick McHardy int i, j, ret; 15139fafcd7bSPatrick McHardy char *tmpname; 15149fafcd7bSPatrick McHardy 15159fafcd7bSPatrick McHardy if (ports_c == 0) 15169fafcd7bSPatrick McHardy ports[ports_c++] = SIP_PORT; 15179fafcd7bSPatrick McHardy 15189fafcd7bSPatrick McHardy for (i = 0; i < ports_c; i++) { 15199fafcd7bSPatrick McHardy memset(&sip[i], 0, sizeof(sip[i])); 15209fafcd7bSPatrick McHardy 15219fafcd7bSPatrick McHardy sip[i][0].tuple.src.l3num = AF_INET; 1522f5b321bdSPatrick McHardy sip[i][0].tuple.dst.protonum = IPPROTO_UDP; 1523f5b321bdSPatrick McHardy sip[i][0].help = sip_help_udp; 1524f5b321bdSPatrick McHardy sip[i][1].tuple.src.l3num = AF_INET; 1525f5b321bdSPatrick McHardy sip[i][1].tuple.dst.protonum = IPPROTO_TCP; 1526f5b321bdSPatrick McHardy sip[i][1].help = sip_help_tcp; 1527f5b321bdSPatrick McHardy 1528f5b321bdSPatrick McHardy sip[i][2].tuple.src.l3num = AF_INET6; 1529f5b321bdSPatrick McHardy sip[i][2].tuple.dst.protonum = IPPROTO_UDP; 1530f5b321bdSPatrick McHardy sip[i][2].help = sip_help_udp; 1531f5b321bdSPatrick McHardy sip[i][3].tuple.src.l3num = AF_INET6; 1532f5b321bdSPatrick McHardy sip[i][3].tuple.dst.protonum = IPPROTO_TCP; 1533f5b321bdSPatrick McHardy sip[i][3].help = sip_help_tcp; 1534f5b321bdSPatrick McHardy 1535f5b321bdSPatrick McHardy for (j = 0; j < ARRAY_SIZE(sip[i]); j++) { 15369fafcd7bSPatrick McHardy sip[i][j].tuple.src.u.udp.port = htons(ports[i]); 15370f32a40fSPatrick McHardy sip[i][j].expect_policy = sip_exp_policy; 15380f32a40fSPatrick McHardy sip[i][j].expect_class_max = SIP_EXPECT_MAX; 15399fafcd7bSPatrick McHardy sip[i][j].me = THIS_MODULE; 15409fafcd7bSPatrick McHardy 15419fafcd7bSPatrick McHardy tmpname = &sip_names[i][j][0]; 15429fafcd7bSPatrick McHardy if (ports[i] == SIP_PORT) 15439fafcd7bSPatrick McHardy sprintf(tmpname, "sip"); 15449fafcd7bSPatrick McHardy else 15459fafcd7bSPatrick McHardy sprintf(tmpname, "sip-%u", i); 15469fafcd7bSPatrick McHardy sip[i][j].name = tmpname; 15479fafcd7bSPatrick McHardy 15480d53778eSPatrick McHardy pr_debug("port #%u: %u\n", i, ports[i]); 15499fafcd7bSPatrick McHardy 15509fafcd7bSPatrick McHardy ret = nf_conntrack_helper_register(&sip[i][j]); 15519fafcd7bSPatrick McHardy if (ret) { 1552654d0fbdSStephen Hemminger printk(KERN_ERR "nf_ct_sip: failed to register" 1553654d0fbdSStephen Hemminger " helper for pf: %u port: %u\n", 15549fafcd7bSPatrick McHardy sip[i][j].tuple.src.l3num, ports[i]); 15559fafcd7bSPatrick McHardy nf_conntrack_sip_fini(); 15569fafcd7bSPatrick McHardy return ret; 15579fafcd7bSPatrick McHardy } 15589fafcd7bSPatrick McHardy } 15599fafcd7bSPatrick McHardy } 15609fafcd7bSPatrick McHardy return 0; 15619fafcd7bSPatrick McHardy } 15629fafcd7bSPatrick McHardy 15639fafcd7bSPatrick McHardy module_init(nf_conntrack_sip_init); 15649fafcd7bSPatrick McHardy module_exit(nf_conntrack_sip_fini); 1565