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 @@
#define SIP_PORT 5060
#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 {
const char *method;
unsigned int len;
......@@ -59,6 +70,7 @@ enum sip_header_types {
SIP_HDR_TO,
SIP_HDR_CONTACT,
SIP_HDR_VIA,
SIP_HDR_EXPIRES,
SIP_HDR_CONTENT_LENGTH,
};
......@@ -75,6 +87,12 @@ enum sdp_header_types {
extern unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb,
const char **dptr,
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,
const char **dptr,
unsigned int *datalen,
......
......@@ -46,6 +46,7 @@ union nf_conntrack_expect_proto {
#include <linux/netfilter/nf_conntrack_pptp.h>
#include <linux/netfilter/nf_conntrack_h323.h>
#include <linux/netfilter/nf_conntrack_sane.h>
#include <linux/netfilter/nf_conntrack_sip.h>
/* per conntrack: application helper private data */
union nf_conntrack_help {
......@@ -54,6 +55,7 @@ union nf_conntrack_help {
struct nf_ct_pptp_master ct_pptp_info;
struct nf_ct_h323_master ct_h323_info;
struct nf_ct_sane_master ct_sane_info;
struct nf_ct_sip_master ct_sip_info;
};
#include <linux/types.h>
......@@ -76,7 +78,7 @@ do { \
struct nf_conntrack_helper;
/* 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 */
struct nf_conn_help {
......
......@@ -205,6 +205,91 @@ static unsigned int ip_nat_sip(struct sk_buff *skb,
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,
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);
}
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.
Mangle it, and change the expectation to match the new version. */
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
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. */
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)
{
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);
synchronize_rcu();
}
......@@ -351,8 +416,10 @@ static void __exit nf_nat_sip_fini(void)
static int __init nf_nat_sip_init(void)
{
BUG_ON(nf_nat_sip_hook != NULL);
BUG_ON(nf_nat_sip_expect_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_expect_hook, ip_nat_sip_expect);
rcu_assign_pointer(nf_nat_sdp_hook, ip_nat_sdp);
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