[NETFILTER]: nf_conntrack_sip: RTP routing optimization
Optimize call routing between NATed endpoints: when an external registrar sends a media description that contains an existing RTP expectation from a different SNATed connection, the gatekeeper is trying to route the call directly between the two endpoints. We assume both endpoints can reach each other directly and "un-NAT" the addresses, which makes the media stream go between the two endpoints directly. Signed-off-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
committed by
David S. Miller
parent
0d0ab0378d
commit
c7f485abd6
@@ -114,6 +114,12 @@ extern unsigned int (*nf_nat_sdp_addr_hook)(struct sk_buff *skb,
|
|||||||
enum sdp_header_types type,
|
enum sdp_header_types type,
|
||||||
enum sdp_header_types term,
|
enum sdp_header_types term,
|
||||||
const union nf_inet_addr *addr);
|
const union nf_inet_addr *addr);
|
||||||
|
extern unsigned int (*nf_nat_sdp_port_hook)(struct sk_buff *skb,
|
||||||
|
const char **dptr,
|
||||||
|
unsigned int *datalen,
|
||||||
|
unsigned int matchoff,
|
||||||
|
unsigned int matchlen,
|
||||||
|
u_int16_t port);
|
||||||
extern unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb,
|
extern unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb,
|
||||||
const char **dptr,
|
const char **dptr,
|
||||||
unsigned int dataoff,
|
unsigned int dataoff,
|
||||||
|
@@ -461,6 +461,7 @@ 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_sip_expect_hook, NULL);
|
||||||
rcu_assign_pointer(nf_nat_sdp_addr_hook, NULL);
|
rcu_assign_pointer(nf_nat_sdp_addr_hook, NULL);
|
||||||
|
rcu_assign_pointer(nf_nat_sdp_port_hook, NULL);
|
||||||
rcu_assign_pointer(nf_nat_sdp_session_hook, NULL);
|
rcu_assign_pointer(nf_nat_sdp_session_hook, NULL);
|
||||||
rcu_assign_pointer(nf_nat_sdp_media_hook, NULL);
|
rcu_assign_pointer(nf_nat_sdp_media_hook, NULL);
|
||||||
synchronize_rcu();
|
synchronize_rcu();
|
||||||
@@ -471,11 +472,13 @@ 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_sip_expect_hook != NULL);
|
||||||
BUG_ON(nf_nat_sdp_addr_hook != NULL);
|
BUG_ON(nf_nat_sdp_addr_hook != NULL);
|
||||||
|
BUG_ON(nf_nat_sdp_port_hook != NULL);
|
||||||
BUG_ON(nf_nat_sdp_session_hook != NULL);
|
BUG_ON(nf_nat_sdp_session_hook != NULL);
|
||||||
BUG_ON(nf_nat_sdp_media_hook != NULL);
|
BUG_ON(nf_nat_sdp_media_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_sip_expect_hook, ip_nat_sip_expect);
|
||||||
rcu_assign_pointer(nf_nat_sdp_addr_hook, ip_nat_sdp_addr);
|
rcu_assign_pointer(nf_nat_sdp_addr_hook, ip_nat_sdp_addr);
|
||||||
|
rcu_assign_pointer(nf_nat_sdp_port_hook, ip_nat_sdp_port);
|
||||||
rcu_assign_pointer(nf_nat_sdp_session_hook, ip_nat_sdp_session);
|
rcu_assign_pointer(nf_nat_sdp_session_hook, ip_nat_sdp_session);
|
||||||
rcu_assign_pointer(nf_nat_sdp_media_hook, ip_nat_sdp_media);
|
rcu_assign_pointer(nf_nat_sdp_media_hook, ip_nat_sdp_media);
|
||||||
return 0;
|
return 0;
|
||||||
|
@@ -70,6 +70,14 @@ unsigned int (*nf_nat_sdp_addr_hook)(struct sk_buff *skb,
|
|||||||
__read_mostly;
|
__read_mostly;
|
||||||
EXPORT_SYMBOL_GPL(nf_nat_sdp_addr_hook);
|
EXPORT_SYMBOL_GPL(nf_nat_sdp_addr_hook);
|
||||||
|
|
||||||
|
unsigned int (*nf_nat_sdp_port_hook)(struct sk_buff *skb,
|
||||||
|
const char **dptr,
|
||||||
|
unsigned int *datalen,
|
||||||
|
unsigned int matchoff,
|
||||||
|
unsigned int matchlen,
|
||||||
|
u_int16_t port) __read_mostly;
|
||||||
|
EXPORT_SYMBOL_GPL(nf_nat_sdp_port_hook);
|
||||||
|
|
||||||
unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb,
|
unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb,
|
||||||
const char **dptr,
|
const char **dptr,
|
||||||
unsigned int dataoff,
|
unsigned int dataoff,
|
||||||
@@ -730,9 +738,10 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb,
|
|||||||
union nf_inet_addr *saddr;
|
union nf_inet_addr *saddr;
|
||||||
struct nf_conntrack_tuple tuple;
|
struct nf_conntrack_tuple tuple;
|
||||||
int family = ct->tuplehash[!dir].tuple.src.l3num;
|
int family = ct->tuplehash[!dir].tuple.src.l3num;
|
||||||
int skip_expect = 0, ret = NF_DROP;
|
int direct_rtp = 0, skip_expect = 0, ret = NF_DROP;
|
||||||
u_int16_t base_port;
|
u_int16_t base_port;
|
||||||
__be16 rtp_port, rtcp_port;
|
__be16 rtp_port, rtcp_port;
|
||||||
|
typeof(nf_nat_sdp_port_hook) nf_nat_sdp_port;
|
||||||
typeof(nf_nat_sdp_media_hook) nf_nat_sdp_media;
|
typeof(nf_nat_sdp_media_hook) nf_nat_sdp_media;
|
||||||
|
|
||||||
saddr = NULL;
|
saddr = NULL;
|
||||||
@@ -746,6 +755,14 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb,
|
|||||||
* to register it since we can see the same media description multiple
|
* to register it since we can see the same media description multiple
|
||||||
* times on different connections in case multiple endpoints receive
|
* times on different connections in case multiple endpoints receive
|
||||||
* the same call.
|
* the same call.
|
||||||
|
*
|
||||||
|
* RTP optimization: if we find a matching media channel expectation
|
||||||
|
* and both the expectation and this connection are SNATed, we assume
|
||||||
|
* both sides can reach each other directly and use the final
|
||||||
|
* destination address from the expectation. We still need to keep
|
||||||
|
* the NATed expectations for media that might arrive from the
|
||||||
|
* outside, and additionally need to expect the direct RTP stream
|
||||||
|
* in case it passes through us even without NAT.
|
||||||
*/
|
*/
|
||||||
memset(&tuple, 0, sizeof(tuple));
|
memset(&tuple, 0, sizeof(tuple));
|
||||||
if (saddr)
|
if (saddr)
|
||||||
@@ -756,20 +773,42 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb,
|
|||||||
tuple.dst.u.udp.port = port;
|
tuple.dst.u.udp.port = port;
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
exp = __nf_ct_expect_find(&tuple);
|
do {
|
||||||
if (exp && exp->master != ct &&
|
exp = __nf_ct_expect_find(&tuple);
|
||||||
nfct_help(exp->master)->helper == nfct_help(ct)->helper &&
|
|
||||||
exp->class == class)
|
|
||||||
skip_expect = 1;
|
|
||||||
rcu_read_unlock();
|
|
||||||
|
|
||||||
if (skip_expect)
|
if (!exp || exp->master == ct ||
|
||||||
return NF_ACCEPT;
|
nfct_help(exp->master)->helper != nfct_help(ct)->helper ||
|
||||||
|
exp->class != class)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (exp->tuple.src.l3num == AF_INET && !direct_rtp &&
|
||||||
|
(exp->saved_ip != exp->tuple.dst.u3.ip ||
|
||||||
|
exp->saved_proto.udp.port != exp->tuple.dst.u.udp.port) &&
|
||||||
|
ct->status & IPS_NAT_MASK) {
|
||||||
|
daddr->ip = exp->saved_ip;
|
||||||
|
tuple.dst.u3.ip = exp->saved_ip;
|
||||||
|
tuple.dst.u.udp.port = exp->saved_proto.udp.port;
|
||||||
|
direct_rtp = 1;
|
||||||
|
} else
|
||||||
|
skip_expect = 1;
|
||||||
|
} while (!skip_expect);
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
base_port = ntohs(tuple.dst.u.udp.port) & ~1;
|
base_port = ntohs(tuple.dst.u.udp.port) & ~1;
|
||||||
rtp_port = htons(base_port);
|
rtp_port = htons(base_port);
|
||||||
rtcp_port = htons(base_port + 1);
|
rtcp_port = htons(base_port + 1);
|
||||||
|
|
||||||
|
if (direct_rtp) {
|
||||||
|
nf_nat_sdp_port = rcu_dereference(nf_nat_sdp_port_hook);
|
||||||
|
if (nf_nat_sdp_port &&
|
||||||
|
!nf_nat_sdp_port(skb, dptr, datalen,
|
||||||
|
mediaoff, medialen, ntohs(rtp_port)))
|
||||||
|
goto err1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skip_expect)
|
||||||
|
return NF_ACCEPT;
|
||||||
|
|
||||||
rtp_exp = nf_ct_expect_alloc(ct);
|
rtp_exp = nf_ct_expect_alloc(ct);
|
||||||
if (rtp_exp == NULL)
|
if (rtp_exp == NULL)
|
||||||
goto err1;
|
goto err1;
|
||||||
@@ -783,7 +822,7 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb,
|
|||||||
IPPROTO_UDP, NULL, &rtcp_port);
|
IPPROTO_UDP, NULL, &rtcp_port);
|
||||||
|
|
||||||
nf_nat_sdp_media = rcu_dereference(nf_nat_sdp_media_hook);
|
nf_nat_sdp_media = rcu_dereference(nf_nat_sdp_media_hook);
|
||||||
if (nf_nat_sdp_media && ct->status & IPS_NAT_MASK)
|
if (nf_nat_sdp_media && ct->status & IPS_NAT_MASK && !direct_rtp)
|
||||||
ret = nf_nat_sdp_media(skb, dptr, datalen, rtp_exp, rtcp_exp,
|
ret = nf_nat_sdp_media(skb, dptr, datalen, rtp_exp, rtcp_exp,
|
||||||
mediaoff, medialen, daddr);
|
mediaoff, medialen, daddr);
|
||||||
else {
|
else {
|
||||||
|
Reference in New Issue
Block a user