Commit message: Merge pull request from GHSA-r374-qrwv-86hh
CVE: CVE-2021-43845
CWE: CWE-125
Vulnerability description: PJSIP is a free and open source multimedia communication library. In version 2.11.1 and prior, if incoming RTCP XR message contain block, the data field is not checked against the received packet size, potentially resulting in an out-of-bound read access. This affects all users that use PJMEDIA and RTCP XR. A malicious actor can send a RTCP XR message with an invalid packet size.

Project: pjproject
File: rtcp_xr.c

void pjmedia_rtcp_xr_rx_rtp( pjmedia_rtcp_xr_session *sess,
			     unsigned seq, 
			     int lost,
			     int dup,
			     int discarded,
			     int jitter,
			     int toh, pj_bool_t toh_ipv4)
{
    pj_uint32_t ext_seq;

    /* Get 32 bit version of sequence */
    ext_seq = extend_seq(sess, (pj_uint16_t)seq);

    /* Update statistics summary */
    sess->stat.rx.stat_sum.count++;

    if (sess->stat.rx.stat_sum.begin_seq == 0 || 
	sess->stat.rx.stat_sum.begin_seq > ext_seq)
    {
	sess->stat.rx.stat_sum.begin_seq = ext_seq;
    }

    if (sess->stat.rx.stat_sum.end_seq == 0 || 
	sess->stat.rx.stat_sum.end_seq < ext_seq)
    {
	sess->stat.rx.stat_sum.end_seq = ext_seq;
    }

    if (lost >= 0) {
	sess->stat.rx.stat_sum.l = PJ_TRUE;
	if (lost > 0)
	    sess->stat.rx.stat_sum.lost++;
    }

    if (dup >= 0) {
	sess->stat.rx.stat_sum.d = PJ_TRUE;
	if (dup > 0)
	    sess->stat.rx.stat_sum.dup++;
    }

    if (jitter >= 0) {
	sess->stat.rx.stat_sum.j = PJ_TRUE;
	pj_math_stat_update(&sess->stat.rx.stat_sum.jitter, jitter);
    }

    if (toh >= 0) {
	sess->stat.rx.stat_sum.t = toh_ipv4? 1 : 2;
	pj_math_stat_update(&sess->stat.rx.stat_sum.toh, toh);
    }

    /* Update burst metrics.
     * There are two terms introduced in the RFC 3611: gap & burst.
     * Gap represents good stream condition, lost+discard rate <= 1/Gmin.
     * Burst represents the opposite, lost+discard rate > 1/Gmin.
     */
    if (lost >= 0 && discarded >= 0) {
	if(lost > 0) {
	    sess->voip_mtc_stat.loss_count++;
	}
	if(discarded > 0) {
	    sess->voip_mtc_stat.discard_count++;
	}
	if(!lost && !discarded) {
	    /* Number of good packets since last lost/discarded */
	    sess->voip_mtc_stat.pkt++;
	}
	else {
	    if(sess->voip_mtc_stat.pkt >= sess->stat.rx.voip_mtc.gmin) {
		/* Gap condition */
		if(sess->voip_mtc_stat.lost == 1) {
		    /* Gap -> Gap */
		    sess->voip_mtc_stat.c14++;
		}
		else {
		    /* Burst -> Gap */
		    sess->voip_mtc_stat.c13++;
		}
		sess->voip_mtc_stat.lost = 1;
		sess->voip_mtc_stat.c11 += sess->voip_mtc_stat.pkt;
	    }
	    else {
		/* Burst condition */
		sess->voip_mtc_stat.lost++;
		if(sess->voip_mtc_stat.pkt == 0) {
		    /* Consecutive losts */
		    sess->voip_mtc_stat.c33++;
		}
		else {
		    /* Any good packets, but still bursting */
		    sess->voip_mtc_stat.c23++;
		    sess->voip_mtc_stat.c22 += (sess->voip_mtc_stat.pkt - 1);
		}
	    }

	    sess->voip_mtc_stat.pkt = 0;
	}
    }
}

Project: pjproject
File: rtcp_xr.c

