diff --git a/README.md b/README.md index 5c5305c08..d03e7ff6f 100644 --- a/README.md +++ b/README.md @@ -177,6 +177,7 @@ option and which are reproduced below: -L, --log-level=INT Mask log priorities above this level --log-facility=daemon|local0|... Syslog facility to use for logging --log-facility-cdr=local0|... Syslog facility to use for logging CDRs + --log-facility-rtcp=local0|... Syslog facility to use for logging RTCP data (take care of traffic amount) -E, --log-stderr Log on stderr instead of syslog -x, --xmlrpc-format=INT XMLRPC timeout request format to use. 0: SEMS DI, 1: call-id only --num-threads=INT Number of worker threads to create @@ -322,6 +323,11 @@ The options are described in more detail below. Same as --log-facility with the difference that only CDRs are written to this log facility. +* --log-facilty-rtcp=daemon|local0|...|local7|... + + Same as --log-facility with the difference that only RTCP data is written to this log facility. + Be careful with this parameter since there may be a lot of information written to it. + * -E, --log-stderr Log to stderr instead of syslog. Only useful in combination with `--foreground`. diff --git a/daemon/Makefile b/daemon/Makefile index 235526e14..8ef84b3af 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -63,7 +63,8 @@ endif SRCS= main.c kernel.c poller.c aux.c control_tcp.c streambuf.c call.c control_udp.c redis.c \ bencode.c cookie_cache.c udp_listener.c control_ng.c sdp.c str.c stun.c rtcp.c \ - crypto.c rtp.c call_interfaces.c dtls.c log.c cli.c graphite.c ice.c + crypto.c rtp.c call_interfaces.c dtls.c log.c cli.c graphite.c ice.c rtcp_xr.c + OBJS= $(SRCS:.c=.o) diff --git a/daemon/call.c b/daemon/call.c index 764725349..533a6397d 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "poller.h" #include "aux.h" @@ -691,6 +692,9 @@ loop_ok: if (!sink && PS_ISSET(stream, RTCP)) { sink = stream->rtcp_sink; rtcp = 1; + if (_log_facility_rtcp) { + parse_and_log_rtcp_report(sfd, s->s, s->len); + } } else if (stream->rtcp_sink) { muxed_rtcp = rtcp_demux(s, media); @@ -1400,9 +1404,13 @@ static void callmaster_timer(void *ptr) { DS(bytes); DS(errors); + if (ke->stats.packets != atomic64_get(&ps->kernel_stats.packets)) atomic64_set(&ps->last_packet, poller_now); + atomic64_set(&ps->stats.in_tos_tclass, ke->stats.in_tos); + atomic64_set(&m->statsps.in_tos_tclass, ke->stats.in_tos); + #if (RE_HAS_MEASUREDELAY) mutex_lock(&m->statspslock); ps->stats.delay_min = m->statsps.delay_min = ke->stats.delay_min; @@ -1414,6 +1422,7 @@ static void callmaster_timer(void *ptr) { atomic64_set(&ps->kernel_stats.bytes, ke->stats.bytes); atomic64_set(&ps->kernel_stats.packets, ke->stats.packets); atomic64_set(&ps->kernel_stats.errors, ke->stats.errors); + atomic64_set(&ps->kernel_stats.in_tos_tclass, ke->stats.in_tos); for (j = 0; j < ke->target.num_payload_types; j++) { pt = ke->target.payload_types[j]; @@ -2774,7 +2783,8 @@ void call_destroy(struct call *c) { "ml%i_midx%u_%s_relayed_packets="UINT64F", " "ml%i_midx%u_%s_relayed_bytes="UINT64F", " "ml%i_midx%u_%s_relayed_errors="UINT64F", " - "ml%i_midx%u_%s_last_packet="UINT64F", ", + "ml%i_midx%u_%s_last_packet="UINT64F", " + "ml%i_midx%u_%s_in_tos_tclass=%" PRIu8 ", ", cdrlinecnt, md->index, protocol, addr, cdrlinecnt, md->index, protocol, ps->endpoint.port, cdrlinecnt, md->index, protocol, (unsigned int) (ps->sfd ? ps->sfd->fd.localport : 0), @@ -2785,7 +2795,9 @@ void call_destroy(struct call *c) { cdrlinecnt, md->index, protocol, atomic64_get(&ps->stats.errors), cdrlinecnt, md->index, protocol, - atomic64_get(&ps->last_packet)); + atomic64_get(&ps->last_packet), + cdrlinecnt, md->index, protocol, + atomic64_get(&ps->stats.in_tos_tclass)); } else { #if (RE_HAS_MEASUREDELAY) cdrbufcur += sprintf(cdrbufcur, @@ -2796,6 +2808,7 @@ void call_destroy(struct call *c) { "ml%i_midx%u_%s_relayed_bytes="UINT64F", " "ml%i_midx%u_%s_relayed_errors="UINT64F", " "ml%i_midx%u_%s_last_packet="UINT64F", " + "ml%i_midx%u_%s_in_tos_tclass=%" PRIu8 ", " "ml%i_midx%u_%s_delay_min=%llu.%09llu, " "ml%i_midx%u_%s_delay_avg=%llu.%09llu, " "ml%i_midx%u_%s_delay_max=%llu.%09llu, ", @@ -2810,6 +2823,8 @@ void call_destroy(struct call *c) { atomic64_get(&ps->stats.errors), cdrlinecnt, md->index, protocol, atomic64_get(&ps->last_packet), + cdrlinecnt, md->index, protocol, + atomic64_get(&ps->stats.in_tos_tclass), cdrlinecnt, md->index, protocol, (unsigned long long) ps->stats.delay_min.tv_sec, (unsigned long long) ps->stats.delay_min.tv_nsec, cdrlinecnt, md->index, protocol, (unsigned long long) ps->stats.delay_avg.tv_sec, (unsigned long long) ps->stats.delay_avg.tv_nsec, cdrlinecnt, md->index, protocol, (unsigned long long) ps->stats.delay_max.tv_sec, (unsigned long long) ps->stats.delay_max.tv_nsec); @@ -2821,7 +2836,8 @@ void call_destroy(struct call *c) { "ml%i_midx%u_%s_relayed_packets="UINT64F", " "ml%i_midx%u_%s_relayed_bytes="UINT64F", " "ml%i_midx%u_%s_relayed_errors="UINT64F", " - "ml%i_midx%u_%s_last_packet="UINT64F", ", + "ml%i_midx%u_%s_last_packet="UINT64F", " + "ml%i_midx%u_%s_in_tos_tclass=%" PRIu8 ", ", cdrlinecnt, md->index, protocol, addr, cdrlinecnt, md->index, protocol, ps->endpoint.port, cdrlinecnt, md->index, protocol, (unsigned int) (ps->sfd ? ps->sfd->fd.localport : 0), @@ -2832,7 +2848,10 @@ void call_destroy(struct call *c) { cdrlinecnt, md->index, protocol, atomic64_get(&ps->stats.errors), cdrlinecnt, md->index, protocol, - atomic64_get(&ps->last_packet)); + atomic64_get(&ps->last_packet), + cdrlinecnt, md->index, protocol, + atomic64_get(&ps->stats.in_tos_tclass)); + #endif } } diff --git a/daemon/call.h b/daemon/call.h index 840ed825d..ab9ab85bd 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -217,6 +217,7 @@ struct stats { struct timespec delay_min; struct timespec delay_avg; struct timespec delay_max; + atomic64 in_tos_tclass; }; struct totalstats { @@ -284,6 +285,7 @@ struct rtp_stats { atomic64 bytes; atomic64 kernel_packets; atomic64 kernel_bytes; + atomic64 in_tos_tclass; }; struct packet_stream { diff --git a/daemon/log.c b/daemon/log.c index 7bd4c2e84..a113c96dd 100644 --- a/daemon/log.c +++ b/daemon/log.c @@ -67,6 +67,7 @@ static const char* const prio_str[] = { gboolean _log_stderr = 0; int _log_facility = LOG_DAEMON; int _log_facility_cdr = 0; +int _log_facility_rtcp = 0; static GHashTable *__log_limiter; @@ -204,6 +205,7 @@ static unsigned int log_limiter_entry_hash(const void *p) { const struct log_limiter_entry *lle = p; return g_str_hash(lle->msg) ^ g_str_hash(lle->prefix); } + static int log_limiter_entry_equal(const void *a, const void *b) { const struct log_limiter_entry *A = a, *B = b; if (!g_str_equal(A->msg, B->msg)) @@ -213,6 +215,14 @@ static int log_limiter_entry_equal(const void *a, const void *b) { return 1; } +void rtcplog(const char* cdrbuffer) { + int previous; + int mask = LOG_MASK (LOG_INFO); + previous = setlogmask(mask); + syslog(LOG_INFO | _log_facility_rtcp, "%s", cdrbuffer); + setlogmask(previous); +} + void log_init() { mutex_init(&__log_limiter_lock); __log_limiter = g_hash_table_new(log_limiter_entry_hash, log_limiter_entry_equal); diff --git a/daemon/log.h b/daemon/log.h index ffe3f941e..91f93620c 100644 --- a/daemon/log.h +++ b/daemon/log.h @@ -31,6 +31,7 @@ struct log_info { extern gboolean _log_stderr; extern int _log_facility; extern int _log_facility_cdr; +extern int _log_facility_rtcp; typedef struct _fac_code { @@ -69,6 +70,7 @@ void __ilog(int prio, const char *fmt, ...)__attribute__ ((format (printf, 2, 3) #endif void cdrlog(const char* cdrbuffer); +void rtcplog(const char* cdrbuffer); #include "obj.h" diff --git a/daemon/main.c b/daemon/main.c index d51d78bec..660080128 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -260,7 +260,8 @@ static void options(int *argc, char ***argv) { char *graphite_prefix_s = NULL; char *redisps = NULL; char *log_facility_s = NULL; - char *log_facility_cdr_s = NULL; + char *log_facility_cdr_s = NULL; + char *log_facility_rtcp_s = NULL; int version = 0; int sip_source = 0; @@ -289,6 +290,7 @@ static void options(int *argc, char ***argv) { { "log-level", 'L', 0, G_OPTION_ARG_INT, (void *)&log_level,"Mask log priorities above this level","INT" }, { "log-facility",0, 0, G_OPTION_ARG_STRING, &log_facility_s, "Syslog facility to use for logging", "daemon|local0|...|local7"}, { "log-facility-cdr",0, 0, G_OPTION_ARG_STRING, &log_facility_cdr_s, "Syslog facility to use for logging CDRs", "daemon|local0|...|local7"}, + { "log-facility-rtcp",0, 0, G_OPTION_ARG_STRING, &log_facility_rtcp_s, "Syslog facility to use for logging RTCP", "daemon|local0|...|local7"}, { "log-stderr", 'E', 0, G_OPTION_ARG_NONE, &_log_stderr, "Log on stderr instead of syslog", NULL }, { "xmlrpc-format",'x', 0, G_OPTION_ARG_INT, &xmlrpc_fmt, "XMLRPC timeout request format to use. 0: SEMS DI, 1: call-id only", "INT" }, { "num-threads", 0, 0, G_OPTION_ARG_INT, &num_threads, "Number of worker threads to create", "INT" }, @@ -381,6 +383,13 @@ static void options(int *argc, char ***argv) { } } + if (log_facility_rtcp_s) { + if (!parse_log_facility(log_facility_rtcp_s, &_log_facility_rtcp)) { + print_available_log_facilities(); + die ("Invalid log facility for RTCP '%s' (--log-facility-rtcp)\n", log_facility_rtcp_s); + } + } + if (_log_stderr) { write_log = log_to_stderr; max_log_line_length = 0; diff --git a/daemon/rtcp.c b/daemon/rtcp.c index 793bc39c5..448f3b66b 100644 --- a/daemon/rtcp.c +++ b/daemon/rtcp.c @@ -11,6 +11,7 @@ #include "log.h" #include "rtp.h" #include "crypto.h" +#include "rtcp_xr.h" @@ -26,9 +27,6 @@ #define SRTCP_R_LENGTH 6 #endif - - - #define RTCP_PT_SR 200 /* sender report */ #define RTCP_PT_RR 201 /* receiver report */ #define RTCP_PT_SDES 202 /* source description */ @@ -36,6 +34,7 @@ #define RTCP_PT_APP 204 /* application specific */ #define RTCP_PT_RTPFB 205 /* transport layer feedback message (RTP/AVPF) */ #define RTCP_PT_PSFB 206 /* payload-specific feedback message (RTP/AVPF) */ +#define RTCP_PT_XR 207 #define SDES_TYPE_END 0 #define SDES_TYPE_CNAME 1 @@ -481,3 +480,73 @@ int rtcp_demux_is_rtcp(const str *s) { return 0; return 1; } + +void print_rtcp_common(char** cdrbufcur, const pjmedia_rtcp_common *common) { + *cdrbufcur += sprintf(*cdrbufcur,"version=%u, padding=%u, count=%u, payloadtype=%u, length=%u, ssrc=%u, ", + common->version, + common->p, + common->count, + common->pt, + common->length, + ntohl(common->ssrc)); +} + +void print_rtcp_sr(char** cdrbufcur, const pjmedia_rtcp_sr* sr) { + *cdrbufcur += sprintf(*cdrbufcur,"ntp_sec=%u, ntp_fractions=%u, rtp_ts=%u, sender_packets=%u, sender_bytes=%u, ", + ntohl(sr->ntp_sec), + ntohl(sr->ntp_frac), + ntohl(sr->rtp_ts), + ntohl(sr->sender_pcount), + ntohl(sr->sender_bcount)); +} + +void print_rtcp_rr(char** cdrbufcur, const pjmedia_rtcp_rr* rr) { + /* Get packet loss */ + u_int32_t packet_loss=0; + packet_loss = (rr->total_lost_2 << 16) + + (rr->total_lost_1 << 8) + + rr->total_lost_0; + + *cdrbufcur += sprintf(*cdrbufcur,"ssrc=%u, fraction_lost=%u, packet_loss=%u, last_seq=%u, jitter=%u, last_sr=%u, delay_since_last_sr=%u, ", + ntohl(rr->ssrc), + ntohl(rr->fract_lost), + ntohl(packet_loss), + ntohl(rr->last_seq), + ntohl(rr->jitter), + ntohl(rr->lsr), + ntohl(rr->dlsr)); +} + +void parse_and_log_rtcp_report(struct stream_fd *sfd, const void *pkt, long size) { + + static const int CDRBUFLENGTH = 1024*1024*1; // 1 MB + char cdrbuffer[CDRBUFLENGTH]; memset(&cdrbuffer,0,CDRBUFLENGTH); + char* cdrbufcur = cdrbuffer; + pjmedia_rtcp_common *common = (pjmedia_rtcp_common*) pkt; + const pjmedia_rtcp_rr *rr = NULL; + const pjmedia_rtcp_sr *sr = NULL; + + cdrbufcur += sprintf(cdrbufcur,"[%s] ",sfd->stream->call->callid); + + print_rtcp_common(&cdrbufcur,common); + + /* Parse RTCP */ + if (common->pt == RTCP_PT_SR) { + sr = (pjmedia_rtcp_sr*) (((char*)pkt) + sizeof(pjmedia_rtcp_common)); + + print_rtcp_sr(&cdrbufcur,sr); + + if (common->count > 0 && size >= (sizeof(pjmedia_rtcp_sr_pkt))) { + rr = (pjmedia_rtcp_rr*)(((char*)pkt) + (sizeof(pjmedia_rtcp_common) + + sizeof(pjmedia_rtcp_sr))); + print_rtcp_rr(&cdrbufcur,rr); + } + } else if (common->pt == RTCP_PT_RR && common->count > 0) { + rr = (pjmedia_rtcp_rr*)(((char*)pkt) + sizeof(pjmedia_rtcp_common)); + print_rtcp_rr(&cdrbufcur,rr); + + } else if (common->pt == RTCP_PT_XR) { + pjmedia_rtcp_xr_rx_rtcp_xr(&cdrbufcur, pkt, size); + } + rtcplog(cdrbuffer); +} diff --git a/daemon/rtcp.h b/daemon/rtcp.h index d6dc353dd..96f88fa8c 100644 --- a/daemon/rtcp.h +++ b/daemon/rtcp.h @@ -2,6 +2,7 @@ #define _RTCP_H_ #include "str.h" +#include "call.h" struct crypto_context; @@ -19,6 +20,83 @@ struct rtcp_packet { u_int32_t ssrc; } __attribute__ ((packed)); +/** + * RTCP sender report. + */ +typedef struct pjmedia_rtcp_sr +{ + u_int32_t ntp_sec; /**< NTP time, seconds part. */ + u_int32_t ntp_frac; /**< NTP time, fractions part. */ + u_int32_t rtp_ts; /**< RTP timestamp. */ + u_int32_t sender_pcount; /**< Sender packet cound. */ + u_int32_t sender_bcount; /**< Sender octet/bytes count. */ +} pjmedia_rtcp_sr; + + +/** + * RTCP receiver report. + */ +typedef struct pjmedia_rtcp_rr +{ + u_int32_t ssrc; /**< SSRC identification. */ +#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0 + u_int32_t fract_lost:8; /**< Fraction lost. */ + u_int32_t total_lost_2:8; /**< Total lost, bit 16-23. */ + u_int32_t total_lost_1:8; /**< Total lost, bit 8-15. */ + u_int32_t total_lost_0:8; /**< Total lost, bit 0-7. */ +#else + u_int32_t fract_lost:8; /**< Fraction lost. */ + u_int32_t total_lost_2:8; /**< Total lost, bit 0-7. */ + u_int32_t total_lost_1:8; /**< Total lost, bit 8-15. */ + u_int32_t total_lost_0:8; /**< Total lost, bit 16-23. */ +#endif + u_int32_t last_seq; /**< Last sequence number. */ + u_int32_t jitter; /**< Jitter. */ + u_int32_t lsr; /**< Last SR. */ + u_int32_t dlsr; /**< Delay since last SR. */ +} pjmedia_rtcp_rr; + + +/** + * RTCP common header. + */ +typedef struct pjmedia_rtcp_common +{ +#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0 + unsigned version:2; /**< packet type */ + unsigned p:1; /**< padding flag */ + unsigned count:5; /**< varies by payload type */ + unsigned pt:8; /**< payload type */ +#else + unsigned count:5; /**< varies by payload type */ + unsigned p:1; /**< padding flag */ + unsigned version:2; /**< packet type */ + unsigned pt:8; /**< payload type */ +#endif + unsigned length:16; /**< packet length */ + u_int32_t ssrc; /**< SSRC identification */ +} pjmedia_rtcp_common; + +/** + * This structure declares default RTCP packet (SR) that is sent by pjmedia. + * Incoming RTCP packet may have different format, and must be parsed + * manually by application. + */ +typedef struct pjmedia_rtcp_sr_pkt +{ + pjmedia_rtcp_common common; /**< Common header. */ + pjmedia_rtcp_sr sr; /**< Sender report. */ + pjmedia_rtcp_rr rr; /**< variable-length list */ +} pjmedia_rtcp_sr_pkt; + +/** + * This structure declares RTCP RR (Receiver Report) packet. + */ +typedef struct pjmedia_rtcp_rr_pkt +{ + pjmedia_rtcp_common common; /**< Common header. */ + pjmedia_rtcp_rr rr; /**< variable-length list */ +} pjmedia_rtcp_rr_pkt; int rtcp_avpf2avp(str *); @@ -27,6 +105,6 @@ int rtcp_savp2avp(str *, struct crypto_context *); int rtcp_demux_is_rtcp(const str *); - +void parse_and_log_rtcp_report(struct stream_fd *sfd, const void *pkt, long size); #endif diff --git a/daemon/rtcp_xr.c b/daemon/rtcp_xr.c new file mode 100644 index 000000000..b43cea3dc --- /dev/null +++ b/daemon/rtcp_xr.c @@ -0,0 +1,160 @@ +/* + * rtcp_xr.c + * + * Created on: Mar 29, 2015 + * Author: fmetz + */ +#include +#include +#include "rtcp_xr.h" + +/* RTCP XR payload type */ +#define RTCP_XR 207 + +/* RTCP XR block types */ +#define BT_LOSS_RLE 1 +#define BT_DUP_RLE 2 +#define BT_RCPT_TIMES 3 +#define BT_RR_TIME 4 +#define BT_DLRR 5 +#define BT_STATS 6 +#define BT_VOIP_METRICS 7 + + +void print_rtcp_xr_common(char* cdrbufcur,const pjmedia_rtcp_xr_pkt *rtcp_xr) { + cdrbufcur += sprintf(cdrbufcur,"version=%u, padding=%u, count=%u, payloadtype=%u, length=%u, ssrc=%u, ", + rtcp_xr->common.version, + rtcp_xr->common.p, + rtcp_xr->common.count, + rtcp_xr->common.pt, + rtcp_xr->common.length, + ntohl(rtcp_xr->common.ssrc)); +} + +void print_rtcp_xr_rb_header(char* cdrbufcur,const pjmedia_rtcp_xr_rb_header *rb_header) { + cdrbufcur += sprintf(cdrbufcur,"rb_header_blocktype=%u, rb_header_blockspecdata=%u, rb_header_blocklength=%u, ", + rb_header->bt, + rb_header->specific, + ntohs(rb_header->length)); +} + +void print_rtcp_xr_rb_rr_time(char* cdrbufcur,const pjmedia_rtcp_xr_rb_rr_time *rb_rr_time) { + print_rtcp_xr_rb_header(cdrbufcur,&rb_rr_time->header); + cdrbufcur += sprintf(cdrbufcur,"rb_rr_time_ntp_sec=%u, rb_rr_time_ntp_frac=%u, ", + ntohl(rb_rr_time->ntp_sec), + ntohl(rb_rr_time->ntp_frac)); +} + +void print_rtcp_xr_rb_dlrr(char* cdrbufcur,const pjmedia_rtcp_xr_rb_dlrr *rb_dlrr) { + print_rtcp_xr_rb_header(cdrbufcur,&rb_dlrr->header); + cdrbufcur += sprintf(cdrbufcur,"rb_dlrr_ssrc=%u, rb_dlrr_lrr=%u, rb_dlrr_dlrr=%u, ", + ntohl(rb_dlrr->item.ssrc), + ntohl(rb_dlrr->item.lrr), + ntohl(rb_dlrr->item.dlrr)); +} + +void print_rtcp_xr_rb_stats(char* cdrbufcur,const pjmedia_rtcp_xr_rb_stats *rb_stats) { + print_rtcp_xr_rb_header(cdrbufcur,&rb_stats->header); + cdrbufcur += sprintf(cdrbufcur,"rb_stats_ssrc=%u, rb_stats_begin_seq=%u, rb_stats_end_seq=%u, rb_stats_lost_packets=%u, rb_stats_duplicate_packets=%u," + "rb_stats_jitter_min=%u, rb_stats_jitter_max=%u, rb_stats_jitter_mean=%u, rb_stats_jitter_deviation=%u," + "rb_stats_toh_min=%u, rb_stats_toh_max=%u, rb_stats_toh_mean=%u, rb_stats_toh_deviation=%u, ", + ntohl(rb_stats->ssrc), + ntohs(rb_stats->begin_seq), + ntohl(rb_stats->end_seq), + ntohl(rb_stats->lost), + ntohl(rb_stats->dup), + ntohl(rb_stats->jitter_min), + ntohl(rb_stats->jitter_max), + ntohl(rb_stats->jitter_mean), + ntohl(rb_stats->jitter_dev), + ntohl(rb_stats->toh_min), + ntohl(rb_stats->toh_max), + ntohl(rb_stats->toh_mean), + ntohl(rb_stats->toh_dev)); +} + +void print_rtcp_xr_rb_voip_mtc(char* cdrbufcur,const pjmedia_rtcp_xr_rb_voip_mtc *rb_voip_mtc) { + print_rtcp_xr_rb_header(cdrbufcur,&rb_voip_mtc->header); + cdrbufcur += sprintf(cdrbufcur,"rb_voip_mtc_ssrc=%u, rb_voip_mtc_loss_rate=%u, rb_voip_mtc_discard_rate=%u, rb_voip_mtc_burst_den=%u, " + "rb_voip_mtc_gap_den=%u, rb_voip_mtc_burst_dur=%u, rb_voip_mtc_gap_dur=%u, rb_voip_mtc_rnd_trip_delay=%u, " + "rb_voip_mtc_end_sys_delay=%u, rb_voip_mtc_signal_lvl=%u, rb_voip_mtc_noise_lvl=%u, rb_voip_mtc_rerl=%u, " + "rb_voip_mtc_gmin=%u, rb_voip_mtc_r_factor=%u, rb_voip_mtc_ext_r_factor=%u, rb_voip_mtc_mos_lq=%u, " + "rb_voip_mtc_mos_cq=%u, rb_voip_mtc_rx_config=%u, rb_voip_mtc_jb_nom=%u, rb_voip_mtc_jb_max=%u, " + "rb_voip_mtc_jb_abs_max=%u, ", + ntohl(rb_voip_mtc->ssrc), + rb_voip_mtc->loss_rate, + rb_voip_mtc->discard_rate, + rb_voip_mtc->burst_den, + rb_voip_mtc->gap_den, + ntohs(rb_voip_mtc->burst_dur), + ntohs(rb_voip_mtc->gap_dur), + ntohs(rb_voip_mtc->rnd_trip_delay), + ntohs(rb_voip_mtc->end_sys_delay), + rb_voip_mtc->signal_lvl, + rb_voip_mtc->noise_lvl, + rb_voip_mtc->rerl, + rb_voip_mtc->gmin, + rb_voip_mtc->r_factor, + rb_voip_mtc->ext_r_factor, + rb_voip_mtc->mos_lq, + rb_voip_mtc->mos_cq, + rb_voip_mtc->rx_config, + ntohs(rb_voip_mtc->jb_nom), + ntohs(rb_voip_mtc->jb_max), + ntohs(rb_voip_mtc->jb_abs_max)); +} + +void pjmedia_rtcp_xr_rx_rtcp_xr(char* cdrbufcur, const void *pkt, 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; + + print_rtcp_xr_common(cdrbufcur,rtcp_xr); + + pkt_len = ntohs((u_int16_t)rtcp_xr->common.length); + + if ((pkt_len + 1) > (size / 4)) + return; + + /* Parse report rpt_types */ + while ((int32_t*)rb_hdr < (int32_t*)pkt + pkt_len) + { + rb_len = ntohs((u_int16_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: + rb_rr_time = (pjmedia_rtcp_xr_rb_rr_time*) rb_hdr; + print_rtcp_xr_rb_rr_time(cdrbufcur,rb_rr_time); + break; + case BT_DLRR: + rb_dlrr = (pjmedia_rtcp_xr_rb_dlrr*) rb_hdr; + print_rtcp_xr_rb_dlrr(cdrbufcur,rb_dlrr); + break; + case BT_STATS: + rb_stats = (pjmedia_rtcp_xr_rb_stats*) rb_hdr; + print_rtcp_xr_rb_stats(cdrbufcur,rb_stats); + break; + case BT_VOIP_METRICS: + rb_voip_mtc = (pjmedia_rtcp_xr_rb_voip_mtc*) rb_hdr; + print_rtcp_xr_rb_voip_mtc(cdrbufcur,rb_voip_mtc); + break; + default: + break; + } + } + rb_hdr = (pjmedia_rtcp_xr_rb_header*) + ((int32_t*)rb_hdr + rb_len + 1); + } +} + diff --git a/daemon/rtcp_xr.h b/daemon/rtcp_xr.h new file mode 100644 index 000000000..b8cbb5b5d --- /dev/null +++ b/daemon/rtcp_xr.h @@ -0,0 +1,229 @@ +/* + * rtcp_xr.h + * + * Created on: Mar 29, 2015 + * Author: fmetz + */ + +#ifndef RTCP_XR_H_ +#define RTCP_XR_H_ + +#include +#include + +/** + * @defgroup PJMED_RTCP_XR RTCP Extended Report (XR) - RFC 3611 + * @ingroup PJMEDIA_SESSION + * @brief RTCP XR extension to RTCP session + * @{ + * + * PJMEDIA implements subsets of RTCP XR specification (RFC 3611) to monitor + * the quality of the real-time media (audio/video) transmission. + */ + +/** + * Enumeration of report types of RTCP XR. Useful for user to enable varying + * combinations of RTCP XR report blocks. + */ +typedef enum { + PJMEDIA_RTCP_XR_LOSS_RLE = (1 << 0), + PJMEDIA_RTCP_XR_DUP_RLE = (1 << 1), + PJMEDIA_RTCP_XR_RCPT_TIMES = (1 << 2), + PJMEDIA_RTCP_XR_RR_TIME = (1 << 3), + PJMEDIA_RTCP_XR_DLRR = (1 << 4), + PJMEDIA_RTCP_XR_STATS = (1 << 5), + PJMEDIA_RTCP_XR_VOIP_METRICS = (1 << 6) +} pjmedia_rtcp_xr_type; + +/** + * Enumeration of info need to be updated manually to RTCP XR. Most info + * could be updated automatically each time RTP received. + */ +typedef enum { + PJMEDIA_RTCP_XR_INFO_SIGNAL_LVL = 1, + PJMEDIA_RTCP_XR_INFO_NOISE_LVL = 2, + PJMEDIA_RTCP_XR_INFO_RERL = 3, + PJMEDIA_RTCP_XR_INFO_R_FACTOR = 4, + PJMEDIA_RTCP_XR_INFO_MOS_LQ = 5, + PJMEDIA_RTCP_XR_INFO_MOS_CQ = 6, + PJMEDIA_RTCP_XR_INFO_CONF_PLC = 7, + PJMEDIA_RTCP_XR_INFO_CONF_JBA = 8, + PJMEDIA_RTCP_XR_INFO_CONF_JBR = 9, + PJMEDIA_RTCP_XR_INFO_JB_NOM = 10, + PJMEDIA_RTCP_XR_INFO_JB_MAX = 11, + PJMEDIA_RTCP_XR_INFO_JB_ABS_MAX = 12 +} pjmedia_rtcp_xr_info; + +/** + * Enumeration of PLC types definitions for RTCP XR report. + */ +typedef enum { + PJMEDIA_RTCP_XR_PLC_UNK = 0, + PJMEDIA_RTCP_XR_PLC_DIS = 1, + PJMEDIA_RTCP_XR_PLC_ENH = 2, + PJMEDIA_RTCP_XR_PLC_STD = 3 +} pjmedia_rtcp_xr_plc_type; + +/** + * Enumeration of jitter buffer types definitions for RTCP XR report. + */ +typedef enum { + PJMEDIA_RTCP_XR_JB_UNKNOWN = 0, + PJMEDIA_RTCP_XR_JB_FIXED = 2, + PJMEDIA_RTCP_XR_JB_ADAPTIVE = 3 +} pjmedia_rtcp_xr_jb_type; + + +#pragma pack(1) + +/** + * This type declares RTCP XR Report Header. + */ +typedef struct pjmedia_rtcp_xr_rb_header +{ + u_int8_t bt; /**< Block type. */ + u_int8_t specific; /**< Block specific data. */ + u_int16_t length; /**< Block length. */ +} pjmedia_rtcp_xr_rb_header; + +/** + * This type declares RTCP XR Receiver Reference Time Report Block. + */ +typedef struct pjmedia_rtcp_xr_rb_rr_time +{ + pjmedia_rtcp_xr_rb_header header; /**< Block header. */ + u_int32_t ntp_sec; /**< NTP time, seconds part. */ + u_int32_t ntp_frac; /**< NTP time, fractions part. */ +} pjmedia_rtcp_xr_rb_rr_time; + + +/** + * This type declares RTCP XR DLRR Report Sub-block + */ +typedef struct pjmedia_rtcp_xr_rb_dlrr_item +{ + u_int32_t ssrc; /**< receiver SSRC */ + u_int32_t lrr; /**< last receiver report */ + u_int32_t dlrr; /**< delay since last receiver + report */ +} pjmedia_rtcp_xr_rb_dlrr_item; + +/** + * This type declares RTCP XR DLRR Report Block + */ +typedef struct pjmedia_rtcp_xr_rb_dlrr +{ + pjmedia_rtcp_xr_rb_header header; /**< Block header. */ + pjmedia_rtcp_xr_rb_dlrr_item item; /**< Block contents, + variable length list */ +} pjmedia_rtcp_xr_rb_dlrr; + +/** + * This type declares RTCP XR Statistics Summary Report Block + */ +typedef struct pjmedia_rtcp_xr_rb_stats +{ + pjmedia_rtcp_xr_rb_header header; /**< Block header. */ + u_int32_t ssrc; /**< Receiver SSRC */ + u_int16_t begin_seq; /**< Begin RTP sequence reported */ + u_int16_t end_seq; /**< End RTP sequence reported */ + u_int32_t lost; /**< Number of packet lost in this + interval */ + u_int32_t dup; /**< Number of duplicated packet in + this interval */ + u_int32_t jitter_min; /**< Minimum jitter in this interval */ + u_int32_t jitter_max; /**< Maximum jitter in this interval */ + u_int32_t jitter_mean; /**< Average jitter in this interval */ + u_int32_t jitter_dev; /**< Jitter deviation in this + interval */ + u_int32_t toh_min:8; /**< Minimum ToH in this interval */ + u_int32_t toh_max:8; /**< Maximum ToH in this interval */ + u_int32_t toh_mean:8; /**< Average ToH in this interval */ + u_int32_t toh_dev:8; /**< ToH deviation in this interval */ +} pjmedia_rtcp_xr_rb_stats; + +/** + * This type declares RTCP XR VoIP Metrics Report Block + */ +typedef struct pjmedia_rtcp_xr_rb_voip_mtc +{ + pjmedia_rtcp_xr_rb_header header; /**< Block header. */ + u_int32_t ssrc; /**< Receiver SSRC */ + u_int8_t loss_rate; /**< Packet loss rate */ + u_int8_t discard_rate; /**< Packet discarded rate */ + u_int8_t burst_den; /**< Burst density */ + u_int8_t gap_den; /**< Gap density */ + u_int16_t burst_dur; /**< Burst duration */ + u_int16_t gap_dur; /**< Gap duration */ + u_int16_t rnd_trip_delay;/**< Round trip delay */ + u_int16_t end_sys_delay; /**< End system delay */ + u_int8_t signal_lvl; /**< Signal level */ + u_int8_t noise_lvl; /**< Noise level */ + u_int8_t rerl; /**< Residual Echo Return Loss */ + u_int8_t gmin; /**< The gap threshold */ + u_int8_t r_factor; /**< Voice quality metric carried + over this RTP session */ + u_int8_t ext_r_factor; /**< Voice quality metric carried + outside of this RTP session*/ + u_int8_t mos_lq; /**< Mean Opinion Score for + Listening Quality */ + u_int8_t mos_cq; /**< Mean Opinion Score for + Conversation Quality */ + u_int8_t rx_config; /**< Receiver configuration */ + u_int8_t reserved2; /**< Not used */ + u_int16_t jb_nom; /**< Current delay by jitter + buffer */ + u_int16_t jb_max; /**< Maximum delay by jitter + buffer */ + u_int16_t jb_abs_max; /**< Maximum possible delay by + jitter buffer */ +} pjmedia_rtcp_xr_rb_voip_mtc; + + +/** + * Constant of RTCP-XR content size. + */ +#define PJMEDIA_RTCP_XR_BUF_SIZE \ + sizeof(pjmedia_rtcp_xr_rb_rr_time) + \ + sizeof(pjmedia_rtcp_xr_rb_dlrr) + \ + sizeof(pjmedia_rtcp_xr_rb_stats) + \ + sizeof(pjmedia_rtcp_xr_rb_voip_mtc) + + +/** + * This structure declares RTCP XR (Extended Report) packet. + */ +typedef struct pjmedia_rtcp_xr_pkt +{ + struct { +#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0 + unsigned version:2; /**< packet type */ + unsigned p:1; /**< padding flag */ + unsigned count:5; /**< varies by payload type */ + unsigned pt:8; /**< payload type */ +#else + unsigned count:5; /**< varies by payload type */ + unsigned p:1; /**< padding flag */ + unsigned version:2; /**< packet type */ + unsigned pt:8; /**< payload type */ +#endif + unsigned length:16; /**< packet length */ + u_int32_t ssrc; /**< SSRC identification */ + } common; + + int8_t buf[PJMEDIA_RTCP_XR_BUF_SIZE]; + /**< Content buffer */ +} pjmedia_rtcp_xr_pkt; + + +/** + * This function is called internally by RTCP session when it receives + * incoming RTCP XR packets. + * + * @param rtcp_pkt The received RTCP XR packet. + * @param size Size of the incoming packet. + */ +void pjmedia_rtcp_xr_rx_rtcp_xr( char* cdrbufcur, const void *rtcp_pkt, size_t size); + + +#endif /* RTCP_XR_H_ */ diff --git a/debian/ngcp-rtpengine-daemon.default b/debian/ngcp-rtpengine-daemon.default index 3f259c0c6..d06a56e74 100644 --- a/debian/ngcp-rtpengine-daemon.default +++ b/debian/ngcp-rtpengine-daemon.default @@ -21,6 +21,7 @@ TABLE=0 # LOG_LEVEL=6 # LOG_FACILITY=daemon # LOG_FACILITY_CDR=daemon +# LOG_FACILITY_RTCP=daemon # NUM_THREADS=5 # DELETE_DELAY=30 # GRAPHITE=9006 diff --git a/debian/ngcp-rtpengine-daemon.init b/debian/ngcp-rtpengine-daemon.init index 64ce42902..f3001db7f 100755 --- a/debian/ngcp-rtpengine-daemon.init +++ b/debian/ngcp-rtpengine-daemon.init @@ -70,6 +70,7 @@ OPTIONS="$OPTIONS --table=$TABLE" [ -z "$LOG_LEVEL" ] || OPTIONS="$OPTIONS --log-level=$LOG_LEVEL" [ -z "$LOG_FACILITY" ] || OPTIONS="$OPTIONS --log-facility=$LOG_FACILITY" [ -z "$LOG_FACILITY_CDR" ] || OPTIONS="$OPTIONS --log-facility-cdr=$LOG_FACILITY_CDR" +[ -z "$LOG_FACILITY_RTCP" ] || OPTIONS="$OPTIONS --log-facility-rtcp=$LOG_FACILITY_RTCP" [ -z "$NUM_THREADS" ] || OPTIONS="$OPTIONS --num-threads=$NUM_THREADS" [ -z "$DELETE_DELAY" ] || OPTIONS="$OPTIONS --delete-delay=$DELETE_DELAY" [ -z "$GRAPHITE" ] || OPTIONS="$OPTIONS --graphite=$GRAPHITE" diff --git a/el/rtpengine.init b/el/rtpengine.init index 3ae663dea..3cf2930ad 100644 --- a/el/rtpengine.init +++ b/el/rtpengine.init @@ -162,6 +162,11 @@ build_opts() { then OPTS+=" --log-facility-cdr=$LOG_FACILITY_CDR" fi + + if [[ -n "$LOG_FACILITY_RTCP" ]] + then + OPTS+=" --log-facility-rtcp=$LOG_FACILITY_RTCP" + fi } start() { diff --git a/kernel-module/xt_RTPENGINE.c b/kernel-module/xt_RTPENGINE.c index f7dd76b89..86271d75f 100644 --- a/kernel-module/xt_RTPENGINE.c +++ b/kernel-module/xt_RTPENGINE.c @@ -152,6 +152,7 @@ struct rtpengine_stats_a { struct timespec delay_min; struct timespec delay_avg; struct timespec delay_max; + atomic_t in_tos; }; struct rtpengine_rtp_stats_a { atomic64_t packets; @@ -862,6 +863,7 @@ static ssize_t proc_blist_read(struct file *f, char __user *b, size_t l, loff_t op.stats.delay_min = g->stats.delay_min; op.stats.delay_max = g->stats.delay_max; op.stats.delay_avg = g->stats.delay_avg; + op.stats.in_tos = atomic64_read(&g->stats.in_tos); for (i = 0; i < g->target.num_payload_types; i++) { op.rtp_stats[i].packets = atomic64_read(&g->rtp_stats[i].packets); @@ -2230,9 +2232,9 @@ static int re_timespec_cmp (struct timespec *a, struct timespec *b) } #if (RE_HAS_MEASUREDELAY) - static unsigned int rtpengine46(struct sk_buff *skb, struct rtpengine_table *t, struct re_address *src, struct timespec *starttime) { + static unsigned int rtpengine46(struct sk_buff *skb, struct rtpengine_table *t, struct re_address *src, struct timespec *starttime, u_int8_t in_tos) { #else - static unsigned int rtpengine46(struct sk_buff *skb, struct rtpengine_table *t, struct re_address *src) { + static unsigned int rtpengine46(struct sk_buff *skb, struct rtpengine_table *t, struct re_address *src, u_int8_t in_tos) { #endif struct udphdr *uh; struct rtpengine_target *g; @@ -2352,6 +2354,10 @@ not_rtp: err = send_proxy_packet(skb, &g->target.src_addr, &g->target.dst_addr, g->target.tos); out: + + if (atomic64_read(&g->stats.packets)==0) + atomic_set(&g->stats.in_tos,in_tos); + if (err) atomic64_inc(&g->stats.errors); else { @@ -2445,9 +2451,9 @@ static unsigned int rtpengine4(struct sk_buff *oskb, const struct xt_action_para src.u.ipv4 = ih->saddr; #if (RE_HAS_MEASUREDELAY) - return rtpengine46(skb, t, &src, &starttime); + return rtpengine46(skb, t, &src, &starttime, (u_int8_t)ih->tos); #else - return rtpengine46(skb, t, &src); + return rtpengine46(skb, t, &src, (u_int8_t)ih->tos); #endif skip2: @@ -2487,6 +2493,7 @@ static unsigned int rtpengine6(struct sk_buff *oskb, const struct xt_action_para skb_reset_network_header(skb); ih = ipv6_hdr(skb); + skb_pull(skb, sizeof(*ih)); if (ih->nexthdr != IPPROTO_UDP) goto skip2; @@ -2496,9 +2503,9 @@ static unsigned int rtpengine6(struct sk_buff *oskb, const struct xt_action_para memcpy(&src.u.ipv6, &ih->saddr, sizeof(src.u.ipv6)); #if (RE_HAS_MEASUREDELAY) - return rtpengine46(skb, t, &src, &starttime); + return rtpengine46(skb, t, &src, &starttime, ipv6_get_dsfield(ih)); #else - return rtpengine46(skb, t, &src); + return rtpengine46(skb, t, &src, ipv6_get_dsfield(ih)); #endif skip2: diff --git a/kernel-module/xt_RTPENGINE.h b/kernel-module/xt_RTPENGINE.h index fac7e33c9..d285f8353 100644 --- a/kernel-module/xt_RTPENGINE.h +++ b/kernel-module/xt_RTPENGINE.h @@ -18,6 +18,7 @@ struct rtpengine_stats { struct timespec delay_min; struct timespec delay_avg; struct timespec delay_max; + u_int8_t in_tos; }; struct rtpengine_rtp_stats { u_int64_t packets;