diff --git a/README.md b/README.md index 4c5329666..d96bb0258 100644 --- a/README.md +++ b/README.md @@ -158,9 +158,12 @@ option and which are reproduced below: -F, --no-fallback Only start when kernel module is available -i, --interface=[NAME/]IP[!IP] Local interface for RTP -l, --listen-tcp=[IP:]PORT TCP port to listen on - -c, --listen-cli=[IP46:]PORT TCP port to listen on, CLI (command line interface) -u, --listen-udp=[IP46:]PORT UDP port to listen on -n, --listen-ng=[IP46:]PORT UDP port to listen on, NG protocol + -c, --listen-cli=[IP46:]PORT TCP port to listen on, CLI (command line interface) + -g, --graphite=IP46:PORT TCP address of graphite statistics server + -G, --graphite-interval=INT Graphite data statistics send interval + --graphite-prefix=STRING Graphite prefix for every line -T, --tos=INT TOS value to set on streams -o, --timeout=SECS RTP timeout -s, --silent-timeout=SECS RTP timeout for muted @@ -185,10 +188,10 @@ option and which are reproduced below: -d, --delete-delay Delay for deleting a session from memory. --sip-source Use SIP source address by default --dtls-passive Always prefer DTLS passive role - -g, --graphite=[IP46:]PORT TCP address of graphite statistics server - -G, --graphite-interval=INT Graphite data statistics send interval - --graphite-prefix=STRING Graphite prefix for every line --max-sessions=INT Limit the number of maximum concurrent sessions + --homer=IP46:PORT Address of Homer server for RTCP stats + --homer-protocol=udp|tcp Transport protocol for Homer (default udp) + --homer-id=INT 'Capture ID' to use within the HEP protocol 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. @@ -278,6 +281,18 @@ The options are described in more detail below. TCP ip and port to listen for the CLI (command line interface). +* -g, --graphite + + Address of the graphite statistics server. + +* -w, --graphite-interval + + Interval of the time when information is sent to the graphite server. + +* --graphite-prefix + + Add a prefix for every graphite line. + * -t, --tos Takes an integer as argument and if given, specifies the TOS value that should be set in outgoing @@ -422,18 +437,6 @@ The options are described in more detail below. Selects the internal format of the XMLRPC callback message for B2BUA call teardown. 0 is for SEMS, 1 is for a generic format containing the call-ID only. -* -g, --graphite - - Address of the graphite statistics server. - -* -w, --graphite-interval - - Interval of the time when information is sent to the graphite server. - -* --graphite-prefix - - Add a prefix for every graphite line. - * --max-sessions Limit the number of maximum concurrent sessions. Set at startup via MAX_SESSIONS in config file. Set at runtime via rtpengine-ctl util. @@ -443,6 +446,21 @@ The options are described in more detail below. Disable feature: 'rtpengine-ctl set maxsessions -1' By default, the feature is disabled (i.e. maxsessions == -1). +* --homer + + Enables sending the decoded contents of RTCP packets to a Homer SIP capture server. The transport + is HEP version 3 and payload format is JSON. This argument takes an IP address and a port number + as value. + +* --homer-protocol + + Can be either "udp" or "tcp" with "udp" being the default. + +* --homer-id + + The HEP protocol used by Homer contains a "capture ID" used to distinguish different sources + of capture data. This ID can be specified using this argument. + A typical command line (enabling both UDP and NG protocols) thus may look like: diff --git a/daemon/Makefile b/daemon/Makefile index d9a6ded7f..3b28d19cb 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -67,7 +67,7 @@ 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 socket.c \ - media_socket.c rtcp_xr.c + media_socket.c rtcp_xr.c homer.c OBJS= $(SRCS:.c=.o) diff --git a/daemon/call.h b/daemon/call.h index b8edec02a..a85580a23 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -210,6 +210,7 @@ struct sdp_ng_flags; struct local_interface; struct call_monologue; struct ice_agent; +struct homer_sender; typedef bencode_buffer_t call_buffer_t; @@ -491,6 +492,8 @@ struct callmaster { struct callmaster_config conf; struct timeval latest_graphite_interval_start; + + struct homer_sender *homer; }; struct call_stats { diff --git a/daemon/graphite.c b/daemon/graphite.c index 3f0c721d5..a85050d24 100644 --- a/daemon/graphite.c +++ b/daemon/graphite.c @@ -241,8 +241,6 @@ void graphite_loop_run(struct callmaster *cm, endpoint_t *graphite_ep, int secon fd_set wfds; FD_ZERO(&wfds); struct timeval tv; - int optval=0; - socklen_t optlen=sizeof(optval); // sanity checks if (!cm) { @@ -276,10 +274,10 @@ void graphite_loop_run(struct callmaster *cm, endpoint_t *graphite_ep, int secon connection_state = STATE_DISCONNECTED; return; } - rc = getsockopt(graphite_sock.fd, 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.fd, strerror(optval)); + rc = socket_error(&graphite_sock); + if (rc < 0) ilog(LOG_ERROR,"getsockopt failure."); + if (rc != 0) { + ilog(LOG_ERROR,"Socket connect failed. fd: %i, Reason: %s\n",graphite_sock.fd, strerror(rc)); close_socket(&graphite_sock); connection_state = STATE_DISCONNECTED; return; diff --git a/daemon/homer.c b/daemon/homer.c new file mode 100644 index 000000000..37442cd17 --- /dev/null +++ b/daemon/homer.c @@ -0,0 +1,559 @@ +#include "homer.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "aux.h" +#include "str.h" + + + + +#define SEND_QUEUE_LIMIT 200 + + + + +struct homer_sender { + mutex_t lock; + + endpoint_t endpoint; + int protocol; + int capture_id; + socket_t socket; + time_t retry; + + GQueue send_queue; + GString *partial; + + int (*state)(struct homer_sender *); +}; + + + + +static int send_hepv3 (GString *s, const str *id, int, const endpoint_t *src, const endpoint_t *dst, + const struct timeval *); + +// state handlers +static int __established(struct homer_sender *hs); +static int __in_progress(struct homer_sender *hs); +static int __no_socket(struct homer_sender *hs); + + + + +static void __reset(struct homer_sender *hs) { + close_socket(&hs->socket); + hs->state = __no_socket; + hs->retry = time(NULL) + 30; + + // discard partially written packet + if (hs->partial) + g_string_free(hs->partial, TRUE); + hs->partial = NULL; +} + +static int __attempt_send(struct homer_sender *hs, GString *gs) { + int ret; + + ret = write(hs->socket.fd, gs->str, gs->len); + if (ret == gs->len) { + // full write + g_string_free(gs, TRUE); + return 0; + } + if (ret < 0) { + if (errno != EWOULDBLOCK && errno != EAGAIN) { + ilog(LOG_ERR, "Write error to Homer at %s: %s", + endpoint_print_buf(&hs->endpoint), strerror(errno)); + __reset(hs); + return 1; + } + ilog(LOG_DEBUG, "Home write blocked"); + // XXX use poller for blocked writes? + return 2; + } + // partial write + ilog(LOG_DEBUG, "Home write blocked (partial write)"); + g_string_erase(gs, 0, ret); + return 3; +} + +static int __established(struct homer_sender *hs) { + char buf[16]; + int ret; + GString *gs; + + // test connection with a dummy read + ret = read(hs->socket.fd, buf, sizeof(buf)); + if (ret < 0) { + if (errno != EWOULDBLOCK && errno != EAGAIN) { + ilog(LOG_ERR, "Connection error from Homer at %s: %s", + endpoint_print_buf(&hs->endpoint), strerror(errno)); + __reset(hs); + return -1; + } + } + // XXX handle return data from Homer? + + if (hs->partial) { + ilog(LOG_DEBUG, "dequeue partial packet to Homer"); + ret = __attempt_send(hs, hs->partial); + if (ret == 3 || ret == 2) // partial write or not sent at all + return 0; + if (ret == 1) // write error, takes care of deleting hs->partial + return -1; + // ret == 0 -> sent OK, drop through to unqueue + g_string_free(hs->partial, TRUE); + hs->partial = NULL; + } + + // unqueue as much as we can + while ((gs = g_queue_pop_head(&hs->send_queue))) { + ilog(LOG_DEBUG, "dequeue send queue to Homer"); + ret = __attempt_send(hs, gs); + if (ret == 0) // everything sent OK + continue; + if (ret == 3) { // partial write + hs->partial = gs; + return 0; + } + g_queue_push_head(&hs->send_queue, gs); + if (ret == 1) // write error + return -1; + // ret == 2 -> blocked + return 0; + } + + // everything unqueued + return 0; +} + +static int __check_conn(struct homer_sender *hs, int ret) { + if (ret == 0) { + ilog(LOG_INFO, "Connection to Homer at %s has been established", + endpoint_print_buf(&hs->endpoint)); + hs->state = __established; + return hs->state(hs); + } + if (ret == 1) { + ilog(LOG_DEBUG, "connection to Homer is in progress"); + hs->state = __in_progress; + return 0; + } + + ilog(LOG_ERR, "Failed to connect to Homer at %s: %s", + endpoint_print_buf(&hs->endpoint), strerror(errno)); + + __reset(hs); + return -1; +} + +static int __in_progress(struct homer_sender *hs) { + int ret; + + ilog(LOG_DEBUG, "connection to Homer is in progress - checking"); + + ret = connect_socket_retry(&hs->socket); + return __check_conn(hs, ret); +} + +static int __no_socket(struct homer_sender *hs) { + int ret; + + if (hs->retry > time(NULL)) + return 0; + + ilog(LOG_INFO, "Connecting to Homer at %s", endpoint_print_buf(&hs->endpoint)); + + ret = connect_socket_nb(&hs->socket, hs->protocol, &hs->endpoint); + return __check_conn(hs, ret); +} + +struct homer_sender *homer_sender_new(const endpoint_t *ep, int protocol, int capture_id) { + struct homer_sender *ret; + + if (is_addr_unspecified(&ep->address)) + return NULL; + + ret = malloc(sizeof(*ret)); + ZERO(*ret); + mutex_init(&ret->lock); + ret->endpoint = *ep; + ret->protocol = protocol; + ret->capture_id = capture_id; + ret->retry = time(NULL); + + ret->state = __no_socket; + + return ret; +} + +// takes over the GString +int homer_send(struct homer_sender *hs, GString *s, const str *id, const endpoint_t *src, + const endpoint_t *dst, const struct timeval *tv) +{ + if (!hs) + goto out; + if (!s) + goto out; + if (!s->len) // empty write, shouldn't happen + goto out; + + ilog(LOG_DEBUG, "JSON to send to Homer: '"STR_FORMAT"'", G_STR_FMT(s)); + + if (send_hepv3(s, id, hs->capture_id, src, dst, tv)) + goto out; + + mutex_lock(&hs->lock); + if (hs->send_queue.length < SEND_QUEUE_LIMIT) { + g_queue_push_tail(&hs->send_queue, s); + s = NULL; + } + else + ilog(LOG_ERR, "Send queue length limit (%i) reached, dropping Homer message", SEND_QUEUE_LIMIT); + hs->state(hs); + mutex_unlock(&hs->lock); + +out: + if (s) + g_string_free(s, TRUE); + return 0; +} + + + + +// from captagent transport_hep.[ch] + +struct hep_chunk { + u_int16_t vendor_id; + u_int16_t type_id; + u_int16_t length; +} __attribute__((packed)); + +typedef struct hep_chunk hep_chunk_t; + +struct hep_chunk_uint8 { + hep_chunk_t chunk; + u_int8_t data; +} __attribute__((packed)); + +typedef struct hep_chunk_uint8 hep_chunk_uint8_t; + +struct hep_chunk_uint16 { + hep_chunk_t chunk; + u_int16_t data; +} __attribute__((packed)); + +typedef struct hep_chunk_uint16 hep_chunk_uint16_t; + +struct hep_chunk_uint32 { + hep_chunk_t chunk; + u_int32_t data; +} __attribute__((packed)); + +typedef struct hep_chunk_uint32 hep_chunk_uint32_t; + +struct hep_chunk_str { + hep_chunk_t chunk; + char *data; +} __attribute__((packed)); + +typedef struct hep_chunk_str hep_chunk_str_t; + +struct hep_chunk_ip4 { + hep_chunk_t chunk; + struct in_addr data; +} __attribute__((packed)); + +typedef struct hep_chunk_ip4 hep_chunk_ip4_t; + +struct hep_chunk_ip6 { + hep_chunk_t chunk; + struct in6_addr data; +} __attribute__((packed)); + +typedef struct hep_chunk_ip6 hep_chunk_ip6_t; + +struct hep_ctrl { + char id[4]; + u_int16_t length; +} __attribute__((packed)); + +typedef struct hep_ctrl hep_ctrl_t; + +struct hep_chunk_payload { + hep_chunk_t chunk; + char *data; +} __attribute__((packed)); + +typedef struct hep_chunk_payload hep_chunk_payload_t; + +/* Structure of HEP */ + +struct hep_generic { + hep_ctrl_t header; + hep_chunk_uint8_t ip_family; + hep_chunk_uint8_t ip_proto; + hep_chunk_uint16_t src_port; + hep_chunk_uint16_t dst_port; + hep_chunk_uint32_t time_sec; + hep_chunk_uint32_t time_usec; + hep_chunk_uint8_t proto_t; + hep_chunk_uint32_t capt_id; +} __attribute__((packed)); + +typedef struct hep_generic hep_generic_t; + +#define PROTO_RTCP_JSON 0x05 + +// modifies the GString in place +static int send_hepv3 (GString *s, const str *id, int capt_id, const endpoint_t *src, const endpoint_t *dst, + const struct timeval *tv) +{ + + struct hep_generic *hg=NULL; + void* buffer; + unsigned int buflen=0, iplen=0,tlen=0; + hep_chunk_ip4_t src_ip4, dst_ip4; + hep_chunk_ip6_t src_ip6, dst_ip6; + hep_chunk_t payload_chunk; + //hep_chunk_t authkey_chunk; + hep_chunk_t correlation_chunk; + //static int errors = 0; + + hg = malloc(sizeof(struct hep_generic)); + memset(hg, 0, sizeof(struct hep_generic)); + + + /* header set */ + memcpy(hg->header.id, "\x48\x45\x50\x33", 4); + + /* IP proto */ + hg->ip_family.chunk.vendor_id = htons(0x0000); + hg->ip_family.chunk.type_id = htons(0x0001); + hg->ip_family.data = src->address.family->af; + hg->ip_family.chunk.length = htons(sizeof(hg->ip_family)); + + /* Proto ID */ + hg->ip_proto.chunk.vendor_id = htons(0x0000); + hg->ip_proto.chunk.type_id = htons(0x0002); + hg->ip_proto.data = IPPROTO_UDP; + hg->ip_proto.chunk.length = htons(sizeof(hg->ip_proto)); + + + /* IPv4 */ + if(hg->ip_family.data == AF_INET) { + /* SRC IP */ + src_ip4.chunk.vendor_id = htons(0x0000); + src_ip4.chunk.type_id = htons(0x0003); + src_ip4.data = src->address.u.ipv4; + src_ip4.chunk.length = htons(sizeof(src_ip4)); + + /* DST IP */ + dst_ip4.chunk.vendor_id = htons(0x0000); + dst_ip4.chunk.type_id = htons(0x0004); + dst_ip4.data = dst->address.u.ipv4; + dst_ip4.chunk.length = htons(sizeof(dst_ip4)); + + iplen = sizeof(dst_ip4) + sizeof(src_ip4); + } + /* IPv6 */ + else if(hg->ip_family.data == AF_INET6) { + /* SRC IPv6 */ + src_ip6.chunk.vendor_id = htons(0x0000); + src_ip6.chunk.type_id = htons(0x0005); + src_ip6.data = src->address.u.ipv6; + src_ip6.chunk.length = htons(sizeof(src_ip6)); + + /* DST IPv6 */ + dst_ip6.chunk.vendor_id = htons(0x0000); + dst_ip6.chunk.type_id = htons(0x0006); + dst_ip6.data = dst->address.u.ipv6; + dst_ip6.chunk.length = htons(sizeof(dst_ip6)); + + iplen = sizeof(dst_ip6) + sizeof(src_ip6); + } + + /* SRC PORT */ + hg->src_port.chunk.vendor_id = htons(0x0000); + hg->src_port.chunk.type_id = htons(0x0007); + hg->src_port.data = htons(src->port); + hg->src_port.chunk.length = htons(sizeof(hg->src_port)); + + /* DST PORT */ + hg->dst_port.chunk.vendor_id = htons(0x0000); + hg->dst_port.chunk.type_id = htons(0x0008); + hg->dst_port.data = htons(dst->port); + hg->dst_port.chunk.length = htons(sizeof(hg->dst_port)); + + + /* TIMESTAMP SEC */ + hg->time_sec.chunk.vendor_id = htons(0x0000); + hg->time_sec.chunk.type_id = htons(0x0009); + hg->time_sec.data = htonl(tv->tv_sec); + hg->time_sec.chunk.length = htons(sizeof(hg->time_sec)); + + + /* TIMESTAMP USEC */ + hg->time_usec.chunk.vendor_id = htons(0x0000); + hg->time_usec.chunk.type_id = htons(0x000a); + hg->time_usec.data = htonl(tv->tv_usec); + hg->time_usec.chunk.length = htons(sizeof(hg->time_usec)); + + /* Protocol TYPE */ + hg->proto_t.chunk.vendor_id = htons(0x0000); + hg->proto_t.chunk.type_id = htons(0x000b); + hg->proto_t.data = PROTO_RTCP_JSON; + hg->proto_t.chunk.length = htons(sizeof(hg->proto_t)); + + /* Capture ID */ + hg->capt_id.chunk.vendor_id = htons(0x0000); + hg->capt_id.chunk.type_id = htons(0x000c); + hg->capt_id.data = capt_id; + hg->capt_id.chunk.length = htons(sizeof(hg->capt_id)); + + /* Payload */ + payload_chunk.vendor_id = htons(0x0000); + payload_chunk.type_id = 0 ? htons(0x0010) : htons(0x000f); + payload_chunk.length = htons(sizeof(payload_chunk) + s->len); + + tlen = sizeof(struct hep_generic) + s->len + iplen + sizeof(hep_chunk_t); + +#if 0 + /* auth key */ + if(profile_transport[idx].capt_password != NULL) { + + tlen += sizeof(hep_chunk_t); + /* Auth key */ + authkey_chunk.vendor_id = htons(0x0000); + authkey_chunk.type_id = htons(0x000e); + authkey_chunk.length = htons(sizeof(authkey_chunk) + strlen(profile_transport[idx].capt_password)); + tlen += strlen(profile_transport[idx].capt_password); + } +#endif + + /* correlation key */ + //if(rcinfo->correlation_id.s && rcinfo->correlation_id.len > 0) { + + tlen += sizeof(hep_chunk_t); + /* Correlation key */ + correlation_chunk.vendor_id = htons(0x0000); + correlation_chunk.type_id = htons(0x0011); + correlation_chunk.length = htons(sizeof(correlation_chunk) + id->len); + tlen += id->len; + //} + + /* total */ + hg->header.length = htons(tlen); + + buffer = (void*)malloc(tlen); + if (buffer==0){ + ilog(LOG_ERR, "ERROR: out of memory"); + free(hg); + return -1; + } + + memcpy((void*) buffer, hg, sizeof(struct hep_generic)); + buflen = sizeof(struct hep_generic); + + /* IPv4 */ + if(hg->ip_family.data == AF_INET) { + /* SRC IP */ + memcpy((void*) buffer+buflen, &src_ip4, sizeof(struct hep_chunk_ip4)); + buflen += sizeof(struct hep_chunk_ip4); + + memcpy((void*) buffer+buflen, &dst_ip4, sizeof(struct hep_chunk_ip4)); + buflen += sizeof(struct hep_chunk_ip4); + } + /* IPv6 */ + else if(hg->ip_family.data == AF_INET6) { + /* SRC IPv6 */ + memcpy((void*) buffer+buflen, &src_ip4, sizeof(struct hep_chunk_ip6)); + buflen += sizeof(struct hep_chunk_ip6); + + memcpy((void*) buffer+buflen, &dst_ip6, sizeof(struct hep_chunk_ip6)); + buflen += sizeof(struct hep_chunk_ip6); + } + +#if 0 + /* AUTH KEY CHUNK */ + if(profile_transport[idx].capt_password != NULL) { + + memcpy((void*) buffer+buflen, &authkey_chunk, sizeof(struct hep_chunk)); + buflen += sizeof(struct hep_chunk); + + /* Now copying payload self */ + memcpy((void*) buffer+buflen, profile_transport[idx].capt_password, strlen(profile_transport[idx].capt_password)); + buflen+=strlen(profile_transport[idx].capt_password); + } +#endif + + /* Correlation KEY CHUNK */ + //if(rcinfo->correlation_id.s && rcinfo->correlation_id.len > 0) { + + memcpy((void*) buffer+buflen, &correlation_chunk, sizeof(struct hep_chunk)); + buflen += sizeof(struct hep_chunk); + + /* Now copying payload self */ + memcpy((void*) buffer+buflen, id->s, id->len); + buflen+= id->len; + //} + + /* PAYLOAD CHUNK */ + memcpy((void*) buffer+buflen, &payload_chunk, sizeof(struct hep_chunk)); + buflen += sizeof(struct hep_chunk); + + /* Now copying payload self */ + memcpy((void*) buffer+buflen, s->str, s->len); + buflen+=s->len; + +#if 0 + /* make sleep after 100 errors */ + if(errors > 50) { + LERR( "HEP server is down... retrying after sleep..."); + if(!profile_transport[idx].usessl) { + sleep(2); + if(init_hepsocket_blocking(idx)) { + profile_transport[idx].initfails++; + } + + errors=0; + } +#ifdef USE_SSL + else { + sleep(2); + + if(initSSL(idx)) profile_transport[idx].initfails++; + + errors=0; + } +#endif /* USE SSL */ + + } + + /* send this packet out of our socket */ + if(send_data(buffer, buflen, idx)) { + errors++; + stats.errors_total++; + } +#endif + + g_string_truncate(s, 0); + g_string_append_len(s, buffer, buflen); + + /* FREE */ + if(buffer) free(buffer); + if(hg) free(hg); + + return 0; +} diff --git a/daemon/homer.h b/daemon/homer.h new file mode 100644 index 000000000..690bc200e --- /dev/null +++ b/daemon/homer.h @@ -0,0 +1,15 @@ +#ifndef __HOMER_H__ +#define __HOMER_H__ + +#include "socket.h" + + +struct homer_sender; + + +struct homer_sender *homer_sender_new(const endpoint_t *, int, int); +int homer_send(struct homer_sender *, GString *, const str *, const endpoint_t *, const endpoint_t *, + const struct timeval *tv); + + +#endif diff --git a/daemon/main.c b/daemon/main.c index 7aa2765d3..b2a055221 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -29,6 +29,7 @@ #include "ice.h" #include "socket.h" #include "media_socket.h" +#include "homer.h" @@ -55,13 +56,16 @@ static char *pidfile; static gboolean foreground; static GQueue interfaces = G_QUEUE_INIT; static GQueue keyspaces = G_QUEUE_INIT; -endpoint_t tcp_listen_ep; -endpoint_t udp_listen_ep; -endpoint_t ng_listen_ep; -endpoint_t cli_listen_ep; -endpoint_t graphite_ep; -endpoint_t redis_ep; -endpoint_t redis_write_ep; +static endpoint_t tcp_listen_ep; +static endpoint_t udp_listen_ep; +static endpoint_t ng_listen_ep; +static endpoint_t cli_listen_ep; +static endpoint_t graphite_ep; +static endpoint_t redis_ep; +static endpoint_t redis_write_ep; +static endpoint_t homer_ep; +static int homer_protocol = SOCK_DGRAM; +static int homer_id = 2001; static int tos; static int table = -1; static int no_fallback; @@ -247,7 +251,7 @@ static int redis_ep_parse(endpoint_t *ep, int *db, char **auth, const char *auth if (l < 0) return -1; *db = l; - if (endpoint_parse_any(ep, str)) + if (endpoint_parse_any_full(ep, str)) return -1; return 0; } @@ -274,6 +278,8 @@ static void options(int *argc, char ***argv) { char *log_facility_rtcp_s = NULL; int version = 0; int sip_source = 0; + char *homerp = NULL; + char *homerproto = NULL; GOptionEntry e[] = { { "version", 'v', 0, G_OPTION_ARG_NONE, &version, "Print build time and exit", NULL }, @@ -285,7 +291,7 @@ static void options(int *argc, char ***argv) { { "listen-udp", 'u', 0, G_OPTION_ARG_STRING, &listenudps, "UDP port to listen on", "[IP46:]PORT" }, { "listen-ng", 'n', 0, G_OPTION_ARG_STRING, &listenngs, "UDP port to listen on, NG protocol", "[IP46:]PORT" }, { "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", 'g', 0, G_OPTION_ARG_STRING, &graphitep, "Address of the graphite server", "IP46:PORT" }, { "graphite-interval", 'G', 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" }, @@ -312,6 +318,9 @@ static void options(int *argc, char ***argv) { { "sip-source", 0, 0, G_OPTION_ARG_NONE, &sip_source, "Use SIP source address by default", NULL }, { "dtls-passive", 0, 0, G_OPTION_ARG_NONE, &dtls_passive_def,"Always prefer DTLS passive role", NULL }, { "max-sessions", 0, 0, G_OPTION_ARG_INT, &max_sessions, "Limit of maximum number of sessions", "INT" }, + { "homer", 0, 0, G_OPTION_ARG_STRING, &homerp, "Address of Homer server for RTCP stats","IP46:PORT"}, + { "homer-protocol",0,0,G_OPTION_ARG_STRING, &homerproto, "Transport protocol for Homer (default udp)", "udp|tcp" }, + { "homer-id", 0, 0, G_OPTION_ARG_STRING, &homer_id, "'Capture ID' to use within the HEP protocol", "INT" }, { NULL, } }; @@ -367,13 +376,26 @@ static void options(int *argc, char ***argv) { die("Invalid IP or port (--listen-cli)"); } - if (graphitep) {if (endpoint_parse_any(&graphite_ep, graphitep)) + if (graphitep) {if (endpoint_parse_any_full(&graphite_ep, graphitep)) die("Invalid IP or port (--graphite)"); } if (graphite_prefix_s) set_prefix(graphite_prefix_s); + if (homerp) { + if (endpoint_parse_any_full(&homer_ep, homerp)) + die("Invalid IP or port (--homer)"); + } + if (homerproto) { + if (!strcmp(homerproto, "tcp")) + homer_protocol = SOCK_STREAM; + else if (!strcmp(homerproto, "udp")) + homer_protocol = SOCK_DGRAM; + else + die("Invalid protocol (--homer-protocol)"); + } + if (tos < 0 || tos > 255) die("Invalid TOS value"); @@ -634,6 +656,8 @@ no_kernel: daemonize(); wpidfile(); + ctx->m->homer = homer_sender_new(&homer_ep, homer_protocol, homer_id); + if (mc.redis) { // start redis restore timer gettimeofday(&redis_start, NULL); diff --git a/daemon/media_socket.c b/daemon/media_socket.c index 4d719171b..202c1229f 100644 --- a/daemon/media_socket.c +++ b/daemon/media_socket.c @@ -537,7 +537,7 @@ struct local_intf *get_any_interface_address(const struct logical_intf *lif, soc static int get_port6(socket_t *r, unsigned int port, struct intf_spec *spec) { if (open_socket(r, SOCK_DGRAM, port, &spec->local_address.addr)) return -1; - + socket_timestamping(r); return 0; } @@ -1013,7 +1013,7 @@ noop: /* XXX split this function into pieces */ /* called lock-free */ -static int stream_packet(struct stream_fd *sfd, str *s, const endpoint_t *fsin) { +static int stream_packet(struct stream_fd *sfd, str *s, const endpoint_t *fsin, const struct timeval *tv) { struct packet_stream *stream, *sink = NULL, *in_srtp, *out_srtp; @@ -1173,8 +1173,8 @@ loop_ok: if (rwf_in) handler_ret = rwf_in(s, in_srtp); if (handler_ret >= 0) { - if (rtcp && _log_facility_rtcp) - parse_and_log_rtcp_report(sfd, s->s, s->len); + if (rtcp) + parse_and_log_rtcp_report(sfd, s, fsin, tv); if (rwf_out) handler_ret += rwf_out(s, out_srtp); } @@ -1358,6 +1358,7 @@ static void stream_fd_readable(int fd, void *p, uintptr_t u) { struct call *ca; str s; endpoint_t ep; + struct timeval tv; if (sfd->socket.fd != fd) goto out; @@ -1373,7 +1374,8 @@ static void stream_fd_readable(int fd, void *p, uintptr_t u) { } #endif - ret = socket_recvfrom(&sfd->socket, buf + RTP_BUFFER_HEAD_ROOM, MAX_RTP_PACKET_SIZE, &ep); + ret = socket_recvfrom_ts(&sfd->socket, buf + RTP_BUFFER_HEAD_ROOM, MAX_RTP_PACKET_SIZE, + &ep, &tv); if (ret < 0) { if (errno == EINTR) @@ -1387,7 +1389,7 @@ static void stream_fd_readable(int fd, void *p, uintptr_t u) { ilog(LOG_WARNING, "UDP packet possibly truncated"); str_init_len(&s, buf + RTP_BUFFER_HEAD_ROOM, ret); - ret = stream_packet(sfd, &s, &ep); + ret = stream_packet(sfd, &s, &ep, &tv); if (ret < 0) { ilog(LOG_WARNING, "Write error on RTP socket: %s", strerror(-ret)); call_destroy(sfd->call); diff --git a/daemon/rtcp.c b/daemon/rtcp.c index bf226c853..5380f8511 100644 --- a/daemon/rtcp.c +++ b/daemon/rtcp.c @@ -12,6 +12,7 @@ #include "rtp.h" #include "crypto.h" #include "rtcp_xr.h" +#include "homer.h" @@ -474,8 +475,9 @@ int rtcp_demux_is_rtcp(const str *s) { 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, ", +static void print_rtcp_common(GString *log, const pjmedia_rtcp_common *common) { + if (log) + g_string_append_printf(log,"version=%u, padding=%u, count=%u, payloadtype=%u, length=%u, ssrc=%u, ", common->version, common->p, common->count, @@ -484,23 +486,49 @@ void print_rtcp_common(char** cdrbufcur, const pjmedia_rtcp_common *common) { 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, ", +static void print_rtcp_sr(GString *log, const pjmedia_rtcp_sr* sr, GString *json) { + if (log) + g_string_append_printf(log,"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)); + + if (json) + g_string_append_printf(json, "\"sender_information\":{\"ntp_timestamp_sec\":%u," + "\"ntp_timestamp_usec\":%u,\"octets\":%u,\"rtp_timestamp\":%u, \"packets\":%u},", + ntohl(sr->ntp_sec), + ntohl(sr->ntp_frac), + ntohl(sr->sender_bcount), + ntohl(sr->rtp_ts), + ntohl(sr->sender_pcount)); } -void print_rtcp_rr(char** cdrbufcur, const pjmedia_rtcp_rr* rr) { +static void print_rtcp_rr_list_start(pjmedia_rtcp_common *common, GString *json) { + if (json) + g_string_append_printf(json, "\"ssrc\":%u,\"type\":%u,\"report_count\":%u,\"report_blocks\":[", + ntohl(common->ssrc), + common->pt, + common->count); +} + +static void print_rtcp_sdes_list_start(pjmedia_rtcp_common *common, GString *json) { + if (json) + g_string_append_printf(json, "\"sdes_ssrc\":%u,\"sdes_report_count\":%u,\"sdes_information\": [ ", + ntohl(common->ssrc), + common->count); +} + +static void print_rtcp_rr(GString *log, const pjmedia_rtcp_rr* rr, GString *json) { /* 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, ", + if (log) + g_string_append_printf(log,"ssrc=%u, fraction_lost=%u, packet_loss=%u, last_seq=%u, jitter=%u, last_sr=%u, delay_since_last_sr=%u, ", ntohl(rr->ssrc), rr->fract_lost, packet_loss, @@ -508,47 +536,206 @@ void print_rtcp_rr(char** cdrbufcur, const pjmedia_rtcp_rr* rr) { ntohl(rr->jitter), ntohl(rr->lsr), ntohl(rr->dlsr)); + + if (json) + g_string_append_printf(json, "{\"source_ssrc\":%u," + "\"highest_seq_no\":%u,\"fraction_lost\":%u,\"ia_jitter\":%u," + "\"packets_lost\":%u,\"lsr\":%u,\"dlsr\":%u},", + ntohl(rr->ssrc), + ntohl(rr->last_seq), + rr->fract_lost, + ntohl(rr->jitter), + packet_loss, + ntohl(rr->lsr), + ntohl(rr->dlsr)); } -void parse_and_log_rtcp_report(struct stream_fd *sfd, const void *pkt, long size) { +static void print_rtcp_sdes_item(GString *json, const rtcp_sdes_chunk_t *chunk, const rtcp_sdes_item_t *item, + const char *data) +{ + int i; + + if (json) { + g_string_append_printf(json, "{\"sdes_chunk_ssrc\":%u,\"type\":%u,\"text\":\"", + htonl(chunk->csrc), + item->type); + + for (i = 0; i < item->len; i++) { + switch (data[i]) { + case '"': + g_string_append(json, "\\\""); + break; + case '\\': + g_string_append(json, "\\\\"); + break; + case '\b': + g_string_append(json, "\\b"); + break; + case '\f': + g_string_append(json, "\\f"); + break; + case '\n': + g_string_append(json, "\\n"); + break; + case '\r': + g_string_append(json, "\\r"); + break; + case '\t': + g_string_append(json, "\\t"); + break; + default: + g_string_append_c(json, data[i]); + break; + } + } - static const int CDRBUFLENGTH = 1024*1024*1; // 1 MB - char cdrbuffer[CDRBUFLENGTH]; - char* cdrbufcur = cdrbuffer; - pjmedia_rtcp_common *common = (pjmedia_rtcp_common*) pkt; - const pjmedia_rtcp_rr *rr = NULL; - const pjmedia_rtcp_sr *sr = NULL; + g_string_append(json, "\"},"); + } +} - cdrbufcur += sprintf(cdrbufcur,"["STR_FORMAT"] ", STR_FMT(&sfd->stream->call->callid)); +static void str_sanitize(GString *s) { + while (s->len > 0 && (s->str[s->len - 1] == ' ' || s->str[s->len - 1] == ',')) + g_string_truncate(s, s->len - 1); +} - if (size < sizeof(*common)) +static void print_rtcp_list_end(GString *json) { + if (json) { + str_sanitize(json); + g_string_append_printf(json, "],"); + } +} + +void parse_and_log_rtcp_report(struct stream_fd *sfd, const str *ori_s, const endpoint_t *src, + const struct timeval *tv) +{ + + GString *log; + str iter_s, comp_s; + pjmedia_rtcp_common *common; + const pjmedia_rtcp_rr *rr; + const pjmedia_rtcp_sr *sr; + const rtcp_sdes_chunk_t *sdes_chunk; + const rtcp_sdes_item_t *sdes_item; + GString *json; + struct call *c = sfd->call; + struct callmaster *cm = c->callmaster; + int i; + + log = _log_facility_rtcp ? g_string_new(NULL) : NULL; + json = cm->homer ? g_string_new("{ ") : NULL; + + // anything to do? + if (!log && !json) return; - print_rtcp_common(&cdrbufcur,common); + if (log) + g_string_append_printf(log, "["STR_FORMAT"] ", STR_FMT(&sfd->stream->call->callid)); + + iter_s = *ori_s; + + while (iter_s.len) { + // procedure throughout here: first assign, then str_shift with check for + // return value (does the length sanity check), then access values. + // we use iter_s to iterate compound packets and comp_s to access component + // data. + + common = (pjmedia_rtcp_common*) iter_s.s; + comp_s = iter_s; + + if (str_shift(&comp_s, sizeof(*common))) // puts comp_s just past the common header + break; + if (str_shift(&iter_s, (ntohs(common->length) + 1) << 2)) // puts iter_s on the next compound packet + break; + + print_rtcp_common(log, common); - /* Parse RTCP */ - if (common->pt == RTCP_PT_SR) { - if (size < (sizeof(*common) + sizeof(*sr))) - return; + /* Parse RTCP */ + switch (common->pt) { + case RTCP_PT_SR: + sr = (pjmedia_rtcp_sr*) ((comp_s.s)); + if (str_shift(&comp_s, sizeof(*sr))) + break; - sr = (pjmedia_rtcp_sr*) (((char*)pkt) + sizeof(pjmedia_rtcp_common)); + print_rtcp_sr(log, sr, json); + // fall through to RTCP_PT_RR - print_rtcp_sr(&cdrbufcur,sr); + case RTCP_PT_RR: + print_rtcp_rr_list_start(common, json); - 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); + for (i = 0; i < common->count; i++) { + rr = (pjmedia_rtcp_rr*)((comp_s.s)); + if (str_shift(&comp_s, sizeof(*rr))) + break; + print_rtcp_rr(log, rr, json); + } + + print_rtcp_list_end(json); + break; + + case RTCP_PT_XR: + pjmedia_rtcp_xr_rx_rtcp_xr(log, common, &comp_s); + break; + + case RTCP_PT_SDES: + print_rtcp_sdes_list_start(common, json); + + // the "common" header actually includes the SDES + // SSRC/CSRC chunk header, so we set our chunk header + // to its SDES field + sdes_chunk = (rtcp_sdes_chunk_t *) &common->ssrc; + // comp_s then points into the first SDES item + + i = 0; + while (1) { + while (comp_s.len) { + sdes_item = (rtcp_sdes_item_t *) comp_s.s; + // check for zero type first + if (str_shift(&comp_s, 1)) + break; + if (!sdes_item->type) + break; + if (str_shift(&comp_s, sizeof(*sdes_item) - 1)) + break; + if (comp_s.len < sdes_item->len) + break; + print_rtcp_sdes_item(json, sdes_chunk, sdes_item, comp_s.s); + str_shift(&comp_s, sdes_item->len); + } + + // remove padding to next chunk + while (comp_s.len % 4 != 0) + str_shift(&comp_s, 1); + + // more chunks? set chunk header + i++; + if (i >= common->count) + break; + sdes_chunk = (rtcp_sdes_chunk_t *) comp_s.s; + if (str_shift(&comp_s, sizeof(*sdes_chunk))) + break; + + } + + print_rtcp_list_end(json); + + break; } - } else if (common->pt == RTCP_PT_RR && common->count > 0) { - if (size < (sizeof(*common) + sizeof(*rr))) - return; + } - rr = (pjmedia_rtcp_rr*)(((char*)pkt) + sizeof(pjmedia_rtcp_common)); - print_rtcp_rr(&cdrbufcur,rr); + if (log) { + str_sanitize(log); + rtcplog(log->str); + } - } else if (common->pt == RTCP_PT_XR) { - pjmedia_rtcp_xr_rx_rtcp_xr(cdrbufcur, pkt, size); + if (json) { + str_sanitize(json); + g_string_append(json, " }"); + homer_send(cm->homer, json, &c->callid, src, &sfd->socket.local, tv); + json = NULL; } - rtcplog(cdrbuffer); + + if (json) + g_string_free(json, TRUE); + if (log) + g_string_free(log, TRUE); } diff --git a/daemon/rtcp.h b/daemon/rtcp.h index 96f88fa8c..c266460f9 100644 --- a/daemon/rtcp.h +++ b/daemon/rtcp.h @@ -3,6 +3,7 @@ #include "str.h" #include "call.h" +#include struct crypto_context; @@ -30,7 +31,7 @@ typedef struct pjmedia_rtcp_sr 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; +} __attribute__ ((packed)) pjmedia_rtcp_sr; /** @@ -39,22 +40,36 @@ typedef struct pjmedia_rtcp_sr typedef struct pjmedia_rtcp_rr { u_int32_t ssrc; /**< SSRC identification. */ -#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0 +#if G_BYTE_ORDER == G_BIG_ENDIAN 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 +#elif G_BYTE_ORDER == G_LITTLE_ENDIAN 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. */ +#else +#error "byte order unknown" #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; +} __attribute__ ((packed)) pjmedia_rtcp_rr; + + +typedef struct _rtcp_sdes_chunk +{ + uint32_t csrc; +} __attribute__ ((packed)) rtcp_sdes_chunk_t; + +typedef struct _rtcp_sdes_item +{ + uint8_t type; + uint8_t len; +} __attribute__ ((packed)) rtcp_sdes_item_t; /** @@ -62,7 +77,7 @@ typedef struct pjmedia_rtcp_rr */ typedef struct pjmedia_rtcp_common { -#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0 +#if G_BYTE_ORDER == G_BIG_ENDIAN unsigned version:2; /**< packet type */ unsigned p:1; /**< padding flag */ unsigned count:5; /**< varies by payload type */ @@ -77,27 +92,6 @@ typedef struct pjmedia_rtcp_common 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 *); int rtcp_avp2savp(str *, struct crypto_context *); @@ -105,6 +99,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); +void parse_and_log_rtcp_report(struct stream_fd *sfd, const str *, const endpoint_t *, const struct timeval *); #endif diff --git a/daemon/rtcp_xr.c b/daemon/rtcp_xr.c index 39e1a7764..db90dd250 100644 --- a/daemon/rtcp_xr.c +++ b/daemon/rtcp_xr.c @@ -4,9 +4,10 @@ * Created on: Mar 29, 2015 * Author: fmetz */ +#include "rtcp_xr.h" #include #include -#include "rtcp_xr.h" +#include /* RTCP XR payload type */ #define RTCP_XR 207 @@ -21,41 +22,31 @@ #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, ", +void print_rtcp_xr_rb_header(GString *log, const pjmedia_rtcp_xr_rb_header *rb_header) { + g_string_append_printf(log, "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, ", +void print_rtcp_xr_rb_rr_time(GString *log, const pjmedia_rtcp_xr_rb_rr_time *rb_rr_time) { + print_rtcp_xr_rb_header(log, &rb_rr_time->header); + g_string_append_printf(log, "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, ", +void print_rtcp_xr_rb_dlrr(GString *log, const pjmedia_rtcp_xr_rb_dlrr *rb_dlrr) { + print_rtcp_xr_rb_header(log, &rb_dlrr->header); + g_string_append_printf(log, "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," +void print_rtcp_xr_rb_stats(GString *log, const pjmedia_rtcp_xr_rb_stats *rb_stats) { + print_rtcp_xr_rb_header(log, &rb_stats->header); + g_string_append_printf(log, "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), @@ -73,9 +64,9 @@ void print_rtcp_xr_rb_stats(char* cdrbufcur,const pjmedia_rtcp_xr_rb_stats *rb_s 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, " +void print_rtcp_xr_rb_voip_mtc(GString *log, const pjmedia_rtcp_xr_rb_voip_mtc *rb_voip_mtc) { + print_rtcp_xr_rb_header(log, &rb_voip_mtc->header); + g_string_append_printf(log, "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, " @@ -104,60 +95,54 @@ void print_rtcp_xr_rb_voip_mtc(char* cdrbufcur,const pjmedia_rtcp_xr_rb_voip_mtc ntohs(rb_voip_mtc->jb_abs_max)); } -void pjmedia_rtcp_xr_rx_rtcp_xr(char* cdrbufcur, const void *pkt, size_t size) { +void pjmedia_rtcp_xr_rx_rtcp_xr(GString *log, pjmedia_rtcp_common *common, str *s) { - 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; + const pjmedia_rtcp_xr_rb_rr_time *rb_rr_time; + const pjmedia_rtcp_xr_rb_dlrr *rb_dlrr; + const pjmedia_rtcp_xr_rb_stats *rb_stats; + const pjmedia_rtcp_xr_rb_voip_mtc *rb_voip_mtc; + const pjmedia_rtcp_xr_rb_header *rb_hdr; unsigned pkt_len, rb_len; - if (size < sizeof(*rtcp_xr)) + if (!log) return; - if (rtcp_xr->common.pt != RTCP_XR) - return; - - print_rtcp_xr_common(cdrbufcur,rtcp_xr); + // packet length is guaranteed to be valid from upstream - pkt_len = ntohs((u_int16_t)rtcp_xr->common.length); - - if ((pkt_len + 1) > (size / 4)) - return; + pkt_len = (ntohs(common->length) + 1) << 2; /* Parse report rpt_types */ - while ((int32_t*)rb_hdr < (int32_t*)pkt + pkt_len) + while (pkt_len >= sizeof(*rb_hdr)) { - rb_len = ntohs((u_int16_t)rb_hdr->length); + rb_hdr = (pjmedia_rtcp_xr_rb_header*) s->s; + rb_len = (ntohs((u_int16_t)rb_hdr->length) + 1) << 2; + if (str_shift(s, rb_len)) + break; + pkt_len -= rb_len; /* Just skip any block with length == 0 (no report content) */ - if (rb_len) { + if (rb_len > 4) { 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); + print_rtcp_xr_rb_rr_time(log, rb_rr_time); break; case BT_DLRR: rb_dlrr = (pjmedia_rtcp_xr_rb_dlrr*) rb_hdr; - print_rtcp_xr_rb_dlrr(cdrbufcur,rb_dlrr); + print_rtcp_xr_rb_dlrr(log, rb_dlrr); break; case BT_STATS: rb_stats = (pjmedia_rtcp_xr_rb_stats*) rb_hdr; - print_rtcp_xr_rb_stats(cdrbufcur,rb_stats); + print_rtcp_xr_rb_stats(log, 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); + print_rtcp_xr_rb_voip_mtc(log, 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 index b8cbb5b5d..01a5e136c 100644 --- a/daemon/rtcp_xr.h +++ b/daemon/rtcp_xr.h @@ -10,6 +10,10 @@ #include #include +#include + +#include "str.h" +#include "rtcp.h" /** * @defgroup PJMED_RTCP_XR RTCP Extended Report (XR) - RFC 3611 @@ -180,42 +184,6 @@ typedef struct pjmedia_rtcp_xr_rb_voip_mtc } 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. @@ -223,7 +191,10 @@ typedef struct pjmedia_rtcp_xr_pkt * @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); +void pjmedia_rtcp_xr_rx_rtcp_xr(GString *, pjmedia_rtcp_common *common, str *s); + + +#pragma pack() #endif /* RTCP_XR_H_ */ diff --git a/daemon/socket.c b/daemon/socket.c index 3f69074d6..951195e8b 100644 --- a/daemon/socket.c +++ b/daemon/socket.c @@ -20,6 +20,7 @@ static int __ip4_is_specified(const sockaddr_t *a); static int __ip6_is_specified(const sockaddr_t *a); static int __ip_bind(socket_t *s, unsigned int, const sockaddr_t *); static int __ip_connect(socket_t *s, const endpoint_t *); +static int __ip_timestamping(socket_t *s); static int __ip4_sockaddr2endpoint(endpoint_t *, const void *); static int __ip6_sockaddr2endpoint(endpoint_t *, const void *); static int __ip4_endpoint2sockaddr(void *, const endpoint_t *); @@ -27,10 +28,12 @@ static int __ip6_endpoint2sockaddr(void *, const endpoint_t *); static int __ip4_addrport2sockaddr(void *, const sockaddr_t *, unsigned int); static int __ip6_addrport2sockaddr(void *, const sockaddr_t *, unsigned int); static ssize_t __ip_recvfrom(socket_t *s, void *buf, size_t len, endpoint_t *ep); +static ssize_t __ip_recvfrom_ts(socket_t *s, void *buf, size_t len, endpoint_t *ep, struct timeval *); static ssize_t __ip_sendmsg(socket_t *s, struct msghdr *mh, const endpoint_t *ep); static ssize_t __ip_sendto(socket_t *s, const void *buf, size_t len, const endpoint_t *ep); static int __ip4_tos(socket_t *, unsigned int); static int __ip6_tos(socket_t *, unsigned int); +static int __ip_error(socket_t *s); static void __ip4_endpoint2kernel(struct re_address *, const endpoint_t *); static void __ip6_endpoint2kernel(struct re_address *, const endpoint_t *); static void __ip4_kernel2endpoint(endpoint_t *ep, const struct re_address *ra); @@ -63,10 +66,13 @@ static struct socket_family __socket_families[__SF_LAST] = { .addrport2sockaddr = __ip4_addrport2sockaddr, .bind = __ip_bind, .connect = __ip_connect, + .timestamping = __ip_timestamping, .recvfrom = __ip_recvfrom, + .recvfrom_ts = __ip_recvfrom_ts, .sendmsg = __ip_sendmsg, .sendto = __ip_sendto, .tos = __ip4_tos, + .error = __ip_error, .endpoint2kernel = __ip4_endpoint2kernel, .kernel2endpoint = __ip4_kernel2endpoint, }, @@ -87,10 +93,13 @@ static struct socket_family __socket_families[__SF_LAST] = { .addrport2sockaddr = __ip6_addrport2sockaddr, .bind = __ip_bind, .connect = __ip_connect, + .timestamping = __ip_timestamping, .recvfrom = __ip_recvfrom, + .recvfrom_ts = __ip_recvfrom_ts, .sendmsg = __ip_sendmsg, .sendto = __ip_sendto, .tos = __ip6_tos, + .error = __ip_error, .endpoint2kernel = __ip6_endpoint2kernel, .kernel2endpoint = __ip6_kernel2endpoint, }, @@ -233,18 +242,52 @@ static int __ip_connect(socket_t *s, const endpoint_t *ep) { } return 0; } -static ssize_t __ip_recvfrom(socket_t *s, void *buf, size_t len, endpoint_t *ep) { +static ssize_t __ip_recvfrom_ts(socket_t *s, void *buf, size_t len, endpoint_t *ep, struct timeval *tv) { ssize_t ret; struct sockaddr_storage sin; - socklen_t sinlen; - - sinlen = s->family->sockaddr_size; - ret = recvfrom(s->fd, buf, len, 0, (void *) &sin, &sinlen); + struct msghdr msg; + struct iovec iov; + char ctrl[32]; + struct cmsghdr *cm; + + ZERO(msg); + msg.msg_name = &sin; + msg.msg_namelen = s->family->sockaddr_size; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = ctrl; + msg.msg_controllen = sizeof(ctrl); + ZERO(iov); + iov.iov_base = buf; + iov.iov_len = len; + + ret = recvmsg(s->fd, &msg, 0); if (ret < 0) return ret; s->family->sockaddr2endpoint(ep, &sin); + + if (tv) { + for (cm = CMSG_FIRSTHDR(&msg); cm; cm = CMSG_NXTHDR(&msg, cm)) { + if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SO_TIMESTAMP) { + *tv = *((struct timeval *) CMSG_DATA(cm)); + tv = NULL; + } + } + if (G_UNLIKELY(tv)) { + ilog(LOG_WARNING, "No receive timestamp received from kernel"); + ZERO(*tv); + } + } + if (G_UNLIKELY((msg.msg_flags & MSG_TRUNC))) + ilog(LOG_WARNING, "Kernel indicates that data was truncated"); + if (G_UNLIKELY((msg.msg_flags & MSG_CTRUNC))) + ilog(LOG_WARNING, "Kernel indicates that ancillary data was truncated"); + return ret; } +static ssize_t __ip_recvfrom(socket_t *s, void *buf, size_t len, endpoint_t *ep) { + return __ip_recvfrom_ts(s, buf, len, ep, NULL); +} static ssize_t __ip_sendmsg(socket_t *s, struct msghdr *mh, const endpoint_t *ep) { struct sockaddr_storage sin; @@ -270,6 +313,19 @@ static int __ip6_tos(socket_t *s, unsigned int tos) { setsockopt(s->fd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos)); return 0; } +static int __ip_error(socket_t *s) { + int optval; + socklen_t optlen = sizeof(optval); + if (getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &optval, &optlen)) + return -1; + return optval; +} +static int __ip_timestamping(socket_t *s) { + int one = 1; + if (setsockopt(s->fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one))) + return -1; + return 0; +} static void __ip4_endpoint2kernel(struct re_address *ra, const endpoint_t *ep) { ZERO(*ra); ra->family = AF_INET; @@ -478,23 +534,15 @@ fail: return -1; } -int connect_socket_nb(socket_t *r, int type, const endpoint_t *ep) { - sockfamily_t *fam; +int connect_socket_retry(socket_t *r) { int ret = 0; - fam = ep->address.family; - - if (__socket(r, type, fam)) - return -1; - nonblock(r->fd); - if (fam->connect(r, ep)) { - if (errno != EINPROGRESS) + if (r->family->connect(r, &r->remote)) { + if (errno != EINPROGRESS && errno != EALREADY) goto fail; ret = 1; } - r->remote = *ep; - return ret; fail: @@ -502,6 +550,19 @@ fail: return -1; } +int connect_socket_nb(socket_t *r, int type, const endpoint_t *ep) { + sockfamily_t *fam; + + fam = ep->address.family; + + if (__socket(r, type, fam)) + return -1; + nonblock(r->fd); + r->remote = *ep; + + return connect_socket_retry(r); +} + int close_socket(socket_t *r) { if (!r || r->fd == -1) { __C_DBG("close() syscall not called, fd=%d", r->fd); diff --git a/daemon/socket.h b/daemon/socket.h index 771ca68d7..aff67bb63 100644 --- a/daemon/socket.h +++ b/daemon/socket.h @@ -59,10 +59,13 @@ struct socket_family { int (*addrport2sockaddr)(void *, const sockaddr_t *, unsigned int); int (*bind)(socket_t *, unsigned int, const sockaddr_t *); int (*connect)(socket_t *, const endpoint_t *); + int (*timestamping)(socket_t *); ssize_t (*recvfrom)(socket_t *, void *, size_t, endpoint_t *); + ssize_t (*recvfrom_ts)(socket_t *, void *, size_t, endpoint_t *, struct timeval *); ssize_t (*sendmsg)(socket_t *, struct msghdr *, const endpoint_t *); ssize_t (*sendto)(socket_t *, const void *, size_t, const endpoint_t *); int (*tos)(socket_t *, unsigned int); + int (*error)(socket_t *); void (*endpoint2kernel)(struct re_address *, const endpoint_t *); void (*kernel2endpoint)(endpoint_t *, const struct re_address *); }; @@ -144,8 +147,11 @@ INLINE int is_addr_unspecified(const sockaddr_t *a) { return !a->family->is_specified(a); } #define socket_recvfrom(s,a...) (s)->family->recvfrom((s), a) +#define socket_recvfrom_ts(s,a...) (s)->family->recvfrom_ts((s), a) #define socket_sendmsg(s,a...) (s)->family->sendmsg((s), a) #define socket_sendto(s,a...) (s)->family->sendto((s), a) +#define socket_error(s) (s)->family->error((s)) +#define socket_timestamping(s) (s)->family->timestamping((s)) INLINE ssize_t socket_sendiov(socket_t *s, const struct iovec *v, unsigned int len, const endpoint_t *dst) { struct msghdr mh; ZERO(mh); @@ -174,7 +180,8 @@ void socket_init(void); int open_socket(socket_t *r, int type, unsigned int port, const sockaddr_t *); int connect_socket(socket_t *r, int type, const endpoint_t *ep); -int connect_socket_nb(socket_t *r, int type, const endpoint_t *ep); +int connect_socket_nb(socket_t *r, int type, const endpoint_t *ep); // 1 == in progress +int connect_socket_retry(socket_t *r); // retries connect() while in progress int close_socket(socket_t *r); sockfamily_t *get_socket_family_rfc(const str *s); @@ -182,7 +189,7 @@ sockfamily_t *__get_socket_family_enum(enum socket_families); int sockaddr_parse_any(sockaddr_t *dst, const char *src); int sockaddr_parse_any_str(sockaddr_t *dst, const str *src); int sockaddr_parse_str(sockaddr_t *dst, sockfamily_t *fam, const str *src); -int endpoint_parse_any(endpoint_t *, const char *); +int endpoint_parse_any(endpoint_t *, const char *); // address optional void kernel2endpoint(endpoint_t *ep, const struct re_address *ra); unsigned int sockaddr_hash(const sockaddr_t *); @@ -206,6 +213,16 @@ INLINE int endpoint_parse_port_any(endpoint_t *e, const char *p, unsigned int po e->port = port; return sockaddr_parse_any(&e->address, p); } +// address required +INLINE int endpoint_parse_any_full(endpoint_t *d, const char *s) { + int ret; + ret = endpoint_parse_any(d, s); + if (ret) + return ret; + if (is_addr_unspecified(&d->address)) + return -1; + return 0; +} INLINE int ipv46_any_convert(endpoint_t *ep) { if (ep->address.family->af != AF_INET) return 0; diff --git a/daemon/str.h b/daemon/str.h index c18c60557..3993951d6 100644 --- a/daemon/str.h +++ b/daemon/str.h @@ -23,6 +23,7 @@ typedef struct _str str; #define STR_FORMAT "%.*s" #define STR_FMT(str) (str)->len, (str)->s #define STR_FMT0(str) ((str) ? (str)->len : 6), ((str) ? (str)->s : "(NULL)") +#define G_STR_FMT(gstr) (int) (gstr)->len, (gstr)->str // for glib GString #define STR_NULL ((str) { NULL, 0 }) #define STR_EMPTY ((str) { "", 0 }) #define STR_CONST_INIT(str) { str, sizeof(str)-1 } diff --git a/debian/changelog b/debian/changelog index 6d59de0d0..f1c37cc2b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,80 @@ +ngcp-rtpengine (4.4.0.0+0~mr4.4.0.0) unstable; urgency=medium + + [ Lucian Balaceanu ] + * [937bb71] Adding images to illustrate redis database layout + * [7ae955c] Fixing reversed parameters when calculating redis restore time + + [ Guillem Jover ] + * [df2126f] MT#16473 Convert debian/copyright to machine-readable format + + [ Pawel Kuzak ] + * [ca04c96] Make creation of iptables chain optional + * [3f4cfff] Fixed inconsistency in rtpengine-ctl list totals + + [ Joseph Frazier ] + * [8fbb0d3] Add hiredis-devel to daemon deps in README.el.md + * [2877f76] Add hiredis-devel to daemon BuildRequires + + [ Richard Fuchs ] + * [7635f51] add hiredis dependency to README + * [6f2dc00] allow srtp-debug-helper to specify the ROC + * [b85a9e3] fix two memory leaks + * [5926048] don't just ignore but also strip invalid/unknown a=crypto + * [c9d797a] retain outgoing DTLS role whenever possible + * [d81c8df] move struct sdp_ng_flags from sdp.h to call_interfaces.h + * [b0a3898] fix sfd assignments when remote port changes + * [5dbadc4] implement port latching option + * [38d031c] fix support for AF switching on the fly + * [43bcabf] fix logic for unspecified ipv6 addresses + * [ff26e00] add STUN SOFTWARE attribute into ICE checks and responses + * [59cfb4f] fix incorrect padding in stun software attr + * [5b33498] fix RTCP content output + * [77d074c] fix length of software STUN attribute + * [b828122] move advertised_address out of intf_spec into local_intf + * [1c56865] fix iovec overflows + * [27d18e2] fix stun xor port response + * [725638b] remove redis-role redundancy + * [d8e1e9f] simplify redis CLI options + * [3f1ae98] unify -r/-R and -w/-W options into single options + * [c1407d6] support Redis server authentication + * [f52884e] report errors returned from redis + * [c4f630c] fix kernel module build for 4.4+ kernels + * [926d9d3] MT#17699 augment module makefile version detection + + [ smititelu ] + * [6378a1e] Graphite change global to local parameters + * [0395a24] Change the graphite connection state logic + * [e48252e] Change abort() behaviour of redis_check_conn() + * [7d03f3d] Improve Redis connection logging + * [50aef80] Rtpengine starts even if redis is down + * [33e72ba] Skip redundant log if r->ctx->err != NULL + * [0447177] Add NO_REDIS_REQUIRED new parameter + * [74ad505] Update README.md for NO_REDIS_REQUIRED parameter + * [65de793] Add config REDIS_NUM_THREADS param + * [eee5a65] Update doku for REDIS_NUM_THREADS + * [a6b4b86] Fix segfault when ps->component=0 + * [bac271b] Add callmaster config lock + + [ Frederic-Philippe Metz ] + * [e376335] Removed graphite hostname and 'totals' in graphite names + + [ Michael Prokop ] + * [47a9564] MT#17699 Bump Standards-Version to 3.9.7 + * [b4a6653] MT#17699 Fix m-a build error + Bump Standards-Version for ngcp-rtpengine-kernel + + [ Stefan Mititelu ] + * [5e7640b] Add/Retrieve ps->component to/from redis + * [9be68a0] Add FINAL_TIMEOUT parameter + * [ef39aa3] Add rtpengine-ctl list/set timeout + * [4343ff0] Update rtpengine-cli set maxopenfiles + * [05302c2] Update rtpengine-cli set maxsessions + * [57aa566] Add offer/answer/delete processing statistics + * [a43996f] Add 'unidirectional' attribute + + [ Sipwise Jenkins Builder ] + + -- Sipwise Jenkins Builder Mon, 21 Mar 2016 22:31:18 +0100 + ngcp-rtpengine (4.3.0.0+0~mr4.3.0.0) unstable; urgency=medium [ Frederic-Philippe Metz ] diff --git a/debian/ngcp-rtpengine-daemon.default b/debian/ngcp-rtpengine-daemon.default index a296a9c88..3c31e387b 100644 --- a/debian/ngcp-rtpengine-daemon.default +++ b/debian/ngcp-rtpengine-daemon.default @@ -36,3 +36,6 @@ TABLE=0 # GRAPHITE_PREFIX=myownprefix. # MAX_SESSIONS=5000 # CREATE_IPTABLES_CHAIN=no +# HOMER=123.234.345.456:65432 +# HOMER_PROTOCOL=udp +# HOMER_ID=2001 diff --git a/debian/ngcp-rtpengine-daemon.init b/debian/ngcp-rtpengine-daemon.init index 2f9426423..d0d12a87f 100755 --- a/debian/ngcp-rtpengine-daemon.init +++ b/debian/ngcp-rtpengine-daemon.init @@ -89,6 +89,9 @@ OPTIONS="$OPTIONS --table=$TABLE" [ -z "$GRAPHITE_INTERVAL" ] || OPTIONS="$OPTIONS --graphite-interval=$GRAPHITE_INTERVAL" [ -z "$GRAPHITE_PREFIX" ] || OPTIONS="$OPTIONS --graphite-prefix=$GRAPHITE_PREFIX" [ -z "$MAX_SESSIONS" ] || OPTIONS="$OPTIONS --max-sessions=$MAX_SESSIONS" +[ -z "$HOMER" ] || OPTIONS="$OPTIONS --homer=$HOMER" +[ -z "$HOMER_PROTOCOL" ] || OPTIONS="$OPTIONS --homer-protocol=$HOMER_PROTOCOL" +[ -z "$HOMER_ID" ] || OPTIONS="$OPTIONS --homer-id=$HOMER_ID" if test "$FORK" = "no" ; then OPTIONS="$OPTIONS --foreground" diff --git a/tests/simulator-ng.pl b/tests/simulator-ng.pl index e2894519a..10f416f3e 100755 --- a/tests/simulator-ng.pl +++ b/tests/simulator-ng.pl @@ -226,8 +226,27 @@ sub rtcp_sr { my $secs = $now[0] + 2208988800; my $frac = $now[1] / 1000000 * 2**32; my $sr = pack('CCnN NNN NN', (2 << 6) | 1, 200, 12, rand(2**32), $secs, $frac, - 12345, 0, 0); - $sr .= pack('N CCCC NNNN', 0, 0, 0, 0, 0, 0, 0, 0, 0); + 12345, rand(12345), rand(4321)); + $sr .= pack('N CCCC NNNN', rand(2**32), rand(256), rand(256), rand(256), rand(256), + rand(2**32), rand(2**32), rand(2**32), rand(2**32)); + # sdes + $sr .= pack('CCn N CC a* CC a* CC a* C C N CC a* CC a* C CCC N CC a* C', + (2 << 6) | 3, 202, 16, + rand(2 ** 32), # csrc + 1, 7, 'blah123', # cname + 2, 6, 'foobar', # name + 3, 7, 'foo@bar', # email, + 0, # eol + 0, # padding + rand(2 ** 32), # csrc + 4, 5, '54321', # phone + 5, 3, 'foo', # loc + 0, # eol + 0,0,0, # padding + rand(2 ** 32), # csrc + 6, 5, 'fubar', # tool + 0, # eol + ); return $sr; }