void pjmedia_rtcp_xr_init( pjmedia_rtcp_xr_session *session, 
			   struct pjmedia_rtcp_session *parent_session,
			   pj_uint8_t gmin,
			   unsigned frames_per_packet)
{
    pj_bzero(session, sizeof(pjmedia_rtcp_xr_session));

    session->name = parent_session->name;
    session->rtcp_session = parent_session;
    pj_memcpy(&session->pkt.common, &session->rtcp_session->rtcp_sr_pkt.common,
	      sizeof(pjmedia_rtcp_common));
    session->pkt.common.pt = RTCP_XR;

    /* Init config */
    session->stat.rx.voip_mtc.gmin = (pj_uint8_t)(gmin? gmin : DEFAULT_GMIN);
    session->ptime = session->rtcp_session->pkt_size * 1000 / 
		     session->rtcp_session->clock_rate;
    session->frames_per_packet = frames_per_packet;

    /* Init Statistics Summary fields which have non-zero default */
    session->stat.rx.stat_sum.jitter.min = (unsigned) -1;
    session->stat.rx.stat_sum.toh.min = (unsigned) -1;

    /* Init VoIP Metrics fields which have non-zero default */
    session->stat.rx.voip_mtc.signal_lvl = 127;
    session->stat.rx.voip_mtc.noise_lvl = 127;
    session->stat.rx.voip_mtc.rerl = 127;
    session->stat.rx.voip_mtc.r_factor = 127;
    session->stat.rx.voip_mtc.ext_r_factor = 127;
    session->stat.rx.voip_mtc.mos_lq = 127;
    session->stat.rx.voip_mtc.mos_cq = 127;

    session->stat.tx.voip_mtc.signal_lvl = 127;
    session->stat.tx.voip_mtc.noise_lvl = 127;
    session->stat.tx.voip_mtc.rerl = 127;
    session->stat.tx.voip_mtc.r_factor = 127;
    session->stat.tx.voip_mtc.ext_r_factor = 127;
    session->stat.tx.voip_mtc.mos_lq = 127;
    session->stat.tx.voip_mtc.mos_cq = 127;
}

Project: pjproject
File: rtcp_xr.c

