TT#146201 support forwarding RTCP FB packets directly

Change-Id: I604c750a1d88f07bd5a4a8551bba30b864c4d150
pull/1682/head
Richard Fuchs 3 years ago
parent c03362cc71
commit 3a57face11

@ -1484,11 +1484,18 @@ static const char *kernelize_one(struct rtpengine_target_info *reti, GQueue *out
ZERO(stream->kernel_stats_in);
if (proto_is_rtp(media->protocol) && sinks && sinks->length) {
if (proto_is_rtp(media->protocol)) {
reti->rtp = 1;
if (!media->monologue->transcoding) {
if (media->protocol->avpf)
reti->rtcp_fb_fw = 1;
}
}
if (reti->rtp && sinks && sinks->length) {
GList *l;
struct rtp_stats *rs;
reti->rtp = 1;
// this code is execute only once: list therefore must be empty
assert(*payload_types == NULL);
*payload_types = g_hash_table_get_values(stream->rtp_stats);

@ -897,7 +897,7 @@ int rtcp_avp2savp(str *s, struct crypto_context *c, struct ssrc_ctx *ssrc_ctx) {
int rtcp_savp2avp(str *s, struct crypto_context *c, struct ssrc_ctx *ssrc_ctx) {
struct rtcp_packet *rtcp;
str payload, to_auth, to_decrypt, auth_tag;
uint32_t idx, *idx_p;
uint32_t idx;
char hmac[20];
const char *err;
@ -923,8 +923,8 @@ int rtcp_savp2avp(str *s, struct crypto_context *c, struct ssrc_ctx *ssrc_ctx) {
if (to_decrypt.len < sizeof(idx))
goto error;
to_decrypt.len -= sizeof(idx);
idx_p = (void *) to_decrypt.s + to_decrypt.len;
idx = ntohl(*idx_p);
memcpy(&idx, to_decrypt.s + to_decrypt.len, sizeof(idx));
idx = ntohl(idx);
crypto_debug_printf(", idx %" PRIu32, idx);

@ -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;

@ -125,6 +125,7 @@ struct rtpengine_target_info {
rtp_only:1,
track_ssrc:1,
rtcp:1,
rtcp_fb_fw:1,
do_intercept:1,
pt_filter:1,
non_forwarding:1, // empty src/dst addr

@ -14,7 +14,7 @@
#include "xt_RTPENGINE.h"
#define NUM_SOCKETS 41
#define PORT_BASE 37526
#define PORT_BASE 36000
#define LOCALHOST htonl(0x7f000001)
#define LEN(x) (sizeof(x)-1)
@ -42,8 +42,8 @@
.sin_port = htons(PORT_BASE + port), \
.sin_addr = { LOCALHOST }, \
}; \
ret = sendto(fds[sock], data, LEN(data), 0, (struct sockaddr *) &sin, sizeof(sin)); \
printf("ret = %i\n", ret); \
ssize_t ret = sendto(fds[sock], data, LEN(data), 0, (struct sockaddr *) &sin, sizeof(sin)); \
printf("ret = %zi\n", ret); \
assert(ret == LEN(data)); \
}
#define EXP(sock, data) \
@ -51,9 +51,9 @@
{ \
char buf[65535]; \
alarm(1); \
ret = recv(fds[sock], buf, sizeof(buf), 0); \
ssize_t ret = recv(fds[sock], buf, sizeof(buf), 0); \
alarm(0); \
printf("ret = %i\n", ret); \
printf("ret = %zi, expect = %zi\n", ret, LEN(data)); \
assert(ret == LEN(data)); \
buf[ret] = '\0'; \
printf("data ="); \
@ -69,9 +69,9 @@
socklen_t sinlen = sizeof(sin); \
char buf[65535]; \
alarm(1); \
ret = recvfrom(fds[sock], buf, sizeof(buf), 0, (struct sockaddr *) &sin, &sinlen); \
ssize_t ret = recvfrom(fds[sock], buf, sizeof(buf), 0, (struct sockaddr *) &sin, &sinlen); \
alarm(0); \
printf("ret = %i\n", ret); \
printf("ret = %zi, expect = %zi\n", ret, LEN(data)); \
assert(ret == LEN(data)); \
buf[ret] = '\0'; \
printf("data ="); \
@ -108,7 +108,7 @@ int main(void) {
.sin_port = htons(PORT_BASE + i),
.sin_addr = { LOCALHOST },
};
ret = bind(fds[i], (struct sockaddr *) &sin, sizeof(sin));
int ret = bind(fds[i], (struct sockaddr *) &sin, sizeof(sin));
assert(ret == 0);
}
@ -1378,5 +1378,162 @@ int main(void) {
);
EXPF(32, "\x81\xc8\x00\x0c\x87\x65\x43\x21\xa8\xf0\x49\x6a\x19\x93\x12\xb7\x15\x08\xaf\xea\x9f\xb8\x07\x51\x0b\x21\xfc\xd2\xcd\x34\x80\x9b\x17\x3d\xfe\xf6\x34\x74\x09\x33\xdb\x77\xb8\xfc\x24\x27\x52\xdf\x47\xe0\xe2\x42\x51\x8b\x99\x56\x3f\x86\x3b\x4f\xe1\x1a\x2e\xc1\xaa\x19\x3f\xae\xae\x0e\xd8\x6d\x0d\xd1\x72\xa4\x80\x00\x00\x01\x86\xe0\x95\x0b\x40\x1f\xa0\x75\x18\x71", 31);
// SRTCP decryption, AES-CM-128
MSG(REMG_ADD_TARGET, target,
.local = {
.family = AF_INET,
.u = {
.ipv4 = LOCALHOST,
},
.port = PORT_BASE + 33,
},
.expected_src = {
.family = AF_INET,
.u = {
.ipv4 = LOCALHOST,
},
.port = 5555,
},
.decrypt = {
.cipher = REC_AES_CM_128,
.hmac = REH_HMAC_SHA1,
.master_key_len = 16,
.master_salt_len = 14,
.session_key_len = 16,
.session_salt_len = 14,
.rtp_auth_tag_len = 10,
.rtcp_auth_tag_len = 10,
.master_key = {0xe1, 0xf9, 0x7a, 0x0d, 0x3e, 0x01, 0x8b, 0xe0,
0xd6, 0x4f, 0xa3, 0x2c, 0x06, 0xde, 0x41, 0x39},
.master_salt = {0x0e, 0xc6, 0x75, 0xad, 0x49, 0x8a, 0xfe, 0xeb,
0xb6, 0x96, 0x0b, 0x3a, 0xab, 0xe6},
},
.src_mismatch = MSM_IGNORE,
.num_destinations = 1,
.rtp = 1,
.rtcp = 1,
.rtcp_fw = 1,
.num_payload_types = 1,
.payload_types = {
{
.pt_num = 0xf,
.clock_rate = 8000,
},
},
);
MSG(REMG_ADD_DESTINATION, destination,
.local = {
.family = AF_INET,
.u = {
.ipv4 = LOCALHOST,
},
.port = PORT_BASE + 33,
},
.num = 0,
.output = {
.src_addr = {
.family = AF_INET,
.u = {
.ipv4 = LOCALHOST,
},
.port = PORT_BASE + 34,
},
.dst_addr = {
.family = AF_INET,
.u = {
.ipv4 = LOCALHOST,
},
.port = PORT_BASE + 35,
},
.encrypt = {
.cipher = REC_NULL,
.hmac = REH_NULL,
},
},
);
SND(40, 33, "\x81\xc8\x00\x0c\x12\x34\x56\x78\x09\x11\x4b\x0c\x97\xba\x5c\x20\x2c\x0c\x52\x0c\xea\x0c\xe6\x5b\x8f\x66\xad\x0d\x0b\x84\xb7\x9e\x0c\x6b\x80\xbc\xb1\x94\xb3\x1a\x0a\x1a\x30\x18\x06\xc7\x14\x91\x5a\xb1\xae\xd1\xee\x23\x64\x54\x82\x65\x27\x9c\x3e\xcd\xdc\x87\xff\x68\x84\x7c\x17\x6d\xd7\xc8\xae\xb6\x4b\x73\x80\x00\x00\x00\x00\x0b\x24\x46\x81\xdd\x28\x6b\xe8\xac");
EXPF(35, "\x81\xc8\x00\x0c\x12\x34\x56\x78xxxxxxxx\x00\x00\x26\xc0\x00\x00\x00\x25\x00\x00\x18\xdc\x00\x00\x12\x34\x06\x00\x00\x01\x00\x00\x04\x0d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81\xca\x00\x05\x00\x00\x16\x1c\x01\x0cqwertyuiopas\x00\x00", 34);
// SRTCP decryption, AES-GCM
MSG(REMG_ADD_TARGET, target,
.local = {
.family = AF_INET,
.u = {
.ipv4 = LOCALHOST,
},
.port = PORT_BASE + 36,
},
.expected_src = {
.family = AF_INET,
.u = {
.ipv4 = LOCALHOST,
},
.port = 5555,
},
.decrypt = {
.cipher = REC_AEAD_AES_GCM_256,
.hmac = REH_NULL,
.master_key_len = 32,
.master_salt_len = 12,
.session_key_len = 32,
.session_salt_len = 12,
.rtp_auth_tag_len = 0,
.master_key = {0x81, 0xa4, 0xe5, 0x86, 0x21, 0x62, 0x6c, 0x57,
0x9c, 0x5b, 0x8b, 0x2f, 0x1e, 0x27, 0x6a, 0x69,
0x3c, 0xf2, 0xd5, 0xf6, 0xd0, 0xbc, 0x9a, 0x53,
0x7c, 0x71, 0xdf, 0x22, 0x95, 0x38, 0x4c, 0xb2},
.master_salt = {0x33, 0xaa, 0xf1, 0x5f, 0x42, 0x81, 0x10, 0x58,
0xb0, 0x03, 0x8c, 0x0c},
},
.src_mismatch = MSM_IGNORE,
.num_destinations = 1,
.rtp = 1,
.rtcp = 1,
.rtcp_fw = 1,
.num_payload_types = 1,
.payload_types = {
{
.pt_num = 0xf,
.clock_rate = 8000,
},
},
);
MSG(REMG_ADD_DESTINATION, destination,
.local = {
.family = AF_INET,
.u = {
.ipv4 = LOCALHOST,
},
.port = PORT_BASE + 36,
},
.num = 0,
.output = {
.src_addr = {
.family = AF_INET,
.u = {
.ipv4 = LOCALHOST,
},
.port = PORT_BASE + 37,
},
.dst_addr = {
.family = AF_INET,
.u = {
.ipv4 = LOCALHOST,
},
.port = PORT_BASE + 38,
},
.encrypt = {
.cipher = REC_NULL,
.hmac = REH_NULL,
},
},
);
SND(40, 36, "\x81\xc8\x00\x0c\x00\x00\x16\x1c\x96\xe5\xb7\xf4\x34\x2e\xed\xfa\x59\xed\x4d\x77\x30\x96\x2a\xb3\x62\x5b\xe9\x4d\x06\xfe\x70\xb2\x9a\x4b\xb9\x27\x14\x78\x64\x15\x0c\xe6\xe6\x0d\xcc\x2f\x7f\x5f\x21\xf3\xfa\x03\x6f\xd2\xc1\xb5\x9c\x12\x76\x1b\x68\xe8\x12\xc8\xa7\x6d\x79\xce\x13\x14\xce\x33\x36\x58\x98\x6f\xe7\x95\xb5\x35\x0c\x25\x92\xbe\x2e\xb3\xb6\x2d\x51\x38\xfb\x09\x80\x00\x00\x00");
EXPF(38, "\x81\xc8\x00\x0c\x00\x00\x16\x1cxxxxxxxx\x00\x00\x26\xc0\x00\x00\x00\x25\x00\x00\x18\xdc\x00\x00\x12\x34\x06\x00\x00\x01\x00\x00\x04\x0d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81\xca\x00\x05\x00\x00\x16\x1c\x01\x0cqwertyuiopas\x00\x00", 37);
return 0;
}

Loading…
Cancel
Save