From 09026c19108a13c5af4d6af850dce2f9067092a9 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Thu, 12 Nov 2020 08:24:35 -0500 Subject: [PATCH] TT#99621 track basic RTP stats in kernel Change-Id: I199c37ca02f44afa45d0fe2b6d9c1b67dff0fba1 --- kernel-module/xt_RTPENGINE.c | 129 +++++++++++++++++++++++++++++++++++ kernel-module/xt_RTPENGINE.h | 24 ++++++- 2 files changed, 152 insertions(+), 1 deletion(-) diff --git a/kernel-module/xt_RTPENGINE.c b/kernel-module/xt_RTPENGINE.c index 962c02dcf..97a63c864 100644 --- a/kernel-module/xt_RTPENGINE.c +++ b/kernel-module/xt_RTPENGINE.c @@ -277,6 +277,8 @@ struct rtpengine_target { struct rtpengine_stats_a stats; struct rtpengine_rtp_stats_a rtp_stats[NUM_PAYLOAD_TYPES]; + spinlock_t ssrc_stats_lock; + struct rtpengine_ssrc_stats ssrc_stats; struct re_crypto_context decrypt; struct re_crypto_context encrypt; @@ -1658,6 +1660,32 @@ static struct re_dest_addr *find_dest_addr(const struct re_dest_addr_hash *h, co +static int table_get_target_stats(struct rtpengine_table *t, struct rtpengine_stats_info *i, int reset) { + struct rtpengine_target *g; + + g = get_target(t, &i->local); + if (!g) + return -ENOENT; + + i->ssrc = g->target.ssrc; + spin_lock(&g->ssrc_stats_lock); + i->ssrc_stats = g->ssrc_stats; + + if (reset) { + g->ssrc_stats.basic_stats.packets = 0; + g->ssrc_stats.basic_stats.bytes = 0; + g->ssrc_stats.total_lost = 0; + } + + spin_unlock(&g->ssrc_stats_lock); + + target_put(g); + + return 0; +} + + + static int table_del_target(struct rtpengine_table *t, const struct re_address *local) { unsigned char hi, lo; struct re_dest_addr *rda; @@ -2131,6 +2159,8 @@ static int table_new_target(struct rtpengine_table *t, struct rtpengine_target_i memcpy(&g->target, i, sizeof(*i)); crypto_context_init(&g->decrypt, &g->target.decrypt); crypto_context_init(&g->encrypt, &g->target.encrypt); + spin_lock_init(&g->ssrc_stats_lock); + g->ssrc_stats.lost_bits = -1; err = gen_session_keys(&g->decrypt, &g->target.decrypt); if (err) @@ -3244,6 +3274,20 @@ static inline ssize_t proc_control_read_write(struct file *file, char __user *ub err = table_new_target(t, &msg->u.target, 1); break; + case REMG_GET_STATS: + err = -EINVAL; + if (!writeable) + goto err; + err = table_get_target_stats(t, &msg->u.stats, 0); + break; + + case REMG_GET_RESET_STATS: + err = -EINVAL; + if (!writeable) + goto err; + err = table_get_target_stats(t, &msg->u.stats, 1); + break; + case REMG_ADD_CALL: err = -EINVAL; if (!writeable) @@ -3925,6 +3969,88 @@ static struct sk_buff *intercept_skb_copy(struct sk_buff *oskb, const struct re_ +static void rtp_stats(struct rtpengine_target *g, struct rtp_parsed *rtp, s64 arrival_time, int pt_idx) { + unsigned long flags; + struct rtpengine_ssrc_stats *s = &g->ssrc_stats; + u_int16_t old_seq_trunc; + u_int32_t last_seq; + u_int16_t seq_diff; + u_int32_t clockrate; + u_int32_t transit; + int32_t d; + + u_int16_t seq = ntohs(rtp->header->seq_num); + u_int32_t ts = ntohl(rtp->header->timestamp); + + spin_lock_irqsave(&g->ssrc_stats_lock, flags); + + s->basic_stats.packets++; + s->basic_stats.bytes += rtp->payload_len; + s->timestamp = ts; + + // track sequence numbers and lost frames + + last_seq = s->ext_seq; + + // old seq or seq reset? + old_seq_trunc = last_seq & 0xffff; + seq_diff = seq - old_seq_trunc; + if (seq_diff == 0 || seq_diff >= 0xfeff) // old/dup seq - ignore + ; + else if (seq_diff > 0x100) { + // reset seq and loss tracker + s->ext_seq = seq; + s->lost_bits = -1; + } + else { + // seq wrap? + u_int32_t new_seq = (last_seq & 0xffff0000) | seq; + while (new_seq < last_seq) { + new_seq += 0x10000; + if ((new_seq & 0xffff0000) == 0) // ext seq wrapped + break; + } + seq_diff = new_seq - s->ext_seq; + s->ext_seq = new_seq; + + // shift loss tracker bit field and count losses + if (seq_diff >= (sizeof(s->lost_bits) * 8)) { + // complete loss + s->total_lost += sizeof(s->lost_bits) * 8; + s->lost_bits = -1; + } + else { + while (seq_diff) { + // shift out one bit and see if we lost it + if ((s->lost_bits & 0x80000000) == 0) + s->total_lost++; + s->lost_bits <<= 1; + seq_diff--; + } + } + } + + // track this frame as being seen + seq_diff = (s->ext_seq & 0xffff) - seq; + if (seq_diff < (sizeof(s->lost_bits) * 8)) + s->lost_bits |= (1 << seq_diff); + + // jitter + // RFC 3550 A.8 + clockrate = g->target.clock_rates[pt_idx]; + transit = (((arrival_time / 1000) * clockrate) / 1000) - ts; + d = 0; + if (s->transit) + d = transit - s->transit; + s->transit = transit; + if (d < 0) + d = -d; + s->jitter += d - ((s->jitter + 8) >> 4); + + spin_unlock_irqrestore(&g->ssrc_stats_lock, flags); +} + + static unsigned int rtpengine46(struct sk_buff *skb, struct rtpengine_table *t, struct re_address *src, struct re_address *dst, u_int8_t in_tos, const struct xt_action_param *par) { @@ -4040,6 +4166,9 @@ src_check_ok: skb_trim(skb, rtp.header_len + rtp.payload_len); + if (g->target.rtp_stats) + rtp_stats(g, &rtp, ktime_to_us(skb->tstamp), rtp_pt_idx); + DBG("packet payload decrypted as %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x...\n", rtp.payload[0], rtp.payload[1], rtp.payload[2], rtp.payload[3], rtp.payload[4], rtp.payload[5], rtp.payload[6], rtp.payload[7], diff --git a/kernel-module/xt_RTPENGINE.h b/kernel-module/xt_RTPENGINE.h index f553e6740..00b0922c7 100644 --- a/kernel-module/xt_RTPENGINE.h +++ b/kernel-module/xt_RTPENGINE.h @@ -24,6 +24,15 @@ struct rtpengine_rtp_stats { u_int64_t packets; u_int64_t bytes; }; +struct rtpengine_ssrc_stats { + struct rtpengine_rtp_stats basic_stats; + u_int32_t timestamp; + u_int32_t ext_seq; + u_int32_t lost_bits; // sliding bitfield, [0] = ext_seq + u_int32_t total_lost; + u_int32_t transit; + u_int32_t jitter; +}; struct re_address { int family; @@ -95,6 +104,7 @@ struct rtpengine_target_info { u_int32_t ssrc_out; // Rewrite SSRC unsigned char payload_types[NUM_PAYLOAD_TYPES]; /* must be sorted */ + u_int32_t clock_rates[NUM_PAYLOAD_TYPES]; unsigned int num_payload_types; unsigned char tos; @@ -105,7 +115,8 @@ struct rtpengine_target_info { rtp_only:1, do_intercept:1, transcoding:1, // SSRC subst and RTP PT filtering - non_forwarding:1; // empty src/dst addr + non_forwarding:1, // empty src/dst addr + rtp_stats:1; // requires SSRC and clock_rates to be set }; struct rtpengine_call_info { @@ -125,6 +136,12 @@ struct rtpengine_packet_info { unsigned int stream_idx; }; +struct rtpengine_stats_info { + struct re_address local; // input + u_int32_t ssrc; // output + struct rtpengine_ssrc_stats ssrc_stats; // output +}; + struct rtpengine_message { enum { REMG_NOOP = 1, @@ -145,6 +162,10 @@ struct rtpengine_message { /* packet_info: */ REMG_PACKET, + /* stats_info: */ + REMG_GET_STATS, + REMG_GET_RESET_STATS, + __REMG_LAST } cmd; @@ -153,6 +174,7 @@ struct rtpengine_message { struct rtpengine_call_info call; struct rtpengine_stream_info stream; struct rtpengine_packet_info packet; + struct rtpengine_stats_info stats; } u; unsigned char data[];