PJ_DEF(void) pjmedia_rtcp_build_rtcp_xr( pjmedia_rtcp_xr_session *sess, 
					 unsigned rpt_types,
					 void **rtcp_pkt, int *len)
{
    pj_uint16_t size = 0;

    /* Receiver Reference Time Report Block */
    /* Build this block if we have received packets since last build */
    if ((rpt_types == 0 || (rpt_types & PJMEDIA_RTCP_XR_RR_TIME)) &&
	sess->rx_last_rr != sess->rtcp_session->stat.rx.pkt)
    {
	pjmedia_rtcp_xr_rb_rr_time *r;
	pjmedia_rtcp_ntp_rec ntp;

	r = (pjmedia_rtcp_xr_rb_rr_time*) &sess->pkt.buf[size];
	pj_bzero(r, sizeof(pjmedia_rtcp_xr_rb_rr_time));

	/* Init block header */
	r->header.bt = BT_RR_TIME;
	r->header.specific = 0;
	r->header.length = pj_htons(2);

	/* Generate block contents */
	pjmedia_rtcp_get_ntp_time(sess->rtcp_session, &ntp);
	r->ntp_sec = pj_htonl(ntp.hi);
	r->ntp_frac = pj_htonl(ntp.lo);

	/* Finally */
	size += sizeof(pjmedia_rtcp_xr_rb_rr_time);
	sess->rx_last_rr = sess->rtcp_session->stat.rx.pkt;
    }

    /* DLRR Report Block */
    /* Build this block if we have received RR NTP (rx_lrr) before */
    if ((rpt_types == 0 || (rpt_types & PJMEDIA_RTCP_XR_DLRR)) && 
	sess->rx_lrr)
    {
	pjmedia_rtcp_xr_rb_dlrr *r;
	pjmedia_rtcp_xr_rb_dlrr_item *dlrr_item;
	pj_timestamp ts;

	r = (pjmedia_rtcp_xr_rb_dlrr*) &sess->pkt.buf[size];
	pj_bzero(r, sizeof(pjmedia_rtcp_xr_rb_dlrr));

	/* Init block header */
	r->header.bt = BT_DLRR;
	r->header.specific = 0;
	r->header.length = pj_htons(sizeof(pjmedia_rtcp_xr_rb_dlrr)/4 - 1);

	/* Generate block contents */
	dlrr_item = &r->item;
	dlrr_item->ssrc = pj_htonl(sess->rtcp_session->peer_ssrc);
	dlrr_item->lrr = pj_htonl(sess->rx_lrr);

	/* Calculate DLRR */
	if (sess->rx_lrr != 0) {
	    pj_get_timestamp(&ts);
	    ts.u64 -= sess->rx_lrr_time.u64;
	
	    /* Convert DLRR time to 1/65536 seconds resolution */
	    ts.u64 = (ts.u64 << 16) / sess->rtcp_session->ts_freq.u64;
	    dlrr_item->dlrr = pj_htonl(ts.u32.lo);
	} else {
	    dlrr_item->dlrr = 0;
	}

	/* Finally */
	size += sizeof(pjmedia_rtcp_xr_rb_dlrr);
    }

    /* Statistics Summary Block */
    /* Build this block if we have received packets since last build */
    if ((rpt_types == 0 || (rpt_types & PJMEDIA_RTCP_XR_STATS)) &&
	sess->stat.rx.stat_sum.count > 0)
    {
	pjmedia_rtcp_xr_rb_stats *r;
	pj_uint8_t specific = 0;

	r = (pjmedia_rtcp_xr_rb_stats*) &sess->pkt.buf[size];
	pj_bzero(r, sizeof(pjmedia_rtcp_xr_rb_stats));

	/* Init block header */
	specific |= sess->stat.rx.stat_sum.l ? (1 << 7) : 0;
	specific |= sess->stat.rx.stat_sum.d ? (1 << 6) : 0;
	specific |= sess->stat.rx.stat_sum.j ? (1 << 5) : 0;
	specific |= (sess->stat.rx.stat_sum.t & 3) << 3;
	r->header.bt = BT_STATS;
	r->header.specific = specific;
	r->header.length = pj_htons(9);

	/* Generate block contents */
	r->ssrc = pj_htonl(sess->rtcp_session->peer_ssrc);
	r->begin_seq = pj_htons((pj_uint16_t)
				(sess->stat.rx.stat_sum.begin_seq & 0xFFFF));
	r->end_seq = pj_htons((pj_uint16_t)
			      (sess->stat.rx.stat_sum.end_seq & 0xFFFF));
	if (sess->stat.rx.stat_sum.l) {
	    r->lost = pj_htonl(sess->stat.rx.stat_sum.lost);
	}
	if (sess->stat.rx.stat_sum.d) {
	    r->dup = pj_htonl(sess->stat.rx.stat_sum.dup);
	}
	if (sess->stat.rx.stat_sum.j) {
	    r->jitter_min = pj_htonl(sess->stat.rx.stat_sum.jitter.min);
	    r->jitter_max = pj_htonl(sess->stat.rx.stat_sum.jitter.max);
	    r->jitter_mean = 
		pj_htonl((unsigned)sess->stat.rx.stat_sum.jitter.mean);
	    r->jitter_dev = 
		pj_htonl(pj_math_stat_get_stddev(&sess->stat.rx.stat_sum.jitter));
	}
	if (sess->stat.rx.stat_sum.t) {
	    r->toh_min = sess->stat.rx.stat_sum.toh.min;
	    r->toh_max = sess->stat.rx.stat_sum.toh.max;
	    r->toh_mean = (unsigned) sess->stat.rx.stat_sum.toh.mean;
	    r->toh_dev = pj_math_stat_get_stddev(&sess->stat.rx.stat_sum.toh);
	}

	/* Reset TX statistics summary each time built */
	pj_bzero(&sess->stat.rx.stat_sum, sizeof(sess->stat.rx.stat_sum));
	sess->stat.rx.stat_sum.jitter.min = (unsigned) -1;
	sess->stat.rx.stat_sum.toh.min = (unsigned) -1;

	/* Finally */
	size += sizeof(pjmedia_rtcp_xr_rb_stats);
	pj_gettimeofday(&sess->stat.rx.stat_sum.update);
    }

    /* Voip Metrics Block */
    /* Build this block if we have received packets */
    if ((rpt_types == 0 || (rpt_types & PJMEDIA_RTCP_XR_VOIP_METRICS)) &&
	sess->rtcp_session->stat.rx.pkt)
    {
	pjmedia_rtcp_xr_rb_voip_mtc *r;
	pj_uint32_t c11;
	pj_uint32_t c13;
	pj_uint32_t c14;
	pj_uint32_t c22;
	pj_uint32_t c23;
	pj_uint32_t c31;
	pj_uint32_t c32;
	pj_uint32_t c33;
	pj_uint32_t ctotal, m;
	unsigned est_extra_delay;

	r = (pjmedia_rtcp_xr_rb_voip_mtc*) &sess->pkt.buf[size];
	pj_bzero(r, sizeof(pjmedia_rtcp_xr_rb_voip_mtc));

	/* Init block header */
	r->header.bt = BT_VOIP_METRICS;
	r->header.specific = 0;
	r->header.length = pj_htons(8);

	/* Use temp vars for easiness. */
	c11 = sess->voip_mtc_stat.c11;
	c13 = sess->voip_mtc_stat.c13;
	c14 = sess->voip_mtc_stat.c14;
	c22 = sess->voip_mtc_stat.c22;
	c23 = sess->voip_mtc_stat.c23;
	c33 = sess->voip_mtc_stat.c33;
	m = sess->ptime * sess->frames_per_packet;

	/* Calculate additional transition counts. */
	c31 = c13;
	c32 = c23;
	ctotal = c11 + c14 + c13 + c22 + c23 + c31 + c32 + c33;

	if (ctotal) {
	    pj_uint32_t p32, p23;

	    //original version:
	    //p32 = c32 / (c31 + c32 + c33);
	    if (c31 + c32 + c33 == 0)
		p32 = 0;
	    else
		p32 = (c32 << 16) / (c31 + c32 + c33);

	    //original version:
	    //if ((c22 + c23) < 1) {
	    //    p23 = 1;
	    //} else {
	    //    p23 = 1 - c22 / (c22 + c23);
	    //}
	    if (c23 == 0) {
	        p23 = 0;
	    } else {
	        p23 = (c23 << 16) / (c22 + c23);
	    }

	    /* Calculate loss/discard densities, scaled of 0-256 */
	    if (c11 == 0)
		sess->stat.rx.voip_mtc.gap_den = 0;
	    else
		sess->stat.rx.voip_mtc.gap_den = (pj_uint8_t)
						 ((c14 << 8) / (c11 + c14));
	    if (p23 == 0)
		sess->stat.rx.voip_mtc.burst_den = 0;
	    else
		sess->stat.rx.voip_mtc.burst_den = (pj_uint8_t)
						   ((p23 << 8) / (p23 + p32));

	    /* Calculate (average) durations, in ms */
	    if (c13 == 0) {
		c13 = 1;
		ctotal += 1;
	    }
	    sess->stat.rx.voip_mtc.gap_dur = (pj_uint16_t)
					    ((c11+c14+c13) * m / c13);
	    sess->stat.rx.voip_mtc.burst_dur = (pj_uint16_t)
					    ((ctotal - (c11+c14+c13)) * m / c13);

	    /* Callculate loss/discard rates, scaled 0-256 */
	    sess->stat.rx.voip_mtc.loss_rate = (pj_uint8_t)
			((sess->voip_mtc_stat.loss_count << 8) / ctotal);
	    sess->stat.rx.voip_mtc.discard_rate = (pj_uint8_t)
			((sess->voip_mtc_stat.discard_count << 8) / ctotal);
	} else {
	    /* No lost/discarded packet yet. */
	    sess->stat.rx.voip_mtc.gap_den = 0;
	    sess->stat.rx.voip_mtc.burst_den = 0;
	    sess->stat.rx.voip_mtc.gap_dur = 0;
	    sess->stat.rx.voip_mtc.burst_dur = 0;
	    sess->stat.rx.voip_mtc.loss_rate = 0;
	    sess->stat.rx.voip_mtc.discard_rate = 0;
	}

	/* Set round trip delay (in ms) to RTT calculated after receiving
	 * DLRR or DLSR.
	 */
	if (sess->stat.rtt.last)
	    sess->stat.rx.voip_mtc.rnd_trip_delay = (pj_uint16_t)
				    (sess->stat.rtt.last / 1000);
	else if (sess->rtcp_session->stat.rtt.last)
	    sess->stat.rx.voip_mtc.rnd_trip_delay = (pj_uint16_t)
				    (sess->rtcp_session->stat.rtt.last / 1000);
	
	/* End system delay = RTT/2 + current jitter buffer size + 
	 *                    EXTRA (estimated extra delay)
	 * EXTRA will cover additional delay introduced by other components of
	 * audio engine, e.g: sound device, codec, AEC, PLC, WSOLA.
	 * Since it is difficult to get the exact value of EXTRA, estimation
	 * is taken to be totally around 30ms + sound device latency.
	 */
	est_extra_delay = 30;

#if !PJMEDIA_AUDIO_DEV_HAS_NULL_AUDIO
	est_extra_delay += PJMEDIA_SND_DEFAULT_REC_LATENCY + 
			   PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
#endif

	sess->stat.rx.voip_mtc.end_sys_delay = (pj_uint16_t)
				 (sess->stat.rx.voip_mtc.rnd_trip_delay / 2 +
				 sess->stat.rx.voip_mtc.jb_nom + 
				 est_extra_delay);

	/* Generate block contents */
	r->ssrc		    = pj_htonl(sess->rtcp_session->peer_ssrc);
	r->loss_rate	    = sess->stat.rx.voip_mtc.loss_rate;
	r->discard_rate	    = sess->stat.rx.voip_mtc.discard_rate;
	r->burst_den	    = sess->stat.rx.voip_mtc.burst_den;
	r->gap_den	    = sess->stat.rx.voip_mtc.gap_den;
	r->burst_dur	    = pj_htons(sess->stat.rx.voip_mtc.burst_dur);
	r->gap_dur	    = pj_htons(sess->stat.rx.voip_mtc.gap_dur);
	r->rnd_trip_delay   = pj_htons(sess->stat.rx.voip_mtc.rnd_trip_delay);
	r->end_sys_delay    = pj_htons(sess->stat.rx.voip_mtc.end_sys_delay);
	/* signal & noise level encoded in two's complement form */
	r->signal_lvl	    = (pj_uint8_t) 
			      ((sess->stat.rx.voip_mtc.signal_lvl >= 0)?
			       sess->stat.rx.voip_mtc.signal_lvl :
			       (sess->stat.rx.voip_mtc.signal_lvl + 256));
	r->noise_lvl	    = (pj_uint8_t)
			      ((sess->stat.rx.voip_mtc.noise_lvl >= 0)?
			       sess->stat.rx.voip_mtc.noise_lvl :
			       (sess->stat.rx.voip_mtc.noise_lvl + 256));
	r->rerl		    = sess->stat.rx.voip_mtc.rerl;
	r->gmin		    = sess->stat.rx.voip_mtc.gmin;
	r->r_factor	    = sess->stat.rx.voip_mtc.r_factor;
	r->ext_r_factor	    = sess->stat.rx.voip_mtc.ext_r_factor;
	r->mos_lq	    = sess->stat.rx.voip_mtc.mos_lq;
	r->mos_cq	    = sess->stat.rx.voip_mtc.mos_cq;
	r->rx_config	    = sess->stat.rx.voip_mtc.rx_config;
	r->jb_nom	    = pj_htons(sess->stat.rx.voip_mtc.jb_nom);
	r->jb_max	    = pj_htons(sess->stat.rx.voip_mtc.jb_max);
	r->jb_abs_max	    = pj_htons(sess->stat.rx.voip_mtc.jb_abs_max);

	/* Finally */
	size += sizeof(pjmedia_rtcp_xr_rb_voip_mtc);
	pj_gettimeofday(&sess->stat.rx.voip_mtc.update);
    }

    /* Add RTCP XR header size */
    size += sizeof(sess->pkt.common);

    /* Set RTCP XR header 'length' to packet size in 32-bit unit minus one */
    sess->pkt.common.length = pj_htons((pj_uint16_t)(size/4 - 1));

    /* Set the return values */
    *rtcp_pkt = (void*) &sess->pkt;
    *len = size;
}

