|
|
|
@ -1758,6 +1758,8 @@ static int proc_list_show(struct seq_file *f, void *v) {
|
|
|
|
|
seq_printf(f, " SSRC-tracking");
|
|
|
|
|
if (g->target.do_intercept)
|
|
|
|
|
seq_printf(f, " intercept");
|
|
|
|
|
if (g->target.rtcp_fb_fw)
|
|
|
|
|
seq_printf(f, " forward-RTCP-FB");
|
|
|
|
|
seq_printf(f, "\n");
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < g->target.num_destinations; i++) {
|
|
|
|
@ -3595,7 +3597,7 @@ static int stream_packet(struct rtpengine_table *t, const struct rtpengine_packe
|
|
|
|
|
if (!len) /* can't have empty packets */
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
DBG("received %u bytes of data from userspace\n", len);
|
|
|
|
|
DBG("received %zu bytes of data from userspace\n", len);
|
|
|
|
|
|
|
|
|
|
err = -ENOENT;
|
|
|
|
|
stream = get_stream_lock(NULL, info->stream_idx);
|
|
|
|
@ -3643,6 +3645,20 @@ static int target_find_ssrc(struct rtpengine_target *g, uint32_t ssrc) {
|
|
|
|
|
return -2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void parse_rtcp(struct rtp_parsed *rtp, struct sk_buff *skb) {
|
|
|
|
|
rtp->ok = 0;
|
|
|
|
|
rtp->rtcp = 0;
|
|
|
|
|
|
|
|
|
|
if (skb->len < sizeof(struct rtcp_header))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
rtp->rtcp_header = (void *) skb->data;
|
|
|
|
|
rtp->header_len = sizeof(struct rtcp_header);
|
|
|
|
|
rtp->payload = skb->data + sizeof(struct rtcp_header);
|
|
|
|
|
rtp->payload_len = skb->len - sizeof(struct rtcp_header);
|
|
|
|
|
rtp->rtcp = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int table_send_rtcp(struct rtpengine_table *t, const struct rtpengine_send_packet_info *info, size_t len)
|
|
|
|
|
{
|
|
|
|
|
struct rtpengine_target *g;
|
|
|
|
@ -3690,14 +3706,8 @@ static int table_send_rtcp(struct rtpengine_table *t, const struct rtpengine_sen
|
|
|
|
|
skb_reserve(skb, MAX_HEADER);
|
|
|
|
|
memcpy(skb_put(skb, len), data, len);
|
|
|
|
|
|
|
|
|
|
// rudimentarily set up header
|
|
|
|
|
memset(&rtp, 0, sizeof(rtp));
|
|
|
|
|
if (len >= sizeof(struct rtcp_header)) {
|
|
|
|
|
rtp.rtcp_header = (void *) skb->data;
|
|
|
|
|
rtp.header_len = sizeof(struct rtcp_header);
|
|
|
|
|
rtp.payload = skb->data + sizeof(struct rtcp_header);
|
|
|
|
|
rtp.payload_len = skb->len - sizeof(struct rtcp_header);
|
|
|
|
|
rtp.rtcp = 1;
|
|
|
|
|
parse_rtcp(&rtp, skb);
|
|
|
|
|
if (rtp.rtcp) {
|
|
|
|
|
ssrc_idx = target_find_ssrc(g, rtp.rtcp_header->ssrc);
|
|
|
|
|
if (ssrc_idx == -2) {
|
|
|
|
|
kfree_skb(skb);
|
|
|
|
@ -4494,6 +4504,61 @@ ok_update:
|
|
|
|
|
ok:
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
static int srtcp_auth_validate(struct re_crypto_context *c,
|
|
|
|
|
struct rtpengine_srtp *s, struct rtp_parsed *r,
|
|
|
|
|
uint64_t *pkt_idx_p)
|
|
|
|
|
{
|
|
|
|
|
uint32_t idx;
|
|
|
|
|
unsigned char *auth_tag = NULL;
|
|
|
|
|
unsigned char hmac[20];
|
|
|
|
|
|
|
|
|
|
if (!c->cipher->decrypt_rtcp)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (s->rtcp_auth_tag_len) {
|
|
|
|
|
// we have an auth tag to verify
|
|
|
|
|
if (s->hmac == REH_NULL)
|
|
|
|
|
return -1;
|
|
|
|
|
if (!c->hmac)
|
|
|
|
|
return -1;
|
|
|
|
|
if (!c->shash)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
// extract auth tag
|
|
|
|
|
if (r->payload_len < s->rtcp_auth_tag_len)
|
|
|
|
|
return -1;
|
|
|
|
|
auth_tag = r->payload + r->payload_len - s->rtcp_auth_tag_len;
|
|
|
|
|
r->payload_len -= s->rtcp_auth_tag_len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// skip MKI
|
|
|
|
|
if (r->payload_len < s->mki_len)
|
|
|
|
|
return -1;
|
|
|
|
|
r->payload_len -= s->mki_len;
|
|
|
|
|
|
|
|
|
|
// extract index
|
|
|
|
|
if (r->payload_len < sizeof(idx))
|
|
|
|
|
return -1;
|
|
|
|
|
memcpy(&idx, r->payload + r->payload_len - sizeof(idx), sizeof(idx));
|
|
|
|
|
idx = ntohl(idx);
|
|
|
|
|
|
|
|
|
|
if (auth_tag) {
|
|
|
|
|
if (srtcp_hash(hmac, c, s, r, idx))
|
|
|
|
|
return -1;
|
|
|
|
|
if (memcmp(auth_tag, hmac, s->rtcp_auth_tag_len))
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r->payload_len -= sizeof(idx);
|
|
|
|
|
|
|
|
|
|
if ((idx & 0x80000000ULL)) {
|
|
|
|
|
*pkt_idx_p = idx & ~0x80000000ULL;
|
|
|
|
|
return 1; // decrypt
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*pkt_idx_p = idx;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* XXX shared code */
|
|
|
|
@ -4898,6 +4963,37 @@ static inline int is_muxed_rtcp(struct sk_buff *skb) {
|
|
|
|
|
return 0;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
static inline int is_rtcp_fb_packet(struct sk_buff *skb) {
|
|
|
|
|
unsigned char m_pt;
|
|
|
|
|
size_t left = skb->len;
|
|
|
|
|
size_t offset = 0;
|
|
|
|
|
unsigned int packets = 0;
|
|
|
|
|
uint16_t len;
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
if (left < 8) // minimum RTCP size
|
|
|
|
|
return 0;
|
|
|
|
|
m_pt = skb->data[offset + 1];
|
|
|
|
|
// only RTPFB and PSFB
|
|
|
|
|
if (m_pt != 205 && m_pt != 206)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
// length check
|
|
|
|
|
len = (((unsigned char) skb->data[offset + 2]) << 8)
|
|
|
|
|
| ((unsigned char) skb->data[offset + 3]);
|
|
|
|
|
len++;
|
|
|
|
|
len <<= 2;
|
|
|
|
|
if (len > left) // invalid
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
left -= len;
|
|
|
|
|
offset += len;
|
|
|
|
|
|
|
|
|
|
if (packets++ >= 8) // limit number of compound packets
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
static inline int is_stun(struct rtpengine_target *g, unsigned int datalen, unsigned char *skb_data) {
|
|
|
|
|
uint32_t *u32;
|
|
|
|
|
if (!g->target.stun)
|
|
|
|
@ -5195,6 +5291,7 @@ static unsigned int rtpengine46(struct sk_buff *skb, struct rtpengine_table *t,
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
unsigned int i;
|
|
|
|
|
unsigned int start_idx, end_idx;
|
|
|
|
|
int is_rtcp;
|
|
|
|
|
|
|
|
|
|
#if (RE_HAS_MEASUREDELAY)
|
|
|
|
|
uint64_t starttime, endtime, delay;
|
|
|
|
@ -5228,9 +5325,9 @@ static unsigned int rtpengine46(struct sk_buff *skb, struct rtpengine_table *t,
|
|
|
|
|
}
|
|
|
|
|
_r_unlock(&g->outputs_lock, flags);
|
|
|
|
|
|
|
|
|
|
DBG("target found, src "MIPF" -> dst "MIPF"\n", MIPP(g->target.src_addr), MIPP(g->target.dst_addr));
|
|
|
|
|
DBG("target decrypt hmac and cipher are %s and %s", g->decrypt.hmac->name,
|
|
|
|
|
g->decrypt.cipher->name);
|
|
|
|
|
DBG("target found, local " MIPF "\n", MIPP(g->target.local));
|
|
|
|
|
DBG("target decrypt RTP hmac and cipher are %s and %s", g->decrypt_rtp.hmac->name,
|
|
|
|
|
g->decrypt_rtp.cipher->name);
|
|
|
|
|
|
|
|
|
|
if (is_stun(g, datalen, skb->data))
|
|
|
|
|
goto out;
|
|
|
|
@ -5259,19 +5356,33 @@ static unsigned int rtpengine46(struct sk_buff *skb, struct rtpengine_table *t,
|
|
|
|
|
|
|
|
|
|
// RTP processing
|
|
|
|
|
rtp.ok = 0;
|
|
|
|
|
rtp.rtcp = 0;
|
|
|
|
|
is_rtcp = 0;
|
|
|
|
|
if (g->target.rtp) {
|
|
|
|
|
if (g->target.rtcp) {
|
|
|
|
|
if (g->target.rtcp_mux) {
|
|
|
|
|
if (is_muxed_rtcp(skb))
|
|
|
|
|
goto out; // pass to userspace
|
|
|
|
|
is_rtcp = 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
goto out; // RTCP only
|
|
|
|
|
is_rtcp = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_rtcp) {
|
|
|
|
|
parse_rtp(&rtp, skb);
|
|
|
|
|
if (!rtp.ok && g->target.rtp_only)
|
|
|
|
|
goto out; // pass to userspace
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (g->target.rtcp_fb_fw && is_rtcp_fb_packet(skb))
|
|
|
|
|
; // forward and then drop
|
|
|
|
|
else
|
|
|
|
|
goto out; // just pass to userspace
|
|
|
|
|
|
|
|
|
|
parse_rtp(&rtp, skb);
|
|
|
|
|
if (!rtp.ok && g->target.rtp_only)
|
|
|
|
|
goto out; // pass to userspace
|
|
|
|
|
parse_rtcp(&rtp, skb);
|
|
|
|
|
if (!rtp.rtcp)
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (rtp.ok) {
|
|
|
|
|
// RTP ok
|
|
|
|
@ -5312,6 +5423,20 @@ static unsigned int rtpengine46(struct sk_buff *skb, struct rtpengine_table *t,
|
|
|
|
|
rtp.payload[12], rtp.payload[13], rtp.payload[14], rtp.payload[15],
|
|
|
|
|
rtp.payload[16], rtp.payload[17], rtp.payload[18], rtp.payload[19]);
|
|
|
|
|
}
|
|
|
|
|
else if (is_rtcp && rtp.rtcp) {
|
|
|
|
|
pkt_idx = 0;
|
|
|
|
|
err = srtcp_auth_validate(&g->decrypt_rtcp, &g->target.decrypt, &rtp, &pkt_idx);
|
|
|
|
|
errstr = "SRTCP authentication tag mismatch";
|
|
|
|
|
if (err == -1)
|
|
|
|
|
goto out_error;
|
|
|
|
|
if (err == 1) {
|
|
|
|
|
// decrypt
|
|
|
|
|
errstr = "SRTCP decryption failed";
|
|
|
|
|
if (srtcp_decrypt(&g->decrypt_rtcp, &g->target.decrypt, &rtp, pkt_idx))
|
|
|
|
|
goto out_error;
|
|
|
|
|
}
|
|
|
|
|
skb_trim(skb, rtp.header_len + rtp.payload_len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (g->target.do_intercept) {
|
|
|
|
|
DBG("do_intercept is set\n");
|
|
|
|
@ -5338,6 +5463,7 @@ static unsigned int rtpengine46(struct sk_buff *skb, struct rtpengine_table *t,
|
|
|
|
|
|
|
|
|
|
for (i = start_idx; i < end_idx; i++) {
|
|
|
|
|
struct rtpengine_output *o = &g->outputs[i];
|
|
|
|
|
DBG("output src " MIPF " -> dst " MIPF "\n", MIPP(o->output.src_addr), MIPP(o->output.dst_addr));
|
|
|
|
|
// do we need a copy?
|
|
|
|
|
if (i == (end_idx - 1)) {
|
|
|
|
|
skb2 = skb; // last iteration - use original
|
|
|
|
@ -5356,7 +5482,8 @@ static unsigned int rtpengine46(struct sk_buff *skb, struct rtpengine_table *t,
|
|
|
|
|
}
|
|
|
|
|
// adjust RTP pointers
|
|
|
|
|
rtp2 = rtp;
|
|
|
|
|
rtp2.rtp_header = (void *) (((char *) rtp2.rtp_header) + offset);
|
|
|
|
|
if (rtp.rtp_header)
|
|
|
|
|
rtp2.rtp_header = (void *) (((char *) rtp2.rtp_header) + offset);
|
|
|
|
|
rtp2.payload = (void *) (((char *) rtp2.payload) + offset);
|
|
|
|
|
|
|
|
|
|
datalen_out = skb2->len;
|
|
|
|
|