diff --git a/README.md b/README.md index bc23cf574..ce7de4691 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 @@ -185,6 +186,7 @@ option and which are reproduced below: --dtls-passive Always prefer DTLS passive role -g, --graphite=[IP46:]PORT TCP address of graphite statistics server -w, --graphite-interval=INT Graphite data statistics send interval + --graphite-prefix=STRING Graphite prefix for every line Most of these options are indeed optional, with two exceptions. It's mandatory to specify at least one local IP address through `--interface`, and at least one of the `--listen-...` options must be given. @@ -321,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`. @@ -363,6 +370,10 @@ The options are described in more detail below. Interval of the time when information is sent to the graphite server. +* --graphite-prefix + + Add a prefix for every graphite line. + A typical command line (enabling both UDP and NG protocols) thus may look like: /usr/sbin/rtpengine --table=0 --interface=10.64.73.31 --interface=2001:db8::4f3:3d \ 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 0d0d338b8..91c50593a 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "poller.h" #include "aux.h" @@ -33,7 +34,7 @@ #include "rtp.h" #include "call_interfaces.h" #include "ice.h" - +#include "rtpengine_config.h" @@ -702,6 +703,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); @@ -1413,12 +1417,25 @@ 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; + ps->stats.delay_avg = m->statsps.delay_avg = ke->stats.delay_avg; + ps->stats.delay_max = m->statsps.delay_max = ke->stats.delay_max; + mutex_unlock(&m->statspslock); +#endif + 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]; @@ -1435,6 +1452,14 @@ static void callmaster_timer(void *ptr) { atomic64_set(&rs->kernel_bytes, ke->rtp_stats[j].bytes); } +#if (RE_HAS_MEASUREDELAY) + mutex_lock(&m->statspslock); + ps->kernel_stats.delay_min = ke->stats.delay_min; + ps->kernel_stats.delay_avg = ke->stats.delay_avg; + ps->kernel_stats.delay_max = ke->stats.delay_max; + mutex_unlock(&m->statspslock); +#endif + update = 0; sink = packet_stream_sink(ps); @@ -2796,25 +2821,86 @@ void call_destroy(struct call *c) { if (_log_facility_cdr) { const char* protocol = (!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) ? "rtcp" : "rtp"; - cdrbufcur += sprintf(cdrbufcur, - "ml%i_midx%u_%s_endpoint_ip=%s, " - "ml%i_midx%u_%s_endpoint_port=%u, " - "ml%i_midx%u_%s_local_relay_port=%u, " - "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", ", - 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), - cdrlinecnt, md->index, protocol, - atomic64_get(&ps->stats.packets), - cdrlinecnt, md->index, protocol, - atomic64_get(&ps->stats.bytes), - cdrlinecnt, md->index, protocol, - atomic64_get(&ps->stats.errors), - cdrlinecnt, md->index, protocol, - atomic64_get(&ps->last_packet)); + + if(!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) { + cdrbufcur += sprintf(cdrbufcur, + "ml%i_midx%u_%s_endpoint_ip=%s, " + "ml%i_midx%u_%s_endpoint_port=%u, " + "ml%i_midx%u_%s_local_relay_port=%u, " + "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_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), + cdrlinecnt, md->index, protocol, + atomic64_get(&ps->stats.packets), + cdrlinecnt, md->index, protocol, + atomic64_get(&ps->stats.bytes), + cdrlinecnt, md->index, protocol, + 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)); + } else { +#if (RE_HAS_MEASUREDELAY) + cdrbufcur += sprintf(cdrbufcur, + "ml%i_midx%u_%s_endpoint_ip=%s, " + "ml%i_midx%u_%s_endpoint_port=%u, " + "ml%i_midx%u_%s_local_relay_port=%u, " + "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_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, ", + 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), + cdrlinecnt, md->index, protocol, + atomic64_get(&ps->stats.packets), + cdrlinecnt, md->index, protocol, + atomic64_get(&ps->stats.bytes), + cdrlinecnt, md->index, protocol, + 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); +#else + cdrbufcur += sprintf(cdrbufcur, + "ml%i_midx%u_%s_endpoint_ip=%s, " + "ml%i_midx%u_%s_endpoint_port=%u, " + "ml%i_midx%u_%s_local_relay_port=%u, " + "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_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), + cdrlinecnt, md->index, protocol, + atomic64_get(&ps->stats.packets), + cdrlinecnt, md->index, protocol, + atomic64_get(&ps->stats.bytes), + cdrlinecnt, md->index, protocol, + 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)); + +#endif + } } ilog(LOG_INFO, "--------- Port %5u <> %15s:%-5hu%s, " diff --git a/daemon/call.h b/daemon/call.h index cff8c1bd0..3914f8b7c 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -210,13 +210,14 @@ struct transport_protocol { }; extern const struct transport_protocol transport_protocols[]; - - - struct stats { atomic64 packets; atomic64 bytes; atomic64 errors; + 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 { @@ -452,6 +454,7 @@ struct callmaster { /* XXX rework these */ struct stats statsps; /* per second stats, running timer */ struct stats stats; /* copied from statsps once a second */ + mutex_t statspslock; struct totalstats totalstats; struct totalstats totalstats_interval; /* control_ng_stats stuff */ diff --git a/daemon/cli.c b/daemon/cli.c index 88cddafe1..e41397c08 100644 --- a/daemon/cli.c +++ b/daemon/cli.c @@ -14,6 +14,7 @@ #include "call.h" #include "cli.h" +#include "rtpengine_config.h" static const char* TRUNCATED = " ... Output truncated. Increase Output Buffer ...\n"; @@ -151,6 +152,37 @@ static void cli_incoming_list_callid(char* buffer, int len, struct callmaster* m if (PS_ISSET(ps, FALLBACK_RTCP)) continue; +#if (RE_HAS_MEASUREDELAY) + if (!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) { + printlen = snprintf(replybuffer,(outbufend-replybuffer), "------ Media #%u, port %5u <> %15s:%-5hu%s, " + ""UINT64F" p, "UINT64F" b, "UINT64F" e, "UINT64F" last_packet\n", + md->index, + (unsigned int) (ps->sfd ? ps->sfd->fd.localport : 0), + smart_ntop_p_buf(&ps->endpoint.ip46), ps->endpoint.port, + (!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) ? " (RTCP)" : "", + atomic64_get(&ps->stats.packets), + atomic64_get(&ps->stats.bytes), + atomic64_get(&ps->stats.errors), + atomic64_get(&ps->last_packet)); + } else { + printlen = snprintf(replybuffer,(outbufend-replybuffer), "------ Media #%u, port %5u <> %15s:%-5hu%s, " + ""UINT64F" p, "UINT64F" b, "UINT64F" e, "UINT64F" last_packet, %llu.%09llu delay_min, %llu.%09llu delay_avg, %llu.%09llu delay_max\n", + md->index, + (unsigned int) (ps->sfd ? ps->sfd->fd.localport : 0), + smart_ntop_p_buf(&ps->endpoint.ip46), ps->endpoint.port, + (!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) ? " (RTCP)" : "", + atomic64_get(&ps->stats.packets), + atomic64_get(&ps->stats.bytes), + atomic64_get(&ps->stats.errors), + atomic64_get(&ps->last_packet), + (unsigned long long) ps->stats.delay_min.tv_sec, + (unsigned long long) ps->stats.delay_min.tv_nsec, + (unsigned long long) ps->stats.delay_avg.tv_sec, + (unsigned long long) ps->stats.delay_avg.tv_nsec, + (unsigned long long) ps->stats.delay_max.tv_sec, + (unsigned long long) ps->stats.delay_max.tv_nsec); + } +#else printlen = snprintf(replybuffer,(outbufend-replybuffer), "------ Media #%u, port %5u <> %15s:%-5hu%s, " ""UINT64F" p, "UINT64F" b, "UINT64F" e, "UINT64F" last_packet\n", md->index, @@ -160,7 +192,8 @@ static void cli_incoming_list_callid(char* buffer, int len, struct callmaster* m atomic64_get(&ps->stats.packets), atomic64_get(&ps->stats.bytes), atomic64_get(&ps->stats.errors), - atomic64_get(&ps->last_packet)); + atomic64_get(&ps->last_packet)); +#endif ADJUSTLEN(printlen,outbufend,replybuffer); } } diff --git a/daemon/graphite.c b/daemon/graphite.c index efba17e4e..14fcd1bb1 100644 --- a/daemon/graphite.c +++ b/daemon/graphite.c @@ -9,21 +9,58 @@ #include #include #include +#include +#include +#include +#include +#include #include "log.h" #include "call.h" +#include "graphite.h" static int graphite_sock=-1; +static int connectinprogress=0; static u_int32_t graphite_ipaddress; static int graphite_port=0; static struct callmaster* cm=0; //struct totalstats totalstats_prev; static time_t next_run; +// HEAD: static time_t g_now, next_run; +static char* graphite_prefix = NULL; + +void set_prefix(char* prefix) { + graphite_prefix = prefix; +} + +/** + * Set a file descriptor to blocking or non-blocking mode. + * + * @param fd The file descriptor + * @param blocking 0:non-blocking mode, 1:blocking mode + * + * @return 1:success, 0:failure. + **/ +int fd_set_blocking(int fd, int blocking) { + /* Save the current flags */ + int flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) + return 0; + + if (blocking) + flags &= ~O_NONBLOCK; + else + flags |= O_NONBLOCK; + return fcntl(fd, F_SETFL, flags) != -1; +} int connect_to_graphite_server(u_int32_t ipaddress, int port) { + if (graphite_sock>0) + close(graphite_sock); + graphite_sock=-1; - //int reconnect=0; + int rc=0; struct sockaddr_in sin; memset(&sin,0,sizeof(sin)); @@ -32,9 +69,9 @@ int connect_to_graphite_server(u_int32_t ipaddress, int port) { graphite_ipaddress = ipaddress; graphite_port = port; - rc = graphite_sock = socket(AF_INET, SOCK_STREAM,0); - if(rc<0) { - ilog(LOG_ERROR,"Couldn't make socket for connecting to graphite."); + graphite_sock = socket(AF_INET, SOCK_STREAM,0); + if(graphite_sock<0) { + ilog(LOG_ERROR,"Couldn't make socket for connecting to graphite.Reason:%s\n",strerror(errno)); return -1; } @@ -48,18 +85,26 @@ int connect_to_graphite_server(u_int32_t ipaddress, int port) { goto error; } + rc = fd_set_blocking(graphite_sock,0); + if (!rc) { + ilog(LOG_ERROR,"Could not set the socket to nonblocking."); + goto error; + } + struct in_addr ip; ip.s_addr = graphite_ipaddress; ilog(LOG_INFO, "Connecting to graphite server %s at port:%i with fd:%i",inet_ntoa(ip),graphite_port,graphite_sock); rc = connect(graphite_sock, (struct sockaddr *)&sin, sizeof(sin)); if (rc==-1) { - ilog(LOG_ERROR, "Connection could not be established. Trying again next time of graphite-interval."); + ilog(LOG_WARN, "Connection information:%s\n",strerror(errno)); + if (errno==EINPROGRESS) { + connectinprogress=1; + return 0; + } goto error; } - ilog(LOG_INFO, "Graphite server connected."); - - return graphite_sock; + return 0; error: close(graphite_sock); @@ -86,7 +131,6 @@ int send_graphite_data() { char data_to_send[8192]; char* ptr = data_to_send; - struct totalstats ts; /* atomically copy values to stack and reset to zero */ @@ -102,20 +146,31 @@ int send_graphite_data() { mutex_lock(&cm->totalstats_interval.total_average_lock); ts.total_average_call_dur = cm->totalstats_interval.total_average_call_dur; ts.total_managed_sess = cm->totalstats_interval.total_managed_sess; - ZERO(ts.total_average_call_dur); - ZERO(ts.total_managed_sess); + ZERO(cm->totalstats_interval.total_average_call_dur); + ZERO(cm->totalstats_interval.total_managed_sess); mutex_unlock(&cm->totalstats_interval.total_average_lock); + if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; } rc = sprintf(ptr,"%s.totals.average_call_dur.tv_sec %llu %llu\n",hostname, (unsigned long long) ts.total_average_call_dur.tv_sec,(unsigned long long)g_now.tv_sec); ptr += rc; + if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; } rc = sprintf(ptr,"%s.totals.average_call_dur.tv_usec %lu %llu\n",hostname, ts.total_average_call_dur.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc; + if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; } rc = sprintf(ptr,"%s.totals.forced_term_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_forced_term_sess),(unsigned long long)g_now.tv_sec); ptr += rc; + if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; } rc = sprintf(ptr,"%s.totals.managed_sess "UINT64F" %llu\n",hostname, ts.total_managed_sess,(unsigned long long)g_now.tv_sec); ptr += rc; + if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; } rc = sprintf(ptr,"%s.totals.nopacket_relayed_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_nopacket_relayed_sess),(unsigned long long)g_now.tv_sec); ptr += rc; + if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; } rc = sprintf(ptr,"%s.totals.oneway_stream_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_oneway_stream_sess),(unsigned long long)g_now.tv_sec); ptr += rc; + if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; } rc = sprintf(ptr,"%s.totals.regular_term_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_regular_term_sess),(unsigned long long)g_now.tv_sec); ptr += rc; + if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; } rc = sprintf(ptr,"%s.totals.relayed_errors "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_relayed_errors),(unsigned long long)g_now.tv_sec); ptr += rc; + if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; } rc = sprintf(ptr,"%s.totals.relayed_packets "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_relayed_packets),(unsigned long long)g_now.tv_sec); ptr += rc; + if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; } rc = sprintf(ptr,"%s.totals.silent_timeout_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_silent_timeout_sess),(unsigned long long)g_now.tv_sec); ptr += rc; + if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; } rc = sprintf(ptr,"%s.totals.timeout_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_timeout_sess),(unsigned long long)g_now.tv_sec); ptr += rc; rc = write(graphite_sock, data_to_send, ptr - data_to_send); @@ -133,29 +188,73 @@ error: void graphite_loop_run(struct callmaster* callmaster, int seconds) { int rc=0; + fd_set wfds; + FD_ZERO(&wfds); + struct timeval tv; + int optval=0; + socklen_t optlen=sizeof(optval); + + if (connectinprogress && graphite_sock>0) { + FD_SET(graphite_sock,&wfds); + tv.tv_sec = 0; + tv.tv_usec = 1000000; + + rc = select (graphite_sock+1, NULL, &wfds, NULL, &tv); + if ((rc == -1) && (errno == EINTR)) { + ilog(LOG_ERROR,"Error on the socket."); + close(graphite_sock); + graphite_sock=-1;connectinprogress=0; + return; + } else if (rc==0) { + // timeout + return; + } else { + if (!FD_ISSET(graphite_sock,&wfds)) { + ilog(LOG_WARN,"fd active but not the graphite fd."); + close(graphite_sock); + graphite_sock=-1;connectinprogress=0; + return; + } + rc = getsockopt(graphite_sock, SOL_SOCKET, SO_ERROR, &optval, &optlen); + if (rc) ilog(LOG_ERROR,"getsockopt failure."); + if (optval != 0) { + ilog(LOG_ERROR,"Socket connect failed. fd: %i, Reason: %s\n",graphite_sock, strerror(optval)); + close(graphite_sock); + graphite_sock=-1;connectinprogress=0; + return; + } + ilog(LOG_INFO, "Graphite server connected."); + connectinprogress=0; + next_run=0; // fake next run to skip sleep after reconnect + } + } gettimeofday(&g_now, NULL); - if (g_now.tv_sec < next_run) - goto sleep; + if (g_now.tv_sec < next_run) { + usleep(100000); + return; + } next_run = g_now.tv_sec + seconds; if (!cm) cm = callmaster; - if (graphite_sock < 0) { + if (graphite_sock < 0 && !connectinprogress) { rc = connect_to_graphite_server(graphite_ipaddress, graphite_port); + if (rc) { + close(graphite_sock); + graphite_sock=-1; + } } - if (rc>=0) { + if (graphite_sock>0 && !connectinprogress) { rc = send_graphite_data(); if (rc<0) { ilog(LOG_ERROR,"Sending graphite data failed."); } } -sleep: - usleep(100000); } void graphite_loop(void *d) { @@ -166,7 +265,7 @@ void graphite_loop(void *d) { cm->conf.graphite_interval=1; } - connect_to_graphite_server(cm->conf.graphite_ip,graphite_port); + connect_to_graphite_server(cm->conf.graphite_ip,cm->conf.graphite_port); while (!g_shutdown) graphite_loop_run(cm,cm->conf.graphite_interval); // time in seconds diff --git a/daemon/graphite.h b/daemon/graphite.h index f00532a8f..67cf1b575 100644 --- a/daemon/graphite.h +++ b/daemon/graphite.h @@ -13,6 +13,7 @@ int connect_to_graphite_server(u_int32_t ipaddress, int port); int send_graphite_data(); void graphite_loop_run(struct callmaster* cm, int seconds); +void set_prefix(char* prefix); void graphite_loop(void *d); #endif /* GRAPHITE_H_ */ diff --git a/daemon/kernel.c b/daemon/kernel.c index 670f5496e..e6cbf9219 100644 --- a/daemon/kernel.c +++ b/daemon/kernel.c @@ -102,7 +102,6 @@ int kernel_del_stream(int fd, u_int16_t p) { return -1; } - GList *kernel_list(unsigned int id) { char str[64]; int fd; 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 99afb65f1..660080128 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -257,9 +257,11 @@ static void options(int *argc, char ***argv) { char *listenngs = NULL; char *listencli = NULL; char *graphitep = NULL; + 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; @@ -274,6 +276,7 @@ static void options(int *argc, char ***argv) { { "listen-cli", 'c', 0, G_OPTION_ARG_STRING, &listencli, "UDP port to listen on, CLI", "[IP46:]PORT" }, { "graphite", 'g', 0, G_OPTION_ARG_STRING, &graphitep, "Address of the graphite server", "[IP46:]PORT" }, { "graphite-interval", 'w', 0, G_OPTION_ARG_INT, &graphite_interval, "Graphite send interval in seconds", "INT" }, + { "graphite-prefix",0, 0, G_OPTION_ARG_STRING, &graphite_prefix_s, "Prefix for graphite line", "STRING"}, { "tos", 'T', 0, G_OPTION_ARG_INT, &tos, "Default TOS value to set on streams", "INT" }, { "timeout", 'o', 0, G_OPTION_ARG_INT, &timeout, "RTP timeout", "SECS" }, { "silent-timeout",'s',0,G_OPTION_ARG_INT, &silent_timeout,"RTP timeout for muted", "SECS" }, @@ -287,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" }, @@ -340,6 +344,9 @@ static void options(int *argc, char ***argv) { die("Invalid IP or port (--graphite)"); } + if (graphite_prefix_s) + set_prefix(graphite_prefix_s); + if (tos < 0 || tos > 255) die("Invalid TOS value"); @@ -369,12 +376,19 @@ static void options(int *argc, char ***argv) { } } - if (log_facility_cdr_s) { - if (!parse_log_facility(log_facility_cdr_s, &_log_facility_cdr)) { - print_available_log_facilities(); - die ("Invalid log facility for CDR '%s' (--log-facility-cdr)\n", log_facility_cdr_s); - } - } + if (log_facility_cdr_s) { + if (!parse_log_facility(log_facility_cdr_s, &_log_facility_cdr)) { + print_available_log_facilities(); + die ("Invalid log facility for CDR '%s' (--log-facility-cdr)\n", log_facility_cdr_s); + } + } + + 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; @@ -638,6 +652,7 @@ int main(int argc, char **argv) { thread_create_detach(sighandler, NULL); thread_create_detach(poller_timer_loop, ctx.p); + if (graphite_ip) thread_create_detach(graphite_loop, ctx.m); thread_create_detach(ice_thread_run, NULL); diff --git a/daemon/rtcp.c b/daemon/rtcp.c index 6e0c782d5..5a0eeb7b2 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 20b10c3d5..d06a56e74 100644 --- a/debian/ngcp-rtpengine-daemon.default +++ b/debian/ngcp-rtpengine-daemon.default @@ -21,7 +21,9 @@ TABLE=0 # LOG_LEVEL=6 # LOG_FACILITY=daemon # LOG_FACILITY_CDR=daemon +# LOG_FACILITY_RTCP=daemon # NUM_THREADS=5 # DELETE_DELAY=30 # GRAPHITE=9006 -# GRAPHITE_INTERVAL=60 \ No newline at end of file +# GRAPHITE_INTERVAL=60 +# GRAPHITE_PREFIX=myownprefix diff --git a/debian/ngcp-rtpengine-daemon.init b/debian/ngcp-rtpengine-daemon.init index 1c7e96f5c..f3001db7f 100755 --- a/debian/ngcp-rtpengine-daemon.init +++ b/debian/ngcp-rtpengine-daemon.init @@ -70,10 +70,12 @@ 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" [ -z "$GRAPHITE_INTERVAL" ] || OPTIONS="$OPTIONS --graphite-interval=$GRAPHITE_INTERVAL" +[ -z "$GRAPHITE_PREFIX" ] || OPTIONS="$OPTIONS --graphite-prefix=$GRAPHITE_PREFIX" if test "$FORK" = "no" ; then OPTIONS="$OPTIONS --foreground" fi diff --git a/el/rtpengine.init b/el/rtpengine.init index c6056242d..3cf2930ad 100644 --- a/el/rtpengine.init +++ b/el/rtpengine.init @@ -153,10 +153,20 @@ build_opts() { OPTS+=" --graphite-interval=$GRAPHITE_INTERVAL" fi + if [[ -n "$GRAPHITE_PREFIX" ]] + then + OPTS+=" --graphite-prefix=$GRAPHITE_PREFIX" + fi + if [[ -n "$LOG_FACILITY_CDR" ]] 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/rtpengine_config.h b/kernel-module/rtpengine_config.h new file mode 100644 index 000000000..13188e85c --- /dev/null +++ b/kernel-module/rtpengine_config.h @@ -0,0 +1,15 @@ +/* + * rtpengine_config.h + * + * Description: Config file with preprocessor config makros + * + * Created on: Mar 18, 2015 + * Author: fmetz + */ + +#ifndef RTPENGINE_CONFIG_H_ +#define RTPENGINE_CONFIG_H_ + +#define RE_HAS_MEASUREDELAY 1 + +#endif /* RTPENGINE_CONFIG_H_ */ diff --git a/kernel-module/xt_RTPENGINE.c b/kernel-module/xt_RTPENGINE.c index 713c34997..86271d75f 100644 --- a/kernel-module/xt_RTPENGINE.c +++ b/kernel-module/xt_RTPENGINE.c @@ -29,6 +29,8 @@ #include "xt_RTPENGINE.h" #endif +#include "rtpengine_config.h" + MODULE_LICENSE("GPL"); @@ -147,6 +149,10 @@ struct rtpengine_stats_a { atomic64_t packets; atomic64_t bytes; atomic64_t errors; + struct timespec delay_min; + struct timespec delay_avg; + struct timespec delay_max; + atomic_t in_tos; }; struct rtpengine_rtp_stats_a { atomic64_t packets; @@ -854,6 +860,10 @@ static ssize_t proc_blist_read(struct file *f, char __user *b, size_t l, loff_t op.stats.packets = atomic64_read(&g->stats.packets); op.stats.bytes = atomic64_read(&g->stats.bytes); op.stats.errors = atomic64_read(&g->stats.errors); + 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); @@ -882,10 +892,6 @@ err: return err; } - - - - static int proc_list_open(struct inode *i, struct file *f) { int err; struct seq_file *p; @@ -1474,6 +1480,9 @@ static int table_new_target(struct rtpengine_table *t, struct rtpengine_target_i atomic64_set(&g->stats.packets, atomic64_read(&og->stats.packets)); atomic64_set(&g->stats.bytes, atomic64_read(&og->stats.bytes)); atomic64_set(&g->stats.errors, atomic64_read(&og->stats.errors)); + g->stats.delay_min = og->stats.delay_min; + g->stats.delay_max = og->stats.delay_max; + g->stats.delay_avg = og->stats.delay_avg; for (j = 0; j < NUM_PAYLOAD_TYPES; j++) { atomic64_set(&g->rtp_stats[j].packets, atomic64_read(&og->rtp_stats[j].packets)); @@ -1816,6 +1825,7 @@ drop: static int send_proxy_packet(struct sk_buff *skb, struct re_address *src, struct re_address *dst, unsigned char tos) { + if (src->family != dst->family) goto drop; @@ -2184,7 +2194,48 @@ static inline int rtp_payload_type(const struct rtp_header *hdr, const struct rt return match - tg->payload_types; } -static unsigned int rtpengine46(struct sk_buff *skb, struct rtpengine_table *t, struct re_address *src) { +static void re_timespec_subtract (struct timespec *result, const struct timespec *a, const struct timespec *b) { + long long nanoseconds=0; + nanoseconds = ((long)a->tv_sec - (long long)b->tv_sec) * (long long)1000000000 + ((long long)a->tv_nsec - (long long)b->tv_nsec); + result->tv_sec = nanoseconds/(long long)1000000000; + result->tv_nsec = nanoseconds%(long long)1000000000; +} + +static void re_timespec_multiply(struct timespec *result, const struct timespec *a, const long long multiplier) { + long long nanoseconds=0; + nanoseconds = ((long)a->tv_sec * (long long)1000000000) + (long long)a->tv_nsec * multiplier; + result->tv_sec = nanoseconds/(long long)1000000000; + result->tv_nsec = nanoseconds%(long long)1000000000; +} + +static void re_timespec_devide(struct timespec *result, const struct timespec *a, const long devisor) { + long long nanoseconds=0; + nanoseconds = ((long)a->tv_sec * (long long)1000000000) + (long long)a->tv_nsec / devisor; + result->tv_sec = nanoseconds/(long long)1000000000; + result->tv_nsec = nanoseconds%(long long)1000000000; +} + +static void re_timespec_add(struct timespec *result, const struct timespec *a, const struct timespec *b) { + long long nanoseconds=0; + nanoseconds = ((long)a->tv_sec + (long long)b->tv_sec) * (long long)1000000000 + ((long long)a->tv_nsec + (long long)b->tv_nsec); + result->tv_sec = nanoseconds/(long long)1000000000; + result->tv_nsec = nanoseconds%(long long)1000000000; +} + +/* Return negative, zero, positive if A < B, A == B, A > B, respectively. + Assume the nanosecond components are in range, or close to it. */ +static int re_timespec_cmp (struct timespec *a, struct timespec *b) +{ + return (a->tv_sec < b->tv_sec ? -1 + : a->tv_sec > b->tv_sec ? 1 + : a->tv_nsec - b->tv_nsec); +} + +#if (RE_HAS_MEASUREDELAY) + 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, u_int8_t in_tos) { +#endif struct udphdr *uh; struct rtpengine_target *g; struct sk_buff *skb2; @@ -2194,6 +2245,10 @@ static unsigned int rtpengine46(struct sk_buff *skb, struct rtpengine_table *t, struct rtp_parsed rtp; u_int64_t pkt_idx = 0, pkt_idx_u; +#if (RE_HAS_MEASUREDELAY) + struct timespec endtime, delay; +#endif + skb_reset_transport_header(skb); uh = udp_hdr(skb); skb_pull(skb, sizeof(*uh)); @@ -2299,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 { @@ -2308,6 +2367,29 @@ out: if (rtp_pt_idx >= 0) { atomic64_inc(&g->rtp_stats[rtp_pt_idx].packets); atomic64_add(datalen, &g->rtp_stats[rtp_pt_idx].bytes); + +#if (RE_HAS_MEASUREDELAY) + getnstimeofday(&endtime); + + re_timespec_subtract(&delay,&endtime, starttime); + + if (atomic64_read(&g->stats.packets)==1) { + g->stats.delay_min=delay; + g->stats.delay_avg=delay; + g->stats.delay_max=delay; + } else { + if (re_timespec_cmp(&g->stats.delay_min,&delay)>0) { + g->stats.delay_min = delay; + } + if (re_timespec_cmp(&g->stats.delay_max,&delay)<0) { + g->stats.delay_max = delay; + } + + re_timespec_multiply(&g->stats.delay_avg,&g->stats.delay_avg,atomic64_read(&g->stats.packets)-1); + re_timespec_add(&g->stats.delay_avg,&g->stats.delay_avg,&delay); + re_timespec_devide(&g->stats.delay_avg,&g->stats.delay_avg,atomic64_read(&g->stats.packets)); + } +#endif } else if (rtp_pt_idx == -2) /* not RTP */ ; @@ -2345,6 +2427,11 @@ static unsigned int rtpengine4(struct sk_buff *oskb, const struct xt_action_para struct rtpengine_table *t; struct re_address src; +#if (RE_HAS_MEASUREDELAY) + struct timespec starttime; + getnstimeofday(&starttime); +#endif + t = get_table(pinfo->id); if (!t) goto skip; @@ -2363,7 +2450,11 @@ static unsigned int rtpengine4(struct sk_buff *oskb, const struct xt_action_para src.family = AF_INET; src.u.ipv4 = ih->saddr; - return rtpengine46(skb, t, &src); +#if (RE_HAS_MEASUREDELAY) + return rtpengine46(skb, t, &src, &starttime, (u_int8_t)ih->tos); +#else + return rtpengine46(skb, t, &src, (u_int8_t)ih->tos); +#endif skip2: kfree_skb(skb); @@ -2387,6 +2478,11 @@ static unsigned int rtpengine6(struct sk_buff *oskb, const struct xt_action_para struct rtpengine_table *t; struct re_address src; +#if (RE_HAS_MEASUREDELAY) + struct timespec starttime; + getnstimeofday(&starttime); +#endif + t = get_table(pinfo->id); if (!t) goto skip; @@ -2397,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; @@ -2405,7 +2502,11 @@ static unsigned int rtpengine6(struct sk_buff *oskb, const struct xt_action_para src.family = AF_INET6; memcpy(&src.u.ipv6, &ih->saddr, sizeof(src.u.ipv6)); - return rtpengine46(skb, t, &src); +#if (RE_HAS_MEASUREDELAY) + return rtpengine46(skb, t, &src, &starttime, ipv6_get_dsfield(ih)); +#else + return rtpengine46(skb, t, &src, ipv6_get_dsfield(ih)); +#endif skip2: kfree_skb(skb); diff --git a/kernel-module/xt_RTPENGINE.h b/kernel-module/xt_RTPENGINE.h index 361a56fb0..d285f8353 100644 --- a/kernel-module/xt_RTPENGINE.h +++ b/kernel-module/xt_RTPENGINE.h @@ -15,6 +15,10 @@ struct rtpengine_stats { u_int64_t packets; u_int64_t bytes; u_int64_t errors; + 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; @@ -99,7 +103,7 @@ struct rtpengine_message { MMG_NOOP = 1, MMG_ADD, MMG_DEL, - MMG_UPDATE, + MMG_UPDATE } cmd; struct rtpengine_target_info target; diff --git a/utils/rtpengine-ctl b/utils/rtpengine-ctl index e69deb12c..464fcf0d4 100755 --- a/utils/rtpengine-ctl +++ b/utils/rtpengine-ctl @@ -1,70 +1,76 @@ -#!/bin/bash -# +#!/usr/bin/perl -host=127.0.0.1 -port=9900 -error_rc=255 +use IO::Socket::INET; -prgname=${0##*/} -prgdir=${0%$prgname} +$num_args = $#ARGV + 1; +if ($num_args == 0) { + showusage(); + exit; +} + +# auto-flush on socket +$| = 1; + +my $argumentstring = ""; +my $ip = "127.0.0.1"; +my $port = "9900"; -showusage() { - echo "" - echo " $0 [ -ip -port ] " - echo "" - echo " Supported commands are:" - echo "" - echo " list [ numsessions | sessions | session ]" - echo " numsessions : prints the number of sessions" - echo " sessions : print one-liner session information" - echo " session : print detail about one session" - echo " totals : print total statistics (does not include current sessions)" - echo "" - echo " terminate [ all | ]" - echo " all : terminates all current sessions" - echo " : session is immediately terminated" - echo "" - echo "" - echo " Return Value:" - echo " 0 on success with ouput from server side, other values for failure." - echo "" - exit 0 +for (my $argnum=0; $argnum <= $#ARGV; $argnum++) { + if ($ARGV[$argnum] eq "-ip") { + die "No argument after -ip\n" unless $argnum+1<=$#ARGV; + $argnum = $argnum+1; + $ip = $ARGV[$argnum]; + } elsif ($ARGV[$argnum] eq "-port") { + die "No argument after -port\n" unless $argnum+1<=$#ARGV; + $argnum = $argnum+1; + $port = $ARGV[$argnum]; + } else { + $argumentstring .= "$ARGV[$argnum] "; + } } -if [ $# -eq 0 ]; then showusage; fi +# create a connecting socket +my $socket = new IO::Socket::INET ( + PeerHost => $ip, + PeerPort => $port, + Proto => 'tcp', +); +die "Cannot connect to the rtpengine $!\n" unless $socket; + +$argumentstring = trim($argumentstring); +my $size = $socket->send($argumentstring); + +# notify server that request has been sent +shutdown($socket, 1); + +# receive a response of up to 10MB +my $response = ""; +$socket->recv($response, 1024*1024*10); +print $response; -command -v nc 2>&1 >/dev/null -if [ $? -ne 0 ]; then - echo "Error: $0 requires netcat to be installed." - exit 0 -fi +$socket->close(); -while [ $# -gt 0 ]; do - case $1 in - "-?"|"-help"|"-h") - showusage - ;; - "-ip") - shift - if [ $# -gt 0 ]; then - host=$1 - else - echo "Missing parameter for option '-ip'" >&2 - fi - ;; - "-port") - shift - if [ $# -gt 0 ]; then - port=$1 - else - echo "Missing parameter for option '-port'" >&2 - fi - ;; - *) - varargs="$varargs $1" - esac - shift -done +sub showusage { + print "\n"; + print " rtpengine-ctl [ -ip -port ] \n"; + print "\n"; + print " Supported commands are:\n"; + print "\n"; + print " list [ numsessions | sessions | session | totals ]\n"; + print " numsessions : prints the number of sessions\n"; + print " sessions : print one-liner session information\n"; + print " session : print detail about one session\n"; + print " totals : print total statistics\n"; + print "\n"; + print " terminate [ all | ]\n"; + print " all : terminates all current sessions\n"; + print " : session is immediately terminated\n"; + print "\n"; + print "\n"; + print " Return Value:\n"; + print " 0 on success with ouput from server side, other values for failure.\n"; + print "\n"; +} -echo -n ${varargs} | nc ${host} ${port} +sub trim { my $s = shift; $s =~ s/^\s+|\s+$//g; return $s };