Project: pjproject
File: rtcp_xr.c

void pjmedia_rtcp_xr_tx_rtp( pjmedia_rtcp_xr_session *session, 
			     unsigned ptsize )
{
    PJ_UNUSED_ARG(session);
    PJ_UNUSED_ARG(ptsize);
}

Project: pjproject
File: rtcp_xr.c

static pj_uint32_t extend_seq(pjmedia_rtcp_xr_session *sess,
			      const pj_uint16_t seq)
{

    pj_uint32_t extended_seq, seq_a, seq_b, diff_a, diff_b;
    if(sess->uninitialized_src_ref_seq) {
	/* This is the first sequence number received.  Place
	 * it in the middle of the extended sequence number
	 * space.
	 */
	sess->src_ref_seq = seq | 0x80000000u;
	sess->uninitialized_src_ref_seq = PJ_FALSE;
	extended_seq = sess->src_ref_seq;
    } else {
	/* Prior sequence numbers have been received.
	 * Propose two candidates for the extended sequence
	 * number: seq_a is without wraparound, seq_b with
	 * wraparound.
	 */
	seq_a = seq | (sess->src_ref_seq & 0xFFFF0000u);
	if(sess->src_ref_seq < seq_a) {
	    seq_b  = seq_a - 0x00010000u;
	    diff_a = seq_a - sess->src_ref_seq;
	    diff_b = sess->src_ref_seq - seq_b;
	} else {
	    seq_b  = seq_a + 0x00010000u;
	    diff_a = sess->src_ref_seq - seq_a;
	    diff_b = seq_b - sess->src_ref_seq;
	}

	/* Choose the closer candidate.  If they are equally
	 * close, the choice is somewhat arbitrary: we choose
	 * the candidate for which no rollover is necessary.
	 */
	if(diff_a < diff_b) {
	    extended_seq = seq_a;
	} else {
	    extended_seq = seq_b;
	}

	/* Set the reference sequence number to be this most
	 * recently-received sequence number.
	 */
	sess->src_ref_seq = extended_seq;
    }

    /* Return our best guess for a 32-bit sequence number that
     * corresponds to the 16-bit number we were given.
     */
    return extended_seq;
}

