|
|
|
|
@ -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],
|
|
|
|
|
|