[NETFILTER]: nf_conntrack_sip: support multiple media channels
Add support for multiple media channels and use it to create expectations for video streams when present. Signed-off-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
4ab9e64e5e
commit
0d0ab0378d
3 changed files with 105 additions and 32 deletions
|
@ -12,10 +12,24 @@ struct nf_ct_sip_master {
|
||||||
enum sip_expectation_classes {
|
enum sip_expectation_classes {
|
||||||
SIP_EXPECT_SIGNALLING,
|
SIP_EXPECT_SIGNALLING,
|
||||||
SIP_EXPECT_AUDIO,
|
SIP_EXPECT_AUDIO,
|
||||||
|
SIP_EXPECT_VIDEO,
|
||||||
__SIP_EXPECT_MAX
|
__SIP_EXPECT_MAX
|
||||||
};
|
};
|
||||||
#define SIP_EXPECT_MAX (__SIP_EXPECT_MAX - 1)
|
#define SIP_EXPECT_MAX (__SIP_EXPECT_MAX - 1)
|
||||||
|
|
||||||
|
struct sdp_media_type {
|
||||||
|
const char *name;
|
||||||
|
unsigned int len;
|
||||||
|
enum sip_expectation_classes class;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SDP_MEDIA_TYPE(__name, __class) \
|
||||||
|
{ \
|
||||||
|
.name = (__name), \
|
||||||
|
.len = sizeof(__name) - 1, \
|
||||||
|
.class = (__class), \
|
||||||
|
}
|
||||||
|
|
||||||
struct sip_handler {
|
struct sip_handler {
|
||||||
const char *method;
|
const char *method;
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
|
|
|
@ -78,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 2
|
#define NF_CT_MAX_EXPECT_CLASSES 3
|
||||||
|
|
||||||
/* 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 {
|
||||||
|
|
|
@ -112,6 +112,21 @@ static int digits_len(const struct nf_conn *ct, const char *dptr,
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* get media type + port length */
|
||||||
|
static int media_len(const struct nf_conn *ct, const char *dptr,
|
||||||
|
const char *limit, int *shift)
|
||||||
|
{
|
||||||
|
int len = string_len(ct, dptr, limit, shift);
|
||||||
|
|
||||||
|
dptr += len;
|
||||||
|
if (dptr >= limit || *dptr != ' ')
|
||||||
|
return 0;
|
||||||
|
len++;
|
||||||
|
dptr++;
|
||||||
|
|
||||||
|
return len + digits_len(ct, dptr, limit, shift);
|
||||||
|
}
|
||||||
|
|
||||||
static int parse_addr(const struct nf_conn *ct, const char *cp,
|
static int parse_addr(const struct nf_conn *ct, const char *cp,
|
||||||
const char **endp, union nf_inet_addr *addr,
|
const char **endp, union nf_inet_addr *addr,
|
||||||
const char *limit)
|
const char *limit)
|
||||||
|
@ -563,7 +578,7 @@ static const struct sip_header ct_sdp_hdrs[] = {
|
||||||
[SDP_HDR_CONNECTION_IP4] = SDP_HDR("c=", "IN IP4 ", epaddr_len),
|
[SDP_HDR_CONNECTION_IP4] = SDP_HDR("c=", "IN IP4 ", epaddr_len),
|
||||||
[SDP_HDR_OWNER_IP6] = SDP_HDR("o=", "IN IP6 ", epaddr_len),
|
[SDP_HDR_OWNER_IP6] = SDP_HDR("o=", "IN IP6 ", epaddr_len),
|
||||||
[SDP_HDR_CONNECTION_IP6] = SDP_HDR("c=", "IN IP6 ", epaddr_len),
|
[SDP_HDR_CONNECTION_IP6] = SDP_HDR("c=", "IN IP6 ", epaddr_len),
|
||||||
[SDP_HDR_MEDIA] = SDP_HDR("m=", "audio ", digits_len),
|
[SDP_HDR_MEDIA] = SDP_HDR("m=", NULL, media_len),
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Linear string search within SDP header values */
|
/* Linear string search within SDP header values */
|
||||||
|
@ -705,6 +720,7 @@ static void flush_expectations(struct nf_conn *ct, bool media)
|
||||||
static int set_expected_rtp_rtcp(struct sk_buff *skb,
|
static int set_expected_rtp_rtcp(struct sk_buff *skb,
|
||||||
const char **dptr, unsigned int *datalen,
|
const char **dptr, unsigned int *datalen,
|
||||||
union nf_inet_addr *daddr, __be16 port,
|
union nf_inet_addr *daddr, __be16 port,
|
||||||
|
enum sip_expectation_classes class,
|
||||||
unsigned int mediaoff, unsigned int medialen)
|
unsigned int mediaoff, unsigned int medialen)
|
||||||
{
|
{
|
||||||
struct nf_conntrack_expect *exp, *rtp_exp, *rtcp_exp;
|
struct nf_conntrack_expect *exp, *rtp_exp, *rtcp_exp;
|
||||||
|
@ -743,7 +759,7 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb,
|
||||||
exp = __nf_ct_expect_find(&tuple);
|
exp = __nf_ct_expect_find(&tuple);
|
||||||
if (exp && exp->master != ct &&
|
if (exp && exp->master != ct &&
|
||||||
nfct_help(exp->master)->helper == nfct_help(ct)->helper &&
|
nfct_help(exp->master)->helper == nfct_help(ct)->helper &&
|
||||||
exp->class == SIP_EXPECT_AUDIO)
|
exp->class == class)
|
||||||
skip_expect = 1;
|
skip_expect = 1;
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
@ -757,13 +773,13 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb,
|
||||||
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;
|
||||||
nf_ct_expect_init(rtp_exp, SIP_EXPECT_AUDIO, family, saddr, daddr,
|
nf_ct_expect_init(rtp_exp, class, family, saddr, daddr,
|
||||||
IPPROTO_UDP, NULL, &rtp_port);
|
IPPROTO_UDP, NULL, &rtp_port);
|
||||||
|
|
||||||
rtcp_exp = nf_ct_expect_alloc(ct);
|
rtcp_exp = nf_ct_expect_alloc(ct);
|
||||||
if (rtcp_exp == NULL)
|
if (rtcp_exp == NULL)
|
||||||
goto err2;
|
goto err2;
|
||||||
nf_ct_expect_init(rtcp_exp, SIP_EXPECT_AUDIO, family, saddr, daddr,
|
nf_ct_expect_init(rtcp_exp, class, family, saddr, daddr,
|
||||||
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);
|
||||||
|
@ -785,6 +801,28 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct sdp_media_type sdp_media_types[] = {
|
||||||
|
SDP_MEDIA_TYPE("audio ", SIP_EXPECT_AUDIO),
|
||||||
|
SDP_MEDIA_TYPE("video ", SIP_EXPECT_VIDEO),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct sdp_media_type *sdp_media_type(const char *dptr,
|
||||||
|
unsigned int matchoff,
|
||||||
|
unsigned int matchlen)
|
||||||
|
{
|
||||||
|
const struct sdp_media_type *t;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(sdp_media_types); i++) {
|
||||||
|
t = &sdp_media_types[i];
|
||||||
|
if (matchlen < t->len ||
|
||||||
|
strncmp(dptr + matchoff, t->name, t->len))
|
||||||
|
continue;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static int process_sdp(struct sk_buff *skb,
|
static int process_sdp(struct sk_buff *skb,
|
||||||
const char **dptr, unsigned int *datalen,
|
const char **dptr, unsigned int *datalen,
|
||||||
unsigned int cseq)
|
unsigned int cseq)
|
||||||
|
@ -796,13 +834,16 @@ static int process_sdp(struct sk_buff *skb,
|
||||||
unsigned int mediaoff, medialen;
|
unsigned int mediaoff, medialen;
|
||||||
unsigned int sdpoff;
|
unsigned int sdpoff;
|
||||||
unsigned int caddr_len, maddr_len;
|
unsigned int caddr_len, maddr_len;
|
||||||
|
unsigned int i;
|
||||||
union nf_inet_addr caddr, maddr, rtp_addr;
|
union nf_inet_addr caddr, maddr, rtp_addr;
|
||||||
unsigned int port;
|
unsigned int port;
|
||||||
enum sdp_header_types c_hdr;
|
enum sdp_header_types c_hdr;
|
||||||
int ret;
|
const struct sdp_media_type *t;
|
||||||
|
int ret = NF_ACCEPT;
|
||||||
typeof(nf_nat_sdp_addr_hook) nf_nat_sdp_addr;
|
typeof(nf_nat_sdp_addr_hook) nf_nat_sdp_addr;
|
||||||
typeof(nf_nat_sdp_session_hook) nf_nat_sdp_session;
|
typeof(nf_nat_sdp_session_hook) nf_nat_sdp_session;
|
||||||
|
|
||||||
|
nf_nat_sdp_addr = rcu_dereference(nf_nat_sdp_addr_hook);
|
||||||
c_hdr = family == AF_INET ? SDP_HDR_CONNECTION_IP4 :
|
c_hdr = family == AF_INET ? SDP_HDR_CONNECTION_IP4 :
|
||||||
SDP_HDR_CONNECTION_IP6;
|
SDP_HDR_CONNECTION_IP6;
|
||||||
|
|
||||||
|
@ -822,12 +863,26 @@ static int process_sdp(struct sk_buff *skb,
|
||||||
&matchoff, &matchlen, &caddr) > 0)
|
&matchoff, &matchlen, &caddr) > 0)
|
||||||
caddr_len = matchlen;
|
caddr_len = matchlen;
|
||||||
|
|
||||||
if (ct_sip_get_sdp_header(ct, *dptr, sdpoff, *datalen,
|
mediaoff = sdpoff;
|
||||||
|
for (i = 0; i < ARRAY_SIZE(sdp_media_types); ) {
|
||||||
|
if (ct_sip_get_sdp_header(ct, *dptr, mediaoff, *datalen,
|
||||||
SDP_HDR_MEDIA, SDP_HDR_UNSPEC,
|
SDP_HDR_MEDIA, SDP_HDR_UNSPEC,
|
||||||
&mediaoff, &medialen) <= 0)
|
&mediaoff, &medialen) <= 0)
|
||||||
return NF_ACCEPT;
|
break;
|
||||||
|
|
||||||
|
/* Get media type and port number. A media port value of zero
|
||||||
|
* indicates an inactive stream. */
|
||||||
|
t = sdp_media_type(*dptr, mediaoff, medialen);
|
||||||
|
if (!t) {
|
||||||
|
mediaoff += medialen;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
mediaoff += t->len;
|
||||||
|
medialen -= t->len;
|
||||||
|
|
||||||
port = simple_strtoul(*dptr + mediaoff, NULL, 10);
|
port = simple_strtoul(*dptr + mediaoff, NULL, 10);
|
||||||
|
if (port == 0)
|
||||||
|
continue;
|
||||||
if (port < 1024 || port > 65535)
|
if (port < 1024 || port > 65535)
|
||||||
return NF_DROP;
|
return NF_DROP;
|
||||||
|
|
||||||
|
@ -843,20 +898,20 @@ static int process_sdp(struct sk_buff *skb,
|
||||||
else
|
else
|
||||||
return NF_DROP;
|
return NF_DROP;
|
||||||
|
|
||||||
ret = set_expected_rtp_rtcp(skb, dptr, datalen, &rtp_addr, htons(port),
|
ret = set_expected_rtp_rtcp(skb, dptr, datalen,
|
||||||
|
&rtp_addr, htons(port), t->class,
|
||||||
mediaoff, medialen);
|
mediaoff, medialen);
|
||||||
if (ret != NF_ACCEPT)
|
if (ret != NF_ACCEPT)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* Update media connection address if present */
|
/* Update media connection address if present */
|
||||||
if (maddr_len) {
|
if (maddr_len && nf_nat_sdp_addr && ct->status & IPS_NAT_MASK) {
|
||||||
nf_nat_sdp_addr = rcu_dereference(nf_nat_sdp_addr_hook);
|
|
||||||
if (nf_nat_sdp_addr && ct->status & IPS_NAT_MASK) {
|
|
||||||
ret = nf_nat_sdp_addr(skb, dptr, mediaoff, datalen,
|
ret = nf_nat_sdp_addr(skb, dptr, mediaoff, datalen,
|
||||||
c_hdr, SDP_HDR_MEDIA, &rtp_addr);
|
c_hdr, SDP_HDR_MEDIA, &rtp_addr);
|
||||||
if (ret != NF_ACCEPT)
|
if (ret != NF_ACCEPT)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update session connection and owner addresses */
|
/* Update session connection and owner addresses */
|
||||||
|
@ -1210,6 +1265,10 @@ static const struct nf_conntrack_expect_policy sip_exp_policy[SIP_EXPECT_MAX + 1
|
||||||
.max_expected = 2 * IP_CT_DIR_MAX,
|
.max_expected = 2 * IP_CT_DIR_MAX,
|
||||||
.timeout = 3 * 60,
|
.timeout = 3 * 60,
|
||||||
},
|
},
|
||||||
|
[SIP_EXPECT_VIDEO] = {
|
||||||
|
.max_expected = 2 * IP_CT_DIR_MAX,
|
||||||
|
.timeout = 3 * 60,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static void nf_conntrack_sip_fini(void)
|
static void nf_conntrack_sip_fini(void)
|
||||||
|
|
Loading…
Add table
Reference in a new issue