Project: pjproject
File: rtcp_xr.c

void pjmedia_rtcp_xr_fini(pjmedia_rtcp_xr_session *session)
{
    PJ_UNUSED_ARG(session);
}

Project: pjproject
File: rtcp_xr.c

void pjmedia_rtcp_xr_rx_rtcp_xr( pjmedia_rtcp_xr_session *sess,
				 const void *pkt,
				 pj_size_t size)
{
    const pjmedia_rtcp_xr_pkt	      *rtcp_xr = (pjmedia_rtcp_xr_pkt*) pkt;
    const pjmedia_rtcp_xr_rb_rr_time  *rb_rr_time = NULL;
    const pjmedia_rtcp_xr_rb_dlrr     *rb_dlrr = NULL;
    const pjmedia_rtcp_xr_rb_stats    *rb_stats = NULL;
    const pjmedia_rtcp_xr_rb_voip_mtc *rb_voip_mtc = NULL;
    const pjmedia_rtcp_xr_rb_header   *rb_hdr = (pjmedia_rtcp_xr_rb_header*) 
						rtcp_xr->buf;
    unsigned pkt_len, rb_len;

    if (rtcp_xr->common.pt != RTCP_XR)
	return;

    pkt_len = pj_ntohs((pj_uint16_t)rtcp_xr->common.length);

    if ((pkt_len + 1) > (size / 4))
	return;

    /* Parse report rpt_types */
    while ((pj_int32_t*)rb_hdr < (pj_int32_t*)pkt + pkt_len)
    {	
	rb_len = pj_ntohs((pj_uint16_t)rb_hdr->length);

	/* Just skip any block with length == 0 (no report content) */
	if (rb_len) {
	    switch (rb_hdr->bt) {
		case BT_RR_TIME:
		    if ((char*)rb_hdr + sizeof(*rb_rr_time) <=
			(char*)pkt + size) 
		    {
			rb_rr_time = (pjmedia_rtcp_xr_rb_rr_time*)rb_hdr;
		    }
		    break;
		case BT_DLRR:
		    if ((char*)rb_hdr + sizeof(*rb_dlrr) <=
			(char*)pkt + size)
		    {
			rb_dlrr = (pjmedia_rtcp_xr_rb_dlrr*)rb_hdr;
		    }
		    break;
		case BT_STATS:
		    if ((char*)rb_hdr + sizeof(*rb_stats) <=
			(char*)pkt + size)
		    {
			rb_stats = (pjmedia_rtcp_xr_rb_stats*)rb_hdr;
		    }
		    break;
		case BT_VOIP_METRICS:
		    if ((char*)rb_hdr + sizeof(*rb_voip_mtc) <=
			(char*)pkt + size)
		    {
			rb_voip_mtc = (pjmedia_rtcp_xr_rb_voip_mtc*)rb_hdr;
		    }
		    break;
		default:
		    break;
	    }
	}
	rb_hdr = (pjmedia_rtcp_xr_rb_header*)
		 ((pj_int32_t*)rb_hdr + rb_len + 1);
    }

    /* Receiving RR Time */
    if (rb_rr_time) {
	/* Save LRR from NTP timestamp of the RR time block report */
	sess->rx_lrr = ((pj_ntohl(rb_rr_time->ntp_sec) & 0x0000FFFF) << 16) | 
		       ((pj_ntohl(rb_rr_time->ntp_frac) >> 16) & 0xFFFF);

	/* Calculate RR arrival time for DLRR */
	pj_get_timestamp(&sess->rx_lrr_time);

	TRACE_((sess->name, "Rx RTCP SR: ntp_ts=%p", sess->rx_lrr,
	       (pj_uint32_t)(sess->rx_lrr_time.u64*65536/
			     sess->rtcp_session->ts_freq.u64)));
    }

    /* Receiving DLRR */
    if (rb_dlrr) {
	pj_uint32_t lrr, now, dlrr;
	pj_uint64_t eedelay;
	pjmedia_rtcp_ntp_rec ntp;

	/* LRR is the middle 32bit of NTP. It has 1/65536 second 
	 * resolution 
	 */
	lrr = pj_ntohl(rb_dlrr->item.lrr);

	/* DLRR is delay since LRR, also in 1/65536 resolution */
	dlrr = pj_ntohl(rb_dlrr->item.dlrr);

	/* Get current time, and convert to 1/65536 resolution */
	pjmedia_rtcp_get_ntp_time(sess->rtcp_session, &ntp);
	now = ((ntp.hi & 0xFFFF) << 16) + (ntp.lo >> 16);

	/* End-to-end delay is (now-lrr-dlrr) */
	eedelay = now - lrr - dlrr;

	/* Convert end to end delay to usec (keeping the calculation in
         * 64bit space)::
	 *   sess->ee_delay = (eedelay * 1000) / 65536;
	 */
	if (eedelay < 4294) {
	    eedelay = (eedelay * 1000000) >> 16;
	} else {
	    eedelay = (eedelay * 1000) >> 16;
	    eedelay *= 1000;
	}

	TRACE_((sess->name, "Rx RTCP XR DLRR: lrr=%p, dlrr=%p (%d:%03dms), "
			   "now=%p, rtt=%p",
		lrr, dlrr, dlrr/65536, (dlrr%65536)*1000/65536,
		now, (pj_uint32_t)eedelay));
	
	/* Only save calculation if "now" is greater than lrr, or
	 * otherwise rtt will be invalid 
	 */
	if (now-dlrr >= lrr) {
	    unsigned rtt = (pj_uint32_t)eedelay;
	    
	    /* Check that eedelay value really makes sense. 
	     * We allow up to 30 seconds RTT!
	     */
	    if (eedelay <= 30 * 1000 * 1000UL) {
		/* "Normalize" rtt value that is exceptionally high.
		 * For such values, "normalize" the rtt to be three times
		 * the average value.
		 */
		if (rtt>((unsigned)sess->stat.rtt.mean*3) && sess->stat.rtt.n!=0)
		{
		    unsigned orig_rtt = rtt;
		    rtt = (unsigned)sess->stat.rtt.mean*3;
		    PJ_LOG(5,(sess->name, 
			      "RTT value %d usec is normalized to %d usec",
			      orig_rtt, rtt));
		}
    	
		TRACE_((sess->name, "RTCP RTT is set to %d usec", rtt));
		pj_math_stat_update(&sess->stat.rtt, rtt);
	    }
	} else {
	    PJ_LOG(5, (sess->name, "Internal RTCP NTP clock skew detected: "
				   "lrr=%p, now=%p, dlrr=%p (%d:%03dms), "
				   "diff=%d",
				   lrr, now, dlrr, dlrr/65536,
				   (dlrr%65536)*1000/65536,
				   dlrr-(now-lrr)));
	}
    }

    /* Receiving Statistics Summary */
    if (rb_stats) {
	pj_uint8_t flags = rb_stats->header.specific;

	pj_bzero(&sess->stat.tx.stat_sum, sizeof(sess->stat.tx.stat_sum));

	/* Range of packets sequence reported in this blocks */
	sess->stat.tx.stat_sum.begin_seq = pj_ntohs(rb_stats->begin_seq);
	sess->stat.tx.stat_sum.end_seq   = pj_ntohs(rb_stats->end_seq);

	/* Get flags of valid fields */
	sess->stat.tx.stat_sum.l = (flags & (1 << 7)) != 0;
	sess->stat.tx.stat_sum.d = (flags & (1 << 6)) != 0;
	sess->stat.tx.stat_sum.j = (flags & (1 << 5)) != 0;
	sess->stat.tx.stat_sum.t = (flags & (3 << 3)) != 0;

	/* Fetch the reports info */
	if (sess->stat.tx.stat_sum.l) {
	    sess->stat.tx.stat_sum.lost = pj_ntohl(rb_stats->lost);
	}

	if (sess->stat.tx.stat_sum.d) {
	    sess->stat.tx.stat_sum.dup = pj_ntohl(rb_stats->dup);
	}

	if (sess->stat.tx.stat_sum.j) {
	    sess->stat.tx.stat_sum.jitter.min = pj_ntohl(rb_stats->jitter_min);
	    sess->stat.tx.stat_sum.jitter.max = pj_ntohl(rb_stats->jitter_max);
	    sess->stat.tx.stat_sum.jitter.mean= pj_ntohl(rb_stats->jitter_mean);
	    pj_math_stat_set_stddev(&sess->stat.tx.stat_sum.jitter, 
				    pj_ntohl(rb_stats->jitter_dev));
	}

	if (sess->stat.tx.stat_sum.t) {
	    sess->stat.tx.stat_sum.toh.min = rb_stats->toh_min;
	    sess->stat.tx.stat_sum.toh.max = rb_stats->toh_max;
	    sess->stat.tx.stat_sum.toh.mean= rb_stats->toh_mean;
	    pj_math_stat_set_stddev(&sess->stat.tx.stat_sum.toh, 
				    pj_ntohl(rb_stats->toh_dev));
	}

	pj_gettimeofday(&sess->stat.tx.stat_sum.update);
    }

    /* Receiving VoIP Metrics */
    if (rb_voip_mtc) {
	sess->stat.tx.voip_mtc.loss_rate = rb_voip_mtc->loss_rate;
	sess->stat.tx.voip_mtc.discard_rate = rb_voip_mtc->discard_rate;
	sess->stat.tx.voip_mtc.burst_den = rb_voip_mtc->burst_den;
	sess->stat.tx.voip_mtc.gap_den = rb_voip_mtc->gap_den;
	sess->stat.tx.voip_mtc.burst_dur = pj_ntohs(rb_voip_mtc->burst_dur);
	sess->stat.tx.voip_mtc.gap_dur = pj_ntohs(rb_voip_mtc->gap_dur);
	sess->stat.tx.voip_mtc.rnd_trip_delay = 
					pj_ntohs(rb_voip_mtc->rnd_trip_delay);
	sess->stat.tx.voip_mtc.end_sys_delay = 
					pj_ntohs(rb_voip_mtc->end_sys_delay);
	/* signal & noise level encoded in two's complement form */
	sess->stat.tx.voip_mtc.signal_lvl = (pj_int8_t)
				    ((rb_voip_mtc->signal_lvl > 127)?
				     ((int)rb_voip_mtc->signal_lvl - 256) : 
				     rb_voip_mtc->signal_lvl);
	sess->stat.tx.voip_mtc.noise_lvl  = (pj_int8_t)
				    ((rb_voip_mtc->noise_lvl > 127)?
				     ((int)rb_voip_mtc->noise_lvl - 256) : 
				     rb_voip_mtc->noise_lvl);
	sess->stat.tx.voip_mtc.rerl = rb_voip_mtc->rerl;
	sess->stat.tx.voip_mtc.gmin = rb_voip_mtc->gmin;
	sess->stat.tx.voip_mtc.r_factor = rb_voip_mtc->r_factor;
	sess->stat.tx.voip_mtc.ext_r_factor = rb_voip_mtc->ext_r_factor;
	sess->stat.tx.voip_mtc.mos_lq = rb_voip_mtc->mos_lq;
	sess->stat.tx.voip_mtc.mos_cq = rb_voip_mtc->mos_cq;
	sess->stat.tx.voip_mtc.rx_config = rb_voip_mtc->rx_config;
	sess->stat.tx.voip_mtc.jb_nom = pj_ntohs(rb_voip_mtc->jb_nom);
	sess->stat.tx.voip_mtc.jb_max = pj_ntohs(rb_voip_mtc->jb_max);
	sess->stat.tx.voip_mtc.jb_abs_max = pj_ntohs(rb_voip_mtc->jb_abs_max);

	pj_gettimeofday(&sess->stat.tx.voip_mtc.update);
    }
}

