[NETFILTER]: nf_conntrack_sip: create signalling expectations
Create expectations for incoming signalling connections when seeing a REGISTER request. This is needed when the registrar uses a different source port number for signalling messages and for receiving incoming calls from other endpoints than the registrar. Signed-off-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
c978cd3a93
Коммит
0f32a40fc9
|
@ -5,6 +5,17 @@
|
||||||
#define SIP_PORT 5060
|
#define SIP_PORT 5060
|
||||||
#define SIP_TIMEOUT 3600
|
#define SIP_TIMEOUT 3600
|
||||||
|
|
||||||
|
struct nf_ct_sip_master {
|
||||||
|
unsigned int register_cseq;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum sip_expectation_classes {
|
||||||
|
SIP_EXPECT_SIGNALLING,
|
||||||
|
SIP_EXPECT_AUDIO,
|
||||||
|
__SIP_EXPECT_MAX
|
||||||
|
};
|
||||||
|
#define SIP_EXPECT_MAX (__SIP_EXPECT_MAX - 1)
|
||||||
|
|
||||||
struct sip_handler {
|
struct sip_handler {
|
||||||
const char *method;
|
const char *method;
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
|
@ -59,6 +70,7 @@ enum sip_header_types {
|
||||||
SIP_HDR_TO,
|
SIP_HDR_TO,
|
||||||
SIP_HDR_CONTACT,
|
SIP_HDR_CONTACT,
|
||||||
SIP_HDR_VIA,
|
SIP_HDR_VIA,
|
||||||
|
SIP_HDR_EXPIRES,
|
||||||
SIP_HDR_CONTENT_LENGTH,
|
SIP_HDR_CONTENT_LENGTH,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -75,6 +87,12 @@ enum sdp_header_types {
|
||||||
extern unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb,
|
extern unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb,
|
||||||
const char **dptr,
|
const char **dptr,
|
||||||
unsigned int *datalen);
|
unsigned int *datalen);
|
||||||
|
extern unsigned int (*nf_nat_sip_expect_hook)(struct sk_buff *skb,
|
||||||
|
const char **dptr,
|
||||||
|
unsigned int *datalen,
|
||||||
|
struct nf_conntrack_expect *exp,
|
||||||
|
unsigned int matchoff,
|
||||||
|
unsigned int matchlen);
|
||||||
extern unsigned int (*nf_nat_sdp_hook)(struct sk_buff *skb,
|
extern unsigned int (*nf_nat_sdp_hook)(struct sk_buff *skb,
|
||||||
const char **dptr,
|
const char **dptr,
|
||||||
unsigned int *datalen,
|
unsigned int *datalen,
|
||||||
|
|
|
@ -46,6 +46,7 @@ union nf_conntrack_expect_proto {
|
||||||
#include <linux/netfilter/nf_conntrack_pptp.h>
|
#include <linux/netfilter/nf_conntrack_pptp.h>
|
||||||
#include <linux/netfilter/nf_conntrack_h323.h>
|
#include <linux/netfilter/nf_conntrack_h323.h>
|
||||||
#include <linux/netfilter/nf_conntrack_sane.h>
|
#include <linux/netfilter/nf_conntrack_sane.h>
|
||||||
|
#include <linux/netfilter/nf_conntrack_sip.h>
|
||||||
|
|
||||||
/* per conntrack: application helper private data */
|
/* per conntrack: application helper private data */
|
||||||
union nf_conntrack_help {
|
union nf_conntrack_help {
|
||||||
|
@ -54,6 +55,7 @@ union nf_conntrack_help {
|
||||||
struct nf_ct_pptp_master ct_pptp_info;
|
struct nf_ct_pptp_master ct_pptp_info;
|
||||||
struct nf_ct_h323_master ct_h323_info;
|
struct nf_ct_h323_master ct_h323_info;
|
||||||
struct nf_ct_sane_master ct_sane_info;
|
struct nf_ct_sane_master ct_sane_info;
|
||||||
|
struct nf_ct_sip_master ct_sip_info;
|
||||||
};
|
};
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
@ -76,7 +78,7 @@ do { \
|
||||||
struct nf_conntrack_helper;
|
struct nf_conntrack_helper;
|
||||||
|
|
||||||
/* Must be kept in sync with the classes defined by helpers */
|
/* Must be kept in sync with the classes defined by helpers */
|
||||||
#define NF_CT_MAX_EXPECT_CLASSES 1
|
#define NF_CT_MAX_EXPECT_CLASSES 2
|
||||||
|
|
||||||
/* nf_conn feature for connections that have a helper */
|
/* nf_conn feature for connections that have a helper */
|
||||||
struct nf_conn_help {
|
struct nf_conn_help {
|
||||||
|
|
|
@ -205,6 +205,91 @@ next:
|
||||||
return NF_ACCEPT;
|
return NF_ACCEPT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Handles expected signalling connections and media streams */
|
||||||
|
static void ip_nat_sip_expected(struct nf_conn *ct,
|
||||||
|
struct nf_conntrack_expect *exp)
|
||||||
|
{
|
||||||
|
struct nf_nat_range range;
|
||||||
|
|
||||||
|
/* This must be a fresh one. */
|
||||||
|
BUG_ON(ct->status & IPS_NAT_DONE_MASK);
|
||||||
|
|
||||||
|
/* For DST manip, map port here to where it's expected. */
|
||||||
|
range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED);
|
||||||
|
range.min = range.max = exp->saved_proto;
|
||||||
|
range.min_ip = range.max_ip = exp->saved_ip;
|
||||||
|
nf_nat_setup_info(ct, &range, IP_NAT_MANIP_DST);
|
||||||
|
|
||||||
|
/* Change src to where master sends to, but only if the connection
|
||||||
|
* actually came from the same source. */
|
||||||
|
if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip ==
|
||||||
|
ct->master->tuplehash[exp->dir].tuple.src.u3.ip) {
|
||||||
|
range.flags = IP_NAT_RANGE_MAP_IPS;
|
||||||
|
range.min_ip = range.max_ip
|
||||||
|
= ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip;
|
||||||
|
nf_nat_setup_info(ct, &range, IP_NAT_MANIP_SRC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int ip_nat_sip_expect(struct sk_buff *skb,
|
||||||
|
const char **dptr, unsigned int *datalen,
|
||||||
|
struct nf_conntrack_expect *exp,
|
||||||
|
unsigned int matchoff,
|
||||||
|
unsigned int matchlen)
|
||||||
|
{
|
||||||
|
enum ip_conntrack_info ctinfo;
|
||||||
|
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
|
||||||
|
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
|
||||||
|
__be32 newip;
|
||||||
|
u_int16_t port;
|
||||||
|
char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
|
||||||
|
unsigned buflen;
|
||||||
|
|
||||||
|
/* Connection will come from reply */
|
||||||
|
if (ct->tuplehash[dir].tuple.src.u3.ip == ct->tuplehash[!dir].tuple.dst.u3.ip)
|
||||||
|
newip = exp->tuple.dst.u3.ip;
|
||||||
|
else
|
||||||
|
newip = ct->tuplehash[!dir].tuple.dst.u3.ip;
|
||||||
|
|
||||||
|
/* If the signalling port matches the connection's source port in the
|
||||||
|
* original direction, try to use the destination port in the opposite
|
||||||
|
* direction. */
|
||||||
|
if (exp->tuple.dst.u.udp.port ==
|
||||||
|
ct->tuplehash[dir].tuple.src.u.udp.port)
|
||||||
|
port = ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.port);
|
||||||
|
else
|
||||||
|
port = ntohs(exp->tuple.dst.u.udp.port);
|
||||||
|
|
||||||
|
exp->saved_ip = exp->tuple.dst.u3.ip;
|
||||||
|
exp->tuple.dst.u3.ip = newip;
|
||||||
|
exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port;
|
||||||
|
exp->dir = !dir;
|
||||||
|
exp->expectfn = ip_nat_sip_expected;
|
||||||
|
|
||||||
|
for (; port != 0; port++) {
|
||||||
|
exp->tuple.dst.u.udp.port = htons(port);
|
||||||
|
if (nf_ct_expect_related(exp) == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (port == 0)
|
||||||
|
return NF_DROP;
|
||||||
|
|
||||||
|
if (exp->tuple.dst.u3.ip != exp->saved_ip ||
|
||||||
|
exp->tuple.dst.u.udp.port != exp->saved_proto.udp.port) {
|
||||||
|
buflen = sprintf(buffer, "%u.%u.%u.%u:%u",
|
||||||
|
NIPQUAD(newip), port);
|
||||||
|
if (!mangle_packet(skb, dptr, datalen, matchoff, matchlen,
|
||||||
|
buffer, buflen))
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
return NF_ACCEPT;
|
||||||
|
|
||||||
|
err:
|
||||||
|
nf_ct_unexpect_related(exp);
|
||||||
|
return NF_DROP;
|
||||||
|
}
|
||||||
|
|
||||||
static int mangle_content_len(struct sk_buff *skb,
|
static int mangle_content_len(struct sk_buff *skb,
|
||||||
const char **dptr, unsigned int *datalen)
|
const char **dptr, unsigned int *datalen)
|
||||||
{
|
{
|
||||||
|
@ -275,27 +360,6 @@ static unsigned int mangle_sdp(struct sk_buff *skb,
|
||||||
return mangle_content_len(skb, dptr, datalen);
|
return mangle_content_len(skb, dptr, datalen);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ip_nat_sdp_expect(struct nf_conn *ct,
|
|
||||||
struct nf_conntrack_expect *exp)
|
|
||||||
{
|
|
||||||
struct nf_nat_range range;
|
|
||||||
|
|
||||||
/* This must be a fresh one. */
|
|
||||||
BUG_ON(ct->status & IPS_NAT_DONE_MASK);
|
|
||||||
|
|
||||||
/* For DST manip, map port here to where it's expected. */
|
|
||||||
range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED);
|
|
||||||
range.min = range.max = exp->saved_proto;
|
|
||||||
range.min_ip = range.max_ip = exp->saved_ip;
|
|
||||||
nf_nat_setup_info(ct, &range, IP_NAT_MANIP_DST);
|
|
||||||
|
|
||||||
/* Change src to where master sends to */
|
|
||||||
range.flags = IP_NAT_RANGE_MAP_IPS;
|
|
||||||
range.min_ip = range.max_ip
|
|
||||||
= ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip;
|
|
||||||
nf_nat_setup_info(ct, &range, IP_NAT_MANIP_SRC);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* So, this packet has hit the connection tracking matching code.
|
/* So, this packet has hit the connection tracking matching code.
|
||||||
Mangle it, and change the expectation to match the new version. */
|
Mangle it, and change the expectation to match the new version. */
|
||||||
static unsigned int ip_nat_sdp(struct sk_buff *skb,
|
static unsigned int ip_nat_sdp(struct sk_buff *skb,
|
||||||
|
@ -322,7 +386,7 @@ static unsigned int ip_nat_sdp(struct sk_buff *skb,
|
||||||
|
|
||||||
/* When you see the packet, we need to NAT it the same as the
|
/* When you see the packet, we need to NAT it the same as the
|
||||||
this one. */
|
this one. */
|
||||||
exp->expectfn = ip_nat_sdp_expect;
|
exp->expectfn = ip_nat_sip_expected;
|
||||||
|
|
||||||
/* Try to get same port: if not, try to change it. */
|
/* Try to get same port: if not, try to change it. */
|
||||||
for (port = ntohs(exp->saved_proto.udp.port); port != 0; port++) {
|
for (port = ntohs(exp->saved_proto.udp.port); port != 0; port++) {
|
||||||
|
@ -344,6 +408,7 @@ static unsigned int ip_nat_sdp(struct sk_buff *skb,
|
||||||
static void __exit nf_nat_sip_fini(void)
|
static void __exit nf_nat_sip_fini(void)
|
||||||
{
|
{
|
||||||
rcu_assign_pointer(nf_nat_sip_hook, NULL);
|
rcu_assign_pointer(nf_nat_sip_hook, NULL);
|
||||||
|
rcu_assign_pointer(nf_nat_sip_expect_hook, NULL);
|
||||||
rcu_assign_pointer(nf_nat_sdp_hook, NULL);
|
rcu_assign_pointer(nf_nat_sdp_hook, NULL);
|
||||||
synchronize_rcu();
|
synchronize_rcu();
|
||||||
}
|
}
|
||||||
|
@ -351,8 +416,10 @@ static void __exit nf_nat_sip_fini(void)
|
||||||
static int __init nf_nat_sip_init(void)
|
static int __init nf_nat_sip_init(void)
|
||||||
{
|
{
|
||||||
BUG_ON(nf_nat_sip_hook != NULL);
|
BUG_ON(nf_nat_sip_hook != NULL);
|
||||||
|
BUG_ON(nf_nat_sip_expect_hook != NULL);
|
||||||
BUG_ON(nf_nat_sdp_hook != NULL);
|
BUG_ON(nf_nat_sdp_hook != NULL);
|
||||||
rcu_assign_pointer(nf_nat_sip_hook, ip_nat_sip);
|
rcu_assign_pointer(nf_nat_sip_hook, ip_nat_sip);
|
||||||
|
rcu_assign_pointer(nf_nat_sip_expect_hook, ip_nat_sip_expect);
|
||||||
rcu_assign_pointer(nf_nat_sdp_hook, ip_nat_sdp);
|
rcu_assign_pointer(nf_nat_sdp_hook, ip_nat_sdp);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,11 +37,24 @@ static unsigned int sip_timeout __read_mostly = SIP_TIMEOUT;
|
||||||
module_param(sip_timeout, uint, 0600);
|
module_param(sip_timeout, uint, 0600);
|
||||||
MODULE_PARM_DESC(sip_timeout, "timeout for the master SIP session");
|
MODULE_PARM_DESC(sip_timeout, "timeout for the master SIP session");
|
||||||
|
|
||||||
|
static int sip_direct_signalling __read_mostly = 1;
|
||||||
|
module_param(sip_direct_signalling, int, 0600);
|
||||||
|
MODULE_PARM_DESC(sip_direct_signalling, "expect incoming calls from registrar "
|
||||||
|
"only (default 1)");
|
||||||
|
|
||||||
unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb,
|
unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb,
|
||||||
const char **dptr,
|
const char **dptr,
|
||||||
unsigned int *datalen) __read_mostly;
|
unsigned int *datalen) __read_mostly;
|
||||||
EXPORT_SYMBOL_GPL(nf_nat_sip_hook);
|
EXPORT_SYMBOL_GPL(nf_nat_sip_hook);
|
||||||
|
|
||||||
|
unsigned int (*nf_nat_sip_expect_hook)(struct sk_buff *skb,
|
||||||
|
const char **dptr,
|
||||||
|
unsigned int *datalen,
|
||||||
|
struct nf_conntrack_expect *exp,
|
||||||
|
unsigned int matchoff,
|
||||||
|
unsigned int matchlen) __read_mostly;
|
||||||
|
EXPORT_SYMBOL_GPL(nf_nat_sip_expect_hook);
|
||||||
|
|
||||||
unsigned int (*nf_nat_sdp_hook)(struct sk_buff *skb,
|
unsigned int (*nf_nat_sdp_hook)(struct sk_buff *skb,
|
||||||
const char **dptr,
|
const char **dptr,
|
||||||
unsigned int *datalen,
|
unsigned int *datalen,
|
||||||
|
@ -218,6 +231,7 @@ static const struct sip_header ct_sip_hdrs[] = {
|
||||||
[SIP_HDR_TO] = SIP_HDR("To", "t", "sip:", skp_epaddr_len),
|
[SIP_HDR_TO] = SIP_HDR("To", "t", "sip:", skp_epaddr_len),
|
||||||
[SIP_HDR_CONTACT] = SIP_HDR("Contact", "m", "sip:", skp_epaddr_len),
|
[SIP_HDR_CONTACT] = SIP_HDR("Contact", "m", "sip:", skp_epaddr_len),
|
||||||
[SIP_HDR_VIA] = SIP_HDR("Via", "v", "UDP ", epaddr_len),
|
[SIP_HDR_VIA] = SIP_HDR("Via", "v", "UDP ", epaddr_len),
|
||||||
|
[SIP_HDR_EXPIRES] = SIP_HDR("Expires", NULL, NULL, digits_len),
|
||||||
[SIP_HDR_CONTENT_LENGTH] = SIP_HDR("Content-Length", "l", NULL, digits_len),
|
[SIP_HDR_CONTENT_LENGTH] = SIP_HDR("Content-Length", "l", NULL, digits_len),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -592,7 +606,35 @@ int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(ct_sip_get_sdp_header);
|
EXPORT_SYMBOL_GPL(ct_sip_get_sdp_header);
|
||||||
|
|
||||||
static void flush_expectations(struct nf_conn *ct)
|
static int refresh_signalling_expectation(struct nf_conn *ct,
|
||||||
|
union nf_inet_addr *addr,
|
||||||
|
__be16 port,
|
||||||
|
unsigned int expires)
|
||||||
|
{
|
||||||
|
struct nf_conn_help *help = nfct_help(ct);
|
||||||
|
struct nf_conntrack_expect *exp;
|
||||||
|
struct hlist_node *n, *next;
|
||||||
|
int found = 0;
|
||||||
|
|
||||||
|
spin_lock_bh(&nf_conntrack_lock);
|
||||||
|
hlist_for_each_entry_safe(exp, n, next, &help->expectations, lnode) {
|
||||||
|
if (exp->class != SIP_EXPECT_SIGNALLING ||
|
||||||
|
!nf_inet_addr_cmp(&exp->tuple.dst.u3, addr) ||
|
||||||
|
exp->tuple.dst.u.udp.port != port)
|
||||||
|
continue;
|
||||||
|
if (!del_timer(&exp->timeout))
|
||||||
|
continue;
|
||||||
|
exp->flags &= ~NF_CT_EXPECT_INACTIVE;
|
||||||
|
exp->timeout.expires = jiffies + expires * HZ;
|
||||||
|
add_timer(&exp->timeout);
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
spin_unlock_bh(&nf_conntrack_lock);
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void flush_expectations(struct nf_conn *ct, bool media)
|
||||||
{
|
{
|
||||||
struct nf_conn_help *help = nfct_help(ct);
|
struct nf_conn_help *help = nfct_help(ct);
|
||||||
struct nf_conntrack_expect *exp;
|
struct nf_conntrack_expect *exp;
|
||||||
|
@ -600,10 +642,14 @@ static void flush_expectations(struct nf_conn *ct)
|
||||||
|
|
||||||
spin_lock_bh(&nf_conntrack_lock);
|
spin_lock_bh(&nf_conntrack_lock);
|
||||||
hlist_for_each_entry_safe(exp, n, next, &help->expectations, lnode) {
|
hlist_for_each_entry_safe(exp, n, next, &help->expectations, lnode) {
|
||||||
|
if ((exp->class != SIP_EXPECT_SIGNALLING) ^ media)
|
||||||
|
continue;
|
||||||
if (!del_timer(&exp->timeout))
|
if (!del_timer(&exp->timeout))
|
||||||
continue;
|
continue;
|
||||||
nf_ct_unlink_expect(exp);
|
nf_ct_unlink_expect(exp);
|
||||||
nf_ct_expect_put(exp);
|
nf_ct_expect_put(exp);
|
||||||
|
if (!media)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
spin_unlock_bh(&nf_conntrack_lock);
|
spin_unlock_bh(&nf_conntrack_lock);
|
||||||
}
|
}
|
||||||
|
@ -623,7 +669,7 @@ static int set_expected_rtp(struct sk_buff *skb,
|
||||||
exp = nf_ct_expect_alloc(ct);
|
exp = nf_ct_expect_alloc(ct);
|
||||||
if (exp == NULL)
|
if (exp == NULL)
|
||||||
return NF_DROP;
|
return NF_DROP;
|
||||||
nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, family,
|
nf_ct_expect_init(exp, SIP_EXPECT_AUDIO, family,
|
||||||
&ct->tuplehash[!dir].tuple.src.u3, addr,
|
&ct->tuplehash[!dir].tuple.src.u3, addr,
|
||||||
IPPROTO_UDP, NULL, &port);
|
IPPROTO_UDP, NULL, &port);
|
||||||
|
|
||||||
|
@ -688,7 +734,7 @@ static int process_invite_response(struct sk_buff *skb,
|
||||||
(code >= 200 && code <= 299))
|
(code >= 200 && code <= 299))
|
||||||
return process_sdp(skb, dptr, datalen, cseq);
|
return process_sdp(skb, dptr, datalen, cseq);
|
||||||
else {
|
else {
|
||||||
flush_expectations(ct);
|
flush_expectations(ct, true);
|
||||||
return NF_ACCEPT;
|
return NF_ACCEPT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -704,7 +750,7 @@ static int process_update_response(struct sk_buff *skb,
|
||||||
(code >= 200 && code <= 299))
|
(code >= 200 && code <= 299))
|
||||||
return process_sdp(skb, dptr, datalen, cseq);
|
return process_sdp(skb, dptr, datalen, cseq);
|
||||||
else {
|
else {
|
||||||
flush_expectations(ct);
|
flush_expectations(ct, true);
|
||||||
return NF_ACCEPT;
|
return NF_ACCEPT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -720,7 +766,7 @@ static int process_prack_response(struct sk_buff *skb,
|
||||||
(code >= 200 && code <= 299))
|
(code >= 200 && code <= 299))
|
||||||
return process_sdp(skb, dptr, datalen, cseq);
|
return process_sdp(skb, dptr, datalen, cseq);
|
||||||
else {
|
else {
|
||||||
flush_expectations(ct);
|
flush_expectations(ct, true);
|
||||||
return NF_ACCEPT;
|
return NF_ACCEPT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -732,7 +778,165 @@ static int process_bye_request(struct sk_buff *skb,
|
||||||
enum ip_conntrack_info ctinfo;
|
enum ip_conntrack_info ctinfo;
|
||||||
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
|
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
|
||||||
|
|
||||||
flush_expectations(ct);
|
flush_expectations(ct, true);
|
||||||
|
return NF_ACCEPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse a REGISTER request and create a permanent expectation for incoming
|
||||||
|
* signalling connections. The expectation is marked inactive and is activated
|
||||||
|
* when receiving a response indicating success from the registrar.
|
||||||
|
*/
|
||||||
|
static int process_register_request(struct sk_buff *skb,
|
||||||
|
const char **dptr, unsigned int *datalen,
|
||||||
|
unsigned int cseq)
|
||||||
|
{
|
||||||
|
enum ip_conntrack_info ctinfo;
|
||||||
|
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
|
||||||
|
struct nf_conn_help *help = nfct_help(ct);
|
||||||
|
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
|
||||||
|
int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
|
||||||
|
unsigned int matchoff, matchlen;
|
||||||
|
struct nf_conntrack_expect *exp;
|
||||||
|
union nf_inet_addr *saddr, daddr;
|
||||||
|
__be16 port;
|
||||||
|
unsigned int expires = 0;
|
||||||
|
int ret;
|
||||||
|
typeof(nf_nat_sip_expect_hook) nf_nat_sip_expect;
|
||||||
|
|
||||||
|
/* Expected connections can not register again. */
|
||||||
|
if (ct->status & IPS_EXPECTED)
|
||||||
|
return NF_ACCEPT;
|
||||||
|
|
||||||
|
/* We must check the expiration time: a value of zero signals the
|
||||||
|
* registrar to release the binding. We'll remove our expectation
|
||||||
|
* when receiving the new bindings in the response, but we don't
|
||||||
|
* want to create new ones.
|
||||||
|
*
|
||||||
|
* The expiration time may be contained in Expires: header, the
|
||||||
|
* Contact: header parameters or the URI parameters.
|
||||||
|
*/
|
||||||
|
if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_EXPIRES,
|
||||||
|
&matchoff, &matchlen) > 0)
|
||||||
|
expires = simple_strtoul(*dptr + matchoff, NULL, 10);
|
||||||
|
|
||||||
|
ret = ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen,
|
||||||
|
SIP_HDR_CONTACT, NULL,
|
||||||
|
&matchoff, &matchlen, &daddr, &port);
|
||||||
|
if (ret < 0)
|
||||||
|
return NF_DROP;
|
||||||
|
else if (ret == 0)
|
||||||
|
return NF_ACCEPT;
|
||||||
|
|
||||||
|
/* We don't support third-party registrations */
|
||||||
|
if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, &daddr))
|
||||||
|
return NF_ACCEPT;
|
||||||
|
|
||||||
|
if (ct_sip_parse_numerical_param(ct, *dptr,
|
||||||
|
matchoff + matchlen, *datalen,
|
||||||
|
"expires=", NULL, NULL, &expires) < 0)
|
||||||
|
return NF_DROP;
|
||||||
|
|
||||||
|
if (expires == 0) {
|
||||||
|
ret = NF_ACCEPT;
|
||||||
|
goto store_cseq;
|
||||||
|
}
|
||||||
|
|
||||||
|
exp = nf_ct_expect_alloc(ct);
|
||||||
|
if (!exp)
|
||||||
|
return NF_DROP;
|
||||||
|
|
||||||
|
saddr = NULL;
|
||||||
|
if (sip_direct_signalling)
|
||||||
|
saddr = &ct->tuplehash[!dir].tuple.src.u3;
|
||||||
|
|
||||||
|
nf_ct_expect_init(exp, SIP_EXPECT_SIGNALLING, family, saddr, &daddr,
|
||||||
|
IPPROTO_UDP, NULL, &port);
|
||||||
|
exp->timeout.expires = sip_timeout * HZ;
|
||||||
|
exp->helper = nfct_help(ct)->helper;
|
||||||
|
exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE;
|
||||||
|
|
||||||
|
nf_nat_sip_expect = rcu_dereference(nf_nat_sip_expect_hook);
|
||||||
|
if (nf_nat_sip_expect && ct->status & IPS_NAT_MASK)
|
||||||
|
ret = nf_nat_sip_expect(skb, dptr, datalen, exp,
|
||||||
|
matchoff, matchlen);
|
||||||
|
else {
|
||||||
|
if (nf_ct_expect_related(exp) != 0)
|
||||||
|
ret = NF_DROP;
|
||||||
|
else
|
||||||
|
ret = NF_ACCEPT;
|
||||||
|
}
|
||||||
|
nf_ct_expect_put(exp);
|
||||||
|
|
||||||
|
store_cseq:
|
||||||
|
if (ret == NF_ACCEPT)
|
||||||
|
help->help.ct_sip_info.register_cseq = cseq;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int process_register_response(struct sk_buff *skb,
|
||||||
|
const char **dptr, unsigned int *datalen,
|
||||||
|
unsigned int cseq, unsigned int code)
|
||||||
|
{
|
||||||
|
enum ip_conntrack_info ctinfo;
|
||||||
|
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
|
||||||
|
struct nf_conn_help *help = nfct_help(ct);
|
||||||
|
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
|
||||||
|
union nf_inet_addr addr;
|
||||||
|
__be16 port;
|
||||||
|
unsigned int matchoff, matchlen, dataoff = 0;
|
||||||
|
unsigned int expires = 0;
|
||||||
|
int in_contact = 0, ret;
|
||||||
|
|
||||||
|
/* According to RFC 3261, "UAs MUST NOT send a new registration until
|
||||||
|
* they have received a final response from the registrar for the
|
||||||
|
* previous one or the previous REGISTER request has timed out".
|
||||||
|
*
|
||||||
|
* However, some servers fail to detect retransmissions and send late
|
||||||
|
* responses, so we store the sequence number of the last valid
|
||||||
|
* request and compare it here.
|
||||||
|
*/
|
||||||
|
if (help->help.ct_sip_info.register_cseq != cseq)
|
||||||
|
return NF_ACCEPT;
|
||||||
|
|
||||||
|
if (code >= 100 && code <= 199)
|
||||||
|
return NF_ACCEPT;
|
||||||
|
if (code < 200 || code > 299)
|
||||||
|
goto flush;
|
||||||
|
|
||||||
|
if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_EXPIRES,
|
||||||
|
&matchoff, &matchlen) > 0)
|
||||||
|
expires = simple_strtoul(*dptr + matchoff, NULL, 10);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
unsigned int c_expires = expires;
|
||||||
|
|
||||||
|
ret = ct_sip_parse_header_uri(ct, *dptr, &dataoff, *datalen,
|
||||||
|
SIP_HDR_CONTACT, &in_contact,
|
||||||
|
&matchoff, &matchlen,
|
||||||
|
&addr, &port);
|
||||||
|
if (ret < 0)
|
||||||
|
return NF_DROP;
|
||||||
|
else if (ret == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* We don't support third-party registrations */
|
||||||
|
if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, &addr))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = ct_sip_parse_numerical_param(ct, *dptr,
|
||||||
|
matchoff + matchlen,
|
||||||
|
*datalen, "expires=",
|
||||||
|
NULL, NULL, &c_expires);
|
||||||
|
if (ret < 0)
|
||||||
|
return NF_DROP;
|
||||||
|
if (c_expires == 0)
|
||||||
|
break;
|
||||||
|
if (refresh_signalling_expectation(ct, &addr, port, c_expires))
|
||||||
|
return NF_ACCEPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
flush:
|
||||||
|
flush_expectations(ct, false);
|
||||||
return NF_ACCEPT;
|
return NF_ACCEPT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -742,6 +946,7 @@ static const struct sip_handler sip_handlers[] = {
|
||||||
SIP_HANDLER("ACK", process_sdp, NULL),
|
SIP_HANDLER("ACK", process_sdp, NULL),
|
||||||
SIP_HANDLER("PRACK", process_sdp, process_prack_response),
|
SIP_HANDLER("PRACK", process_sdp, process_prack_response),
|
||||||
SIP_HANDLER("BYE", process_bye_request, NULL),
|
SIP_HANDLER("BYE", process_bye_request, NULL),
|
||||||
|
SIP_HANDLER("REGISTER", process_register_request, process_register_response),
|
||||||
};
|
};
|
||||||
|
|
||||||
static int process_sip_response(struct sk_buff *skb,
|
static int process_sip_response(struct sk_buff *skb,
|
||||||
|
@ -853,9 +1058,15 @@ static int sip_help(struct sk_buff *skb,
|
||||||
static struct nf_conntrack_helper sip[MAX_PORTS][2] __read_mostly;
|
static struct nf_conntrack_helper sip[MAX_PORTS][2] __read_mostly;
|
||||||
static char sip_names[MAX_PORTS][2][sizeof("sip-65535")] __read_mostly;
|
static char sip_names[MAX_PORTS][2][sizeof("sip-65535")] __read_mostly;
|
||||||
|
|
||||||
static const struct nf_conntrack_expect_policy sip_exp_policy = {
|
static const struct nf_conntrack_expect_policy sip_exp_policy[SIP_EXPECT_MAX + 1] = {
|
||||||
.max_expected = 2,
|
[SIP_EXPECT_SIGNALLING] = {
|
||||||
.timeout = 3 * 60,
|
.max_expected = 1,
|
||||||
|
.timeout = 3 * 60,
|
||||||
|
},
|
||||||
|
[SIP_EXPECT_AUDIO] = {
|
||||||
|
.max_expected = IP_CT_DIR_MAX,
|
||||||
|
.timeout = 3 * 60,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static void nf_conntrack_sip_fini(void)
|
static void nf_conntrack_sip_fini(void)
|
||||||
|
@ -887,7 +1098,8 @@ static int __init nf_conntrack_sip_init(void)
|
||||||
for (j = 0; j < 2; j++) {
|
for (j = 0; j < 2; j++) {
|
||||||
sip[i][j].tuple.dst.protonum = IPPROTO_UDP;
|
sip[i][j].tuple.dst.protonum = IPPROTO_UDP;
|
||||||
sip[i][j].tuple.src.u.udp.port = htons(ports[i]);
|
sip[i][j].tuple.src.u.udp.port = htons(ports[i]);
|
||||||
sip[i][j].expect_policy = &sip_exp_policy;
|
sip[i][j].expect_policy = sip_exp_policy;
|
||||||
|
sip[i][j].expect_class_max = SIP_EXPECT_MAX;
|
||||||
sip[i][j].me = THIS_MODULE;
|
sip[i][j].me = THIS_MODULE;
|
||||||
sip[i][j].help = sip_help;
|
sip[i][j].help = sip_help;
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче