Commit 0f32a40f authored by Patrick McHardy's avatar Patrick McHardy Committed by David S. Miller

[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: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c978cd3a
...@@ -5,6 +5,17 @@ ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { \ ...@@ -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 @@ static unsigned int ip_nat_sip(struct sk_buff *skb, ...@@ -205,6 +205,91 @@ static unsigned int ip_nat_sip(struct sk_buff *skb,
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, ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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;
} }
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment