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 1555adbb9fbSSimon Horman if (!ct) 1565adbb9fbSSimon Horman return 0; 1575adbb9fbSSimon Horman 158fa913ddfSPatrick McHardy memset(addr, 0, sizeof(*addr)); 1595e8fbe2aSPatrick McHardy switch (nf_ct_l3num(ct)) { 1609fafcd7bSPatrick McHardy case AF_INET: 1619fafcd7bSPatrick McHardy ret = in4_pton(cp, limit - cp, (u8 *)&addr->ip, -1, &end); 1629fafcd7bSPatrick McHardy break; 1639fafcd7bSPatrick McHardy case AF_INET6: 1649fafcd7bSPatrick McHardy ret = in6_pton(cp, limit - cp, (u8 *)&addr->ip6, -1, &end); 1659fafcd7bSPatrick McHardy break; 1669fafcd7bSPatrick McHardy default: 1679fafcd7bSPatrick McHardy BUG(); 1689fafcd7bSPatrick McHardy } 1699fafcd7bSPatrick McHardy 1709fafcd7bSPatrick McHardy if (ret == 0 || end == cp) 1719fafcd7bSPatrick McHardy return 0; 1729fafcd7bSPatrick McHardy if (endp) 1739fafcd7bSPatrick McHardy *endp = end; 1749fafcd7bSPatrick McHardy return 1; 1759fafcd7bSPatrick McHardy } 1769fafcd7bSPatrick McHardy 1779fafcd7bSPatrick McHardy /* skip ip address. returns its length. */ 17813f7d63cSJan Engelhardt static int epaddr_len(const struct nf_conn *ct, const char *dptr, 1799fafcd7bSPatrick McHardy const char *limit, int *shift) 1809fafcd7bSPatrick McHardy { 181643a2c15SJan Engelhardt union nf_inet_addr addr; 1829fafcd7bSPatrick McHardy const char *aux = dptr; 1839fafcd7bSPatrick McHardy 1849fafcd7bSPatrick McHardy if (!parse_addr(ct, dptr, &dptr, &addr, limit)) { 1850d53778eSPatrick McHardy pr_debug("ip: %s parse failed.!\n", dptr); 1869fafcd7bSPatrick McHardy return 0; 1879fafcd7bSPatrick McHardy } 1889fafcd7bSPatrick McHardy 1899fafcd7bSPatrick McHardy /* Port number */ 1909fafcd7bSPatrick McHardy if (*dptr == ':') { 1919fafcd7bSPatrick McHardy dptr++; 1929fafcd7bSPatrick McHardy dptr += digits_len(ct, dptr, limit, shift); 1939fafcd7bSPatrick McHardy } 1949fafcd7bSPatrick McHardy return dptr - aux; 1959fafcd7bSPatrick McHardy } 1969fafcd7bSPatrick McHardy 1979fafcd7bSPatrick McHardy /* get address length, skiping user info. */ 19813f7d63cSJan Engelhardt static int skp_epaddr_len(const struct nf_conn *ct, const char *dptr, 1999fafcd7bSPatrick McHardy const char *limit, int *shift) 2009fafcd7bSPatrick McHardy { 201aa584edaSPatrick McHardy const char *start = dptr; 2029fafcd7bSPatrick McHardy int s = *shift; 2039fafcd7bSPatrick McHardy 2047da5bfbbSLars Immisch /* Search for @, but stop at the end of the line. 2057da5bfbbSLars Immisch * We are inside a sip: URI, so we don't need to worry about 2067da5bfbbSLars Immisch * continuation lines. */ 207b1ec488bSPatrick McHardy while (dptr < limit && 2087da5bfbbSLars Immisch *dptr != '@' && *dptr != '\r' && *dptr != '\n') { 2099fafcd7bSPatrick McHardy (*shift)++; 2107da5bfbbSLars Immisch dptr++; 2117da5bfbbSLars Immisch } 2129fafcd7bSPatrick McHardy 213b1ec488bSPatrick McHardy if (dptr < limit && *dptr == '@') { 2149fafcd7bSPatrick McHardy dptr++; 2159fafcd7bSPatrick McHardy (*shift)++; 216aa584edaSPatrick McHardy } else { 217aa584edaSPatrick McHardy dptr = start; 2189fafcd7bSPatrick McHardy *shift = s; 219aa584edaSPatrick McHardy } 2209fafcd7bSPatrick McHardy 2219fafcd7bSPatrick McHardy return epaddr_len(ct, dptr, limit, shift); 2229fafcd7bSPatrick McHardy } 2239fafcd7bSPatrick McHardy 224ac367740SPatrick McHardy /* Parse a SIP request line of the form: 225ac367740SPatrick McHardy * 226ac367740SPatrick McHardy * Request-Line = Method SP Request-URI SP SIP-Version CRLF 227ac367740SPatrick McHardy * 228ac367740SPatrick McHardy * and return the offset and length of the address contained in the Request-URI. 229ac367740SPatrick McHardy */ 230ac367740SPatrick McHardy int ct_sip_parse_request(const struct nf_conn *ct, 231ac367740SPatrick McHardy const char *dptr, unsigned int datalen, 232624f8b7bSPatrick McHardy unsigned int *matchoff, unsigned int *matchlen, 233624f8b7bSPatrick McHardy union nf_inet_addr *addr, __be16 *port) 234ac367740SPatrick McHardy { 235624f8b7bSPatrick McHardy const char *start = dptr, *limit = dptr + datalen, *end; 236ac367740SPatrick McHardy unsigned int mlen; 237624f8b7bSPatrick McHardy unsigned int p; 238ac367740SPatrick McHardy int shift = 0; 239ac367740SPatrick McHardy 240ac367740SPatrick McHardy /* Skip method and following whitespace */ 241ac367740SPatrick McHardy mlen = string_len(ct, dptr, limit, NULL); 242ac367740SPatrick McHardy if (!mlen) 243ac367740SPatrick McHardy return 0; 244ac367740SPatrick McHardy dptr += mlen; 245ac367740SPatrick McHardy if (++dptr >= limit) 246ac367740SPatrick McHardy return 0; 247ac367740SPatrick McHardy 248ac367740SPatrick McHardy /* Find SIP URI */ 24954101f4fSPatrick McHardy for (; dptr < limit - strlen("sip:"); dptr++) { 250ac367740SPatrick McHardy if (*dptr == '\r' || *dptr == '\n') 251ac367740SPatrick McHardy return -1; 25254101f4fSPatrick McHardy if (strnicmp(dptr, "sip:", strlen("sip:")) == 0) { 25354101f4fSPatrick McHardy dptr += strlen("sip:"); 254ac367740SPatrick McHardy break; 255ac367740SPatrick McHardy } 25654101f4fSPatrick McHardy } 257624f8b7bSPatrick McHardy if (!skp_epaddr_len(ct, dptr, limit, &shift)) 258ac367740SPatrick McHardy return 0; 259624f8b7bSPatrick McHardy dptr += shift; 260624f8b7bSPatrick McHardy 261624f8b7bSPatrick McHardy if (!parse_addr(ct, dptr, &end, addr, limit)) 262624f8b7bSPatrick McHardy return -1; 263624f8b7bSPatrick McHardy if (end < limit && *end == ':') { 264624f8b7bSPatrick McHardy end++; 265624f8b7bSPatrick McHardy p = simple_strtoul(end, (char **)&end, 10); 266624f8b7bSPatrick McHardy if (p < 1024 || p > 65535) 267624f8b7bSPatrick McHardy return -1; 268624f8b7bSPatrick McHardy *port = htons(p); 269624f8b7bSPatrick McHardy } else 270624f8b7bSPatrick McHardy *port = htons(SIP_PORT); 271624f8b7bSPatrick McHardy 272624f8b7bSPatrick McHardy if (end == dptr) 273624f8b7bSPatrick McHardy return 0; 274624f8b7bSPatrick McHardy *matchoff = dptr - start; 275624f8b7bSPatrick McHardy *matchlen = end - dptr; 276ac367740SPatrick McHardy return 1; 277ac367740SPatrick McHardy } 278ac367740SPatrick McHardy EXPORT_SYMBOL_GPL(ct_sip_parse_request); 279ac367740SPatrick McHardy 280ea45f12aSPatrick McHardy /* SIP header parsing: SIP headers are located at the beginning of a line, but 281ea45f12aSPatrick McHardy * may span several lines, in which case the continuation lines begin with a 282ea45f12aSPatrick McHardy * whitespace character. RFC 2543 allows lines to be terminated with CR, LF or 283ea45f12aSPatrick McHardy * CRLF, RFC 3261 allows only CRLF, we support both. 284ea45f12aSPatrick McHardy * 285ea45f12aSPatrick McHardy * Headers are followed by (optionally) whitespace, a colon, again (optionally) 286ea45f12aSPatrick McHardy * whitespace and the values. Whitespace in this context means any amount of 287ea45f12aSPatrick McHardy * tabs, spaces and continuation lines, which are treated as a single whitespace 288ea45f12aSPatrick McHardy * character. 28905e3ced2SPatrick McHardy * 2903ad2f3fbSDaniel Mack * Some headers may appear multiple times. A comma separated list of values is 29105e3ced2SPatrick McHardy * equivalent to multiple headers. 292ea45f12aSPatrick McHardy */ 293ea45f12aSPatrick McHardy static const struct sip_header ct_sip_hdrs[] = { 29430f33e6dSPatrick McHardy [SIP_HDR_CSEQ] = SIP_HDR("CSeq", NULL, NULL, digits_len), 295ea45f12aSPatrick McHardy [SIP_HDR_FROM] = SIP_HDR("From", "f", "sip:", skp_epaddr_len), 296ea45f12aSPatrick McHardy [SIP_HDR_TO] = SIP_HDR("To", "t", "sip:", skp_epaddr_len), 297ea45f12aSPatrick McHardy [SIP_HDR_CONTACT] = SIP_HDR("Contact", "m", "sip:", skp_epaddr_len), 298f5b321bdSPatrick McHardy [SIP_HDR_VIA_UDP] = SIP_HDR("Via", "v", "UDP ", epaddr_len), 299f5b321bdSPatrick McHardy [SIP_HDR_VIA_TCP] = SIP_HDR("Via", "v", "TCP ", epaddr_len), 3000f32a40fSPatrick McHardy [SIP_HDR_EXPIRES] = SIP_HDR("Expires", NULL, NULL, digits_len), 301ea45f12aSPatrick McHardy [SIP_HDR_CONTENT_LENGTH] = SIP_HDR("Content-Length", "l", NULL, digits_len), 302ea45f12aSPatrick McHardy }; 303ea45f12aSPatrick McHardy 304ea45f12aSPatrick McHardy static const char *sip_follow_continuation(const char *dptr, const char *limit) 3059fafcd7bSPatrick McHardy { 306ea45f12aSPatrick McHardy /* Walk past newline */ 307ea45f12aSPatrick McHardy if (++dptr >= limit) 308ea45f12aSPatrick McHardy return NULL; 3099fafcd7bSPatrick McHardy 310ea45f12aSPatrick McHardy /* Skip '\n' in CR LF */ 311ea45f12aSPatrick McHardy if (*(dptr - 1) == '\r' && *dptr == '\n') { 312ea45f12aSPatrick McHardy if (++dptr >= limit) 313ea45f12aSPatrick McHardy return NULL; 314ea45f12aSPatrick McHardy } 3159fafcd7bSPatrick McHardy 316ea45f12aSPatrick McHardy /* Continuation line? */ 317ea45f12aSPatrick McHardy if (*dptr != ' ' && *dptr != '\t') 318ea45f12aSPatrick McHardy return NULL; 319ea45f12aSPatrick McHardy 320ea45f12aSPatrick McHardy /* skip leading whitespace */ 321ea45f12aSPatrick McHardy for (; dptr < limit; dptr++) { 322ea45f12aSPatrick McHardy if (*dptr != ' ' && *dptr != '\t') 323ea45f12aSPatrick McHardy break; 324ea45f12aSPatrick McHardy } 325ea45f12aSPatrick McHardy return dptr; 326ea45f12aSPatrick McHardy } 327ea45f12aSPatrick McHardy 328ea45f12aSPatrick McHardy static const char *sip_skip_whitespace(const char *dptr, const char *limit) 329ea45f12aSPatrick McHardy { 330ea45f12aSPatrick McHardy for (; dptr < limit; dptr++) { 331ea45f12aSPatrick McHardy if (*dptr == ' ') 332ea45f12aSPatrick McHardy continue; 333ea45f12aSPatrick McHardy if (*dptr != '\r' && *dptr != '\n') 334ea45f12aSPatrick McHardy break; 335ea45f12aSPatrick McHardy dptr = sip_follow_continuation(dptr, limit); 336ea45f12aSPatrick McHardy if (dptr == NULL) 337ea45f12aSPatrick McHardy return NULL; 338ea45f12aSPatrick McHardy } 339ea45f12aSPatrick McHardy return dptr; 340ea45f12aSPatrick McHardy } 341ea45f12aSPatrick McHardy 342ea45f12aSPatrick McHardy /* Search within a SIP header value, dealing with continuation lines */ 343ea45f12aSPatrick McHardy static const char *ct_sip_header_search(const char *dptr, const char *limit, 344ea45f12aSPatrick McHardy const char *needle, unsigned int len) 345ea45f12aSPatrick McHardy { 346ea45f12aSPatrick McHardy for (limit -= len; dptr < limit; dptr++) { 347ea45f12aSPatrick McHardy if (*dptr == '\r' || *dptr == '\n') { 348ea45f12aSPatrick McHardy dptr = sip_follow_continuation(dptr, limit); 349ea45f12aSPatrick McHardy if (dptr == NULL) 350ea45f12aSPatrick McHardy break; 3519fafcd7bSPatrick McHardy continue; 3529fafcd7bSPatrick McHardy } 3539fafcd7bSPatrick McHardy 354ea45f12aSPatrick McHardy if (strnicmp(dptr, needle, len) == 0) 355ea45f12aSPatrick McHardy return dptr; 356ea45f12aSPatrick McHardy } 357ea45f12aSPatrick McHardy return NULL; 358ea45f12aSPatrick McHardy } 359ea45f12aSPatrick McHardy 360ea45f12aSPatrick McHardy int ct_sip_get_header(const struct nf_conn *ct, const char *dptr, 361ea45f12aSPatrick McHardy unsigned int dataoff, unsigned int datalen, 362ea45f12aSPatrick McHardy enum sip_header_types type, 363ea45f12aSPatrick McHardy unsigned int *matchoff, unsigned int *matchlen) 364ea45f12aSPatrick McHardy { 365ea45f12aSPatrick McHardy const struct sip_header *hdr = &ct_sip_hdrs[type]; 366ea45f12aSPatrick McHardy const char *start = dptr, *limit = dptr + datalen; 367ea45f12aSPatrick McHardy int shift = 0; 368ea45f12aSPatrick McHardy 369ea45f12aSPatrick McHardy for (dptr += dataoff; dptr < limit; dptr++) { 370ea45f12aSPatrick McHardy /* Find beginning of line */ 371ea45f12aSPatrick McHardy if (*dptr != '\r' && *dptr != '\n') 372ea45f12aSPatrick McHardy continue; 373ea45f12aSPatrick McHardy if (++dptr >= limit) 374ea45f12aSPatrick McHardy break; 375ea45f12aSPatrick McHardy if (*(dptr - 1) == '\r' && *dptr == '\n') { 376ea45f12aSPatrick McHardy if (++dptr >= limit) 377ea45f12aSPatrick McHardy break; 378ea45f12aSPatrick McHardy } 379ea45f12aSPatrick McHardy 380ea45f12aSPatrick McHardy /* Skip continuation lines */ 381ea45f12aSPatrick McHardy if (*dptr == ' ' || *dptr == '\t') 382ea45f12aSPatrick McHardy continue; 383ea45f12aSPatrick McHardy 384ea45f12aSPatrick McHardy /* Find header. Compact headers must be followed by a 385ea45f12aSPatrick McHardy * non-alphabetic character to avoid mismatches. */ 386ea45f12aSPatrick McHardy if (limit - dptr >= hdr->len && 387ea45f12aSPatrick McHardy strnicmp(dptr, hdr->name, hdr->len) == 0) 388ea45f12aSPatrick McHardy dptr += hdr->len; 389ea45f12aSPatrick McHardy else if (hdr->cname && limit - dptr >= hdr->clen + 1 && 390ea45f12aSPatrick McHardy strnicmp(dptr, hdr->cname, hdr->clen) == 0 && 391135d0189SPatrick McHardy !isalpha(*(dptr + hdr->clen))) 392ea45f12aSPatrick McHardy dptr += hdr->clen; 393ea45f12aSPatrick McHardy else 394ea45f12aSPatrick McHardy continue; 395ea45f12aSPatrick McHardy 396ea45f12aSPatrick McHardy /* Find and skip colon */ 397ea45f12aSPatrick McHardy dptr = sip_skip_whitespace(dptr, limit); 398ea45f12aSPatrick McHardy if (dptr == NULL) 399ea45f12aSPatrick McHardy break; 400ea45f12aSPatrick McHardy if (*dptr != ':' || ++dptr >= limit) 401ea45f12aSPatrick McHardy break; 402ea45f12aSPatrick McHardy 403ea45f12aSPatrick McHardy /* Skip whitespace after colon */ 404ea45f12aSPatrick McHardy dptr = sip_skip_whitespace(dptr, limit); 405ea45f12aSPatrick McHardy if (dptr == NULL) 406ea45f12aSPatrick McHardy break; 407ea45f12aSPatrick McHardy 408ea45f12aSPatrick McHardy *matchoff = dptr - start; 409ea45f12aSPatrick McHardy if (hdr->search) { 410ea45f12aSPatrick McHardy dptr = ct_sip_header_search(dptr, limit, hdr->search, 411ea45f12aSPatrick McHardy hdr->slen); 412ea45f12aSPatrick McHardy if (!dptr) 413ea45f12aSPatrick McHardy return -1; 414ea45f12aSPatrick McHardy dptr += hdr->slen; 415ea45f12aSPatrick McHardy } 416ea45f12aSPatrick McHardy 417ea45f12aSPatrick McHardy *matchlen = hdr->match_len(ct, dptr, limit, &shift); 4189fafcd7bSPatrick McHardy if (!*matchlen) 4199fafcd7bSPatrick McHardy return -1; 420ea45f12aSPatrick McHardy *matchoff = dptr - start + shift; 4219fafcd7bSPatrick McHardy return 1; 4229fafcd7bSPatrick McHardy } 4239fafcd7bSPatrick McHardy return 0; 4249fafcd7bSPatrick McHardy } 425ea45f12aSPatrick McHardy EXPORT_SYMBOL_GPL(ct_sip_get_header); 4269fafcd7bSPatrick McHardy 4273ad2f3fbSDaniel Mack /* Get next header field in a list of comma separated values */ 42805e3ced2SPatrick McHardy static int ct_sip_next_header(const struct nf_conn *ct, const char *dptr, 42905e3ced2SPatrick McHardy unsigned int dataoff, unsigned int datalen, 43005e3ced2SPatrick McHardy enum sip_header_types type, 43105e3ced2SPatrick McHardy unsigned int *matchoff, unsigned int *matchlen) 43205e3ced2SPatrick McHardy { 43305e3ced2SPatrick McHardy const struct sip_header *hdr = &ct_sip_hdrs[type]; 43405e3ced2SPatrick McHardy const char *start = dptr, *limit = dptr + datalen; 43505e3ced2SPatrick McHardy int shift = 0; 43605e3ced2SPatrick McHardy 43705e3ced2SPatrick McHardy dptr += dataoff; 43805e3ced2SPatrick McHardy 43905e3ced2SPatrick McHardy dptr = ct_sip_header_search(dptr, limit, ",", strlen(",")); 44005e3ced2SPatrick McHardy if (!dptr) 44105e3ced2SPatrick McHardy return 0; 44205e3ced2SPatrick McHardy 44305e3ced2SPatrick McHardy dptr = ct_sip_header_search(dptr, limit, hdr->search, hdr->slen); 44405e3ced2SPatrick McHardy if (!dptr) 44505e3ced2SPatrick McHardy return 0; 44605e3ced2SPatrick McHardy dptr += hdr->slen; 44705e3ced2SPatrick McHardy 44805e3ced2SPatrick McHardy *matchoff = dptr - start; 44905e3ced2SPatrick McHardy *matchlen = hdr->match_len(ct, dptr, limit, &shift); 45005e3ced2SPatrick McHardy if (!*matchlen) 45105e3ced2SPatrick McHardy return -1; 45205e3ced2SPatrick McHardy *matchoff += shift; 45305e3ced2SPatrick McHardy return 1; 45405e3ced2SPatrick McHardy } 45505e3ced2SPatrick McHardy 45605e3ced2SPatrick McHardy /* Walk through headers until a parsable one is found or no header of the 45705e3ced2SPatrick McHardy * given type is left. */ 45805e3ced2SPatrick McHardy static int ct_sip_walk_headers(const struct nf_conn *ct, const char *dptr, 45905e3ced2SPatrick McHardy unsigned int dataoff, unsigned int datalen, 46005e3ced2SPatrick McHardy enum sip_header_types type, int *in_header, 46105e3ced2SPatrick McHardy unsigned int *matchoff, unsigned int *matchlen) 46205e3ced2SPatrick McHardy { 46305e3ced2SPatrick McHardy int ret; 46405e3ced2SPatrick McHardy 46505e3ced2SPatrick McHardy if (in_header && *in_header) { 46605e3ced2SPatrick McHardy while (1) { 46705e3ced2SPatrick McHardy ret = ct_sip_next_header(ct, dptr, dataoff, datalen, 46805e3ced2SPatrick McHardy type, matchoff, matchlen); 46905e3ced2SPatrick McHardy if (ret > 0) 47005e3ced2SPatrick McHardy return ret; 47105e3ced2SPatrick McHardy if (ret == 0) 47205e3ced2SPatrick McHardy break; 47305e3ced2SPatrick McHardy dataoff += *matchoff; 47405e3ced2SPatrick McHardy } 47505e3ced2SPatrick McHardy *in_header = 0; 47605e3ced2SPatrick McHardy } 47705e3ced2SPatrick McHardy 47805e3ced2SPatrick McHardy while (1) { 47905e3ced2SPatrick McHardy ret = ct_sip_get_header(ct, dptr, dataoff, datalen, 48005e3ced2SPatrick McHardy type, matchoff, matchlen); 48105e3ced2SPatrick McHardy if (ret > 0) 48205e3ced2SPatrick McHardy break; 48305e3ced2SPatrick McHardy if (ret == 0) 48405e3ced2SPatrick McHardy return ret; 48505e3ced2SPatrick McHardy dataoff += *matchoff; 48605e3ced2SPatrick McHardy } 48705e3ced2SPatrick McHardy 48805e3ced2SPatrick McHardy if (in_header) 48905e3ced2SPatrick McHardy *in_header = 1; 49005e3ced2SPatrick McHardy return 1; 49105e3ced2SPatrick McHardy } 49205e3ced2SPatrick McHardy 49305e3ced2SPatrick McHardy /* Locate a SIP header, parse the URI and return the offset and length of 49405e3ced2SPatrick McHardy * the address as well as the address and port themselves. A stream of 49505e3ced2SPatrick McHardy * headers can be parsed by handing in a non-NULL datalen and in_header 49605e3ced2SPatrick McHardy * pointer. 49705e3ced2SPatrick McHardy */ 49805e3ced2SPatrick McHardy int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr, 49905e3ced2SPatrick McHardy unsigned int *dataoff, unsigned int datalen, 50005e3ced2SPatrick McHardy enum sip_header_types type, int *in_header, 50105e3ced2SPatrick McHardy unsigned int *matchoff, unsigned int *matchlen, 50205e3ced2SPatrick McHardy union nf_inet_addr *addr, __be16 *port) 50305e3ced2SPatrick McHardy { 50405e3ced2SPatrick McHardy const char *c, *limit = dptr + datalen; 50505e3ced2SPatrick McHardy unsigned int p; 50605e3ced2SPatrick McHardy int ret; 50705e3ced2SPatrick McHardy 50805e3ced2SPatrick McHardy ret = ct_sip_walk_headers(ct, dptr, dataoff ? *dataoff : 0, datalen, 50905e3ced2SPatrick McHardy type, in_header, matchoff, matchlen); 51005e3ced2SPatrick McHardy WARN_ON(ret < 0); 51105e3ced2SPatrick McHardy if (ret == 0) 51205e3ced2SPatrick McHardy return ret; 51305e3ced2SPatrick McHardy 51405e3ced2SPatrick McHardy if (!parse_addr(ct, dptr + *matchoff, &c, addr, limit)) 51505e3ced2SPatrick McHardy return -1; 51605e3ced2SPatrick McHardy if (*c == ':') { 51705e3ced2SPatrick McHardy c++; 51805e3ced2SPatrick McHardy p = simple_strtoul(c, (char **)&c, 10); 51905e3ced2SPatrick McHardy if (p < 1024 || p > 65535) 52005e3ced2SPatrick McHardy return -1; 52105e3ced2SPatrick McHardy *port = htons(p); 52205e3ced2SPatrick McHardy } else 52305e3ced2SPatrick McHardy *port = htons(SIP_PORT); 52405e3ced2SPatrick McHardy 52505e3ced2SPatrick McHardy if (dataoff) 52605e3ced2SPatrick McHardy *dataoff = c - dptr; 52705e3ced2SPatrick McHardy return 1; 52805e3ced2SPatrick McHardy } 52905e3ced2SPatrick McHardy EXPORT_SYMBOL_GPL(ct_sip_parse_header_uri); 53005e3ced2SPatrick McHardy 531f5b321bdSPatrick McHardy static int ct_sip_parse_param(const struct nf_conn *ct, const char *dptr, 532f5b321bdSPatrick McHardy unsigned int dataoff, unsigned int datalen, 533f5b321bdSPatrick McHardy const char *name, 534f5b321bdSPatrick McHardy unsigned int *matchoff, unsigned int *matchlen) 535f5b321bdSPatrick McHardy { 536f5b321bdSPatrick McHardy const char *limit = dptr + datalen; 537f5b321bdSPatrick McHardy const char *start; 538f5b321bdSPatrick McHardy const char *end; 539f5b321bdSPatrick McHardy 540f5b321bdSPatrick McHardy limit = ct_sip_header_search(dptr + dataoff, limit, ",", strlen(",")); 541f5b321bdSPatrick McHardy if (!limit) 542f5b321bdSPatrick McHardy limit = dptr + datalen; 543f5b321bdSPatrick McHardy 544f5b321bdSPatrick McHardy start = ct_sip_header_search(dptr + dataoff, limit, name, strlen(name)); 545f5b321bdSPatrick McHardy if (!start) 546f5b321bdSPatrick McHardy return 0; 547f5b321bdSPatrick McHardy start += strlen(name); 548f5b321bdSPatrick McHardy 549f5b321bdSPatrick McHardy end = ct_sip_header_search(start, limit, ";", strlen(";")); 550f5b321bdSPatrick McHardy if (!end) 551f5b321bdSPatrick McHardy end = limit; 552f5b321bdSPatrick McHardy 553f5b321bdSPatrick McHardy *matchoff = start - dptr; 554f5b321bdSPatrick McHardy *matchlen = end - start; 555f5b321bdSPatrick McHardy return 1; 556f5b321bdSPatrick McHardy } 557f5b321bdSPatrick McHardy 5582bbb2116SPatrick McHardy /* Parse address from header parameter and return address, offset and length */ 5592bbb2116SPatrick McHardy int ct_sip_parse_address_param(const struct nf_conn *ct, const char *dptr, 5602bbb2116SPatrick McHardy unsigned int dataoff, unsigned int datalen, 5612bbb2116SPatrick McHardy const char *name, 5622bbb2116SPatrick McHardy unsigned int *matchoff, unsigned int *matchlen, 5632bbb2116SPatrick McHardy union nf_inet_addr *addr) 5642bbb2116SPatrick McHardy { 5652bbb2116SPatrick McHardy const char *limit = dptr + datalen; 5662bbb2116SPatrick McHardy const char *start, *end; 5672bbb2116SPatrick McHardy 5682bbb2116SPatrick McHardy limit = ct_sip_header_search(dptr + dataoff, limit, ",", strlen(",")); 5692bbb2116SPatrick McHardy if (!limit) 5702bbb2116SPatrick McHardy limit = dptr + datalen; 5712bbb2116SPatrick McHardy 5722bbb2116SPatrick McHardy start = ct_sip_header_search(dptr + dataoff, limit, name, strlen(name)); 5732bbb2116SPatrick McHardy if (!start) 5742bbb2116SPatrick McHardy return 0; 5752bbb2116SPatrick McHardy 5762bbb2116SPatrick McHardy start += strlen(name); 5772bbb2116SPatrick McHardy if (!parse_addr(ct, start, &end, addr, limit)) 5782bbb2116SPatrick McHardy return 0; 5792bbb2116SPatrick McHardy *matchoff = start - dptr; 5802bbb2116SPatrick McHardy *matchlen = end - start; 5812bbb2116SPatrick McHardy return 1; 5822bbb2116SPatrick McHardy } 5832bbb2116SPatrick McHardy EXPORT_SYMBOL_GPL(ct_sip_parse_address_param); 5842bbb2116SPatrick McHardy 5852bbb2116SPatrick McHardy /* Parse numerical header parameter and return value, offset and length */ 5862bbb2116SPatrick McHardy int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr, 5872bbb2116SPatrick McHardy unsigned int dataoff, unsigned int datalen, 5882bbb2116SPatrick McHardy const char *name, 5892bbb2116SPatrick McHardy unsigned int *matchoff, unsigned int *matchlen, 5902bbb2116SPatrick McHardy unsigned int *val) 5912bbb2116SPatrick McHardy { 5922bbb2116SPatrick McHardy const char *limit = dptr + datalen; 5932bbb2116SPatrick McHardy const char *start; 5942bbb2116SPatrick McHardy char *end; 5952bbb2116SPatrick McHardy 5962bbb2116SPatrick McHardy limit = ct_sip_header_search(dptr + dataoff, limit, ",", strlen(",")); 5972bbb2116SPatrick McHardy if (!limit) 5982bbb2116SPatrick McHardy limit = dptr + datalen; 5992bbb2116SPatrick McHardy 6002bbb2116SPatrick McHardy start = ct_sip_header_search(dptr + dataoff, limit, name, strlen(name)); 6012bbb2116SPatrick McHardy if (!start) 6022bbb2116SPatrick McHardy return 0; 6032bbb2116SPatrick McHardy 6042bbb2116SPatrick McHardy start += strlen(name); 6052bbb2116SPatrick McHardy *val = simple_strtoul(start, &end, 0); 6062bbb2116SPatrick McHardy if (start == end) 6072bbb2116SPatrick McHardy return 0; 6082bbb2116SPatrick McHardy if (matchoff && matchlen) { 6092bbb2116SPatrick McHardy *matchoff = start - dptr; 6102bbb2116SPatrick McHardy *matchlen = end - start; 6112bbb2116SPatrick McHardy } 6122bbb2116SPatrick McHardy return 1; 6132bbb2116SPatrick McHardy } 6142bbb2116SPatrick McHardy EXPORT_SYMBOL_GPL(ct_sip_parse_numerical_param); 6152bbb2116SPatrick McHardy 616f5b321bdSPatrick McHardy static int ct_sip_parse_transport(struct nf_conn *ct, const char *dptr, 617f5b321bdSPatrick McHardy unsigned int dataoff, unsigned int datalen, 618f5b321bdSPatrick McHardy u8 *proto) 619f5b321bdSPatrick McHardy { 620f5b321bdSPatrick McHardy unsigned int matchoff, matchlen; 621f5b321bdSPatrick McHardy 622f5b321bdSPatrick McHardy if (ct_sip_parse_param(ct, dptr, dataoff, datalen, "transport=", 623f5b321bdSPatrick McHardy &matchoff, &matchlen)) { 624f5b321bdSPatrick McHardy if (!strnicmp(dptr + matchoff, "TCP", strlen("TCP"))) 625f5b321bdSPatrick McHardy *proto = IPPROTO_TCP; 626f5b321bdSPatrick McHardy else if (!strnicmp(dptr + matchoff, "UDP", strlen("UDP"))) 627f5b321bdSPatrick McHardy *proto = IPPROTO_UDP; 628f5b321bdSPatrick McHardy else 629f5b321bdSPatrick McHardy return 0; 630f5b321bdSPatrick McHardy 631f5b321bdSPatrick McHardy if (*proto != nf_ct_protonum(ct)) 632f5b321bdSPatrick McHardy return 0; 633f5b321bdSPatrick McHardy } else 634f5b321bdSPatrick McHardy *proto = nf_ct_protonum(ct); 635f5b321bdSPatrick McHardy 636f5b321bdSPatrick McHardy return 1; 637f5b321bdSPatrick McHardy } 638f5b321bdSPatrick McHardy 6393e9b4600SPatrick McHardy /* SDP header parsing: a SDP session description contains an ordered set of 6403e9b4600SPatrick McHardy * headers, starting with a section containing general session parameters, 6413e9b4600SPatrick McHardy * optionally followed by multiple media descriptions. 6423e9b4600SPatrick McHardy * 6433e9b4600SPatrick McHardy * SDP headers always start at the beginning of a line. According to RFC 2327: 6443e9b4600SPatrick McHardy * "The sequence CRLF (0x0d0a) is used to end a record, although parsers should 6453e9b4600SPatrick McHardy * be tolerant and also accept records terminated with a single newline 6463e9b4600SPatrick McHardy * character". We handle both cases. 6473e9b4600SPatrick McHardy */ 6483e9b4600SPatrick McHardy static const struct sip_header ct_sdp_hdrs[] = { 6493e9b4600SPatrick McHardy [SDP_HDR_VERSION] = SDP_HDR("v=", NULL, digits_len), 6503e9b4600SPatrick McHardy [SDP_HDR_OWNER_IP4] = SDP_HDR("o=", "IN IP4 ", epaddr_len), 6513e9b4600SPatrick McHardy [SDP_HDR_CONNECTION_IP4] = SDP_HDR("c=", "IN IP4 ", epaddr_len), 6523e9b4600SPatrick McHardy [SDP_HDR_OWNER_IP6] = SDP_HDR("o=", "IN IP6 ", epaddr_len), 6533e9b4600SPatrick McHardy [SDP_HDR_CONNECTION_IP6] = SDP_HDR("c=", "IN IP6 ", epaddr_len), 6540d0ab037SPatrick McHardy [SDP_HDR_MEDIA] = SDP_HDR("m=", NULL, media_len), 6553e9b4600SPatrick McHardy }; 6563e9b4600SPatrick McHardy 6573e9b4600SPatrick McHardy /* Linear string search within SDP header values */ 6583e9b4600SPatrick McHardy static const char *ct_sdp_header_search(const char *dptr, const char *limit, 6593e9b4600SPatrick McHardy const char *needle, unsigned int len) 6603e9b4600SPatrick McHardy { 6613e9b4600SPatrick McHardy for (limit -= len; dptr < limit; dptr++) { 6623e9b4600SPatrick McHardy if (*dptr == '\r' || *dptr == '\n') 6633e9b4600SPatrick McHardy break; 6643e9b4600SPatrick McHardy if (strncmp(dptr, needle, len) == 0) 6653e9b4600SPatrick McHardy return dptr; 6663e9b4600SPatrick McHardy } 6673e9b4600SPatrick McHardy return NULL; 6683e9b4600SPatrick McHardy } 6693e9b4600SPatrick McHardy 6703e9b4600SPatrick McHardy /* Locate a SDP header (optionally a substring within the header value), 6713e9b4600SPatrick McHardy * optionally stopping at the first occurence of the term header, parse 6723e9b4600SPatrick McHardy * it and return the offset and length of the data we're interested in. 6733e9b4600SPatrick McHardy */ 6743e9b4600SPatrick McHardy int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr, 6753e9b4600SPatrick McHardy unsigned int dataoff, unsigned int datalen, 6763e9b4600SPatrick McHardy enum sdp_header_types type, 6773e9b4600SPatrick McHardy enum sdp_header_types term, 6783e9b4600SPatrick McHardy unsigned int *matchoff, unsigned int *matchlen) 6793e9b4600SPatrick McHardy { 6803e9b4600SPatrick McHardy const struct sip_header *hdr = &ct_sdp_hdrs[type]; 6813e9b4600SPatrick McHardy const struct sip_header *thdr = &ct_sdp_hdrs[term]; 6823e9b4600SPatrick McHardy const char *start = dptr, *limit = dptr + datalen; 6833e9b4600SPatrick McHardy int shift = 0; 6843e9b4600SPatrick McHardy 6853e9b4600SPatrick McHardy for (dptr += dataoff; dptr < limit; dptr++) { 6863e9b4600SPatrick McHardy /* Find beginning of line */ 6873e9b4600SPatrick McHardy if (*dptr != '\r' && *dptr != '\n') 6883e9b4600SPatrick McHardy continue; 6893e9b4600SPatrick McHardy if (++dptr >= limit) 6903e9b4600SPatrick McHardy break; 6913e9b4600SPatrick McHardy if (*(dptr - 1) == '\r' && *dptr == '\n') { 6923e9b4600SPatrick McHardy if (++dptr >= limit) 6933e9b4600SPatrick McHardy break; 6943e9b4600SPatrick McHardy } 6953e9b4600SPatrick McHardy 6963e9b4600SPatrick McHardy if (term != SDP_HDR_UNSPEC && 6973e9b4600SPatrick McHardy limit - dptr >= thdr->len && 6983e9b4600SPatrick McHardy strnicmp(dptr, thdr->name, thdr->len) == 0) 6993e9b4600SPatrick McHardy break; 7003e9b4600SPatrick McHardy else if (limit - dptr >= hdr->len && 7013e9b4600SPatrick McHardy strnicmp(dptr, hdr->name, hdr->len) == 0) 7023e9b4600SPatrick McHardy dptr += hdr->len; 7033e9b4600SPatrick McHardy else 7043e9b4600SPatrick McHardy continue; 7053e9b4600SPatrick McHardy 7063e9b4600SPatrick McHardy *matchoff = dptr - start; 7073e9b4600SPatrick McHardy if (hdr->search) { 7083e9b4600SPatrick McHardy dptr = ct_sdp_header_search(dptr, limit, hdr->search, 7093e9b4600SPatrick McHardy hdr->slen); 7103e9b4600SPatrick McHardy if (!dptr) 7113e9b4600SPatrick McHardy return -1; 7123e9b4600SPatrick McHardy dptr += hdr->slen; 7133e9b4600SPatrick McHardy } 7143e9b4600SPatrick McHardy 7153e9b4600SPatrick McHardy *matchlen = hdr->match_len(ct, dptr, limit, &shift); 7163e9b4600SPatrick McHardy if (!*matchlen) 7173e9b4600SPatrick McHardy return -1; 7183e9b4600SPatrick McHardy *matchoff = dptr - start + shift; 7193e9b4600SPatrick McHardy return 1; 7203e9b4600SPatrick McHardy } 7213e9b4600SPatrick McHardy return 0; 7223e9b4600SPatrick McHardy } 7233e9b4600SPatrick McHardy EXPORT_SYMBOL_GPL(ct_sip_get_sdp_header); 7243e9b4600SPatrick McHardy 7254ab9e64eSPatrick McHardy static int ct_sip_parse_sdp_addr(const struct nf_conn *ct, const char *dptr, 7264ab9e64eSPatrick McHardy unsigned int dataoff, unsigned int datalen, 7274ab9e64eSPatrick McHardy enum sdp_header_types type, 7284ab9e64eSPatrick McHardy enum sdp_header_types term, 7294ab9e64eSPatrick McHardy unsigned int *matchoff, unsigned int *matchlen, 7304ab9e64eSPatrick McHardy union nf_inet_addr *addr) 7314ab9e64eSPatrick McHardy { 7324ab9e64eSPatrick McHardy int ret; 7334ab9e64eSPatrick McHardy 7344ab9e64eSPatrick McHardy ret = ct_sip_get_sdp_header(ct, dptr, dataoff, datalen, type, term, 7354ab9e64eSPatrick McHardy matchoff, matchlen); 7364ab9e64eSPatrick McHardy if (ret <= 0) 7374ab9e64eSPatrick McHardy return ret; 7384ab9e64eSPatrick McHardy 7394ab9e64eSPatrick McHardy if (!parse_addr(ct, dptr + *matchoff, NULL, addr, 7404ab9e64eSPatrick McHardy dptr + *matchoff + *matchlen)) 7414ab9e64eSPatrick McHardy return -1; 7424ab9e64eSPatrick McHardy return 1; 7434ab9e64eSPatrick McHardy } 7444ab9e64eSPatrick McHardy 7450f32a40fSPatrick McHardy static int refresh_signalling_expectation(struct nf_conn *ct, 7460f32a40fSPatrick McHardy union nf_inet_addr *addr, 747f5b321bdSPatrick McHardy u8 proto, __be16 port, 7480f32a40fSPatrick McHardy unsigned int expires) 7490f32a40fSPatrick McHardy { 7500f32a40fSPatrick McHardy struct nf_conn_help *help = nfct_help(ct); 7510f32a40fSPatrick McHardy struct nf_conntrack_expect *exp; 7520f32a40fSPatrick McHardy struct hlist_node *n, *next; 7530f32a40fSPatrick McHardy int found = 0; 7540f32a40fSPatrick McHardy 7550f32a40fSPatrick McHardy spin_lock_bh(&nf_conntrack_lock); 7560f32a40fSPatrick McHardy hlist_for_each_entry_safe(exp, n, next, &help->expectations, lnode) { 7570f32a40fSPatrick McHardy if (exp->class != SIP_EXPECT_SIGNALLING || 7580f32a40fSPatrick McHardy !nf_inet_addr_cmp(&exp->tuple.dst.u3, addr) || 759f5b321bdSPatrick McHardy exp->tuple.dst.protonum != proto || 7600f32a40fSPatrick McHardy exp->tuple.dst.u.udp.port != port) 7610f32a40fSPatrick McHardy continue; 7620f32a40fSPatrick McHardy if (!del_timer(&exp->timeout)) 7630f32a40fSPatrick McHardy continue; 7640f32a40fSPatrick McHardy exp->flags &= ~NF_CT_EXPECT_INACTIVE; 7650f32a40fSPatrick McHardy exp->timeout.expires = jiffies + expires * HZ; 7660f32a40fSPatrick McHardy add_timer(&exp->timeout); 7670f32a40fSPatrick McHardy found = 1; 7680f32a40fSPatrick McHardy break; 7690f32a40fSPatrick McHardy } 7700f32a40fSPatrick McHardy spin_unlock_bh(&nf_conntrack_lock); 7710f32a40fSPatrick McHardy return found; 7720f32a40fSPatrick McHardy } 7730f32a40fSPatrick McHardy 7740f32a40fSPatrick McHardy static void flush_expectations(struct nf_conn *ct, bool media) 7759467ee38SPatrick McHardy { 7769467ee38SPatrick McHardy struct nf_conn_help *help = nfct_help(ct); 7779467ee38SPatrick McHardy struct nf_conntrack_expect *exp; 7789467ee38SPatrick McHardy struct hlist_node *n, *next; 7799467ee38SPatrick McHardy 7809467ee38SPatrick McHardy spin_lock_bh(&nf_conntrack_lock); 7819467ee38SPatrick McHardy hlist_for_each_entry_safe(exp, n, next, &help->expectations, lnode) { 7820f32a40fSPatrick McHardy if ((exp->class != SIP_EXPECT_SIGNALLING) ^ media) 7830f32a40fSPatrick McHardy continue; 7849467ee38SPatrick McHardy if (!del_timer(&exp->timeout)) 7859467ee38SPatrick McHardy continue; 7869467ee38SPatrick McHardy nf_ct_unlink_expect(exp); 7879467ee38SPatrick McHardy nf_ct_expect_put(exp); 7880f32a40fSPatrick McHardy if (!media) 7890f32a40fSPatrick McHardy break; 7909467ee38SPatrick McHardy } 7919467ee38SPatrick McHardy spin_unlock_bh(&nf_conntrack_lock); 7929467ee38SPatrick McHardy } 7939467ee38SPatrick McHardy 7943b6b9fabSPatrick McHardy static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int dataoff, 795212440a7SPatrick McHardy const char **dptr, unsigned int *datalen, 7964ab9e64eSPatrick McHardy union nf_inet_addr *daddr, __be16 port, 7970d0ab037SPatrick McHardy enum sip_expectation_classes class, 7984ab9e64eSPatrick McHardy unsigned int mediaoff, unsigned int medialen) 7999fafcd7bSPatrick McHardy { 800a9c1d359SPatrick McHardy struct nf_conntrack_expect *exp, *rtp_exp, *rtcp_exp; 801212440a7SPatrick McHardy enum ip_conntrack_info ctinfo; 802212440a7SPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 803a5c3a800SAlexey Dobriyan struct net *net = nf_ct_net(ct); 8049fafcd7bSPatrick McHardy enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); 805d901a936SPatrick McHardy union nf_inet_addr *saddr; 806d901a936SPatrick McHardy struct nf_conntrack_tuple tuple; 807c7f485abSPatrick McHardy int direct_rtp = 0, skip_expect = 0, ret = NF_DROP; 808a9c1d359SPatrick McHardy u_int16_t base_port; 809a9c1d359SPatrick McHardy __be16 rtp_port, rtcp_port; 810c7f485abSPatrick McHardy typeof(nf_nat_sdp_port_hook) nf_nat_sdp_port; 8114ab9e64eSPatrick McHardy typeof(nf_nat_sdp_media_hook) nf_nat_sdp_media; 8129fafcd7bSPatrick McHardy 813d901a936SPatrick McHardy saddr = NULL; 814d901a936SPatrick McHardy if (sip_direct_media) { 815d901a936SPatrick McHardy if (!nf_inet_addr_cmp(daddr, &ct->tuplehash[dir].tuple.src.u3)) 816d901a936SPatrick McHardy return NF_ACCEPT; 817d901a936SPatrick McHardy saddr = &ct->tuplehash[!dir].tuple.src.u3; 818d901a936SPatrick McHardy } 819d901a936SPatrick McHardy 820d901a936SPatrick McHardy /* We need to check whether the registration exists before attempting 821d901a936SPatrick McHardy * to register it since we can see the same media description multiple 822d901a936SPatrick McHardy * times on different connections in case multiple endpoints receive 823d901a936SPatrick McHardy * the same call. 824c7f485abSPatrick McHardy * 825c7f485abSPatrick McHardy * RTP optimization: if we find a matching media channel expectation 826c7f485abSPatrick McHardy * and both the expectation and this connection are SNATed, we assume 827c7f485abSPatrick McHardy * both sides can reach each other directly and use the final 828c7f485abSPatrick McHardy * destination address from the expectation. We still need to keep 829c7f485abSPatrick McHardy * the NATed expectations for media that might arrive from the 830c7f485abSPatrick McHardy * outside, and additionally need to expect the direct RTP stream 831c7f485abSPatrick McHardy * in case it passes through us even without NAT. 832d901a936SPatrick McHardy */ 833d901a936SPatrick McHardy memset(&tuple, 0, sizeof(tuple)); 834d901a936SPatrick McHardy if (saddr) 835d901a936SPatrick McHardy tuple.src.u3 = *saddr; 8365e8fbe2aSPatrick McHardy tuple.src.l3num = nf_ct_l3num(ct); 837d901a936SPatrick McHardy tuple.dst.protonum = IPPROTO_UDP; 838d901a936SPatrick McHardy tuple.dst.u3 = *daddr; 839d901a936SPatrick McHardy tuple.dst.u.udp.port = port; 840d901a936SPatrick McHardy 841d901a936SPatrick McHardy rcu_read_lock(); 842c7f485abSPatrick McHardy do { 8435d0aa2ccSPatrick McHardy exp = __nf_ct_expect_find(net, nf_ct_zone(ct), &tuple); 844d901a936SPatrick McHardy 845c7f485abSPatrick McHardy if (!exp || exp->master == ct || 846c7f485abSPatrick McHardy nfct_help(exp->master)->helper != nfct_help(ct)->helper || 847c7f485abSPatrick McHardy exp->class != class) 848c7f485abSPatrick McHardy break; 849e1f9a464SPatrick McHardy #ifdef CONFIG_NF_NAT_NEEDED 850c7f485abSPatrick McHardy if (exp->tuple.src.l3num == AF_INET && !direct_rtp && 851c7f485abSPatrick McHardy (exp->saved_ip != exp->tuple.dst.u3.ip || 852c7f485abSPatrick McHardy exp->saved_proto.udp.port != exp->tuple.dst.u.udp.port) && 853c7f485abSPatrick McHardy ct->status & IPS_NAT_MASK) { 854c7f485abSPatrick McHardy daddr->ip = exp->saved_ip; 855c7f485abSPatrick McHardy tuple.dst.u3.ip = exp->saved_ip; 856c7f485abSPatrick McHardy tuple.dst.u.udp.port = exp->saved_proto.udp.port; 857c7f485abSPatrick McHardy direct_rtp = 1; 858c7f485abSPatrick McHardy } else 859e1f9a464SPatrick McHardy #endif 860c7f485abSPatrick McHardy skip_expect = 1; 861c7f485abSPatrick McHardy } while (!skip_expect); 862c7f485abSPatrick McHardy rcu_read_unlock(); 863d901a936SPatrick McHardy 864a9c1d359SPatrick McHardy base_port = ntohs(tuple.dst.u.udp.port) & ~1; 865a9c1d359SPatrick McHardy rtp_port = htons(base_port); 866a9c1d359SPatrick McHardy rtcp_port = htons(base_port + 1); 867a9c1d359SPatrick McHardy 868c7f485abSPatrick McHardy if (direct_rtp) { 869c7f485abSPatrick McHardy nf_nat_sdp_port = rcu_dereference(nf_nat_sdp_port_hook); 870c7f485abSPatrick McHardy if (nf_nat_sdp_port && 8713b6b9fabSPatrick McHardy !nf_nat_sdp_port(skb, dataoff, dptr, datalen, 872c7f485abSPatrick McHardy mediaoff, medialen, ntohs(rtp_port))) 873c7f485abSPatrick McHardy goto err1; 874c7f485abSPatrick McHardy } 875c7f485abSPatrick McHardy 876c7f485abSPatrick McHardy if (skip_expect) 877c7f485abSPatrick McHardy return NF_ACCEPT; 878c7f485abSPatrick McHardy 879a9c1d359SPatrick McHardy rtp_exp = nf_ct_expect_alloc(ct); 880a9c1d359SPatrick McHardy if (rtp_exp == NULL) 881a9c1d359SPatrick McHardy goto err1; 8825e8fbe2aSPatrick McHardy nf_ct_expect_init(rtp_exp, class, nf_ct_l3num(ct), saddr, daddr, 883a9c1d359SPatrick McHardy IPPROTO_UDP, NULL, &rtp_port); 884a9c1d359SPatrick McHardy 885a9c1d359SPatrick McHardy rtcp_exp = nf_ct_expect_alloc(ct); 886a9c1d359SPatrick McHardy if (rtcp_exp == NULL) 887a9c1d359SPatrick McHardy goto err2; 8885e8fbe2aSPatrick McHardy nf_ct_expect_init(rtcp_exp, class, nf_ct_l3num(ct), saddr, daddr, 889a9c1d359SPatrick McHardy IPPROTO_UDP, NULL, &rtcp_port); 8909fafcd7bSPatrick McHardy 8914ab9e64eSPatrick McHardy nf_nat_sdp_media = rcu_dereference(nf_nat_sdp_media_hook); 892c7f485abSPatrick McHardy if (nf_nat_sdp_media && ct->status & IPS_NAT_MASK && !direct_rtp) 8933b6b9fabSPatrick McHardy ret = nf_nat_sdp_media(skb, dataoff, dptr, datalen, 8943b6b9fabSPatrick McHardy rtp_exp, rtcp_exp, 8954ab9e64eSPatrick McHardy mediaoff, medialen, daddr); 8969fafcd7bSPatrick McHardy else { 897a9c1d359SPatrick McHardy if (nf_ct_expect_related(rtp_exp) == 0) { 898a9c1d359SPatrick McHardy if (nf_ct_expect_related(rtcp_exp) != 0) 899a9c1d359SPatrick McHardy nf_ct_unexpect_related(rtp_exp); 9009fafcd7bSPatrick McHardy else 9019fafcd7bSPatrick McHardy ret = NF_ACCEPT; 9029fafcd7bSPatrick McHardy } 903a9c1d359SPatrick McHardy } 904a9c1d359SPatrick McHardy nf_ct_expect_put(rtcp_exp); 905a9c1d359SPatrick McHardy err2: 906a9c1d359SPatrick McHardy nf_ct_expect_put(rtp_exp); 907a9c1d359SPatrick McHardy err1: 9089fafcd7bSPatrick McHardy return ret; 9099fafcd7bSPatrick McHardy } 9109fafcd7bSPatrick McHardy 9110d0ab037SPatrick McHardy static const struct sdp_media_type sdp_media_types[] = { 9120d0ab037SPatrick McHardy SDP_MEDIA_TYPE("audio ", SIP_EXPECT_AUDIO), 9130d0ab037SPatrick McHardy SDP_MEDIA_TYPE("video ", SIP_EXPECT_VIDEO), 9149d288dffSPatrick McHardy SDP_MEDIA_TYPE("image ", SIP_EXPECT_IMAGE), 9150d0ab037SPatrick McHardy }; 9160d0ab037SPatrick McHardy 9170d0ab037SPatrick McHardy static const struct sdp_media_type *sdp_media_type(const char *dptr, 9180d0ab037SPatrick McHardy unsigned int matchoff, 9190d0ab037SPatrick McHardy unsigned int matchlen) 9200d0ab037SPatrick McHardy { 9210d0ab037SPatrick McHardy const struct sdp_media_type *t; 9220d0ab037SPatrick McHardy unsigned int i; 9230d0ab037SPatrick McHardy 9240d0ab037SPatrick McHardy for (i = 0; i < ARRAY_SIZE(sdp_media_types); i++) { 9250d0ab037SPatrick McHardy t = &sdp_media_types[i]; 9260d0ab037SPatrick McHardy if (matchlen < t->len || 9270d0ab037SPatrick McHardy strncmp(dptr + matchoff, t->name, t->len)) 9280d0ab037SPatrick McHardy continue; 9290d0ab037SPatrick McHardy return t; 9300d0ab037SPatrick McHardy } 9310d0ab037SPatrick McHardy return NULL; 9320d0ab037SPatrick McHardy } 9330d0ab037SPatrick McHardy 9343b6b9fabSPatrick McHardy static int process_sdp(struct sk_buff *skb, unsigned int dataoff, 93530f33e6dSPatrick McHardy const char **dptr, unsigned int *datalen, 93630f33e6dSPatrick McHardy unsigned int cseq) 9377d3dd043SPatrick McHardy { 9387d3dd043SPatrick McHardy enum ip_conntrack_info ctinfo; 9397d3dd043SPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 9407d3dd043SPatrick McHardy unsigned int matchoff, matchlen; 9414ab9e64eSPatrick McHardy unsigned int mediaoff, medialen; 9424ab9e64eSPatrick McHardy unsigned int sdpoff; 9434ab9e64eSPatrick McHardy unsigned int caddr_len, maddr_len; 9440d0ab037SPatrick McHardy unsigned int i; 9454ab9e64eSPatrick McHardy union nf_inet_addr caddr, maddr, rtp_addr; 9467d3dd043SPatrick McHardy unsigned int port; 9474ab9e64eSPatrick McHardy enum sdp_header_types c_hdr; 9480d0ab037SPatrick McHardy const struct sdp_media_type *t; 9490d0ab037SPatrick McHardy int ret = NF_ACCEPT; 9504ab9e64eSPatrick McHardy typeof(nf_nat_sdp_addr_hook) nf_nat_sdp_addr; 9514ab9e64eSPatrick McHardy typeof(nf_nat_sdp_session_hook) nf_nat_sdp_session; 9527d3dd043SPatrick McHardy 9530d0ab037SPatrick McHardy nf_nat_sdp_addr = rcu_dereference(nf_nat_sdp_addr_hook); 9545e8fbe2aSPatrick McHardy c_hdr = nf_ct_l3num(ct) == AF_INET ? SDP_HDR_CONNECTION_IP4 : 9557d3dd043SPatrick McHardy SDP_HDR_CONNECTION_IP6; 9567d3dd043SPatrick McHardy 9574ab9e64eSPatrick McHardy /* Find beginning of session description */ 9587d3dd043SPatrick McHardy if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen, 9594ab9e64eSPatrick McHardy SDP_HDR_VERSION, SDP_HDR_UNSPEC, 9607d3dd043SPatrick McHardy &matchoff, &matchlen) <= 0) 9617d3dd043SPatrick McHardy return NF_ACCEPT; 9624ab9e64eSPatrick McHardy sdpoff = matchoff; 9637d3dd043SPatrick McHardy 9644ab9e64eSPatrick McHardy /* The connection information is contained in the session description 9654ab9e64eSPatrick McHardy * and/or once per media description. The first media description marks 9664ab9e64eSPatrick McHardy * the end of the session description. */ 9674ab9e64eSPatrick McHardy caddr_len = 0; 9684ab9e64eSPatrick McHardy if (ct_sip_parse_sdp_addr(ct, *dptr, sdpoff, *datalen, 9694ab9e64eSPatrick McHardy c_hdr, SDP_HDR_MEDIA, 9704ab9e64eSPatrick McHardy &matchoff, &matchlen, &caddr) > 0) 9714ab9e64eSPatrick McHardy caddr_len = matchlen; 9727d3dd043SPatrick McHardy 9730d0ab037SPatrick McHardy mediaoff = sdpoff; 9740d0ab037SPatrick McHardy for (i = 0; i < ARRAY_SIZE(sdp_media_types); ) { 9750d0ab037SPatrick McHardy if (ct_sip_get_sdp_header(ct, *dptr, mediaoff, *datalen, 9767d3dd043SPatrick McHardy SDP_HDR_MEDIA, SDP_HDR_UNSPEC, 9774ab9e64eSPatrick McHardy &mediaoff, &medialen) <= 0) 9780d0ab037SPatrick McHardy break; 9790d0ab037SPatrick McHardy 9800d0ab037SPatrick McHardy /* Get media type and port number. A media port value of zero 9810d0ab037SPatrick McHardy * indicates an inactive stream. */ 9820d0ab037SPatrick McHardy t = sdp_media_type(*dptr, mediaoff, medialen); 9830d0ab037SPatrick McHardy if (!t) { 9840d0ab037SPatrick McHardy mediaoff += medialen; 9850d0ab037SPatrick McHardy continue; 9860d0ab037SPatrick McHardy } 9870d0ab037SPatrick McHardy mediaoff += t->len; 9880d0ab037SPatrick McHardy medialen -= t->len; 9897d3dd043SPatrick McHardy 9904ab9e64eSPatrick McHardy port = simple_strtoul(*dptr + mediaoff, NULL, 10); 9910d0ab037SPatrick McHardy if (port == 0) 9920d0ab037SPatrick McHardy continue; 9937d3dd043SPatrick McHardy if (port < 1024 || port > 65535) 9947d3dd043SPatrick McHardy return NF_DROP; 9957d3dd043SPatrick McHardy 9964ab9e64eSPatrick McHardy /* The media description overrides the session description. */ 9974ab9e64eSPatrick McHardy maddr_len = 0; 9984ab9e64eSPatrick McHardy if (ct_sip_parse_sdp_addr(ct, *dptr, mediaoff, *datalen, 9994ab9e64eSPatrick McHardy c_hdr, SDP_HDR_MEDIA, 10004ab9e64eSPatrick McHardy &matchoff, &matchlen, &maddr) > 0) { 10014ab9e64eSPatrick McHardy maddr_len = matchlen; 10024ab9e64eSPatrick McHardy memcpy(&rtp_addr, &maddr, sizeof(rtp_addr)); 10034ab9e64eSPatrick McHardy } else if (caddr_len) 10044ab9e64eSPatrick McHardy memcpy(&rtp_addr, &caddr, sizeof(rtp_addr)); 10054ab9e64eSPatrick McHardy else 10064ab9e64eSPatrick McHardy return NF_DROP; 10074ab9e64eSPatrick McHardy 10083b6b9fabSPatrick McHardy ret = set_expected_rtp_rtcp(skb, dataoff, dptr, datalen, 10090d0ab037SPatrick McHardy &rtp_addr, htons(port), t->class, 10104ab9e64eSPatrick McHardy mediaoff, medialen); 10114ab9e64eSPatrick McHardy if (ret != NF_ACCEPT) 10124ab9e64eSPatrick McHardy return ret; 10134ab9e64eSPatrick McHardy 10144ab9e64eSPatrick McHardy /* Update media connection address if present */ 10150d0ab037SPatrick McHardy if (maddr_len && nf_nat_sdp_addr && ct->status & IPS_NAT_MASK) { 10163b6b9fabSPatrick McHardy ret = nf_nat_sdp_addr(skb, dataoff, dptr, datalen, 10173b6b9fabSPatrick McHardy mediaoff, c_hdr, SDP_HDR_MEDIA, 10183b6b9fabSPatrick McHardy &rtp_addr); 10194ab9e64eSPatrick McHardy if (ret != NF_ACCEPT) 10204ab9e64eSPatrick McHardy return ret; 10214ab9e64eSPatrick McHardy } 10220d0ab037SPatrick McHardy i++; 10234ab9e64eSPatrick McHardy } 10244ab9e64eSPatrick McHardy 10254ab9e64eSPatrick McHardy /* Update session connection and owner addresses */ 10264ab9e64eSPatrick McHardy nf_nat_sdp_session = rcu_dereference(nf_nat_sdp_session_hook); 10274ab9e64eSPatrick McHardy if (nf_nat_sdp_session && ct->status & IPS_NAT_MASK) 10283b6b9fabSPatrick McHardy ret = nf_nat_sdp_session(skb, dataoff, dptr, datalen, sdpoff, 10293b6b9fabSPatrick McHardy &rtp_addr); 10304ab9e64eSPatrick McHardy 10314ab9e64eSPatrick McHardy return ret; 10327d3dd043SPatrick McHardy } 10333b6b9fabSPatrick McHardy static int process_invite_response(struct sk_buff *skb, unsigned int dataoff, 103430f33e6dSPatrick McHardy const char **dptr, unsigned int *datalen, 103530f33e6dSPatrick McHardy unsigned int cseq, unsigned int code) 103630f33e6dSPatrick McHardy { 10379467ee38SPatrick McHardy enum ip_conntrack_info ctinfo; 10389467ee38SPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 1039ef75d49fSPatrick McHardy struct nf_conn_help *help = nfct_help(ct); 10409467ee38SPatrick McHardy 104130f33e6dSPatrick McHardy if ((code >= 100 && code <= 199) || 104230f33e6dSPatrick McHardy (code >= 200 && code <= 299)) 10433b6b9fabSPatrick McHardy return process_sdp(skb, dataoff, dptr, datalen, cseq); 1044ef75d49fSPatrick McHardy else if (help->help.ct_sip_info.invite_cseq == cseq) 10450f32a40fSPatrick McHardy flush_expectations(ct, true); 104630f33e6dSPatrick McHardy return NF_ACCEPT; 104730f33e6dSPatrick McHardy } 104830f33e6dSPatrick McHardy 10493b6b9fabSPatrick McHardy static int process_update_response(struct sk_buff *skb, unsigned int dataoff, 105030f33e6dSPatrick McHardy const char **dptr, unsigned int *datalen, 105130f33e6dSPatrick McHardy unsigned int cseq, unsigned int code) 105230f33e6dSPatrick McHardy { 10539467ee38SPatrick McHardy enum ip_conntrack_info ctinfo; 10549467ee38SPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 1055ef75d49fSPatrick McHardy struct nf_conn_help *help = nfct_help(ct); 10569467ee38SPatrick McHardy 105730f33e6dSPatrick McHardy if ((code >= 100 && code <= 199) || 105830f33e6dSPatrick McHardy (code >= 200 && code <= 299)) 10593b6b9fabSPatrick McHardy return process_sdp(skb, dataoff, dptr, datalen, cseq); 1060ef75d49fSPatrick McHardy else if (help->help.ct_sip_info.invite_cseq == cseq) 10610f32a40fSPatrick McHardy flush_expectations(ct, true); 106230f33e6dSPatrick McHardy return NF_ACCEPT; 106330f33e6dSPatrick McHardy } 106430f33e6dSPatrick McHardy 10653b6b9fabSPatrick McHardy static int process_prack_response(struct sk_buff *skb, unsigned int dataoff, 1066595a8ecbSPatrick McHardy const char **dptr, unsigned int *datalen, 1067595a8ecbSPatrick McHardy unsigned int cseq, unsigned int code) 1068595a8ecbSPatrick McHardy { 10699467ee38SPatrick McHardy enum ip_conntrack_info ctinfo; 10709467ee38SPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 1071ef75d49fSPatrick McHardy struct nf_conn_help *help = nfct_help(ct); 10729467ee38SPatrick McHardy 1073595a8ecbSPatrick McHardy if ((code >= 100 && code <= 199) || 1074595a8ecbSPatrick McHardy (code >= 200 && code <= 299)) 10753b6b9fabSPatrick McHardy return process_sdp(skb, dataoff, dptr, datalen, cseq); 1076ef75d49fSPatrick McHardy else if (help->help.ct_sip_info.invite_cseq == cseq) 10770f32a40fSPatrick McHardy flush_expectations(ct, true); 10789467ee38SPatrick McHardy return NF_ACCEPT; 10799467ee38SPatrick McHardy } 1080595a8ecbSPatrick McHardy 10819d288dffSPatrick McHardy static int process_invite_request(struct sk_buff *skb, unsigned int dataoff, 10829d288dffSPatrick McHardy const char **dptr, unsigned int *datalen, 10839d288dffSPatrick McHardy unsigned int cseq) 10849d288dffSPatrick McHardy { 10859d288dffSPatrick McHardy enum ip_conntrack_info ctinfo; 10869d288dffSPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 10879d288dffSPatrick McHardy struct nf_conn_help *help = nfct_help(ct); 10889d288dffSPatrick McHardy unsigned int ret; 10899d288dffSPatrick McHardy 10909d288dffSPatrick McHardy flush_expectations(ct, true); 10919d288dffSPatrick McHardy ret = process_sdp(skb, dataoff, dptr, datalen, cseq); 10929d288dffSPatrick McHardy if (ret == NF_ACCEPT) 10939d288dffSPatrick McHardy help->help.ct_sip_info.invite_cseq = cseq; 10949d288dffSPatrick McHardy return ret; 10959d288dffSPatrick McHardy } 10969d288dffSPatrick McHardy 10973b6b9fabSPatrick McHardy static int process_bye_request(struct sk_buff *skb, unsigned int dataoff, 10989467ee38SPatrick McHardy const char **dptr, unsigned int *datalen, 10999467ee38SPatrick McHardy unsigned int cseq) 11009467ee38SPatrick McHardy { 11019467ee38SPatrick McHardy enum ip_conntrack_info ctinfo; 11029467ee38SPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 11039467ee38SPatrick McHardy 11040f32a40fSPatrick McHardy flush_expectations(ct, true); 11050f32a40fSPatrick McHardy return NF_ACCEPT; 11060f32a40fSPatrick McHardy } 11070f32a40fSPatrick McHardy 11080f32a40fSPatrick McHardy /* Parse a REGISTER request and create a permanent expectation for incoming 11090f32a40fSPatrick McHardy * signalling connections. The expectation is marked inactive and is activated 11100f32a40fSPatrick McHardy * when receiving a response indicating success from the registrar. 11110f32a40fSPatrick McHardy */ 11123b6b9fabSPatrick McHardy static int process_register_request(struct sk_buff *skb, unsigned int dataoff, 11130f32a40fSPatrick McHardy const char **dptr, unsigned int *datalen, 11140f32a40fSPatrick McHardy unsigned int cseq) 11150f32a40fSPatrick McHardy { 11160f32a40fSPatrick McHardy enum ip_conntrack_info ctinfo; 11170f32a40fSPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 11180f32a40fSPatrick McHardy struct nf_conn_help *help = nfct_help(ct); 11190f32a40fSPatrick McHardy enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); 11200f32a40fSPatrick McHardy unsigned int matchoff, matchlen; 11210f32a40fSPatrick McHardy struct nf_conntrack_expect *exp; 11220f32a40fSPatrick McHardy union nf_inet_addr *saddr, daddr; 11230f32a40fSPatrick McHardy __be16 port; 1124f5b321bdSPatrick McHardy u8 proto; 11250f32a40fSPatrick McHardy unsigned int expires = 0; 11260f32a40fSPatrick McHardy int ret; 11270f32a40fSPatrick McHardy typeof(nf_nat_sip_expect_hook) nf_nat_sip_expect; 11280f32a40fSPatrick McHardy 11290f32a40fSPatrick McHardy /* Expected connections can not register again. */ 11300f32a40fSPatrick McHardy if (ct->status & IPS_EXPECTED) 11310f32a40fSPatrick McHardy return NF_ACCEPT; 11320f32a40fSPatrick McHardy 11330f32a40fSPatrick McHardy /* We must check the expiration time: a value of zero signals the 11340f32a40fSPatrick McHardy * registrar to release the binding. We'll remove our expectation 11350f32a40fSPatrick McHardy * when receiving the new bindings in the response, but we don't 11360f32a40fSPatrick McHardy * want to create new ones. 11370f32a40fSPatrick McHardy * 11380f32a40fSPatrick McHardy * The expiration time may be contained in Expires: header, the 11390f32a40fSPatrick McHardy * Contact: header parameters or the URI parameters. 11400f32a40fSPatrick McHardy */ 11410f32a40fSPatrick McHardy if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_EXPIRES, 11420f32a40fSPatrick McHardy &matchoff, &matchlen) > 0) 11430f32a40fSPatrick McHardy expires = simple_strtoul(*dptr + matchoff, NULL, 10); 11440f32a40fSPatrick McHardy 11450f32a40fSPatrick McHardy ret = ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, 11460f32a40fSPatrick McHardy SIP_HDR_CONTACT, NULL, 11470f32a40fSPatrick McHardy &matchoff, &matchlen, &daddr, &port); 11480f32a40fSPatrick McHardy if (ret < 0) 11490f32a40fSPatrick McHardy return NF_DROP; 11500f32a40fSPatrick McHardy else if (ret == 0) 11510f32a40fSPatrick McHardy return NF_ACCEPT; 11520f32a40fSPatrick McHardy 11530f32a40fSPatrick McHardy /* We don't support third-party registrations */ 11540f32a40fSPatrick McHardy if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, &daddr)) 11550f32a40fSPatrick McHardy return NF_ACCEPT; 11560f32a40fSPatrick McHardy 1157f5b321bdSPatrick McHardy if (ct_sip_parse_transport(ct, *dptr, matchoff + matchlen, *datalen, 1158f5b321bdSPatrick McHardy &proto) == 0) 1159f5b321bdSPatrick McHardy return NF_ACCEPT; 1160f5b321bdSPatrick McHardy 11610f32a40fSPatrick McHardy if (ct_sip_parse_numerical_param(ct, *dptr, 11620f32a40fSPatrick McHardy matchoff + matchlen, *datalen, 11630f32a40fSPatrick McHardy "expires=", NULL, NULL, &expires) < 0) 11640f32a40fSPatrick McHardy return NF_DROP; 11650f32a40fSPatrick McHardy 11660f32a40fSPatrick McHardy if (expires == 0) { 11670f32a40fSPatrick McHardy ret = NF_ACCEPT; 11680f32a40fSPatrick McHardy goto store_cseq; 11690f32a40fSPatrick McHardy } 11700f32a40fSPatrick McHardy 11710f32a40fSPatrick McHardy exp = nf_ct_expect_alloc(ct); 11720f32a40fSPatrick McHardy if (!exp) 11730f32a40fSPatrick McHardy return NF_DROP; 11740f32a40fSPatrick McHardy 11750f32a40fSPatrick McHardy saddr = NULL; 11760f32a40fSPatrick McHardy if (sip_direct_signalling) 11770f32a40fSPatrick McHardy saddr = &ct->tuplehash[!dir].tuple.src.u3; 11780f32a40fSPatrick McHardy 11795e8fbe2aSPatrick McHardy nf_ct_expect_init(exp, SIP_EXPECT_SIGNALLING, nf_ct_l3num(ct), 1180f5b321bdSPatrick McHardy saddr, &daddr, proto, NULL, &port); 11810f32a40fSPatrick McHardy exp->timeout.expires = sip_timeout * HZ; 11820f32a40fSPatrick McHardy exp->helper = nfct_help(ct)->helper; 11830f32a40fSPatrick McHardy exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE; 11840f32a40fSPatrick McHardy 11850f32a40fSPatrick McHardy nf_nat_sip_expect = rcu_dereference(nf_nat_sip_expect_hook); 11860f32a40fSPatrick McHardy if (nf_nat_sip_expect && ct->status & IPS_NAT_MASK) 11873b6b9fabSPatrick McHardy ret = nf_nat_sip_expect(skb, dataoff, dptr, datalen, exp, 11880f32a40fSPatrick McHardy matchoff, matchlen); 11890f32a40fSPatrick McHardy else { 11900f32a40fSPatrick McHardy if (nf_ct_expect_related(exp) != 0) 11910f32a40fSPatrick McHardy ret = NF_DROP; 11920f32a40fSPatrick McHardy else 11930f32a40fSPatrick McHardy ret = NF_ACCEPT; 11940f32a40fSPatrick McHardy } 11950f32a40fSPatrick McHardy nf_ct_expect_put(exp); 11960f32a40fSPatrick McHardy 11970f32a40fSPatrick McHardy store_cseq: 11980f32a40fSPatrick McHardy if (ret == NF_ACCEPT) 11990f32a40fSPatrick McHardy help->help.ct_sip_info.register_cseq = cseq; 12000f32a40fSPatrick McHardy return ret; 12010f32a40fSPatrick McHardy } 12020f32a40fSPatrick McHardy 12033b6b9fabSPatrick McHardy static int process_register_response(struct sk_buff *skb, unsigned int dataoff, 12040f32a40fSPatrick McHardy const char **dptr, unsigned int *datalen, 12050f32a40fSPatrick McHardy unsigned int cseq, unsigned int code) 12060f32a40fSPatrick McHardy { 12070f32a40fSPatrick McHardy enum ip_conntrack_info ctinfo; 12080f32a40fSPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 12090f32a40fSPatrick McHardy struct nf_conn_help *help = nfct_help(ct); 12100f32a40fSPatrick McHardy enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); 12110f32a40fSPatrick McHardy union nf_inet_addr addr; 12120f32a40fSPatrick McHardy __be16 port; 1213f5b321bdSPatrick McHardy u8 proto; 12143b6b9fabSPatrick McHardy unsigned int matchoff, matchlen, coff = 0; 12150f32a40fSPatrick McHardy unsigned int expires = 0; 12160f32a40fSPatrick McHardy int in_contact = 0, ret; 12170f32a40fSPatrick McHardy 12180f32a40fSPatrick McHardy /* According to RFC 3261, "UAs MUST NOT send a new registration until 12190f32a40fSPatrick McHardy * they have received a final response from the registrar for the 12200f32a40fSPatrick McHardy * previous one or the previous REGISTER request has timed out". 12210f32a40fSPatrick McHardy * 12220f32a40fSPatrick McHardy * However, some servers fail to detect retransmissions and send late 12230f32a40fSPatrick McHardy * responses, so we store the sequence number of the last valid 12240f32a40fSPatrick McHardy * request and compare it here. 12250f32a40fSPatrick McHardy */ 12260f32a40fSPatrick McHardy if (help->help.ct_sip_info.register_cseq != cseq) 12270f32a40fSPatrick McHardy return NF_ACCEPT; 12280f32a40fSPatrick McHardy 12290f32a40fSPatrick McHardy if (code >= 100 && code <= 199) 12300f32a40fSPatrick McHardy return NF_ACCEPT; 12310f32a40fSPatrick McHardy if (code < 200 || code > 299) 12320f32a40fSPatrick McHardy goto flush; 12330f32a40fSPatrick McHardy 12340f32a40fSPatrick McHardy if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_EXPIRES, 12350f32a40fSPatrick McHardy &matchoff, &matchlen) > 0) 12360f32a40fSPatrick McHardy expires = simple_strtoul(*dptr + matchoff, NULL, 10); 12370f32a40fSPatrick McHardy 12380f32a40fSPatrick McHardy while (1) { 12390f32a40fSPatrick McHardy unsigned int c_expires = expires; 12400f32a40fSPatrick McHardy 12413b6b9fabSPatrick McHardy ret = ct_sip_parse_header_uri(ct, *dptr, &coff, *datalen, 12420f32a40fSPatrick McHardy SIP_HDR_CONTACT, &in_contact, 12430f32a40fSPatrick McHardy &matchoff, &matchlen, 12440f32a40fSPatrick McHardy &addr, &port); 12450f32a40fSPatrick McHardy if (ret < 0) 12460f32a40fSPatrick McHardy return NF_DROP; 12470f32a40fSPatrick McHardy else if (ret == 0) 12480f32a40fSPatrick McHardy break; 12490f32a40fSPatrick McHardy 12500f32a40fSPatrick McHardy /* We don't support third-party registrations */ 12510f32a40fSPatrick McHardy if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, &addr)) 12520f32a40fSPatrick McHardy continue; 12530f32a40fSPatrick McHardy 1254f5b321bdSPatrick McHardy if (ct_sip_parse_transport(ct, *dptr, matchoff + matchlen, 1255f5b321bdSPatrick McHardy *datalen, &proto) == 0) 1256f5b321bdSPatrick McHardy continue; 1257f5b321bdSPatrick McHardy 12580f32a40fSPatrick McHardy ret = ct_sip_parse_numerical_param(ct, *dptr, 12590f32a40fSPatrick McHardy matchoff + matchlen, 12600f32a40fSPatrick McHardy *datalen, "expires=", 12610f32a40fSPatrick McHardy NULL, NULL, &c_expires); 12620f32a40fSPatrick McHardy if (ret < 0) 12630f32a40fSPatrick McHardy return NF_DROP; 12640f32a40fSPatrick McHardy if (c_expires == 0) 12650f32a40fSPatrick McHardy break; 1266f5b321bdSPatrick McHardy if (refresh_signalling_expectation(ct, &addr, proto, port, 1267f5b321bdSPatrick McHardy c_expires)) 12680f32a40fSPatrick McHardy return NF_ACCEPT; 12690f32a40fSPatrick McHardy } 12700f32a40fSPatrick McHardy 12710f32a40fSPatrick McHardy flush: 12720f32a40fSPatrick McHardy flush_expectations(ct, false); 1273595a8ecbSPatrick McHardy return NF_ACCEPT; 1274595a8ecbSPatrick McHardy } 1275595a8ecbSPatrick McHardy 127630f33e6dSPatrick McHardy static const struct sip_handler sip_handlers[] = { 12779d288dffSPatrick McHardy SIP_HANDLER("INVITE", process_invite_request, process_invite_response), 127830f33e6dSPatrick McHardy SIP_HANDLER("UPDATE", process_sdp, process_update_response), 1279595a8ecbSPatrick McHardy SIP_HANDLER("ACK", process_sdp, NULL), 1280595a8ecbSPatrick McHardy SIP_HANDLER("PRACK", process_sdp, process_prack_response), 12819467ee38SPatrick McHardy SIP_HANDLER("BYE", process_bye_request, NULL), 12820f32a40fSPatrick McHardy SIP_HANDLER("REGISTER", process_register_request, process_register_response), 128330f33e6dSPatrick McHardy }; 128430f33e6dSPatrick McHardy 12853b6b9fabSPatrick McHardy static int process_sip_response(struct sk_buff *skb, unsigned int dataoff, 128630f33e6dSPatrick McHardy const char **dptr, unsigned int *datalen) 128730f33e6dSPatrick McHardy { 128830f33e6dSPatrick McHardy enum ip_conntrack_info ctinfo; 128930f33e6dSPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 12903b6b9fabSPatrick McHardy unsigned int matchoff, matchlen, matchend; 12913b6b9fabSPatrick McHardy unsigned int code, cseq, i; 129230f33e6dSPatrick McHardy 129330f33e6dSPatrick McHardy if (*datalen < strlen("SIP/2.0 200")) 129430f33e6dSPatrick McHardy return NF_ACCEPT; 129530f33e6dSPatrick McHardy code = simple_strtoul(*dptr + strlen("SIP/2.0 "), NULL, 10); 129630f33e6dSPatrick McHardy if (!code) 129730f33e6dSPatrick McHardy return NF_DROP; 129830f33e6dSPatrick McHardy 129930f33e6dSPatrick McHardy if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ, 130030f33e6dSPatrick McHardy &matchoff, &matchlen) <= 0) 130130f33e6dSPatrick McHardy return NF_DROP; 130230f33e6dSPatrick McHardy cseq = simple_strtoul(*dptr + matchoff, NULL, 10); 130330f33e6dSPatrick McHardy if (!cseq) 130430f33e6dSPatrick McHardy return NF_DROP; 13053b6b9fabSPatrick McHardy matchend = matchoff + matchlen + 1; 130630f33e6dSPatrick McHardy 130730f33e6dSPatrick McHardy for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) { 130866bf7918SAlexey Dobriyan const struct sip_handler *handler; 130966bf7918SAlexey Dobriyan 131030f33e6dSPatrick McHardy handler = &sip_handlers[i]; 131130f33e6dSPatrick McHardy if (handler->response == NULL) 131230f33e6dSPatrick McHardy continue; 13133b6b9fabSPatrick McHardy if (*datalen < matchend + handler->len || 13143b6b9fabSPatrick McHardy strnicmp(*dptr + matchend, handler->method, handler->len)) 131530f33e6dSPatrick McHardy continue; 13163b6b9fabSPatrick McHardy return handler->response(skb, dataoff, dptr, datalen, 13173b6b9fabSPatrick McHardy cseq, code); 131830f33e6dSPatrick McHardy } 131930f33e6dSPatrick McHardy return NF_ACCEPT; 132030f33e6dSPatrick McHardy } 132130f33e6dSPatrick McHardy 13223b6b9fabSPatrick McHardy static int process_sip_request(struct sk_buff *skb, unsigned int dataoff, 132330f33e6dSPatrick McHardy const char **dptr, unsigned int *datalen) 132430f33e6dSPatrick McHardy { 132530f33e6dSPatrick McHardy enum ip_conntrack_info ctinfo; 132630f33e6dSPatrick McHardy struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 132730f33e6dSPatrick McHardy unsigned int matchoff, matchlen; 132830f33e6dSPatrick McHardy unsigned int cseq, i; 132930f33e6dSPatrick McHardy 133030f33e6dSPatrick McHardy for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) { 133166bf7918SAlexey Dobriyan const struct sip_handler *handler; 133266bf7918SAlexey Dobriyan 133330f33e6dSPatrick McHardy handler = &sip_handlers[i]; 133430f33e6dSPatrick McHardy if (handler->request == NULL) 133530f33e6dSPatrick McHardy continue; 133630f33e6dSPatrick McHardy if (*datalen < handler->len || 133730f33e6dSPatrick McHardy strnicmp(*dptr, handler->method, handler->len)) 133830f33e6dSPatrick McHardy continue; 133930f33e6dSPatrick McHardy 134030f33e6dSPatrick McHardy if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ, 134130f33e6dSPatrick McHardy &matchoff, &matchlen) <= 0) 134230f33e6dSPatrick McHardy return NF_DROP; 134330f33e6dSPatrick McHardy cseq = simple_strtoul(*dptr + matchoff, NULL, 10); 134430f33e6dSPatrick McHardy if (!cseq) 134530f33e6dSPatrick McHardy return NF_DROP; 134630f33e6dSPatrick McHardy 13473b6b9fabSPatrick McHardy return handler->request(skb, dataoff, dptr, datalen, cseq); 134830f33e6dSPatrick McHardy } 134930f33e6dSPatrick McHardy return NF_ACCEPT; 135030f33e6dSPatrick McHardy } 13517d3dd043SPatrick McHardy 1352f5b321bdSPatrick McHardy static int process_sip_msg(struct sk_buff *skb, struct nf_conn *ct, 1353f5b321bdSPatrick McHardy unsigned int dataoff, const char **dptr, 1354f5b321bdSPatrick McHardy unsigned int *datalen) 1355f5b321bdSPatrick McHardy { 1356f5b321bdSPatrick McHardy typeof(nf_nat_sip_hook) nf_nat_sip; 1357f5b321bdSPatrick McHardy int ret; 1358f5b321bdSPatrick McHardy 1359f5b321bdSPatrick McHardy if (strnicmp(*dptr, "SIP/2.0 ", strlen("SIP/2.0 ")) != 0) 1360f5b321bdSPatrick McHardy ret = process_sip_request(skb, dataoff, dptr, datalen); 1361f5b321bdSPatrick McHardy else 1362f5b321bdSPatrick McHardy ret = process_sip_response(skb, dataoff, dptr, datalen); 1363f5b321bdSPatrick McHardy 1364f5b321bdSPatrick McHardy if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) { 1365f5b321bdSPatrick McHardy nf_nat_sip = rcu_dereference(nf_nat_sip_hook); 1366f5b321bdSPatrick McHardy if (nf_nat_sip && !nf_nat_sip(skb, dataoff, dptr, datalen)) 1367f5b321bdSPatrick McHardy ret = NF_DROP; 1368f5b321bdSPatrick McHardy } 1369f5b321bdSPatrick McHardy 1370f5b321bdSPatrick McHardy return ret; 1371f5b321bdSPatrick McHardy } 1372f5b321bdSPatrick McHardy 1373f5b321bdSPatrick McHardy static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, 1374f5b321bdSPatrick McHardy struct nf_conn *ct, enum ip_conntrack_info ctinfo) 1375f5b321bdSPatrick McHardy { 1376f5b321bdSPatrick McHardy struct tcphdr *th, _tcph; 1377f5b321bdSPatrick McHardy unsigned int dataoff, datalen; 1378f5b321bdSPatrick McHardy unsigned int matchoff, matchlen, clen; 1379f5b321bdSPatrick McHardy unsigned int msglen, origlen; 1380f5b321bdSPatrick McHardy const char *dptr, *end; 1381f5b321bdSPatrick McHardy s16 diff, tdiff = 0; 1382f5b321bdSPatrick McHardy int ret; 138348f8ac26SPatrick McHardy typeof(nf_nat_sip_seq_adjust_hook) nf_nat_sip_seq_adjust; 1384f5b321bdSPatrick McHardy 1385f5b321bdSPatrick McHardy if (ctinfo != IP_CT_ESTABLISHED && 1386f5b321bdSPatrick McHardy ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) 1387f5b321bdSPatrick McHardy return NF_ACCEPT; 1388f5b321bdSPatrick McHardy 1389f5b321bdSPatrick McHardy /* No Data ? */ 1390f5b321bdSPatrick McHardy th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph); 1391f5b321bdSPatrick McHardy if (th == NULL) 1392f5b321bdSPatrick McHardy return NF_ACCEPT; 1393f5b321bdSPatrick McHardy dataoff = protoff + th->doff * 4; 1394f5b321bdSPatrick McHardy if (dataoff >= skb->len) 1395f5b321bdSPatrick McHardy return NF_ACCEPT; 1396f5b321bdSPatrick McHardy 1397f5b321bdSPatrick McHardy nf_ct_refresh(ct, skb, sip_timeout * HZ); 1398f5b321bdSPatrick McHardy 1399a1d7c1b4SPatrick McHardy if (unlikely(skb_linearize(skb))) 1400a1d7c1b4SPatrick McHardy return NF_DROP; 1401f5b321bdSPatrick McHardy 1402f5b321bdSPatrick McHardy dptr = skb->data + dataoff; 1403f5b321bdSPatrick McHardy datalen = skb->len - dataoff; 1404f5b321bdSPatrick McHardy if (datalen < strlen("SIP/2.0 200")) 1405f5b321bdSPatrick McHardy return NF_ACCEPT; 1406f5b321bdSPatrick McHardy 1407f5b321bdSPatrick McHardy while (1) { 1408f5b321bdSPatrick McHardy if (ct_sip_get_header(ct, dptr, 0, datalen, 1409f5b321bdSPatrick McHardy SIP_HDR_CONTENT_LENGTH, 1410f5b321bdSPatrick McHardy &matchoff, &matchlen) <= 0) 1411f5b321bdSPatrick McHardy break; 1412f5b321bdSPatrick McHardy 1413f5b321bdSPatrick McHardy clen = simple_strtoul(dptr + matchoff, (char **)&end, 10); 1414f5b321bdSPatrick McHardy if (dptr + matchoff == end) 1415f5b321bdSPatrick McHardy break; 1416f5b321bdSPatrick McHardy 1417f5b321bdSPatrick McHardy if (end + strlen("\r\n\r\n") > dptr + datalen) 1418f5b321bdSPatrick McHardy break; 1419f5b321bdSPatrick McHardy if (end[0] != '\r' || end[1] != '\n' || 1420f5b321bdSPatrick McHardy end[2] != '\r' || end[3] != '\n') 1421f5b321bdSPatrick McHardy break; 1422f5b321bdSPatrick McHardy end += strlen("\r\n\r\n") + clen; 1423f5b321bdSPatrick McHardy 1424f5b321bdSPatrick McHardy msglen = origlen = end - dptr; 1425f5b321bdSPatrick McHardy 1426f5b321bdSPatrick McHardy ret = process_sip_msg(skb, ct, dataoff, &dptr, &msglen); 1427f5b321bdSPatrick McHardy if (ret != NF_ACCEPT) 1428f5b321bdSPatrick McHardy break; 1429f5b321bdSPatrick McHardy diff = msglen - origlen; 1430f5b321bdSPatrick McHardy tdiff += diff; 1431f5b321bdSPatrick McHardy 1432f5b321bdSPatrick McHardy dataoff += msglen; 1433f5b321bdSPatrick McHardy dptr += msglen; 1434f5b321bdSPatrick McHardy datalen = datalen + diff - msglen; 1435f5b321bdSPatrick McHardy } 1436f5b321bdSPatrick McHardy 143748f8ac26SPatrick McHardy if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) { 143848f8ac26SPatrick McHardy nf_nat_sip_seq_adjust = rcu_dereference(nf_nat_sip_seq_adjust_hook); 143948f8ac26SPatrick McHardy if (nf_nat_sip_seq_adjust) 144048f8ac26SPatrick McHardy nf_nat_sip_seq_adjust(skb, tdiff); 144148f8ac26SPatrick McHardy } 144248f8ac26SPatrick McHardy 1443f5b321bdSPatrick McHardy return ret; 1444f5b321bdSPatrick McHardy } 1445f5b321bdSPatrick McHardy 1446f5b321bdSPatrick McHardy static int sip_help_udp(struct sk_buff *skb, unsigned int protoff, 1447f5b321bdSPatrick McHardy struct nf_conn *ct, enum ip_conntrack_info ctinfo) 14489fafcd7bSPatrick McHardy { 14499fafcd7bSPatrick McHardy unsigned int dataoff, datalen; 14509fafcd7bSPatrick McHardy const char *dptr; 14519fafcd7bSPatrick McHardy 14529fafcd7bSPatrick McHardy /* No Data ? */ 14539fafcd7bSPatrick McHardy dataoff = protoff + sizeof(struct udphdr); 14543db05feaSHerbert Xu if (dataoff >= skb->len) 14559fafcd7bSPatrick McHardy return NF_ACCEPT; 14569fafcd7bSPatrick McHardy 14573db05feaSHerbert Xu nf_ct_refresh(ct, skb, sip_timeout * HZ); 14589fafcd7bSPatrick McHardy 1459a1d7c1b4SPatrick McHardy if (unlikely(skb_linearize(skb))) 1460a1d7c1b4SPatrick McHardy return NF_DROP; 14619fafcd7bSPatrick McHardy 1462f5b321bdSPatrick McHardy dptr = skb->data + dataoff; 14633db05feaSHerbert Xu datalen = skb->len - dataoff; 1464779382ebSPatrick McHardy if (datalen < strlen("SIP/2.0 200")) 14657d3dd043SPatrick McHardy return NF_ACCEPT; 14669fafcd7bSPatrick McHardy 1467f5b321bdSPatrick McHardy return process_sip_msg(skb, ct, dataoff, &dptr, &datalen); 146833cb1e9aSPatrick McHardy } 146933cb1e9aSPatrick McHardy 1470f5b321bdSPatrick McHardy static struct nf_conntrack_helper sip[MAX_PORTS][4] __read_mostly; 1471f5b321bdSPatrick McHardy static char sip_names[MAX_PORTS][4][sizeof("sip-65535")] __read_mostly; 14729fafcd7bSPatrick McHardy 14730f32a40fSPatrick McHardy static const struct nf_conntrack_expect_policy sip_exp_policy[SIP_EXPECT_MAX + 1] = { 14740f32a40fSPatrick McHardy [SIP_EXPECT_SIGNALLING] = { 1475b87921bdSPatrick McHardy .name = "signalling", 14760f32a40fSPatrick McHardy .max_expected = 1, 14776002f266SPatrick McHardy .timeout = 3 * 60, 14780f32a40fSPatrick McHardy }, 14790f32a40fSPatrick McHardy [SIP_EXPECT_AUDIO] = { 1480b87921bdSPatrick McHardy .name = "audio", 1481a9c1d359SPatrick McHardy .max_expected = 2 * IP_CT_DIR_MAX, 14820f32a40fSPatrick McHardy .timeout = 3 * 60, 14830f32a40fSPatrick McHardy }, 14840d0ab037SPatrick McHardy [SIP_EXPECT_VIDEO] = { 1485b87921bdSPatrick McHardy .name = "video", 14860d0ab037SPatrick McHardy .max_expected = 2 * IP_CT_DIR_MAX, 14870d0ab037SPatrick McHardy .timeout = 3 * 60, 14880d0ab037SPatrick McHardy }, 14899d288dffSPatrick McHardy [SIP_EXPECT_IMAGE] = { 14909d288dffSPatrick McHardy .name = "image", 14919d288dffSPatrick McHardy .max_expected = IP_CT_DIR_MAX, 14929d288dffSPatrick McHardy .timeout = 3 * 60, 14939d288dffSPatrick McHardy }, 14946002f266SPatrick McHardy }; 14956002f266SPatrick McHardy 14969fafcd7bSPatrick McHardy static void nf_conntrack_sip_fini(void) 14979fafcd7bSPatrick McHardy { 14989fafcd7bSPatrick McHardy int i, j; 14999fafcd7bSPatrick McHardy 15009fafcd7bSPatrick McHardy for (i = 0; i < ports_c; i++) { 1501f5b321bdSPatrick McHardy for (j = 0; j < ARRAY_SIZE(sip[i]); j++) { 15029fafcd7bSPatrick McHardy if (sip[i][j].me == NULL) 15039fafcd7bSPatrick McHardy continue; 15049fafcd7bSPatrick McHardy nf_conntrack_helper_unregister(&sip[i][j]); 15059fafcd7bSPatrick McHardy } 15069fafcd7bSPatrick McHardy } 15079fafcd7bSPatrick McHardy } 15089fafcd7bSPatrick McHardy 15099fafcd7bSPatrick McHardy static int __init nf_conntrack_sip_init(void) 15109fafcd7bSPatrick McHardy { 15119fafcd7bSPatrick McHardy int i, j, ret; 15129fafcd7bSPatrick McHardy char *tmpname; 15139fafcd7bSPatrick McHardy 15149fafcd7bSPatrick McHardy if (ports_c == 0) 15159fafcd7bSPatrick McHardy ports[ports_c++] = SIP_PORT; 15169fafcd7bSPatrick McHardy 15179fafcd7bSPatrick McHardy for (i = 0; i < ports_c; i++) { 15189fafcd7bSPatrick McHardy memset(&sip[i], 0, sizeof(sip[i])); 15199fafcd7bSPatrick McHardy 15209fafcd7bSPatrick McHardy sip[i][0].tuple.src.l3num = AF_INET; 1521f5b321bdSPatrick McHardy sip[i][0].tuple.dst.protonum = IPPROTO_UDP; 1522f5b321bdSPatrick McHardy sip[i][0].help = sip_help_udp; 1523f5b321bdSPatrick McHardy sip[i][1].tuple.src.l3num = AF_INET; 1524f5b321bdSPatrick McHardy sip[i][1].tuple.dst.protonum = IPPROTO_TCP; 1525f5b321bdSPatrick McHardy sip[i][1].help = sip_help_tcp; 1526f5b321bdSPatrick McHardy 1527f5b321bdSPatrick McHardy sip[i][2].tuple.src.l3num = AF_INET6; 1528f5b321bdSPatrick McHardy sip[i][2].tuple.dst.protonum = IPPROTO_UDP; 1529f5b321bdSPatrick McHardy sip[i][2].help = sip_help_udp; 1530f5b321bdSPatrick McHardy sip[i][3].tuple.src.l3num = AF_INET6; 1531f5b321bdSPatrick McHardy sip[i][3].tuple.dst.protonum = IPPROTO_TCP; 1532f5b321bdSPatrick McHardy sip[i][3].help = sip_help_tcp; 1533f5b321bdSPatrick McHardy 1534f5b321bdSPatrick McHardy for (j = 0; j < ARRAY_SIZE(sip[i]); j++) { 15359fafcd7bSPatrick McHardy sip[i][j].tuple.src.u.udp.port = htons(ports[i]); 15360f32a40fSPatrick McHardy sip[i][j].expect_policy = sip_exp_policy; 15370f32a40fSPatrick McHardy sip[i][j].expect_class_max = SIP_EXPECT_MAX; 15389fafcd7bSPatrick McHardy sip[i][j].me = THIS_MODULE; 15399fafcd7bSPatrick McHardy 15409fafcd7bSPatrick McHardy tmpname = &sip_names[i][j][0]; 15419fafcd7bSPatrick McHardy if (ports[i] == SIP_PORT) 15429fafcd7bSPatrick McHardy sprintf(tmpname, "sip"); 15439fafcd7bSPatrick McHardy else 15449fafcd7bSPatrick McHardy sprintf(tmpname, "sip-%u", i); 15459fafcd7bSPatrick McHardy sip[i][j].name = tmpname; 15469fafcd7bSPatrick McHardy 15470d53778eSPatrick McHardy pr_debug("port #%u: %u\n", i, ports[i]); 15489fafcd7bSPatrick McHardy 15499fafcd7bSPatrick McHardy ret = nf_conntrack_helper_register(&sip[i][j]); 15509fafcd7bSPatrick McHardy if (ret) { 1551654d0fbdSStephen Hemminger printk(KERN_ERR "nf_ct_sip: failed to register" 1552654d0fbdSStephen Hemminger " helper for pf: %u port: %u\n", 15539fafcd7bSPatrick McHardy sip[i][j].tuple.src.l3num, ports[i]); 15549fafcd7bSPatrick McHardy nf_conntrack_sip_fini(); 15559fafcd7bSPatrick McHardy return ret; 15569fafcd7bSPatrick McHardy } 15579fafcd7bSPatrick McHardy } 15589fafcd7bSPatrick McHardy } 15599fafcd7bSPatrick McHardy return 0; 15609fafcd7bSPatrick McHardy } 15619fafcd7bSPatrick McHardy 15629fafcd7bSPatrick McHardy module_init(nf_conntrack_sip_init); 15639fafcd7bSPatrick McHardy module_exit(nf_conntrack_sip_fini); 1564