Project: pjproject
File: rtcp_xr.c

PJ_DEF(pj_status_t) pjmedia_rtcp_xr_update_info( 
					 pjmedia_rtcp_xr_session *sess,
					 unsigned info,
					 pj_int32_t val)
{
    int v = val;

    switch(info) {
	case PJMEDIA_RTCP_XR_INFO_SIGNAL_LVL:
	    sess->stat.rx.voip_mtc.signal_lvl = (pj_int8_t) v;
	    break;

	case PJMEDIA_RTCP_XR_INFO_NOISE_LVL:
	    sess->stat.rx.voip_mtc.noise_lvl = (pj_int8_t) v;
	    break;

	case PJMEDIA_RTCP_XR_INFO_RERL:
	    sess->stat.rx.voip_mtc.rerl = (pj_uint8_t) v;
	    break;

	case PJMEDIA_RTCP_XR_INFO_R_FACTOR:
	    sess->stat.rx.voip_mtc.ext_r_factor = (pj_uint8_t) v;
	    break;

	case PJMEDIA_RTCP_XR_INFO_MOS_LQ:
	    sess->stat.rx.voip_mtc.mos_lq = (pj_uint8_t) v;
	    break;

	case PJMEDIA_RTCP_XR_INFO_MOS_CQ:
	    sess->stat.rx.voip_mtc.mos_cq = (pj_uint8_t) v;
	    break;

	case PJMEDIA_RTCP_XR_INFO_CONF_PLC:
	    if (v >= 0 && v <= 3) {
		sess->stat.rx.voip_mtc.rx_config &= 0x3F;
		sess->stat.rx.voip_mtc.rx_config |= (pj_uint8_t) (v << 6);
	    }
	    break;

	case PJMEDIA_RTCP_XR_INFO_CONF_JBA:
	    if (v >= 0 && v <= 3) {
		sess->stat.rx.voip_mtc.rx_config &= 0xCF;
		sess->stat.rx.voip_mtc.rx_config |= (pj_uint8_t) (v << 4);
	    }
	    break;

	case PJMEDIA_RTCP_XR_INFO_CONF_JBR:
	    if (v >= 0 && v <= 15) {
		sess->stat.rx.voip_mtc.rx_config &= 0xF0;
		sess->stat.rx.voip_mtc.rx_config |= (pj_uint8_t) v;
	    }
	    break;

	case PJMEDIA_RTCP_XR_INFO_JB_NOM:
	    sess->stat.rx.voip_mtc.jb_nom = (pj_uint16_t) v;
	    break;

	case PJMEDIA_RTCP_XR_INFO_JB_MAX:
	    sess->stat.rx.voip_mtc.jb_max = (pj_uint16_t) v;
	    break;

	case PJMEDIA_RTCP_XR_INFO_JB_ABS_MAX:
	    sess->stat.rx.voip_mtc.jb_abs_max = (pj_uint16_t) v;
	    break;

	default:
	    return PJ_EINVAL;
    }

    return PJ_SUCCESS;
}