From ee4f2d22ab446d484de91f853293edb6e8801c77 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Mon, 9 Feb 2015 08:21:59 -0500 Subject: [PATCH 01/82] convert ilog() into macro --- daemon/log.c | 11 +---------- daemon/log.h | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/daemon/log.c b/daemon/log.c index 3eec0c1eb..6991f92b0 100644 --- a/daemon/log.c +++ b/daemon/log.c @@ -85,7 +85,7 @@ void log_to_stderr(int facility_priority, char *format, ...) { free(msg); } -void ilog(int prio, const char *fmt, ...) { +void __ilog(int prio, const char *fmt, ...) { char prefix[256]; char *msg, *piece; const char *infix = ""; @@ -94,15 +94,6 @@ void ilog(int prio, const char *fmt, ...) { xprio = LOG_LEVEL_MASK(prio); -#ifndef __DEBUG - int level; /* thank you C99 */ - level = get_log_level(); - if (xprio > LOG_LEVEL_MASK(level)) - return; - if ((level & LOG_FLAG_RESTORE) && !(prio & LOG_FLAG_RESTORE)) - return; -#endif - switch (log_info.e) { case LOG_INFO_NONE: prefix[0] = 0; diff --git a/daemon/log.h b/daemon/log.h index c68a87177..73d49ec23 100644 --- a/daemon/log.h +++ b/daemon/log.h @@ -45,7 +45,20 @@ extern unsigned int max_log_line_length; void log_init(void); -void ilog(int prio, const char *fmt, ...)__attribute__ ((format (printf, 2, 3))); +void __ilog(int prio, const char *fmt, ...)__attribute__ ((format (printf, 2, 3))); +#ifndef __DEBUG +#define ilog(prio, fmt...) \ + do { \ + int loglevel = get_log_level(); \ + if (LOG_LEVEL_MASK((prio)) > LOG_LEVEL_MASK(loglevel)) \ + break; \ + if ((loglevel & LOG_FLAG_RESTORE) && !((prio) & LOG_FLAG_RESTORE)) \ + break; \ + __ilog(prio, fmt); \ + } while (0) +#else +#define ilog(prio, fmt...) __ilog(prio, fmt) +#endif void cdrlog(const char* cdrbuffer); From 411a888f9b4d4629021994657eebfe545d5bd037 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Mon, 9 Feb 2015 09:56:38 -0500 Subject: [PATCH 02/82] use atomic ops for stats --- daemon/aux.h | 42 ++++++++++++++++ daemon/call.c | 104 ++++++++++++++++++--------------------- daemon/call.h | 15 +++--- daemon/call_interfaces.c | 32 +++++------- daemon/cli.c | 8 +-- 5 files changed, 115 insertions(+), 86 deletions(-) diff --git a/daemon/aux.h b/daemon/aux.h index 96e7df160..3552393da 100644 --- a/daemon/aux.h +++ b/daemon/aux.h @@ -510,4 +510,46 @@ INLINE void g_queue_append(GQueue *dst, const GQueue *src) { unsigned int in6_addr_hash(const void *p); int in6_addr_eq(const void *a, const void *b); + + +#if GLIB_SIZEOF_VOID_P >= 8 + +typedef struct { + void *p; +} atomic_uint64; + +INLINE u_int64_t atomic_uint64_get(const atomic_uint64 *u) { + return (u_int64_t) g_atomic_pointer_get(&u->p); +} +INLINE u_int64_t atomic_uint64_get_na(const atomic_uint64 *u) { + return (u_int64_t) u->p; +} +INLINE void atomic_uint64_set(atomic_uint64 *u, u_int64_t a) { + g_atomic_pointer_set(&u->p, (void *) a); +} +INLINE void atomic_uint64_set_na(atomic_uint64 *u, u_int64_t a) { + u->p = (void *) a; +} +INLINE void atomic_uint64_inc(atomic_uint64 *u) { + g_atomic_pointer_add(&u->p, 1); +} +INLINE void atomic_uint64_add(atomic_uint64 *u, u_int64_t a) { + g_atomic_pointer_add(&u->p, a); +} +INLINE void atomic_uint64_add_na(atomic_uint64 *u, u_int64_t a) { + u->p = (void *) (((u_int64_t) u->p) + a); +} +INLINE u_int64_t atomic_uint64_get_set(atomic_uint64 *u, u_int64_t a) { + u_int64_t old; + do { + old = atomic_uint64_get(u); + if (g_atomic_pointer_compare_and_exchange(&u->p, (void *) old, (void *) a)) + return old; + } while (1); +} + +#endif + + + #endif diff --git a/daemon/call.c b/daemon/call.c index 6492a0abb..990f45d60 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -687,12 +687,9 @@ loop_ok: if (!sink || !sink->sfd || !out_srtp->sfd || !in_srtp->sfd) { ilog(LOG_WARNING, "RTP packet from %s discarded", addr); - mutex_lock(&stream->in_lock); - stream->stats.errors++; - mutex_lock(&cm->statspslock); - cm->statsps.errors++; - mutex_unlock(&cm->statspslock); - goto done; + atomic_uint64_inc(&stream->stats.errors); + atomic_uint64_inc(&cm->statsps.errors); + goto unlock_out; } mutex_lock(&in_srtp->in_lock); @@ -755,7 +752,7 @@ use_cand: mutex_unlock(&stream->out_lock); if (tmp && PS_ISSET(stream, STRICT_SOURCE)) { - stream->stats.errors++; + atomic_uint64_inc(&stream->stats.errors); goto drop; } } @@ -844,10 +841,8 @@ forward: if (ret == -1) { ret = -errno; ilog(LOG_DEBUG,"Error when sending message. Error: %s",strerror(errno)); - stream->stats.errors++; - mutex_lock(&cm->statspslock); - cm->statsps.errors++; - mutex_unlock(&cm->statspslock); + atomic_uint64_inc(&stream->stats.errors); + atomic_uint64_inc(&cm->statsps.errors); goto out; } @@ -857,13 +852,11 @@ drop: if (sink) mutex_unlock(&sink->out_lock); ret = 0; - stream->stats.packets++; - stream->stats.bytes += s->len; - stream->last_packet = poller_now; - mutex_lock(&cm->statspslock); - cm->statsps.packets++; - cm->statsps.bytes += s->len; - mutex_unlock(&cm->statspslock); + atomic_uint64_inc(&stream->stats.packets); + atomic_uint64_add(&stream->stats.bytes, s->len); + atomic_uint64_set(&stream->last_packet, poller_now); + atomic_uint64_inc(&cm->statsps.packets); + atomic_uint64_add(&cm->statsps.bytes, s->len); out: if (ret == 0 && update) @@ -1078,7 +1071,7 @@ no_sfd: tmp_t_reason = 2; } - if (poller_now - ps->last_packet < check) + if (poller_now - atomic_uint64_get(&ps->last_packet) < check) good = 1; next: @@ -1288,16 +1281,14 @@ destroy: #define DS(x) do { \ - mutex_lock(&ps->in_lock); \ - if (ke->stats.x < ps->kernel_stats.x) \ + u_int64_t ks_val, d; \ + ks_val = atomic_uint64_get(&ps->kernel_stats.x); \ + if (ke->stats.x < ks_val) \ d = 0; \ else \ - d = ke->stats.x - ps->kernel_stats.x; \ - ps->stats.x += d; \ - mutex_unlock(&ps->in_lock); \ - mutex_lock(&m->statspslock); \ - m->statsps.x += d; \ - mutex_unlock(&m->statspslock); \ + d = ke->stats.x - ks_val; \ + atomic_uint64_add(&ps->stats.x, d); \ + atomic_uint64_add(&m->statsps.x, d); \ } while (0) static void callmaster_timer(void *ptr) { struct callmaster *m = ptr; @@ -1305,7 +1296,6 @@ static void callmaster_timer(void *ptr) { GList *i; struct rtpengine_list_entry *ke; struct packet_stream *ps, *sink; - u_int64_t d; struct stats tmpstats; int j, update; struct stream_fd *sfd; @@ -1316,13 +1306,13 @@ static void callmaster_timer(void *ptr) { g_hash_table_foreach(m->callhash, call_timer_iterator, &hlp); rwlock_unlock_r(&m->hashlock); - mutex_lock(&m->statspslock); - memcpy(&tmpstats, &m->statsps, sizeof(tmpstats)); - ZERO(m->statsps); - mutex_unlock(&m->statspslock); - mutex_lock(&m->statslock); - memcpy(&m->stats, &tmpstats, sizeof(m->stats)); - mutex_unlock(&m->statslock); + atomic_uint64_set_na(&tmpstats.bytes, atomic_uint64_get_set(&m->statsps.bytes, 0)); + atomic_uint64_set_na(&tmpstats.packets, atomic_uint64_get_set(&m->statsps.packets, 0)); + atomic_uint64_set_na(&tmpstats.errors, atomic_uint64_get_set(&m->statsps.errors, 0)); + + atomic_uint64_set(&m->stats.bytes, atomic_uint64_get_na(&tmpstats.bytes)); + atomic_uint64_set(&m->stats.packets, atomic_uint64_get_na(&tmpstats.packets)); + atomic_uint64_set(&m->stats.errors, atomic_uint64_get_na(&tmpstats.errors)); i = (m->conf.kernelid >= 0) ? kernel_list(m->conf.kernelid) : NULL; while (i) { @@ -1346,12 +1336,12 @@ static void callmaster_timer(void *ptr) { mutex_lock(&ps->in_lock); - if (ke->stats.packets != ps->kernel_stats.packets) - ps->last_packet = poller_now; + if (ke->stats.packets != atomic_uint64_get(&ps->kernel_stats.packets)) + atomic_uint64_set(&ps->last_packet, poller_now); - ps->kernel_stats.packets = ke->stats.packets; - ps->kernel_stats.bytes = ke->stats.bytes; - ps->kernel_stats.errors = ke->stats.errors; + atomic_uint64_set(&ps->kernel_stats.bytes, ke->stats.bytes); + atomic_uint64_set(&ps->kernel_stats.packets, ke->stats.packets); + atomic_uint64_set(&ps->kernel_stats.errors, ke->stats.errors); update = 0; @@ -1764,7 +1754,7 @@ struct packet_stream *__packet_stream_new(struct call *call) { mutex_init(&stream->in_lock); mutex_init(&stream->out_lock); stream->call = call; - stream->last_packet = poller_now; + atomic_uint64_set_na(&stream->last_packet, poller_now); call->streams = g_slist_prepend(call->streams, stream); return stream; @@ -2498,10 +2488,14 @@ void call_destroy(struct call *c) { 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, (unsigned long long) ps->stats.packets, - cdrlinecnt, md->index, protocol, (unsigned long long) ps->stats.bytes, - cdrlinecnt, md->index, protocol, (unsigned long long) ps->stats.errors, - cdrlinecnt, md->index, protocol, (unsigned long long) ps->last_packet); + cdrlinecnt, md->index, protocol, + (unsigned long long) atomic_uint64_get(&ps->stats.packets), + cdrlinecnt, md->index, protocol, + (unsigned long long) atomic_uint64_get(&ps->stats.bytes), + cdrlinecnt, md->index, protocol, + (unsigned long long) atomic_uint64_get(&ps->stats.errors), + cdrlinecnt, md->index, protocol, + (unsigned long long) atomic_uint64_get(&ps->last_packet)); } ilog(LOG_INFO, "------ Media #%u, port %5u <> %15s:%-5hu%s, " @@ -2510,16 +2504,16 @@ void call_destroy(struct call *c) { (unsigned int) (ps->sfd ? ps->sfd->fd.localport : 0), addr, ps->endpoint.port, (!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) ? " (RTCP)" : "", - (unsigned long long) ps->stats.packets, - (unsigned long long) ps->stats.bytes, - (unsigned long long) ps->stats.errors, - (unsigned long long) ps->last_packet); + (unsigned long long) atomic_uint64_get(&ps->stats.packets), + (unsigned long long) atomic_uint64_get(&ps->stats.bytes), + (unsigned long long) atomic_uint64_get(&ps->stats.errors), + (unsigned long long) atomic_uint64_get(&ps->last_packet)); mutex_lock(&m->totalstats_lock); - m->totalstats.total_relayed_packets += (unsigned long long) ps->stats.packets; - m->totalstats_interval.total_relayed_packets += (unsigned long long) ps->stats.packets; - m->totalstats.total_relayed_errors += (unsigned long long) ps->stats.errors; - m->totalstats_interval.total_relayed_errors += (unsigned long long) ps->stats.errors; + m->totalstats.total_relayed_packets += atomic_uint64_get(&ps->stats.packets); + m->totalstats_interval.total_relayed_packets += atomic_uint64_get(&ps->stats.packets); + m->totalstats.total_relayed_errors += atomic_uint64_get(&ps->stats.errors); + m->totalstats_interval.total_relayed_errors += atomic_uint64_get(&ps->stats.errors); mutex_unlock(&m->totalstats_lock); } } @@ -2569,9 +2563,9 @@ void call_destroy(struct call *c) { } } - if (ps && ps2 && ps2->stats.packets==0) { + if (ps && ps2 && atomic_uint64_get(&ps2->stats.packets)==0) { mutex_lock(&m->totalstats_lock); - if (ps->stats.packets!=0) { + if (atomic_uint64_get(&ps->stats.packets)!=0) { m->totalstats.total_oneway_stream_sess++; m->totalstats_interval.total_oneway_stream_sess++; } diff --git a/daemon/call.h b/daemon/call.h index 8be512487..b1954b88c 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -12,6 +12,7 @@ #include #include "compat.h" #include "control_ng.h" +#include "aux.h" enum termination_reason { UNKNOWN=0, @@ -187,9 +188,9 @@ extern const struct transport_protocol transport_protocols[]; struct stats { - u_int64_t packets; - u_int64_t bytes; - u_int64_t errors; + atomic_uint64 packets; + atomic_uint64 bytes; + atomic_uint64 errors; }; struct totalstats { @@ -268,9 +269,9 @@ struct packet_stream { struct endpoint advertised_endpoint; /* RO */ struct crypto_context crypto; /* OUT direction, LOCK: out_lock */ - struct stats stats; /* LOCK: in_lock */ - struct stats kernel_stats; /* LOCK: in_lock */ - time_t last_packet; /* LOCK: in_lock */ + struct stats stats; + struct stats kernel_stats; + atomic_uint64 last_packet; #if RTP_LOOP_PROTECT /* LOCK: in_lock: */ @@ -406,9 +407,7 @@ struct callmaster { BIT_ARRAY_DECLARE(ports_used, 0x10000); /* XXX rework these */ - mutex_t statspslock; struct stats statsps; /* per second stats, running timer */ - mutex_t statslock; struct stats stats; /* copied from statsps once a second */ mutex_t totalstats_lock; /* for both of them */ struct totalstats totalstats; diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 67ce881f5..651b80a71 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -386,8 +386,8 @@ str *call_query_udp(char **out, struct callmaster *m) { ret = str_sprintf("%s %lld "UINT64F" "UINT64F" "UINT64F" "UINT64F"\n", out[RE_UDP_COOKIE], (long long int) m->conf.silent_timeout - (poller_now - stats.last_packet), - stats.totals[0].packets, stats.totals[1].packets, - stats.totals[2].packets, stats.totals[3].packets); + atomic_uint64_get_na(&stats.totals[0].packets), atomic_uint64_get_na(&stats.totals[1].packets), + atomic_uint64_get_na(&stats.totals[2].packets), atomic_uint64_get_na(&stats.totals[3].packets)); goto out; err: @@ -431,20 +431,14 @@ static void call_status_iterator(struct call *c, struct control_stream *s) { } void calls_status_tcp(struct callmaster *m, struct control_stream *s) { - struct stats st; GQueue q = G_QUEUE_INIT; struct call *c; - mutex_lock(&m->statslock); - st = m->stats; - mutex_unlock(&m->statslock); - callmaster_get_all_calls(m, &q); - control_stream_printf(s, "proxy %u "UINT64F"/"UINT64F"/"UINT64F"\n", + control_stream_printf(s, "proxy %u "UINT64F"/%i/%i\n", g_queue_get_length(&q), - st.bytes, st.bytes - st.errors, - st.bytes * 2 - st.errors); + atomic_uint64_get(&m->stats.bytes), 0, 0); while (q.head) { c = g_queue_pop_head(&q); @@ -726,14 +720,14 @@ const char *call_delete_ng(bencode_item_t *input, struct callmaster *m, bencode_ } static void ng_stats(bencode_item_t *d, const struct stats *s, struct stats *totals) { - bencode_dictionary_add_integer(d, "packets", s->packets); - bencode_dictionary_add_integer(d, "bytes", s->bytes); - bencode_dictionary_add_integer(d, "errors", s->errors); + bencode_dictionary_add_integer(d, "packets", atomic_uint64_get(&s->packets)); + bencode_dictionary_add_integer(d, "bytes", atomic_uint64_get(&s->bytes)); + bencode_dictionary_add_integer(d, "errors", atomic_uint64_get(&s->errors)); if (!totals) return; - totals->packets += s->packets; - totals->bytes += s->bytes; - totals->errors += s->errors; + atomic_uint64_add_na(&totals->packets, atomic_uint64_get(&s->packets)); + atomic_uint64_add_na(&totals->bytes, atomic_uint64_get(&s->bytes)); + atomic_uint64_add_na(&totals->errors, atomic_uint64_get(&s->errors)); } static void ng_stats_endpoint(bencode_item_t *dict, const struct endpoint *ep) { @@ -772,7 +766,7 @@ static void ng_stats_stream(bencode_item_t *list, const struct packet_stream *ps if (ps->crypto.params.crypto_suite) bencode_dictionary_add_string(dict, "crypto suite", ps->crypto.params.crypto_suite->name); - bencode_dictionary_add_integer(dict, "last packet", ps->last_packet); + bencode_dictionary_add_integer(dict, "last packet", atomic_uint64_get(&ps->last_packet)); flags = bencode_dictionary_add_list(dict, "flags"); @@ -788,8 +782,8 @@ static void ng_stats_stream(bencode_item_t *list, const struct packet_stream *ps BF_PS("media handover", MEDIA_HANDOVER); stats: - if (totals->last_packet < ps->last_packet) - totals->last_packet = ps->last_packet; + if (totals->last_packet < atomic_uint64_get(&ps->last_packet)) + totals->last_packet = atomic_uint64_get(&ps->last_packet); /* XXX distinguish between input and output */ s = &totals->totals[0]; diff --git a/daemon/cli.c b/daemon/cli.c index e6e4300ab..7941f0193 100644 --- a/daemon/cli.c +++ b/daemon/cli.c @@ -153,10 +153,10 @@ static void cli_incoming_list_callid(char* buffer, int len, struct callmaster* m (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)" : "", - (unsigned long long) ps->stats.packets, - (unsigned long long) ps->stats.bytes, - (unsigned long long) ps->stats.errors, - (unsigned long long) ps->last_packet); + (unsigned long long) atomic_uint64_get(&ps->stats.packets), + (unsigned long long) atomic_uint64_get(&ps->stats.bytes), + (unsigned long long) atomic_uint64_get(&ps->stats.errors), + (unsigned long long) atomic_uint64_get(&ps->last_packet)); ADJUSTLEN(printlen,outbufend,replybuffer); } } From 28c6db9e5300e8d511d0d2fadae5081bb271b2aa Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Mon, 9 Feb 2015 10:09:51 -0500 Subject: [PATCH 03/82] fix references and locking in CLI --- daemon/cli.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/daemon/cli.c b/daemon/cli.c index e6e4300ab..37e9a6aad 100644 --- a/daemon/cli.c +++ b/daemon/cli.c @@ -165,6 +165,7 @@ static void cli_incoming_list_callid(char* buffer, int len, struct callmaster* m ADJUSTLEN(printlen,outbufend,replybuffer); rwlock_unlock_w(&c->master_lock); // because of call_get(..) + obj_put(c); } static void cli_incoming_list(char* buffer, int len, struct callmaster* m, char* replybuffer, const char* outbufend) { @@ -274,13 +275,15 @@ static void cli_incoming_terminate(char* buffer, int len, struct callmaster* m, ml->term_reason = FORCED; } } - call_destroy(c); printlen = snprintf(replybuffer, outbufend-replybuffer, "\nCall Id (%s) successfully terminated by operator.\n\n",termparam.s); ADJUSTLEN(printlen,outbufend,replybuffer); ilog(LOG_WARN, "Call Id (%s) successfully terminated by operator.",termparam.s); rwlock_unlock_w(&c->master_lock); + + call_destroy(c); + obj_put(c); } static void cli_incoming(int fd, void *p, uintptr_t u) { From 20fd255acc791371b35689cab4a5786512e31500 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Mon, 9 Feb 2015 10:15:01 -0500 Subject: [PATCH 04/82] use UINT64F instead of %llu where appropriate --- daemon/call.c | 20 ++++++++++---------- daemon/cli.c | 26 +++++++++++++------------- daemon/graphite.c | 18 +++++++++--------- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index 990f45d60..a54c55bef 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -2481,32 +2481,32 @@ void call_destroy(struct call *c) { "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=%llu, " - "ml%i_midx%u_%s_relayed_bytes=%llu, " - "ml%i_midx%u_%s_relayed_errors=%llu, " + "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=%llu, ", 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, - (unsigned long long) atomic_uint64_get(&ps->stats.packets), + atomic_uint64_get(&ps->stats.packets), cdrlinecnt, md->index, protocol, - (unsigned long long) atomic_uint64_get(&ps->stats.bytes), + atomic_uint64_get(&ps->stats.bytes), cdrlinecnt, md->index, protocol, - (unsigned long long) atomic_uint64_get(&ps->stats.errors), + atomic_uint64_get(&ps->stats.errors), cdrlinecnt, md->index, protocol, (unsigned long long) atomic_uint64_get(&ps->last_packet)); } ilog(LOG_INFO, "------ Media #%u, port %5u <> %15s:%-5hu%s, " - "%llu p, %llu b, %llu e, %llu last_packet", + ""UINT64F" p, "UINT64F" b, "UINT64F" e, %llu last_packet", md->index, (unsigned int) (ps->sfd ? ps->sfd->fd.localport : 0), addr, ps->endpoint.port, (!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) ? " (RTCP)" : "", - (unsigned long long) atomic_uint64_get(&ps->stats.packets), - (unsigned long long) atomic_uint64_get(&ps->stats.bytes), - (unsigned long long) atomic_uint64_get(&ps->stats.errors), + atomic_uint64_get(&ps->stats.packets), + atomic_uint64_get(&ps->stats.bytes), + atomic_uint64_get(&ps->stats.errors), (unsigned long long) atomic_uint64_get(&ps->last_packet)); mutex_lock(&m->totalstats_lock); diff --git a/daemon/cli.c b/daemon/cli.c index 7941f0193..afb376507 100644 --- a/daemon/cli.c +++ b/daemon/cli.c @@ -32,23 +32,23 @@ static void cli_incoming_list_totals(char* buffer, int len, struct callmaster* m ADJUSTLEN(printlen,outbufend,replybuffer); printlen = snprintf(replybuffer,(outbufend-replybuffer), " Uptime of rtpengine :%llu seconds\n", (unsigned long long)time(NULL)-m->totalstats.started); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total managed sessions :%llu\n", (unsigned long long)m->totalstats.total_managed_sess); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total managed sessions :"UINT64F"\n", m->totalstats.total_managed_sess); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total timed-out sessions via TIMEOUT :%llu\n",(unsigned long long)m->totalstats.total_timeout_sess); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total timed-out sessions via TIMEOUT :"UINT64F"\n",m->totalstats.total_timeout_sess); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total timed-out sessions via SILENT_TIMEOUT :%llu\n",(unsigned long long)m->totalstats.total_silent_timeout_sess); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total timed-out sessions via SILENT_TIMEOUT :"UINT64F"\n",m->totalstats.total_silent_timeout_sess); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total regular terminated sessions :%llu\n",(unsigned long long)m->totalstats.total_regular_term_sess); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total regular terminated sessions :"UINT64F"\n",m->totalstats.total_regular_term_sess); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total forced terminated sessions :%llu\n",(unsigned long long)m->totalstats.total_forced_term_sess); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total forced terminated sessions :"UINT64F"\n",m->totalstats.total_forced_term_sess); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total relayed packets :%llu\n",(unsigned long long)m->totalstats.total_relayed_packets); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total relayed packets :"UINT64F"\n",m->totalstats.total_relayed_packets); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total relayed packet errors :%llu\n",(unsigned long long)m->totalstats.total_relayed_errors); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total relayed packet errors :"UINT64F"\n",m->totalstats.total_relayed_errors); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total number of streams with no relayed packets :%llu\n", (unsigned long long)m->totalstats.total_nopacket_relayed_sess); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total number of streams with no relayed packets :"UINT64F"\n", m->totalstats.total_nopacket_relayed_sess); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total number of 1-way streams :%llu\n",(unsigned long long)m->totalstats.total_oneway_stream_sess); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total number of 1-way streams :"UINT64F"\n",m->totalstats.total_oneway_stream_sess); ADJUSTLEN(printlen,outbufend,replybuffer); printlen = snprintf(replybuffer,(outbufend-replybuffer), " Average call duration :%ld.%06ld\n\n",m->totalstats.total_average_call_dur.tv_sec,m->totalstats.total_average_call_dur.tv_usec); ADJUSTLEN(printlen,outbufend,replybuffer); @@ -148,14 +148,14 @@ static void cli_incoming_list_callid(char* buffer, int len, struct callmaster* m continue; printlen = snprintf(replybuffer,(outbufend-replybuffer), "------ Media #%u, port %5u <> %15s:%-5hu%s, " - "%llu p, %llu b, %llu e, %llu last_packet\n", + ""UINT64F" p, "UINT64F" b, "UINT64F" e, %llu 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)" : "", - (unsigned long long) atomic_uint64_get(&ps->stats.packets), - (unsigned long long) atomic_uint64_get(&ps->stats.bytes), - (unsigned long long) atomic_uint64_get(&ps->stats.errors), + atomic_uint64_get(&ps->stats.packets), + atomic_uint64_get(&ps->stats.bytes), + atomic_uint64_get(&ps->stats.errors), (unsigned long long) atomic_uint64_get(&ps->last_packet)); ADJUSTLEN(printlen,outbufend,replybuffer); } diff --git a/daemon/graphite.c b/daemon/graphite.c index f886584af..482dae3a1 100644 --- a/daemon/graphite.c +++ b/daemon/graphite.c @@ -91,15 +91,15 @@ int send_graphite_data() { rc = sprintf(ptr,"%s.totals.average_call_dur.tv_sec %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_average_call_dur.tv_sec,(unsigned long long)g_now); ptr += rc; rc = sprintf(ptr,"%s.totals.average_call_dur.tv_usec %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_average_call_dur.tv_usec,(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.forced_term_sess %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_forced_term_sess,(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.managed_sess %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_managed_sess,(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.nopacket_relayed_sess %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_nopacket_relayed_sess,(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.oneway_stream_sess %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_oneway_stream_sess,(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.regular_term_sess %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_regular_term_sess,(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.relayed_errors %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_relayed_errors,(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.relayed_packets %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_relayed_packets,(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.silent_timeout_sess %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_silent_timeout_sess,(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.timeout_sess %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_timeout_sess,(unsigned long long)g_now); ptr += rc; + rc = sprintf(ptr,"%s.totals.forced_term_sess "UINT64F" %llu\n",hostname, cm->totalstats_interval.total_forced_term_sess,(unsigned long long)g_now); ptr += rc; + rc = sprintf(ptr,"%s.totals.managed_sess "UINT64F" %llu\n",hostname, cm->totalstats_interval.total_managed_sess,(unsigned long long)g_now); ptr += rc; + rc = sprintf(ptr,"%s.totals.nopacket_relayed_sess "UINT64F" %llu\n",hostname, cm->totalstats_interval.total_nopacket_relayed_sess,(unsigned long long)g_now); ptr += rc; + rc = sprintf(ptr,"%s.totals.oneway_stream_sess "UINT64F" %llu\n",hostname, cm->totalstats_interval.total_oneway_stream_sess,(unsigned long long)g_now); ptr += rc; + rc = sprintf(ptr,"%s.totals.regular_term_sess "UINT64F" %llu\n",hostname, cm->totalstats_interval.total_regular_term_sess,(unsigned long long)g_now); ptr += rc; + rc = sprintf(ptr,"%s.totals.relayed_errors "UINT64F" %llu\n",hostname, cm->totalstats_interval.total_relayed_errors,(unsigned long long)g_now); ptr += rc; + rc = sprintf(ptr,"%s.totals.relayed_packets "UINT64F" %llu\n",hostname, cm->totalstats_interval.total_relayed_packets,(unsigned long long)g_now); ptr += rc; + rc = sprintf(ptr,"%s.totals.silent_timeout_sess "UINT64F" %llu\n",hostname, cm->totalstats_interval.total_silent_timeout_sess,(unsigned long long)g_now); ptr += rc; + rc = sprintf(ptr,"%s.totals.timeout_sess "UINT64F" %llu\n",hostname, cm->totalstats_interval.total_timeout_sess,(unsigned long long)g_now); ptr += rc; ZERO(cm->totalstats_interval); From 3bc1672a0e302a527a753fe2d68794746ef5225d Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Mon, 9 Feb 2015 11:52:06 -0500 Subject: [PATCH 05/82] more atomic stats, and fix average length calc --- daemon/aux.h | 5 +++ daemon/call.c | 107 +++++++++++++++++++++++++--------------------- daemon/call.h | 23 +++++----- daemon/cli.c | 29 +++++++------ daemon/graphite.c | 47 ++++++++++++-------- 5 files changed, 121 insertions(+), 90 deletions(-) diff --git a/daemon/aux.h b/daemon/aux.h index 3552393da..b5092526d 100644 --- a/daemon/aux.h +++ b/daemon/aux.h @@ -547,6 +547,11 @@ INLINE u_int64_t atomic_uint64_get_set(atomic_uint64 *u, u_int64_t a) { return old; } while (1); } +INLINE void atomic_uint64_local_copy_zero(atomic_uint64 *dst, atomic_uint64 *src) { + atomic_uint64_set_na(dst, atomic_uint64_get_set(src, 0)); +} +#define atomic_uint64_local_copy_zero_struct(d, s, member) \ + atomic_uint64_local_copy_zero(&((d)->member), &((s)->member)) #endif diff --git a/daemon/call.c b/daemon/call.c index a54c55bef..b81755b88 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -1086,7 +1086,6 @@ next: for (i = c->monologues; i; i = i->next) { ml = i->data; - memset(&ml->terminated,0,sizeof(struct timeval)); gettimeofday(&(ml->terminated),NULL); if (tmp_t_reason==1) { ml->term_reason = TIMEOUT; @@ -1306,9 +1305,9 @@ static void callmaster_timer(void *ptr) { g_hash_table_foreach(m->callhash, call_timer_iterator, &hlp); rwlock_unlock_r(&m->hashlock); - atomic_uint64_set_na(&tmpstats.bytes, atomic_uint64_get_set(&m->statsps.bytes, 0)); - atomic_uint64_set_na(&tmpstats.packets, atomic_uint64_get_set(&m->statsps.packets, 0)); - atomic_uint64_set_na(&tmpstats.errors, atomic_uint64_get_set(&m->statsps.errors, 0)); + atomic_uint64_local_copy_zero_struct(&tmpstats, &m->statsps, bytes); + atomic_uint64_local_copy_zero_struct(&tmpstats, &m->statsps, packets); + atomic_uint64_local_copy_zero_struct(&tmpstats, &m->statsps, errors); atomic_uint64_set(&m->stats.bytes, atomic_uint64_get_na(&tmpstats.bytes)); atomic_uint64_set(&m->stats.packets, atomic_uint64_get_na(&tmpstats.packets)); @@ -1413,7 +1412,8 @@ struct callmaster *callmaster_new(struct poller *p) { poller_add_timer(p, callmaster_timer, &c->obj); - mutex_init(&c->totalstats_lock); + mutex_init(&c->totalstats.total_average_lock); + mutex_init(&c->totalstats_interval.total_average_lock); c->totalstats.started = poller_now; mutex_init(&c->cngs_lock); @@ -2379,9 +2379,9 @@ void timeval_multiply(struct timeval *result, const struct timeval *a, const lon result->tv_usec = microseconds%(long)1000000; } -void timeval_devide(struct timeval *result, const struct timeval *a, const long devisor) { +void timeval_divide(struct timeval *result, const struct timeval *a, const long divisor) { long microseconds=0; - microseconds = (((long)a->tv_sec * 1000000) + (long)a->tv_usec) / devisor; + microseconds = (((long)a->tv_sec * 1000000) + (long)a->tv_usec) / divisor; result->tv_sec = microseconds/(long)1000000; result->tv_usec = microseconds%(long)1000000; } @@ -2393,6 +2393,31 @@ void timeval_add(struct timeval *result, const struct timeval *a, const struct t result->tv_usec = microseconds%(long)1000000; } +static void timeval_totalstats_average_add(struct totalstats *s, const struct timeval *add) { + struct timeval dp, oa; + + mutex_lock(&s->total_average_lock); + + // new average = ((old average * old num sessions) + datapoint) / new num sessions + // ... but this will overflow when num sessions becomes very large + + // timeval_multiply(&t, &s->total_average_call_dur, s->total_managed_sess); + // timeval_add(&t, &t, add); + // s->total_managed_sess++; + // timeval_divide(&s->total_average_call_dur, &t, s->total_managed_sess); + + // alternative: + // new average = old average + (datapoint / new num sessions) - (old average / new num sessions) + + s->total_managed_sess++; + timeval_divide(&dp, add, s->total_managed_sess); + timeval_divide(&oa, &s->total_average_call_dur, s->total_managed_sess); + timeval_add(&s->total_average_call_dur, &s->total_average_call_dur, &dp); + timeval_subtract(&s->total_average_call_dur, &s->total_average_call_dur, &oa); + + mutex_unlock(&s->total_average_lock); +} + /* called lock-free, but must hold a reference to the call */ void call_destroy(struct call *c) { struct callmaster *m = c->callmaster; @@ -2437,9 +2462,8 @@ void call_destroy(struct call *c) { cdrbufcur += sprintf(cdrbufcur,"tos=%u, ", (unsigned int)c->tos); for (l = c->monologues; l; l = l->next) { ml = l->data; + timeval_subtract(&tim_result_duration,&ml->terminated,&ml->started); if (_log_facility_cdr) { - memset(&tim_result_duration,0,sizeof(struct timeval)); - timeval_subtract(&tim_result_duration,&ml->terminated,&ml->started); cdrbufcur += sprintf(cdrbufcur, "ml%i_start_time=%ld.%06lu, " "ml%i_end_time=%ld.%06ld, " "ml%i_duration=%ld.%06ld, " @@ -2509,12 +2533,14 @@ void call_destroy(struct call *c) { atomic_uint64_get(&ps->stats.errors), (unsigned long long) atomic_uint64_get(&ps->last_packet)); - mutex_lock(&m->totalstats_lock); - m->totalstats.total_relayed_packets += atomic_uint64_get(&ps->stats.packets); - m->totalstats_interval.total_relayed_packets += atomic_uint64_get(&ps->stats.packets); - m->totalstats.total_relayed_errors += atomic_uint64_get(&ps->stats.errors); - m->totalstats_interval.total_relayed_errors += atomic_uint64_get(&ps->stats.errors); - mutex_unlock(&m->totalstats_lock); + atomic_uint64_add(&m->totalstats.total_relayed_packets, + atomic_uint64_get(&ps->stats.packets)); + atomic_uint64_add(&m->totalstats_interval.total_relayed_packets, + atomic_uint64_get(&ps->stats.packets)); + atomic_uint64_add(&m->totalstats.total_relayed_errors, + atomic_uint64_get(&ps->stats.errors)); + atomic_uint64_add(&m->totalstats_interval.total_relayed_errors, + atomic_uint64_get(&ps->stats.errors)); } } if (_log_facility_cdr) @@ -2522,10 +2548,7 @@ void call_destroy(struct call *c) { } // --- for statistics getting one way stream or no relay at all - mutex_lock(&m->totalstats_lock); - m->totalstats.total_nopacket_relayed_sess *= 2; - m->totalstats_interval.total_nopacket_relayed_sess *= 2; - mutex_unlock(&m->totalstats_lock); + int total_nopacket_relayed_sess = 0; for (l = c->monologues; l; l = l->next) { ml = l->data; @@ -2564,51 +2587,37 @@ void call_destroy(struct call *c) { } if (ps && ps2 && atomic_uint64_get(&ps2->stats.packets)==0) { - mutex_lock(&m->totalstats_lock); if (atomic_uint64_get(&ps->stats.packets)!=0) { - m->totalstats.total_oneway_stream_sess++; - m->totalstats_interval.total_oneway_stream_sess++; + atomic_uint64_inc(&m->totalstats.total_oneway_stream_sess); + atomic_uint64_inc(&m->totalstats_interval.total_oneway_stream_sess); } else { - m->totalstats.total_nopacket_relayed_sess++; - m->totalstats_interval.total_nopacket_relayed_sess++; + total_nopacket_relayed_sess++; } - mutex_unlock(&m->totalstats_lock); } } - mutex_lock(&m->totalstats_lock); - - m->totalstats.total_nopacket_relayed_sess /= 2; - m->totalstats_interval.total_nopacket_relayed_sess /= 2; - m->totalstats.total_managed_sess += 1; - m->totalstats_interval.total_managed_sess += 1; + atomic_uint64_add(&m->totalstats.total_nopacket_relayed_sess, total_nopacket_relayed_sess / 2); + atomic_uint64_add(&m->totalstats_interval.total_nopacket_relayed_sess, total_nopacket_relayed_sess / 2); ml = c->monologues->data; if (ml->term_reason==TIMEOUT) { - m->totalstats.total_timeout_sess++; - m->totalstats_interval.total_timeout_sess++; + atomic_uint64_inc(&m->totalstats.total_timeout_sess); + atomic_uint64_inc(&m->totalstats_interval.total_timeout_sess); } else if (ml->term_reason==SILENT_TIMEOUT) { - m->totalstats.total_silent_timeout_sess++; - m->totalstats_interval.total_silent_timeout_sess++; + atomic_uint64_inc(&m->totalstats.total_silent_timeout_sess); + atomic_uint64_inc(&m->totalstats_interval.total_silent_timeout_sess); } else if (ml->term_reason==REGULAR) { - m->totalstats.total_regular_term_sess++; - m->totalstats_interval.total_regular_term_sess++; + atomic_uint64_inc(&m->totalstats.total_regular_term_sess); + atomic_uint64_inc(&m->totalstats_interval.total_regular_term_sess); } else if (ml->term_reason==FORCED) { - m->totalstats.total_forced_term_sess++; - m->totalstats_interval.total_forced_term_sess++; + atomic_uint64_inc(&m->totalstats.total_forced_term_sess); + atomic_uint64_inc(&m->totalstats_interval.total_forced_term_sess); } - timeval_multiply(&m->totalstats.total_average_call_dur,&m->totalstats.total_average_call_dur,m->totalstats.total_managed_sess-1); - timeval_add(&m->totalstats.total_average_call_dur,&m->totalstats.total_average_call_dur,&tim_result_duration); - timeval_devide(&m->totalstats.total_average_call_dur,&m->totalstats.total_average_call_dur,m->totalstats.total_managed_sess); - - timeval_multiply(&m->totalstats_interval.total_average_call_dur,&m->totalstats_interval.total_average_call_dur,m->totalstats_interval.total_managed_sess-1); - timeval_add(&m->totalstats_interval.total_average_call_dur,&m->totalstats_interval.total_average_call_dur,&tim_result_duration); - timeval_devide(&m->totalstats_interval.total_average_call_dur,&m->totalstats_interval.total_average_call_dur,m->totalstats_interval.total_managed_sess); - - mutex_unlock(&m->totalstats_lock); + timeval_totalstats_average_add(&m->totalstats, &tim_result_duration); + timeval_totalstats_average_add(&m->totalstats_interval, &tim_result_duration); if (_log_facility_cdr) /* log it */ @@ -2846,6 +2855,7 @@ struct call_monologue *__monologue_create(struct call *call) { ret->created = poller_now; ret->other_tags = g_hash_table_new(str_hash, str_equal); g_queue_init(&ret->medias); + gettimeofday(&ret->started, NULL); call->monologues = g_slist_prepend(call->monologues, ret); @@ -3020,7 +3030,6 @@ int call_delete_branch(struct callmaster *m, const str *callid, const str *branc for (i = c->monologues; i; i = i->next) { ml = i->data; - memset(&ml->terminated,0,sizeof(struct timeval)); gettimeofday(&(ml->terminated), NULL); ml->term_reason = REGULAR; } diff --git a/daemon/call.h b/daemon/call.h index b1954b88c..731d80abf 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -195,16 +195,18 @@ struct stats { struct totalstats { time_t started; + atomic_uint64 total_timeout_sess; + atomic_uint64 total_silent_timeout_sess; + atomic_uint64 total_regular_term_sess; + atomic_uint64 total_forced_term_sess; + atomic_uint64 total_relayed_packets; + atomic_uint64 total_relayed_errors; + atomic_uint64 total_nopacket_relayed_sess; + atomic_uint64 total_oneway_stream_sess; + + mutex_t total_average_lock; /* for these two below */ u_int64_t total_managed_sess; - u_int64_t total_timeout_sess; - u_int64_t total_silent_timeout_sess; - u_int64_t total_regular_term_sess; - u_int64_t total_forced_term_sess; - u_int64_t total_relayed_packets; - u_int64_t total_relayed_errors; - u_int64_t total_nopacket_relayed_sess; - u_int64_t total_oneway_stream_sess; - struct timeval total_average_call_dur; + struct timeval total_average_call_dur; }; struct udp_fd { @@ -409,7 +411,6 @@ struct callmaster { /* XXX rework these */ struct stats statsps; /* per second stats, running timer */ struct stats stats; /* copied from statsps once a second */ - mutex_t totalstats_lock; /* for both of them */ struct totalstats totalstats; struct totalstats totalstats_interval; /* control_ng_stats stuff */ @@ -468,7 +469,7 @@ const struct transport_protocol *transport_protocol(const str *s); void timeval_subtract (struct timeval *result, const struct timeval *a, const struct timeval *b); void timeval_multiply(struct timeval *result, const struct timeval *a, const long multiplier); -void timeval_devide(struct timeval *result, const struct timeval *a, const long devisor); +void timeval_divide(struct timeval *result, const struct timeval *a, const long divisor); void timeval_add(struct timeval *result, const struct timeval *a, const struct timeval *b); diff --git a/daemon/cli.c b/daemon/cli.c index afb376507..010656a69 100644 --- a/daemon/cli.c +++ b/daemon/cli.c @@ -25,36 +25,39 @@ static const char* TRUNCATED = " ... Output truncated. Increase Output Buffer static void cli_incoming_list_totals(char* buffer, int len, struct callmaster* m, char* replybuffer, const char* outbufend) { int printlen=0; + struct timeval avg; + u_int64_t num_sessions; - mutex_lock(&m->totalstats_lock); + mutex_lock(&m->totalstats.total_average_lock); + avg = m->totalstats.total_average_call_dur; + num_sessions = m->totalstats.total_managed_sess; + mutex_unlock(&m->totalstats.total_average_lock); printlen = snprintf(replybuffer,(outbufend-replybuffer), "\nTotal statistics (does not include current running sessions):\n\n"); ADJUSTLEN(printlen,outbufend,replybuffer); printlen = snprintf(replybuffer,(outbufend-replybuffer), " Uptime of rtpengine :%llu seconds\n", (unsigned long long)time(NULL)-m->totalstats.started); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total managed sessions :"UINT64F"\n", m->totalstats.total_managed_sess); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total managed sessions :"UINT64F"\n", num_sessions); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total timed-out sessions via TIMEOUT :"UINT64F"\n",m->totalstats.total_timeout_sess); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total timed-out sessions via TIMEOUT :"UINT64F"\n",atomic_uint64_get(&m->totalstats.total_timeout_sess)); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total timed-out sessions via SILENT_TIMEOUT :"UINT64F"\n",m->totalstats.total_silent_timeout_sess); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total timed-out sessions via SILENT_TIMEOUT :"UINT64F"\n",atomic_uint64_get(&m->totalstats.total_silent_timeout_sess)); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total regular terminated sessions :"UINT64F"\n",m->totalstats.total_regular_term_sess); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total regular terminated sessions :"UINT64F"\n",atomic_uint64_get(&m->totalstats.total_regular_term_sess)); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total forced terminated sessions :"UINT64F"\n",m->totalstats.total_forced_term_sess); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total forced terminated sessions :"UINT64F"\n",atomic_uint64_get(&m->totalstats.total_forced_term_sess)); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total relayed packets :"UINT64F"\n",m->totalstats.total_relayed_packets); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total relayed packets :"UINT64F"\n",atomic_uint64_get(&m->totalstats.total_relayed_packets)); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total relayed packet errors :"UINT64F"\n",m->totalstats.total_relayed_errors); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total relayed packet errors :"UINT64F"\n",atomic_uint64_get(&m->totalstats.total_relayed_errors)); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total number of streams with no relayed packets :"UINT64F"\n", m->totalstats.total_nopacket_relayed_sess); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total number of streams with no relayed packets :"UINT64F"\n", atomic_uint64_get(&m->totalstats.total_nopacket_relayed_sess)); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total number of 1-way streams :"UINT64F"\n",m->totalstats.total_oneway_stream_sess); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total number of 1-way streams :"UINT64F"\n",atomic_uint64_get(&m->totalstats.total_oneway_stream_sess)); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Average call duration :%ld.%06ld\n\n",m->totalstats.total_average_call_dur.tv_sec,m->totalstats.total_average_call_dur.tv_usec); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Average call duration :%ld.%06ld\n\n",avg.tv_sec,avg.tv_usec); ADJUSTLEN(printlen,outbufend,replybuffer); - mutex_unlock(&m->totalstats_lock); - printlen = snprintf(replybuffer,(outbufend-replybuffer), "Control statistics:\n\n"); ADJUSTLEN(printlen,outbufend,replybuffer); printlen = snprintf(replybuffer,(outbufend-replybuffer), " %10s | %10s | %10s | %10s | %10s | %10s | %10s | %10s \n", diff --git a/daemon/graphite.c b/daemon/graphite.c index 482dae3a1..1c76ccfbb 100644 --- a/daemon/graphite.c +++ b/daemon/graphite.c @@ -87,23 +87,36 @@ int send_graphite_data() { char data_to_send[8192]; memset(&data_to_send,0,8192); char* ptr = data_to_send; - mutex_lock(&cm->totalstats_lock); - - rc = sprintf(ptr,"%s.totals.average_call_dur.tv_sec %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_average_call_dur.tv_sec,(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.average_call_dur.tv_usec %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_average_call_dur.tv_usec,(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.forced_term_sess "UINT64F" %llu\n",hostname, cm->totalstats_interval.total_forced_term_sess,(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.managed_sess "UINT64F" %llu\n",hostname, cm->totalstats_interval.total_managed_sess,(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.nopacket_relayed_sess "UINT64F" %llu\n",hostname, cm->totalstats_interval.total_nopacket_relayed_sess,(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.oneway_stream_sess "UINT64F" %llu\n",hostname, cm->totalstats_interval.total_oneway_stream_sess,(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.regular_term_sess "UINT64F" %llu\n",hostname, cm->totalstats_interval.total_regular_term_sess,(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.relayed_errors "UINT64F" %llu\n",hostname, cm->totalstats_interval.total_relayed_errors,(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.relayed_packets "UINT64F" %llu\n",hostname, cm->totalstats_interval.total_relayed_packets,(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.silent_timeout_sess "UINT64F" %llu\n",hostname, cm->totalstats_interval.total_silent_timeout_sess,(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.timeout_sess "UINT64F" %llu\n",hostname, cm->totalstats_interval.total_timeout_sess,(unsigned long long)g_now); ptr += rc; - - ZERO(cm->totalstats_interval); - - mutex_unlock(&cm->totalstats_lock); + struct totalstats ts; + + /* atomically copy values to stack and reset to zero */ + atomic_uint64_local_copy_zero_struct(&ts, &cm->totalstats_interval, total_timeout_sess); + atomic_uint64_local_copy_zero_struct(&ts, &cm->totalstats_interval, total_silent_timeout_sess); + atomic_uint64_local_copy_zero_struct(&ts, &cm->totalstats_interval, total_regular_term_sess); + atomic_uint64_local_copy_zero_struct(&ts, &cm->totalstats_interval, total_forced_term_sess); + atomic_uint64_local_copy_zero_struct(&ts, &cm->totalstats_interval, total_relayed_packets); + atomic_uint64_local_copy_zero_struct(&ts, &cm->totalstats_interval, total_relayed_errors); + atomic_uint64_local_copy_zero_struct(&ts, &cm->totalstats_interval, total_nopacket_relayed_sess); + atomic_uint64_local_copy_zero_struct(&ts, &cm->totalstats_interval, total_oneway_stream_sess); + + 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); + mutex_unlock(&cm->totalstats_interval.total_average_lock); + + 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); ptr += rc; + rc = sprintf(ptr,"%s.totals.average_call_dur.tv_usec %llu %llu\n",hostname, (unsigned long long) ts.total_average_call_dur.tv_usec,(unsigned long long)g_now); ptr += rc; + rc = sprintf(ptr,"%s.totals.forced_term_sess "UINT64F" %llu\n",hostname, atomic_uint64_get_na(&ts.total_forced_term_sess),(unsigned long long)g_now); ptr += rc; + rc = sprintf(ptr,"%s.totals.managed_sess "UINT64F" %llu\n",hostname, ts.total_managed_sess,(unsigned long long)g_now); ptr += rc; + rc = sprintf(ptr,"%s.totals.nopacket_relayed_sess "UINT64F" %llu\n",hostname, atomic_uint64_get_na(&ts.total_nopacket_relayed_sess),(unsigned long long)g_now); ptr += rc; + rc = sprintf(ptr,"%s.totals.oneway_stream_sess "UINT64F" %llu\n",hostname, atomic_uint64_get_na(&ts.total_oneway_stream_sess),(unsigned long long)g_now); ptr += rc; + rc = sprintf(ptr,"%s.totals.regular_term_sess "UINT64F" %llu\n",hostname, atomic_uint64_get_na(&ts.total_regular_term_sess),(unsigned long long)g_now); ptr += rc; + rc = sprintf(ptr,"%s.totals.relayed_errors "UINT64F" %llu\n",hostname, atomic_uint64_get_na(&ts.total_relayed_errors),(unsigned long long)g_now); ptr += rc; + rc = sprintf(ptr,"%s.totals.relayed_packets "UINT64F" %llu\n",hostname, atomic_uint64_get_na(&ts.total_relayed_packets),(unsigned long long)g_now); ptr += rc; + rc = sprintf(ptr,"%s.totals.silent_timeout_sess "UINT64F" %llu\n",hostname, atomic_uint64_get_na(&ts.total_silent_timeout_sess),(unsigned long long)g_now); ptr += rc; + rc = sprintf(ptr,"%s.totals.timeout_sess "UINT64F" %llu\n",hostname, atomic_uint64_get_na(&ts.total_timeout_sess),(unsigned long long)g_now); ptr += rc; rc = write(graphite_sock, data_to_send, strlen(data_to_send)); if (rc<0) { From fedaaddf033aff32a322109e6948b5070c5b2a7b Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Mon, 9 Feb 2015 12:49:14 -0500 Subject: [PATCH 06/82] remove superfluous \n from log messages --- daemon/cli.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/daemon/cli.c b/daemon/cli.c index 010656a69..ca5ad72e9 100644 --- a/daemon/cli.c +++ b/daemon/cli.c @@ -309,7 +309,7 @@ next: sprintf(replybuffer, "Could currently not accept CLI commands. Reason:%s\n", strerror(errno)); goto cleanup; } - ilog(LOG_INFO, "Accept error:%s\n", strerror(errno)); + ilog(LOG_INFO, "Accept error:%s", strerror(errno)); goto next; } @@ -319,15 +319,15 @@ next: readbytes = read(nfd, inbuf+inlen, MAXINPUT); if (readbytes == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { - ilog(LOG_INFO, "Could currently not read CLI commands. Reason:%s\n", strerror(errno)); + ilog(LOG_INFO, "Could currently not read CLI commands. Reason:%s", strerror(errno)); goto cleanup; } - ilog(LOG_INFO, "Could currently not read CLI commands. Reason:%s\n", strerror(errno)); + ilog(LOG_INFO, "Could currently not read CLI commands. Reason:%s", strerror(errno)); } inlen += readbytes; } while (readbytes > 0); - ilog(LOG_INFO, "Got CLI command:%s\n",inbuf); + ilog(LOG_INFO, "Got CLI command:%s",inbuf); static const char* LIST = "list"; static const char* TERMINATE = "terminate"; From 1e3f06a46f841587f9d1e633b256e1b57037ad01 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Mon, 9 Feb 2015 14:33:09 -0500 Subject: [PATCH 07/82] split some perl SRTP stuff into module and add debug script --- tests/simulator-ng.pl | 271 ++++---------------------------------- utils/SRTP.pm | 281 ++++++++++++++++++++++++++++++++++++++++ utils/srtp-debug-helper | 35 +++++ 3 files changed, 342 insertions(+), 245 deletions(-) create mode 100644 utils/SRTP.pm create mode 100755 utils/srtp-debug-helper diff --git a/tests/simulator-ng.pl b/tests/simulator-ng.pl index 5d34cc30b..86833b1d9 100755 --- a/tests/simulator-ng.pl +++ b/tests/simulator-ng.pl @@ -13,6 +13,7 @@ use Crypt::Rijndael; use Digest::SHA qw(hmac_sha1); use MIME::Base64; use Data::Dumper; +use SRTP; my ($NUM, $RUNTIME, $STREAMS, $PAYLOAD, $INTERVAL, $RTCP_INTERVAL, $STATS_INTERVAL) = (1000, 30, 1, 160, 20, 5, 5); @@ -105,216 +106,33 @@ sub send_receive { return $x; } -sub aes_cm { - my ($data, $key, $iv) = @_; - - my $c = Crypt::Rijndael->new($key) or die; - length($iv) == 16 or die; - my @iv = unpack("C16", $iv); - my $out = ''; - - while ($data ne '') { - $iv = pack("C16", @iv); - my $key_segment = $c->encrypt($iv); - length($key_segment) == 16 or die; - my @ks = unpack("C16", $key_segment); - my @ds = unpack("C16", $data); - - for my $i (0 .. $#ds) { - my $ss = $ds[$i]; - my $kk = $ks[$i]; - $out .= chr($ss ^ $kk); - } - - substr($data, 0, 16, ''); - $data eq '' and last; - - for my $i (reverse(0 .. 15)) { - $iv[$i]++; - if ($iv[$i] == 256) { - $iv[$i] = 0; - } - else { - last; - } - } - } - - return $out; -} - -sub aes_f8 { - my ($data, $key, $iv, $salt) = @_; - - my $m = $salt . "\x55\x55"; - my $c = Crypt::Rijndael->new(xor_128($key, $m)) or die; - my $ivx = $c->encrypt($iv); - undef($c); - - $c = Crypt::Rijndael->new($key) or die; - my $p_s = "\0" x 16; - my $j = 0; - my $out = ''; - - while ($data ne '') { - my $jx = ("\0" x 12) . pack("N", $j); - my $key_segment = $c->encrypt(xor_128($ivx, $jx, $p_s)); - length($key_segment) == 16 or die; - my @ks = unpack("C16", $key_segment); - my @ds = unpack("C16", $data); - - for my $i (0 .. $#ds) { - my $ss = $ds[$i]; - my $kk = $ks[$i]; - $out .= chr($ss ^ $kk); - } - - substr($data, 0, 16, ''); - $data eq '' and last; - - $p_s = $key_segment; - $j++; - } - - return $out; -} - - -sub prf_n { - my ($n, $key, $x) = @_; - my $d = "\0" x ($n / 8); - my $ks = aes_cm($d, $key, $x . "\0\0"); - return substr($ks, 0, $n / 8); -} - -sub xor_n { - my ($n, @l) = @_; - $n /= 8; - my @o = (0) x $n; - for my $e (@l) { - my @e = unpack("C$n", $e); - if (@e < $n) { - unshift(@e, ((0) x ($n - @e))); - } - for my $i (0 .. $#o) { - $o[$i] ^= $e[$i]; - } - } - return pack("C$n", @o); -} - -sub xor_112 { - return xor_n(112, @_); -} -sub xor_128 { - return xor_n(128, @_); -} - -sub gen_rtp_session_keys { - my ($master_key, $master_salt) = @_; - - my $session_key = prf_n(128, $master_key, xor_112($master_salt, "\0\0\0\0\0\0\0")); - my $auth_key = prf_n(160, $master_key, xor_112($master_salt, "\1\0\0\0\0\0\0")); - my $session_salt = prf_n(112, $master_key, xor_112($master_salt, "\2\0\0\0\0\0\0")); -# print("RTP keys generated for master key " . unpack("H8", $master_key) . "... and salt " . -# unpack("H8", $master_salt) . "... are: " . -# unpack("H8", $session_key) . "..., " . -# unpack("H*", $auth_key) . ", " . -# unpack("H8", $session_salt) . "...\n"); - - return ($session_key, $auth_key, $session_salt); -} - -sub gen_rtcp_session_keys { - my ($master_key, $master_salt) = @_; - - my $session_key = prf_n(128, $master_key, xor_112($master_salt, "\3\0\0\0\0\0\0")); - my $auth_key = prf_n(160, $master_key, xor_112($master_salt, "\4\0\0\0\0\0\0")); - my $session_salt = prf_n(112, $master_key, xor_112($master_salt, "\5\0\0\0\0\0\0")); -# print("RTCP keys generated for master key " . unpack("H8", $master_key) . "... and salt " . -# unpack("H8", $master_salt) . "... are: " . -# unpack("H8", $session_key) . "..., " . -# unpack("H*", $auth_key) . ", " . -# unpack("H8", $session_salt) . "...\n"); - - return ($session_key, $auth_key, $session_salt); -} - -sub aes_cm_iv_rtp { - my ($ctx, $r) = @_; - - my ($hdr, $seq, $ts, $ssrc) = unpack('a2na4a4', $r); - my $iv = xor_128($$ctx{rtp_session_salt} . "\0\0", - $ssrc . "\0\0\0\0\0\0\0\0", pack("Nnn", $$ctx{rtp_roc}, $seq, 0)); - return $iv; -} - -sub aes_cm_iv_rtcp { - my ($ctx, $r) = @_; - - my $idx = $$ctx{rtcp_index} || 0; - my ($hdr, $ssrc) = unpack('a4a4', $r); - my $iv = xor_128($$ctx{rtcp_session_salt} . "\0\0", - $ssrc . "\0\0\0\0\0\0\0\0", pack("Nn", $idx, 0)); - return $iv; -} - -sub aes_f8_iv_rtp { - my ($ctx, $r) = @_; - - my ($hdr, $fields) = unpack('a1a11', $r); - my $iv = pack('Ca*N', 0, $fields, $$ctx{rtp_roc}); - return $iv; -} - -sub aes_f8_iv_rtcp { - my ($ctx, $r) = @_; - - my ($fields) = unpack('a8', $r); - my $iv = pack('a*Na*', "\0\0\0\0", (($$ctx{rtcp_index} || 0) | 0x80000000), $fields); - return $iv; -} - -sub append_mki { - my ($ctx_dir, $pack_r) = @_; - - $$ctx_dir{rtp_mki_len} or return; - - my $mki = pack('N', $$ctx_dir{rtp_mki}); - while (length($mki) < $$ctx_dir{rtp_mki_len}) { - $mki = "\x00" . $mki; - } - if (length($mki) > $$ctx_dir{rtp_mki_len}) { - $mki = substr($mki, -$$ctx_dir{rtp_mki_len}); - } - $$pack_r .= $mki; -} - sub rtcp_encrypt { my ($r, $ctx, $dir) = @_; - if (!$$ctx{$dir}{rtcp_session_key}) { - ($$ctx{$dir}{rtcp_session_key}, $$ctx{$dir}{rtcp_session_auth_key}, $$ctx{$dir}{rtcp_session_salt}) - = gen_rtcp_session_keys($$ctx{$dir}{rtp_master_key}, $$ctx{$dir}{rtp_master_salt}); + my $dctx = $$ctx{$dir}; + + if (!$$dctx{rtcp_session_key}) { + ($$dctx{rtcp_session_key}, $$dctx{rtcp_session_auth_key}, $$dctx{rtcp_session_salt}) + = SRTP::gen_rtcp_session_keys($$dctx{rtp_master_key}, $$dctx{rtp_master_salt}); } ($NOENC && $NOENC{rtcp_packet}) and return $NOENC{rtcp_packet}; - my $iv = $$ctx{$dir}{crypto_suite}{iv_rtcp}->($$ctx{$dir}, $r); + my $iv = $$dctx{crypto_suite}{iv_rtcp}->($dctx, $r); my ($hdr, $to_enc) = unpack('a8a*', $r); - my $enc = $$ctx{$dir}{crypto_suite}{enc_func}->($to_enc, $$ctx{$dir}{rtcp_session_key}, - $iv, $$ctx{$dir}{rtcp_session_salt}); + my $enc = $$dctx{crypto_suite}{enc_func}->($to_enc, $$dctx{rtcp_session_key}, + $iv, $$dctx{rtcp_session_salt}); my $pkt = $hdr . $enc; - $pkt .= pack("N", (($$ctx{$dir}{rtcp_index} || 0) | 0x80000000)); + $pkt .= pack("N", (($$dctx{rtcp_index} || 0) | 0x80000000)); - my $hmac = hmac_sha1($pkt, $$ctx{$dir}{rtcp_session_auth_key}); + my $hmac = hmac_sha1($pkt, $$dctx{rtcp_session_auth_key}); - append_mki($$ctx{$dir}, \$pkt); + SRTP::append_mki(\$pkt, @$dctx{qw(rtp_mki_len rtp_mki)}); #$pkt .= pack("N", 1); # mki $pkt .= substr($hmac, 0, 10); - $$ctx{$dir}{rtcp_index}++; + $$dctx{rtcp_index}++; $NOENC{rtcp_packet} = $pkt; @@ -324,68 +142,32 @@ sub rtcp_encrypt { sub rtp_encrypt { my ($r, $ctx, $dir) = @_; - if (!$$ctx{$dir}{rtp_session_key}) { - ($$ctx{$dir}{rtp_session_key}, $$ctx{$dir}{rtp_session_auth_key}, $$ctx{$dir}{rtp_session_salt}) - = gen_rtp_session_keys($$ctx{$dir}{rtp_master_key}, $$ctx{$dir}{rtp_master_salt}); + my $dctx = $$ctx{$dir}; + + if (!$$dctx{rtp_session_key}) { + ($$dctx{rtp_session_key}, $$dctx{rtp_session_auth_key}, $$dctx{rtp_session_salt}) + = SRTP::gen_rtp_session_keys($$dctx{rtp_master_key}, $$dctx{rtp_master_salt}); } ($NOENC && $NOENC{rtp_packet}) and return $NOENC{rtp_packet}; - my ($hdr, $seq, $h2, $to_enc) = unpack('a2na8a*', $r); - my $roc = $$ctx{$dir}{rtp_roc} || 0; - $seq == 0 and $roc++; - $$ctx{$dir}{rtp_roc} = $roc; - - my $iv = $$ctx{$dir}{crypto_suite}{iv_rtp}->($$ctx{$dir}, $r); - my $enc = $$ctx{$dir}{crypto_suite}{enc_func}->($to_enc, $$ctx{$dir}{rtp_session_key}, - $iv, $$ctx{$dir}{rtp_session_salt}); - my $pkt = pack('a*na*a*', $hdr, $seq, $h2, $enc); - - my $hmac = hmac_sha1($pkt . pack("N", $$ctx{$dir}{rtp_roc}), $$ctx{$dir}{rtp_session_auth_key}); -# print("HMAC for packet " . unpack("H*", $pkt) . " ROC $roc is " . unpack("H*", $hmac) . "\n"); - - append_mki($$ctx{$dir}, \$pkt); - - #$pkt .= pack("N", 1); # mki - $pkt .= substr($hmac, 0, $$ctx{$dir}{crypto_suite}{auth_tag}); + my ($pkt, $roc) = SRTP::encrypt_rtp(@$dctx{qw(crypto_suite rtp_session_key rtp_session_salt + rtp_session_auth_key rtp_roc rtp_mki rtp_mki_len)}, $r); + $$dctx{rtp_roc} = $roc; $NOENC{rtp_packet} = $pkt; return $pkt; } -my @crypto_suites = ( - { - str => 'AES_CM_128_HMAC_SHA1_80', - auth_tag => 10, - enc_func => \&aes_cm, - iv_rtp => \&aes_cm_iv_rtp, - iv_rtcp => \&aes_cm_iv_rtcp, - }, - { - str => 'AES_CM_128_HMAC_SHA1_32', - auth_tag => 4, - enc_func => \&aes_cm, - iv_rtp => \&aes_cm_iv_rtp, - iv_rtcp => \&aes_cm_iv_rtcp, - }, - { - str => 'F8_128_HMAC_SHA1_80', - auth_tag => 10, - enc_func => \&aes_f8, - iv_rtp => \&aes_f8_iv_rtp, - iv_rtcp => \&aes_f8_iv_rtcp, - }, -); -$SUITES and @crypto_suites = grep {my $x = $$_{str}; grep {$x eq $_} @$SUITES} @crypto_suites; -my %crypto_suites = map {$$_{str} => $_} @crypto_suites; +$SUITES and @SRTP::crypto_suites = grep {my $x = $$_{str}; grep {$x eq $_} @$SUITES} @SRTP::crypto_suites; sub savp_sdp { my ($ctx, $ctx_o) = @_; if (!$$ctx{out}{crypto_suite}) { $$ctx{out}{crypto_suite} = $$ctx_o{in}{crypto_suite} ? $$ctx_o{in}{crypto_suite} - : $crypto_suites[rand(@crypto_suites)]; + : $SRTP::crypto_suites[rand(@SRTP::crypto_suites)]; $$ctx{out}{rtp_mki_len} = 0; if (rand() > .5) { @@ -512,10 +294,9 @@ sub savp_crypto { @a or die; my $i = 0; while (@a >= 6) { - $$ctx[$i]{in}{crypto_suite} = $crypto_suites{$a[0]} or die; - my $ks = decode_base64($a[1]); - length($ks) == 30 or die; - ($$ctx[$i]{in}{rtp_master_key}, $$ctx[$i]{in}{rtp_master_salt}) = unpack('a16a14', $ks); + $$ctx[$i]{in}{crypto_suite} = $SRTP::crypto_suites{$a[0]} or die; + ($$ctx[$i]{in}{rtp_master_key}, $$ctx[$i]{in}{rtp_master_salt}) + = SRTP::decode_inline_base64($a[1]); $$ctx[$i]{in}{rtp_mki} = $a[4]; $$ctx[$i]{in}{rtp_mki_len} = $a[5]; undef($$ctx[$i]{in}{rtp_session_key}); diff --git a/utils/SRTP.pm b/utils/SRTP.pm new file mode 100644 index 000000000..83e19edd0 --- /dev/null +++ b/utils/SRTP.pm @@ -0,0 +1,281 @@ +package SRTP; + +use strict; +use warnings; +use Crypt::Rijndael; +use Digest::SHA qw(hmac_sha1); +use MIME::Base64; + +our $SRTP_DEBUG = 0; + +our @crypto_suites = ( + { + str => 'AES_CM_128_HMAC_SHA1_80', + auth_tag => 10, + enc_func => \&aes_cm, + iv_rtp => \&aes_cm_iv_rtp, + iv_rtcp => \&aes_cm_iv_rtcp, + }, + { + str => 'AES_CM_128_HMAC_SHA1_32', + auth_tag => 4, + enc_func => \&aes_cm, + iv_rtp => \&aes_cm_iv_rtp, + iv_rtcp => \&aes_cm_iv_rtcp, + }, + { + str => 'F8_128_HMAC_SHA1_80', + auth_tag => 10, + enc_func => \&aes_f8, + iv_rtp => \&aes_f8_iv_rtp, + iv_rtcp => \&aes_f8_iv_rtcp, + }, +); +our %crypto_suites = map {$$_{str} => $_} @crypto_suites; + +sub aes_cm { + my ($data, $key, $iv) = @_; + + my $c = Crypt::Rijndael->new($key) or die; + length($iv) == 16 or die; + my @iv = unpack("C16", $iv); + my $out = ''; + + while ($data ne '') { + $iv = pack("C16", @iv); + my $key_segment = $c->encrypt($iv); + length($key_segment) == 16 or die; + my @ks = unpack("C16", $key_segment); + my @ds = unpack("C16", $data); + + for my $i (0 .. $#ds) { + my $ss = $ds[$i]; + my $kk = $ks[$i]; + $out .= chr($ss ^ $kk); + } + + substr($data, 0, 16, ''); + $data eq '' and last; + + for my $i (reverse(0 .. 15)) { + $iv[$i]++; + if ($iv[$i] == 256) { + $iv[$i] = 0; + } + else { + last; + } + } + } + + return $out; +} + +sub aes_f8 { + my ($data, $key, $iv, $salt) = @_; + + my $m = $salt . "\x55\x55"; + my $c = Crypt::Rijndael->new(xor_128($key, $m)) or die; + my $ivx = $c->encrypt($iv); + undef($c); + + $c = Crypt::Rijndael->new($key) or die; + my $p_s = "\0" x 16; + my $j = 0; + my $out = ''; + + while ($data ne '') { + my $jx = ("\0" x 12) . pack("N", $j); + my $key_segment = $c->encrypt(xor_128($ivx, $jx, $p_s)); + length($key_segment) == 16 or die; + my @ks = unpack("C16", $key_segment); + my @ds = unpack("C16", $data); + + for my $i (0 .. $#ds) { + my $ss = $ds[$i]; + my $kk = $ks[$i]; + $out .= chr($ss ^ $kk); + } + + substr($data, 0, 16, ''); + $data eq '' and last; + + $p_s = $key_segment; + $j++; + } + + return $out; +} + +sub prf_n { + my ($n, $key, $x) = @_; + my $d = "\0" x ($n / 8); + my $ks = aes_cm($d, $key, $x . "\0\0"); + return substr($ks, 0, $n / 8); +} + +sub xor_n { + my ($n, @l) = @_; + $n /= 8; + my @o = (0) x $n; + for my $e (@l) { + my @e = unpack("C$n", $e); + if (@e < $n) { + unshift(@e, ((0) x ($n - @e))); + } + for my $i (0 .. $#o) { + $o[$i] ^= $e[$i]; + } + } + return pack("C$n", @o); +} + +sub xor_112 { + return xor_n(112, @_); +} +sub xor_128 { + return xor_n(128, @_); +} + +sub gen_rtp_session_keys { + my ($master_key, $master_salt) = @_; + + my $session_key = prf_n(128, $master_key, xor_112($master_salt, "\0\0\0\0\0\0\0")); + my $auth_key = prf_n(160, $master_key, xor_112($master_salt, "\1\0\0\0\0\0\0")); + my $session_salt = prf_n(112, $master_key, xor_112($master_salt, "\2\0\0\0\0\0\0")); + if ($SRTP_DEBUG) { + print("RTP keys generated for master key " . unpack("H8", $master_key) . "... and salt " . + unpack("H8", $master_salt) . "... are: " . + unpack("H8", $session_key) . "..., " . + unpack("H*", $auth_key) . ", " . + unpack("H8", $session_salt) . "...\n"); + } + + return ($session_key, $auth_key, $session_salt); +} + +sub gen_rtcp_session_keys { + my ($master_key, $master_salt) = @_; + + my $session_key = prf_n(128, $master_key, xor_112($master_salt, "\3\0\0\0\0\0\0")); + my $auth_key = prf_n(160, $master_key, xor_112($master_salt, "\4\0\0\0\0\0\0")); + my $session_salt = prf_n(112, $master_key, xor_112($master_salt, "\5\0\0\0\0\0\0")); + if ($SRTP_DEBUG) { + print("RTCP keys generated for master key " . unpack("H8", $master_key) . "... and salt " . + unpack("H8", $master_salt) . "... are: " . + unpack("H8", $session_key) . "..., " . + unpack("H*", $auth_key) . ", " . + unpack("H8", $session_salt) . "...\n"); + } + + return ($session_key, $auth_key, $session_salt); +} + +sub aes_cm_iv_rtp { + my ($r, $ssalt, $roc) = @_; + + my ($hdr, $seq, $ts, $ssrc) = unpack('a2na4a4', $r); + my $iv = xor_128($ssalt . "\0\0", + $ssrc . "\0\0\0\0\0\0\0\0", pack("Nnn", $roc, $seq, 0)); + return $iv; +} + +sub aes_cm_iv_rtcp { + my ($ctx, $r) = @_; + + my $idx = $$ctx{rtcp_index} || 0; + my ($hdr, $ssrc) = unpack('a4a4', $r); + my $iv = xor_128($$ctx{rtcp_session_salt} . "\0\0", + $ssrc . "\0\0\0\0\0\0\0\0", pack("Nn", $idx, 0)); + return $iv; +} + +sub aes_f8_iv_rtp { + my ($r, $ssalt, $roc) = @_; + + my ($hdr, $fields) = unpack('a1a11', $r); + my $iv = pack('Ca*N', 0, $fields, $roc); + return $iv; +} + +sub aes_f8_iv_rtcp { + my ($ctx, $r) = @_; + + my ($fields) = unpack('a8', $r); + my $iv = pack('a*Na*', "\0\0\0\0", (($$ctx{rtcp_index} || 0) | 0x80000000), $fields); + return $iv; +} + +sub decode_inline_base64 { + my ($b64) = @_; + my $ks = decode_base64($b64); + length($ks) == 30 or die; + my @ret = unpack('a16a14', $ks); + return @ret; +} + +sub encrypt_rtp { + my ($suite, $skey, $ssalt, $sauth, $roc, $mki, $mki_len, $packet) = @_; + + my ($hdr, $seq, $h2, $to_enc) = unpack('a2na8a*', $packet); + $roc = $roc || 0; + $seq == 0 and $roc++; + + my $iv = $$suite{iv_rtp}->($packet, $ssalt, $roc); + my $enc = $$suite{enc_func}->($to_enc, $skey, + $iv, $ssalt); + my $pkt = pack('a*na*a*', $hdr, $seq, $h2, $enc); + + my $hmac = hmac_sha1($pkt . pack("N", $roc), $sauth); +# print("HMAC for packet " . unpack("H*", $pkt) . " ROC $roc is " . unpack("H*", $hmac) . "\n"); + + append_mki(\$pkt, $mki_len, $mki); + + #$pkt .= pack("N", 1); # mki + $pkt .= substr($hmac, 0, $$suite{auth_tag}); + + return ($pkt, $roc); +} + +sub decrypt_rtp { + my ($suite, $skey, $ssalt, $sauth, $roc, $packet) = @_; + + # XXX MKI + + my $plen = length($packet); + my $auth_tag = substr($packet, $plen - $$suite{auth_tag}, $$suite{auth_tag}); + $packet = substr($packet, 0, $plen - $$suite{auth_tag}); + + my ($hdr, $seq, $h2, $to_enc) = unpack('a2na8a*', $packet); + $roc = $roc || 0; + $seq == 0 and $roc++; + + my $iv = $$suite{iv_rtp}->($packet, $ssalt, $roc); + my $enc = $$suite{enc_func}->($to_enc, $skey, + $iv, $ssalt); + my $pkt = pack('a*na*a*', $hdr, $seq, $h2, $enc); + + my $hmac = hmac_sha1($packet . pack("N", $roc), $sauth); +# print("HMAC for packet " . unpack("H*", $pkt) . " ROC $roc is " . unpack("H*", $hmac) . "\n"); + + #$pkt .= pack("N", 1); # mki + + return ($pkt, $roc, $auth_tag, $hmac); +} + +sub append_mki { + my ($pack_r, $mki_len, $mki) = @_; + + $mki_len or return; + + $mki = pack('N', $mki); + while (length($mki) < $mki_len) { + $mki = "\x00" . $mki; + } + if (length($mki) > $mki_len) { + $mki = substr($mki, -$mki_len); + } + $$pack_r .= $mki; +} + +1; diff --git a/utils/srtp-debug-helper b/utils/srtp-debug-helper new file mode 100755 index 000000000..1c0794957 --- /dev/null +++ b/utils/srtp-debug-helper @@ -0,0 +1,35 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use MIME::Base64; +use SRTP; + +my $cs = $SRTP::crypto_suites{$ARGV[0]} or die; +my $inline_key = $ARGV[1] or die; +my ($key, $salt) = SRTP::decode_inline_base64($inline_key); +my ($skey, $sauth, $ssalt) = SRTP::gen_rtp_session_keys($key, $salt); +print("Master key: " . unpack("H*", $key) . "\n"); +print("Master salt: " . unpack("H*", $salt) . "\n"); +print("RTP session key: " . unpack("H*", $skey) . "\n"); +print("RTP session auth key: " . unpack("H*", $sauth) . "\n"); +print("RTP session salt: " . unpack("H*", $ssalt) . "\n"); + +my $pack = $ARGV[2]; +my @pack; +if ($pack =~ /:/) { + my @pack = split(/:/, $pack); + $pack = join('', (map {chr(hex($_))} @pack)); +} +else { + $pack = pack("H*", $pack); +} + +print("Packet length: " . length($pack) . " bytes\n"); + +my ($dec, $roc, $tag, $hmac) = SRTP::decrypt_rtp($cs, $skey, $ssalt, $sauth, 0, $pack); + +print("Auth tag from packet: " . unpack("H*", $tag) . "\n"); +print("Computer auth tag: " . unpack("H*", $hmac) . "\n"); + + From deba231143e76809b0dd683b77e9e48b543a72b4 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Tue, 10 Feb 2015 09:04:38 -0500 Subject: [PATCH 08/82] rename an old MP_ variable to RE_ --- daemon/.ycm_extra_conf.py | 2 +- daemon/Makefile | 2 +- daemon/main.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/daemon/.ycm_extra_conf.py b/daemon/.ycm_extra_conf.py index f46cabb73..2a2b9d4fc 100644 --- a/daemon/.ycm_extra_conf.py +++ b/daemon/.ycm_extra_conf.py @@ -23,7 +23,7 @@ flags = [ '-D_GNU_SOURCE', '-D__DEBUG=1', '-DRTPENGINE_VERSION="dummy"', -'-DMP_PLUGIN_DIR="/usr/lib/rtpengine"', +'-DRE_PLUGIN_DIR="/usr/lib/rtpengine"', '-O2', '-fstack-protector', '--param=ssp-buffer-size=4', diff --git a/daemon/Makefile b/daemon/Makefile index 24d0637b6..cd2f1e670 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -29,7 +29,7 @@ ifeq ($(RTPENGINE_VERSION),) endif CFLAGS+= -DRTPENGINE_VERSION="\"$(RTPENGINE_VERSION)\"" -CFLAGS+= -DMP_PLUGIN_DIR="\"/usr/lib/rtpengine\"" +CFLAGS+= -DRE_PLUGIN_DIR="\"/usr/lib/rtpengine\"" #CFLAGS+= -DSRTCP_KEY_DERIVATION_RFC_COMPLIANCE #CFLAGS+= -DTERMINATE_SDP_AT_BLANK_LINE diff --git a/daemon/main.c b/daemon/main.c index 50da00044..e7f433c3f 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -596,8 +596,8 @@ no_kernel: } if (redis_ip) { - dlh = dlopen(MP_PLUGIN_DIR "/rtpengine-redis.so", RTLD_NOW | RTLD_GLOBAL); - if (!dlh && !g_file_test(MP_PLUGIN_DIR "/rtpengine-redis.so", G_FILE_TEST_IS_REGULAR) + dlh = dlopen(RE_PLUGIN_DIR "/rtpengine-redis.so", RTLD_NOW | RTLD_GLOBAL); + if (!dlh && !g_file_test(RE_PLUGIN_DIR "/rtpengine-redis.so", G_FILE_TEST_IS_REGULAR) && g_file_test("../../rtpengine-redis/redis.so", G_FILE_TEST_IS_REGULAR)) dlh = dlopen("../../rtpengine-redis/redis.so", RTLD_NOW | RTLD_GLOBAL); if (!dlh) From fb9ed6ef0ab8ea33aa7ed9dad13e8488aac5698c Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Tue, 10 Feb 2015 09:11:54 -0500 Subject: [PATCH 09/82] use trial & error approach to guess ROC in case of SRTP auth failures Fixes #71 --- daemon/rtp.c | 34 ++++++++++++++++++++---- kernel-module/xt_RTPENGINE.c | 50 +++++++++++++++++++++++++++++++----- 2 files changed, 73 insertions(+), 11 deletions(-) diff --git a/daemon/rtp.c b/daemon/rtp.c index 79ef2b52c..c7fd65d9b 100644 --- a/daemon/rtp.c +++ b/daemon/rtp.c @@ -184,13 +184,37 @@ int rtp_savp2avp(str *s, struct crypto_context *c) { s, &payload)) return -1; - if (auth_tag.len) { - assert(sizeof(hmac) >= auth_tag.len); + if (!auth_tag.len) + goto decrypt; + + /* authenticate */ + assert(sizeof(hmac) >= auth_tag.len); + c->params.crypto_suite->hash_rtp(c, hmac, &to_auth, index); + if (!str_memcmp(&auth_tag, hmac)) + goto decrypt; + /* possible ROC mismatch, attempt to guess */ + /* first, let's see if we missed a rollover */ + index += 0x10000; + c->params.crypto_suite->hash_rtp(c, hmac, &to_auth, index); + if (!str_memcmp(&auth_tag, hmac)) + goto decrypt_idx; + /* or maybe we did a rollover too many */ + if (index >= 0x20000) { + index -= 0x20000; c->params.crypto_suite->hash_rtp(c, hmac, &to_auth, index); - if (str_memcmp(&auth_tag, hmac)) - goto error; + if (!str_memcmp(&auth_tag, hmac)) + goto decrypt_idx; } - + /* last guess: reset ROC to zero */ + index &= 0xffff; + c->params.crypto_suite->hash_rtp(c, hmac, &to_auth, index); + if (!str_memcmp(&auth_tag, hmac)) + goto decrypt_idx; + goto error; + +decrypt_idx: + c->last_index = index; +decrypt: if (crypto_decrypt_rtp(c, rtp, &to_decrypt, index)) return -1; diff --git a/kernel-module/xt_RTPENGINE.c b/kernel-module/xt_RTPENGINE.c index 8589a02ca..798653040 100644 --- a/kernel-module/xt_RTPENGINE.c +++ b/kernel-module/xt_RTPENGINE.c @@ -1899,6 +1899,17 @@ static u_int64_t packet_index(struct re_crypto_context *c, return index; } +static void update_packet_index(struct re_crypto_context *c, + struct rtpengine_srtp *s, u_int64_t idx) +{ + unsigned long flags; + + spin_lock_irqsave(&c->lock, flags); + s->last_index = idx; + c->roc = (idx >> 16); + spin_unlock_irqrestore(&c->lock, flags); +} + static int srtp_hash(unsigned char *hmac, struct re_crypto_context *c, struct rtpengine_srtp *s, struct rtp_parsed *r, @@ -1983,10 +1994,11 @@ static int srtp_authenticate(struct re_crypto_context *c, static int srtp_auth_validate(struct re_crypto_context *c, struct rtpengine_srtp *s, struct rtp_parsed *r, - u_int64_t pkt_idx) + u_int64_t *pkt_idx_p) { unsigned char *auth_tag; unsigned char hmac[20]; + u_int64_t pkt_idx = *pkt_idx_p; if (s->hmac == REH_NULL) return 0; @@ -2015,12 +2027,36 @@ static int srtp_auth_validate(struct re_crypto_context *c, if (srtp_hash(hmac, c, s, r, pkt_idx)) return -1; + if (!memcmp(auth_tag, hmac, s->auth_tag_len)) + goto ok; - if (memcmp(auth_tag, hmac, s->auth_tag_len)) + /* possible ROC mismatch, attempt to guess */ + /* first, let's see if we missed a rollover */ + pkt_idx += 0x10000; + if (srtp_hash(hmac, c, s, r, pkt_idx)) + return -1; + if (!memcmp(auth_tag, hmac, s->auth_tag_len)) + goto ok; + /* or maybe we did a rollover too many */ + if (pkt_idx >= 0x20000) { + pkt_idx -= 0x20000; + if (srtp_hash(hmac, c, s, r, pkt_idx)) + return -1; + if (!memcmp(auth_tag, hmac, s->auth_tag_len)) + goto ok; + } + /* last guess: reset ROC to zero */ + pkt_idx &= 0xffff; + if (srtp_hash(hmac, c, s, r, pkt_idx)) return -1; + if (!memcmp(auth_tag, hmac, s->auth_tag_len)) + goto ok; - return 0; + return -1; +ok: + *pkt_idx_p = pkt_idx; + return 0; } @@ -2114,7 +2150,7 @@ static unsigned int rtpengine46(struct sk_buff *skb, struct rtpengine_table *t, unsigned long flags; u_int32_t *u32; struct rtp_parsed rtp; - u_int64_t pkt_idx = 0; + u_int64_t pkt_idx = 0, pkt_idx_u; skb_reset_transport_header(skb); uh = udp_hdr(skb); @@ -2177,9 +2213,11 @@ src_check_ok: } if (g->target.rtcp_mux && is_muxed_rtcp(&rtp)) goto skip1; - pkt_idx = packet_index(&g->decrypt, &g->target.decrypt, rtp.header); - if (srtp_auth_validate(&g->decrypt, &g->target.decrypt, &rtp, pkt_idx)) + pkt_idx_u = pkt_idx = packet_index(&g->decrypt, &g->target.decrypt, rtp.header); + if (srtp_auth_validate(&g->decrypt, &g->target.decrypt, &rtp, &pkt_idx)) goto skip_error; + if (pkt_idx != pkt_idx_u) + update_packet_index(&g->decrypt, &g->target.decrypt, pkt_idx); if (srtp_decrypt(&g->decrypt, &g->target.decrypt, &rtp, pkt_idx)) goto skip_error; From f6dee07acd5234969297e45334fe636a9463af70 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Tue, 10 Feb 2015 09:51:25 -0500 Subject: [PATCH 10/82] fix initial seqnum in simulator --- tests/simulator-ng.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/simulator-ng.pl b/tests/simulator-ng.pl index 86833b1d9..365d1f802 100755 --- a/tests/simulator-ng.pl +++ b/tests/simulator-ng.pl @@ -261,7 +261,7 @@ sub rtcp_savpf { sub rtp { my ($ctx) = @_; my $seq = $$ctx{rtp_seqnum}; - defined($seq) or $seq = int(rand(0xfffff)) + 1; + defined($seq) or $seq = int(rand(0xfffe)) + 1; my $hdr = pack("CCnNN", 0x80, 0x00, $seq, rand(2**32), rand(2**32)); my $pack = $hdr . rand_str($PAYLOAD); $$ctx{rtp_seqnum} = (++$seq & 0xffff); From 787d90f004a1b992ad5b2e56663752c1e61d31de Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Tue, 10 Feb 2015 10:01:09 -0500 Subject: [PATCH 11/82] rename atomic_uint64 to atomic64 for brevity --- daemon/aux.h | 28 +++++----- daemon/call.c | 118 +++++++++++++++++++-------------------- daemon/call.h | 24 ++++---- daemon/call_interfaces.c | 24 ++++---- daemon/cli.c | 26 ++++----- daemon/graphite.c | 34 +++++------ 6 files changed, 127 insertions(+), 127 deletions(-) diff --git a/daemon/aux.h b/daemon/aux.h index b5092526d..6b748bae1 100644 --- a/daemon/aux.h +++ b/daemon/aux.h @@ -516,42 +516,42 @@ int in6_addr_eq(const void *a, const void *b); typedef struct { void *p; -} atomic_uint64; +} atomic64; -INLINE u_int64_t atomic_uint64_get(const atomic_uint64 *u) { +INLINE u_int64_t atomic64_get(const atomic64 *u) { return (u_int64_t) g_atomic_pointer_get(&u->p); } -INLINE u_int64_t atomic_uint64_get_na(const atomic_uint64 *u) { +INLINE u_int64_t atomic64_get_na(const atomic64 *u) { return (u_int64_t) u->p; } -INLINE void atomic_uint64_set(atomic_uint64 *u, u_int64_t a) { +INLINE void atomic64_set(atomic64 *u, u_int64_t a) { g_atomic_pointer_set(&u->p, (void *) a); } -INLINE void atomic_uint64_set_na(atomic_uint64 *u, u_int64_t a) { +INLINE void atomic64_set_na(atomic64 *u, u_int64_t a) { u->p = (void *) a; } -INLINE void atomic_uint64_inc(atomic_uint64 *u) { +INLINE void atomic64_inc(atomic64 *u) { g_atomic_pointer_add(&u->p, 1); } -INLINE void atomic_uint64_add(atomic_uint64 *u, u_int64_t a) { +INLINE void atomic64_add(atomic64 *u, u_int64_t a) { g_atomic_pointer_add(&u->p, a); } -INLINE void atomic_uint64_add_na(atomic_uint64 *u, u_int64_t a) { +INLINE void atomic64_add_na(atomic64 *u, u_int64_t a) { u->p = (void *) (((u_int64_t) u->p) + a); } -INLINE u_int64_t atomic_uint64_get_set(atomic_uint64 *u, u_int64_t a) { +INLINE u_int64_t atomic64_get_set(atomic64 *u, u_int64_t a) { u_int64_t old; do { - old = atomic_uint64_get(u); + old = atomic64_get(u); if (g_atomic_pointer_compare_and_exchange(&u->p, (void *) old, (void *) a)) return old; } while (1); } -INLINE void atomic_uint64_local_copy_zero(atomic_uint64 *dst, atomic_uint64 *src) { - atomic_uint64_set_na(dst, atomic_uint64_get_set(src, 0)); +INLINE void atomic64_local_copy_zero(atomic64 *dst, atomic64 *src) { + atomic64_set_na(dst, atomic64_get_set(src, 0)); } -#define atomic_uint64_local_copy_zero_struct(d, s, member) \ - atomic_uint64_local_copy_zero(&((d)->member), &((s)->member)) +#define atomic64_local_copy_zero_struct(d, s, member) \ + atomic64_local_copy_zero(&((d)->member), &((s)->member)) #endif diff --git a/daemon/call.c b/daemon/call.c index b81755b88..9a31e08f2 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -687,8 +687,8 @@ loop_ok: if (!sink || !sink->sfd || !out_srtp->sfd || !in_srtp->sfd) { ilog(LOG_WARNING, "RTP packet from %s discarded", addr); - atomic_uint64_inc(&stream->stats.errors); - atomic_uint64_inc(&cm->statsps.errors); + atomic64_inc(&stream->stats.errors); + atomic64_inc(&cm->statsps.errors); goto unlock_out; } @@ -752,7 +752,7 @@ use_cand: mutex_unlock(&stream->out_lock); if (tmp && PS_ISSET(stream, STRICT_SOURCE)) { - atomic_uint64_inc(&stream->stats.errors); + atomic64_inc(&stream->stats.errors); goto drop; } } @@ -841,8 +841,8 @@ forward: if (ret == -1) { ret = -errno; ilog(LOG_DEBUG,"Error when sending message. Error: %s",strerror(errno)); - atomic_uint64_inc(&stream->stats.errors); - atomic_uint64_inc(&cm->statsps.errors); + atomic64_inc(&stream->stats.errors); + atomic64_inc(&cm->statsps.errors); goto out; } @@ -852,11 +852,11 @@ drop: if (sink) mutex_unlock(&sink->out_lock); ret = 0; - atomic_uint64_inc(&stream->stats.packets); - atomic_uint64_add(&stream->stats.bytes, s->len); - atomic_uint64_set(&stream->last_packet, poller_now); - atomic_uint64_inc(&cm->statsps.packets); - atomic_uint64_add(&cm->statsps.bytes, s->len); + atomic64_inc(&stream->stats.packets); + atomic64_add(&stream->stats.bytes, s->len); + atomic64_set(&stream->last_packet, poller_now); + atomic64_inc(&cm->statsps.packets); + atomic64_add(&cm->statsps.bytes, s->len); out: if (ret == 0 && update) @@ -1071,7 +1071,7 @@ no_sfd: tmp_t_reason = 2; } - if (poller_now - atomic_uint64_get(&ps->last_packet) < check) + if (poller_now - atomic64_get(&ps->last_packet) < check) good = 1; next: @@ -1281,13 +1281,13 @@ destroy: #define DS(x) do { \ u_int64_t ks_val, d; \ - ks_val = atomic_uint64_get(&ps->kernel_stats.x); \ + ks_val = atomic64_get(&ps->kernel_stats.x); \ if (ke->stats.x < ks_val) \ d = 0; \ else \ d = ke->stats.x - ks_val; \ - atomic_uint64_add(&ps->stats.x, d); \ - atomic_uint64_add(&m->statsps.x, d); \ + atomic64_add(&ps->stats.x, d); \ + atomic64_add(&m->statsps.x, d); \ } while (0) static void callmaster_timer(void *ptr) { struct callmaster *m = ptr; @@ -1305,13 +1305,13 @@ static void callmaster_timer(void *ptr) { g_hash_table_foreach(m->callhash, call_timer_iterator, &hlp); rwlock_unlock_r(&m->hashlock); - atomic_uint64_local_copy_zero_struct(&tmpstats, &m->statsps, bytes); - atomic_uint64_local_copy_zero_struct(&tmpstats, &m->statsps, packets); - atomic_uint64_local_copy_zero_struct(&tmpstats, &m->statsps, errors); + atomic64_local_copy_zero_struct(&tmpstats, &m->statsps, bytes); + atomic64_local_copy_zero_struct(&tmpstats, &m->statsps, packets); + atomic64_local_copy_zero_struct(&tmpstats, &m->statsps, errors); - atomic_uint64_set(&m->stats.bytes, atomic_uint64_get_na(&tmpstats.bytes)); - atomic_uint64_set(&m->stats.packets, atomic_uint64_get_na(&tmpstats.packets)); - atomic_uint64_set(&m->stats.errors, atomic_uint64_get_na(&tmpstats.errors)); + atomic64_set(&m->stats.bytes, atomic64_get_na(&tmpstats.bytes)); + atomic64_set(&m->stats.packets, atomic64_get_na(&tmpstats.packets)); + atomic64_set(&m->stats.errors, atomic64_get_na(&tmpstats.errors)); i = (m->conf.kernelid >= 0) ? kernel_list(m->conf.kernelid) : NULL; while (i) { @@ -1335,12 +1335,12 @@ static void callmaster_timer(void *ptr) { mutex_lock(&ps->in_lock); - if (ke->stats.packets != atomic_uint64_get(&ps->kernel_stats.packets)) - atomic_uint64_set(&ps->last_packet, poller_now); + if (ke->stats.packets != atomic64_get(&ps->kernel_stats.packets)) + atomic64_set(&ps->last_packet, poller_now); - atomic_uint64_set(&ps->kernel_stats.bytes, ke->stats.bytes); - atomic_uint64_set(&ps->kernel_stats.packets, ke->stats.packets); - atomic_uint64_set(&ps->kernel_stats.errors, ke->stats.errors); + 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); update = 0; @@ -1754,7 +1754,7 @@ struct packet_stream *__packet_stream_new(struct call *call) { mutex_init(&stream->in_lock); mutex_init(&stream->out_lock); stream->call = call; - atomic_uint64_set_na(&stream->last_packet, poller_now); + atomic64_set_na(&stream->last_packet, poller_now); call->streams = g_slist_prepend(call->streams, stream); return stream; @@ -2508,39 +2508,39 @@ void call_destroy(struct call *c) { "ml%i_midx%u_%s_relayed_packets="UINT64F", " "ml%i_midx%u_%s_relayed_bytes="UINT64F", " "ml%i_midx%u_%s_relayed_errors="UINT64F", " - "ml%i_midx%u_%s_last_packet=%llu, ", + "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, - atomic_uint64_get(&ps->stats.packets), + atomic64_get(&ps->stats.packets), cdrlinecnt, md->index, protocol, - atomic_uint64_get(&ps->stats.bytes), + atomic64_get(&ps->stats.bytes), cdrlinecnt, md->index, protocol, - atomic_uint64_get(&ps->stats.errors), + atomic64_get(&ps->stats.errors), cdrlinecnt, md->index, protocol, - (unsigned long long) atomic_uint64_get(&ps->last_packet)); + atomic64_get(&ps->last_packet)); } ilog(LOG_INFO, "------ Media #%u, port %5u <> %15s:%-5hu%s, " - ""UINT64F" p, "UINT64F" b, "UINT64F" e, %llu last_packet", + ""UINT64F" p, "UINT64F" b, "UINT64F" e, "UINT64F" last_packet", md->index, (unsigned int) (ps->sfd ? ps->sfd->fd.localport : 0), addr, ps->endpoint.port, (!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) ? " (RTCP)" : "", - atomic_uint64_get(&ps->stats.packets), - atomic_uint64_get(&ps->stats.bytes), - atomic_uint64_get(&ps->stats.errors), - (unsigned long long) atomic_uint64_get(&ps->last_packet)); - - atomic_uint64_add(&m->totalstats.total_relayed_packets, - atomic_uint64_get(&ps->stats.packets)); - atomic_uint64_add(&m->totalstats_interval.total_relayed_packets, - atomic_uint64_get(&ps->stats.packets)); - atomic_uint64_add(&m->totalstats.total_relayed_errors, - atomic_uint64_get(&ps->stats.errors)); - atomic_uint64_add(&m->totalstats_interval.total_relayed_errors, - atomic_uint64_get(&ps->stats.errors)); + atomic64_get(&ps->stats.packets), + atomic64_get(&ps->stats.bytes), + atomic64_get(&ps->stats.errors), + atomic64_get(&ps->last_packet)); + + atomic64_add(&m->totalstats.total_relayed_packets, + atomic64_get(&ps->stats.packets)); + atomic64_add(&m->totalstats_interval.total_relayed_packets, + atomic64_get(&ps->stats.packets)); + atomic64_add(&m->totalstats.total_relayed_errors, + atomic64_get(&ps->stats.errors)); + atomic64_add(&m->totalstats_interval.total_relayed_errors, + atomic64_get(&ps->stats.errors)); } } if (_log_facility_cdr) @@ -2586,10 +2586,10 @@ void call_destroy(struct call *c) { } } - if (ps && ps2 && atomic_uint64_get(&ps2->stats.packets)==0) { - if (atomic_uint64_get(&ps->stats.packets)!=0) { - atomic_uint64_inc(&m->totalstats.total_oneway_stream_sess); - atomic_uint64_inc(&m->totalstats_interval.total_oneway_stream_sess); + if (ps && ps2 && atomic64_get(&ps2->stats.packets)==0) { + if (atomic64_get(&ps->stats.packets)!=0) { + atomic64_inc(&m->totalstats.total_oneway_stream_sess); + atomic64_inc(&m->totalstats_interval.total_oneway_stream_sess); } else { total_nopacket_relayed_sess++; @@ -2598,22 +2598,22 @@ void call_destroy(struct call *c) { } - atomic_uint64_add(&m->totalstats.total_nopacket_relayed_sess, total_nopacket_relayed_sess / 2); - atomic_uint64_add(&m->totalstats_interval.total_nopacket_relayed_sess, total_nopacket_relayed_sess / 2); + atomic64_add(&m->totalstats.total_nopacket_relayed_sess, total_nopacket_relayed_sess / 2); + atomic64_add(&m->totalstats_interval.total_nopacket_relayed_sess, total_nopacket_relayed_sess / 2); ml = c->monologues->data; if (ml->term_reason==TIMEOUT) { - atomic_uint64_inc(&m->totalstats.total_timeout_sess); - atomic_uint64_inc(&m->totalstats_interval.total_timeout_sess); + atomic64_inc(&m->totalstats.total_timeout_sess); + atomic64_inc(&m->totalstats_interval.total_timeout_sess); } else if (ml->term_reason==SILENT_TIMEOUT) { - atomic_uint64_inc(&m->totalstats.total_silent_timeout_sess); - atomic_uint64_inc(&m->totalstats_interval.total_silent_timeout_sess); + atomic64_inc(&m->totalstats.total_silent_timeout_sess); + atomic64_inc(&m->totalstats_interval.total_silent_timeout_sess); } else if (ml->term_reason==REGULAR) { - atomic_uint64_inc(&m->totalstats.total_regular_term_sess); - atomic_uint64_inc(&m->totalstats_interval.total_regular_term_sess); + atomic64_inc(&m->totalstats.total_regular_term_sess); + atomic64_inc(&m->totalstats_interval.total_regular_term_sess); } else if (ml->term_reason==FORCED) { - atomic_uint64_inc(&m->totalstats.total_forced_term_sess); - atomic_uint64_inc(&m->totalstats_interval.total_forced_term_sess); + atomic64_inc(&m->totalstats.total_forced_term_sess); + atomic64_inc(&m->totalstats_interval.total_forced_term_sess); } timeval_totalstats_average_add(&m->totalstats, &tim_result_duration); diff --git a/daemon/call.h b/daemon/call.h index 731d80abf..67572d0de 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -188,21 +188,21 @@ extern const struct transport_protocol transport_protocols[]; struct stats { - atomic_uint64 packets; - atomic_uint64 bytes; - atomic_uint64 errors; + atomic64 packets; + atomic64 bytes; + atomic64 errors; }; struct totalstats { time_t started; - atomic_uint64 total_timeout_sess; - atomic_uint64 total_silent_timeout_sess; - atomic_uint64 total_regular_term_sess; - atomic_uint64 total_forced_term_sess; - atomic_uint64 total_relayed_packets; - atomic_uint64 total_relayed_errors; - atomic_uint64 total_nopacket_relayed_sess; - atomic_uint64 total_oneway_stream_sess; + atomic64 total_timeout_sess; + atomic64 total_silent_timeout_sess; + atomic64 total_regular_term_sess; + atomic64 total_forced_term_sess; + atomic64 total_relayed_packets; + atomic64 total_relayed_errors; + atomic64 total_nopacket_relayed_sess; + atomic64 total_oneway_stream_sess; mutex_t total_average_lock; /* for these two below */ u_int64_t total_managed_sess; @@ -273,7 +273,7 @@ struct packet_stream { struct stats stats; struct stats kernel_stats; - atomic_uint64 last_packet; + atomic64 last_packet; #if RTP_LOOP_PROTECT /* LOCK: in_lock: */ diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 651b80a71..fa6af5243 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -386,8 +386,8 @@ str *call_query_udp(char **out, struct callmaster *m) { ret = str_sprintf("%s %lld "UINT64F" "UINT64F" "UINT64F" "UINT64F"\n", out[RE_UDP_COOKIE], (long long int) m->conf.silent_timeout - (poller_now - stats.last_packet), - atomic_uint64_get_na(&stats.totals[0].packets), atomic_uint64_get_na(&stats.totals[1].packets), - atomic_uint64_get_na(&stats.totals[2].packets), atomic_uint64_get_na(&stats.totals[3].packets)); + atomic64_get_na(&stats.totals[0].packets), atomic64_get_na(&stats.totals[1].packets), + atomic64_get_na(&stats.totals[2].packets), atomic64_get_na(&stats.totals[3].packets)); goto out; err: @@ -438,7 +438,7 @@ void calls_status_tcp(struct callmaster *m, struct control_stream *s) { control_stream_printf(s, "proxy %u "UINT64F"/%i/%i\n", g_queue_get_length(&q), - atomic_uint64_get(&m->stats.bytes), 0, 0); + atomic64_get(&m->stats.bytes), 0, 0); while (q.head) { c = g_queue_pop_head(&q); @@ -720,14 +720,14 @@ const char *call_delete_ng(bencode_item_t *input, struct callmaster *m, bencode_ } static void ng_stats(bencode_item_t *d, const struct stats *s, struct stats *totals) { - bencode_dictionary_add_integer(d, "packets", atomic_uint64_get(&s->packets)); - bencode_dictionary_add_integer(d, "bytes", atomic_uint64_get(&s->bytes)); - bencode_dictionary_add_integer(d, "errors", atomic_uint64_get(&s->errors)); + bencode_dictionary_add_integer(d, "packets", atomic64_get(&s->packets)); + bencode_dictionary_add_integer(d, "bytes", atomic64_get(&s->bytes)); + bencode_dictionary_add_integer(d, "errors", atomic64_get(&s->errors)); if (!totals) return; - atomic_uint64_add_na(&totals->packets, atomic_uint64_get(&s->packets)); - atomic_uint64_add_na(&totals->bytes, atomic_uint64_get(&s->bytes)); - atomic_uint64_add_na(&totals->errors, atomic_uint64_get(&s->errors)); + atomic64_add_na(&totals->packets, atomic64_get(&s->packets)); + atomic64_add_na(&totals->bytes, atomic64_get(&s->bytes)); + atomic64_add_na(&totals->errors, atomic64_get(&s->errors)); } static void ng_stats_endpoint(bencode_item_t *dict, const struct endpoint *ep) { @@ -766,7 +766,7 @@ static void ng_stats_stream(bencode_item_t *list, const struct packet_stream *ps if (ps->crypto.params.crypto_suite) bencode_dictionary_add_string(dict, "crypto suite", ps->crypto.params.crypto_suite->name); - bencode_dictionary_add_integer(dict, "last packet", atomic_uint64_get(&ps->last_packet)); + bencode_dictionary_add_integer(dict, "last packet", atomic64_get(&ps->last_packet)); flags = bencode_dictionary_add_list(dict, "flags"); @@ -782,8 +782,8 @@ static void ng_stats_stream(bencode_item_t *list, const struct packet_stream *ps BF_PS("media handover", MEDIA_HANDOVER); stats: - if (totals->last_packet < atomic_uint64_get(&ps->last_packet)) - totals->last_packet = atomic_uint64_get(&ps->last_packet); + if (totals->last_packet < atomic64_get(&ps->last_packet)) + totals->last_packet = atomic64_get(&ps->last_packet); /* XXX distinguish between input and output */ s = &totals->totals[0]; diff --git a/daemon/cli.c b/daemon/cli.c index ca5ad72e9..9dc774898 100644 --- a/daemon/cli.c +++ b/daemon/cli.c @@ -39,21 +39,21 @@ static void cli_incoming_list_totals(char* buffer, int len, struct callmaster* m ADJUSTLEN(printlen,outbufend,replybuffer); printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total managed sessions :"UINT64F"\n", num_sessions); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total timed-out sessions via TIMEOUT :"UINT64F"\n",atomic_uint64_get(&m->totalstats.total_timeout_sess)); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total timed-out sessions via TIMEOUT :"UINT64F"\n",atomic64_get(&m->totalstats.total_timeout_sess)); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total timed-out sessions via SILENT_TIMEOUT :"UINT64F"\n",atomic_uint64_get(&m->totalstats.total_silent_timeout_sess)); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total timed-out sessions via SILENT_TIMEOUT :"UINT64F"\n",atomic64_get(&m->totalstats.total_silent_timeout_sess)); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total regular terminated sessions :"UINT64F"\n",atomic_uint64_get(&m->totalstats.total_regular_term_sess)); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total regular terminated sessions :"UINT64F"\n",atomic64_get(&m->totalstats.total_regular_term_sess)); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total forced terminated sessions :"UINT64F"\n",atomic_uint64_get(&m->totalstats.total_forced_term_sess)); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total forced terminated sessions :"UINT64F"\n",atomic64_get(&m->totalstats.total_forced_term_sess)); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total relayed packets :"UINT64F"\n",atomic_uint64_get(&m->totalstats.total_relayed_packets)); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total relayed packets :"UINT64F"\n",atomic64_get(&m->totalstats.total_relayed_packets)); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total relayed packet errors :"UINT64F"\n",atomic_uint64_get(&m->totalstats.total_relayed_errors)); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total relayed packet errors :"UINT64F"\n",atomic64_get(&m->totalstats.total_relayed_errors)); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total number of streams with no relayed packets :"UINT64F"\n", atomic_uint64_get(&m->totalstats.total_nopacket_relayed_sess)); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total number of streams with no relayed packets :"UINT64F"\n", atomic64_get(&m->totalstats.total_nopacket_relayed_sess)); ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total number of 1-way streams :"UINT64F"\n",atomic_uint64_get(&m->totalstats.total_oneway_stream_sess)); + printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total number of 1-way streams :"UINT64F"\n",atomic64_get(&m->totalstats.total_oneway_stream_sess)); ADJUSTLEN(printlen,outbufend,replybuffer); printlen = snprintf(replybuffer,(outbufend-replybuffer), " Average call duration :%ld.%06ld\n\n",avg.tv_sec,avg.tv_usec); ADJUSTLEN(printlen,outbufend,replybuffer); @@ -151,15 +151,15 @@ static void cli_incoming_list_callid(char* buffer, int len, struct callmaster* m continue; printlen = snprintf(replybuffer,(outbufend-replybuffer), "------ Media #%u, port %5u <> %15s:%-5hu%s, " - ""UINT64F" p, "UINT64F" b, "UINT64F" e, %llu last_packet\n", + ""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)" : "", - atomic_uint64_get(&ps->stats.packets), - atomic_uint64_get(&ps->stats.bytes), - atomic_uint64_get(&ps->stats.errors), - (unsigned long long) atomic_uint64_get(&ps->last_packet)); + atomic64_get(&ps->stats.packets), + atomic64_get(&ps->stats.bytes), + atomic64_get(&ps->stats.errors), + atomic64_get(&ps->last_packet)); ADJUSTLEN(printlen,outbufend,replybuffer); } } diff --git a/daemon/graphite.c b/daemon/graphite.c index 1c76ccfbb..ba96e2e6a 100644 --- a/daemon/graphite.c +++ b/daemon/graphite.c @@ -90,14 +90,14 @@ int send_graphite_data() { struct totalstats ts; /* atomically copy values to stack and reset to zero */ - atomic_uint64_local_copy_zero_struct(&ts, &cm->totalstats_interval, total_timeout_sess); - atomic_uint64_local_copy_zero_struct(&ts, &cm->totalstats_interval, total_silent_timeout_sess); - atomic_uint64_local_copy_zero_struct(&ts, &cm->totalstats_interval, total_regular_term_sess); - atomic_uint64_local_copy_zero_struct(&ts, &cm->totalstats_interval, total_forced_term_sess); - atomic_uint64_local_copy_zero_struct(&ts, &cm->totalstats_interval, total_relayed_packets); - atomic_uint64_local_copy_zero_struct(&ts, &cm->totalstats_interval, total_relayed_errors); - atomic_uint64_local_copy_zero_struct(&ts, &cm->totalstats_interval, total_nopacket_relayed_sess); - atomic_uint64_local_copy_zero_struct(&ts, &cm->totalstats_interval, total_oneway_stream_sess); + atomic64_local_copy_zero_struct(&ts, &cm->totalstats_interval, total_timeout_sess); + atomic64_local_copy_zero_struct(&ts, &cm->totalstats_interval, total_silent_timeout_sess); + atomic64_local_copy_zero_struct(&ts, &cm->totalstats_interval, total_regular_term_sess); + atomic64_local_copy_zero_struct(&ts, &cm->totalstats_interval, total_forced_term_sess); + atomic64_local_copy_zero_struct(&ts, &cm->totalstats_interval, total_relayed_packets); + atomic64_local_copy_zero_struct(&ts, &cm->totalstats_interval, total_relayed_errors); + atomic64_local_copy_zero_struct(&ts, &cm->totalstats_interval, total_nopacket_relayed_sess); + atomic64_local_copy_zero_struct(&ts, &cm->totalstats_interval, total_oneway_stream_sess); mutex_lock(&cm->totalstats_interval.total_average_lock); ts.total_average_call_dur = cm->totalstats_interval.total_average_call_dur; @@ -107,16 +107,16 @@ int send_graphite_data() { mutex_unlock(&cm->totalstats_interval.total_average_lock); 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); ptr += rc; - rc = sprintf(ptr,"%s.totals.average_call_dur.tv_usec %llu %llu\n",hostname, (unsigned long long) ts.total_average_call_dur.tv_usec,(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.forced_term_sess "UINT64F" %llu\n",hostname, atomic_uint64_get_na(&ts.total_forced_term_sess),(unsigned long long)g_now); 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); 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); ptr += rc; rc = sprintf(ptr,"%s.totals.managed_sess "UINT64F" %llu\n",hostname, ts.total_managed_sess,(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.nopacket_relayed_sess "UINT64F" %llu\n",hostname, atomic_uint64_get_na(&ts.total_nopacket_relayed_sess),(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.oneway_stream_sess "UINT64F" %llu\n",hostname, atomic_uint64_get_na(&ts.total_oneway_stream_sess),(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.regular_term_sess "UINT64F" %llu\n",hostname, atomic_uint64_get_na(&ts.total_regular_term_sess),(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.relayed_errors "UINT64F" %llu\n",hostname, atomic_uint64_get_na(&ts.total_relayed_errors),(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.relayed_packets "UINT64F" %llu\n",hostname, atomic_uint64_get_na(&ts.total_relayed_packets),(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.silent_timeout_sess "UINT64F" %llu\n",hostname, atomic_uint64_get_na(&ts.total_silent_timeout_sess),(unsigned long long)g_now); ptr += rc; - rc = sprintf(ptr,"%s.totals.timeout_sess "UINT64F" %llu\n",hostname, atomic_uint64_get_na(&ts.total_timeout_sess),(unsigned long long)g_now); 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); 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); 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); 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); 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); 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); 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); ptr += rc; rc = write(graphite_sock, data_to_send, strlen(data_to_send)); if (rc<0) { From 49328cd8b52b0a482527af5330247e4177d4aa76 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Tue, 10 Feb 2015 10:12:54 -0500 Subject: [PATCH 12/82] implement atomic64 for non-64-bit platforms --- daemon/aux.c | 4 ++++ daemon/aux.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/daemon/aux.c b/daemon/aux.c index ad34fc449..3331dd2f0 100644 --- a/daemon/aux.c +++ b/daemon/aux.c @@ -34,6 +34,10 @@ static cond_t threads_cond = COND_STATIC_INIT; static struct thread_buf __thread t_bufs[NUM_THREAD_BUFS]; static int __thread t_buf_idx; +#ifdef NEED_ATOMIC64_MUTEX +mutex_t __atomic64_mutex = MUTEX_STATIC_INIT; +#endif + GList *g_list_link(GList *list, GList *el) { diff --git a/daemon/aux.h b/daemon/aux.h index 6b748bae1..0a9ad5893 100644 --- a/daemon/aux.h +++ b/daemon/aux.h @@ -530,9 +530,6 @@ INLINE void atomic64_set(atomic64 *u, u_int64_t a) { INLINE void atomic64_set_na(atomic64 *u, u_int64_t a) { u->p = (void *) a; } -INLINE void atomic64_inc(atomic64 *u) { - g_atomic_pointer_add(&u->p, 1); -} INLINE void atomic64_add(atomic64 *u, u_int64_t a) { g_atomic_pointer_add(&u->p, a); } @@ -547,14 +544,65 @@ INLINE u_int64_t atomic64_get_set(atomic64 *u, u_int64_t a) { return old; } while (1); } + +#else + +/* Simulate atomic u64 with a global mutex on non-64-bit platforms. + * Bad performance possible, thus not recommended. */ + +typedef struct { + u_int64_t u; +} atomic64; + +#define NEED_ATOMIC64_MUTEX +extern mutex_t __atomic64_mutex; + +INLINE u_int64_t atomic64_get(const atomic64 *u) { + u_int64_t ret; + mutex_lock(&__atomic64_mutex); + ret = u->u; + mutex_unlock(&__atomic64_mutex); + return ret; +} +INLINE u_int64_t atomic64_get_na(const atomic64 *u) { + return u->u; +} +INLINE void atomic64_set(atomic64 *u, u_int64_t a) { + mutex_lock(&__atomic64_mutex); + u->u = a; + mutex_unlock(&__atomic64_mutex); +} +INLINE void atomic64_set_na(atomic64 *u, u_int64_t a) { + u->u = a; +} +INLINE void atomic64_add(atomic64 *u, u_int64_t a) { + mutex_lock(&__atomic64_mutex); + u->u += a; + mutex_unlock(&__atomic64_mutex); +} +INLINE void atomic64_add_na(atomic64 *u, u_int64_t a) { + u->u += a; +} +INLINE u_int64_t atomic64_get_set(atomic64 *u, u_int64_t a) { + u_int64_t old; + mutex_lock(&__atomic64_mutex); + old = u->u; + u->u = a; + mutex_unlock(&__atomic64_mutex); + return old; +} + +#endif + +INLINE void atomic64_inc(atomic64 *u) { + atomic64_add(u, 1); +} INLINE void atomic64_local_copy_zero(atomic64 *dst, atomic64 *src) { atomic64_set_na(dst, atomic64_get_set(src, 0)); } #define atomic64_local_copy_zero_struct(d, s, member) \ atomic64_local_copy_zero(&((d)->member), &((s)->member)) -#endif - #endif From da6b6a119aec6b5038fdf6534b2347166fb98efc Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Tue, 10 Feb 2015 10:40:40 -0500 Subject: [PATCH 13/82] fix compiler warning on 32bit --- daemon/sdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/sdp.c b/daemon/sdp.c index 497adc757..f0aa3030c 100644 --- a/daemon/sdp.c +++ b/daemon/sdp.c @@ -910,7 +910,7 @@ int sdp_parse(str *body, GQueue *sessions) { return 0; error: - ilog(LOG_WARNING, "Error parsing SDP at offset %li: %s", b - body->s, errstr); + ilog(LOG_WARNING, "Error parsing SDP at offset %li: %s", (long) (b - body->s), errstr); sdp_free(sessions); return -1; } From ca149e6b81b8f255535c7603684d538760886a57 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Tue, 10 Feb 2015 10:46:29 -0500 Subject: [PATCH 14/82] "long" can be 32 bits only and may truncate math --- daemon/call.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index 9a31e08f2..b3652e99e 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -2366,31 +2366,31 @@ static void unkernelize(struct packet_stream *p) { } void timeval_subtract (struct timeval *result, const struct timeval *a, const struct timeval *b) { - long microseconds=0; - microseconds = ((long)a->tv_sec - (long)b->tv_sec) * 1000000 + ((long)a->tv_usec - (long)b->tv_usec); - result->tv_sec = microseconds/(long)1000000; - result->tv_usec = microseconds%(long)1000000; + u_int64_t microseconds=0; + microseconds = ((u_int64_t)a->tv_sec - (u_int64_t)b->tv_sec) * 1000000LLU + (a->tv_usec - b->tv_usec); + result->tv_sec = microseconds/1000000LLU; + result->tv_usec = microseconds%1000000LLU; } void timeval_multiply(struct timeval *result, const struct timeval *a, const long multiplier) { - long microseconds=0; - microseconds = (((long)a->tv_sec * 1000000) + (long)a->tv_usec) * multiplier; - result->tv_sec = microseconds/(long)1000000; - result->tv_usec = microseconds%(long)1000000; + u_int64_t microseconds=0; + microseconds = (((u_int64_t)a->tv_sec * 1000000LLU) + a->tv_usec) * multiplier; + result->tv_sec = microseconds/1000000LLU; + result->tv_usec = microseconds%1000000LLU; } void timeval_divide(struct timeval *result, const struct timeval *a, const long divisor) { - long microseconds=0; - microseconds = (((long)a->tv_sec * 1000000) + (long)a->tv_usec) / divisor; - result->tv_sec = microseconds/(long)1000000; - result->tv_usec = microseconds%(long)1000000; + u_int64_t microseconds=0; + microseconds = (((u_int64_t)a->tv_sec * 1000000LLU) + a->tv_usec) / divisor; + result->tv_sec = microseconds/1000000LLU; + result->tv_usec = microseconds%1000000LLU; } void timeval_add(struct timeval *result, const struct timeval *a, const struct timeval *b) { - long microseconds=0; - microseconds = ((long)a->tv_sec + (long)b->tv_sec) * (long)1000000 + ((long)a->tv_usec + (long)b->tv_usec); - result->tv_sec = microseconds/(long)1000000; - result->tv_usec = microseconds%(long)1000000; + u_int64_t microseconds=0; + microseconds = ((u_int64_t)a->tv_sec + (u_int64_t)b->tv_sec) * 1000000LLU + (a->tv_usec + b->tv_usec); + result->tv_sec = microseconds/1000000LLU; + result->tv_usec = microseconds%1000000LLU; } static void timeval_totalstats_average_add(struct totalstats *s, const struct timeval *add) { From b023d6c173875289dff8ccf0ea4622a45ba8c4ef Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Tue, 10 Feb 2015 12:06:40 -0500 Subject: [PATCH 15/82] update redis mod version to 7 --- daemon/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/main.c b/daemon/main.c index 50da00044..9154871f1 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -30,7 +30,7 @@ -#define REDIS_MODULE_VERSION "redis/6" +#define REDIS_MODULE_VERSION "redis/7" From adeb0b33a3d53aa60cd976072be60f57d6fb81bd Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Wed, 11 Feb 2015 11:29:12 -0500 Subject: [PATCH 16/82] the log rate limiter must take the prefix into account --- daemon/log.c | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/daemon/log.c b/daemon/log.c index 6991f92b0..f8e4b6854 100644 --- a/daemon/log.c +++ b/daemon/log.c @@ -9,6 +9,14 @@ + +struct log_limiter_entry { + char *prefix; + char *msg; +}; + + + struct log_info __thread log_info; #ifndef __DEBUG volatile gint log_level = LOG_INFO; @@ -121,6 +129,10 @@ void __ilog(int prio, const char *fmt, ...) { if ((prio & LOG_FLAG_LIMIT)) { time_t when; + struct log_limiter_entry lle, *llep; + + lle.prefix = prefix; + lle.msg = msg; mutex_lock(&__log_limiter_lock); @@ -130,10 +142,13 @@ void __ilog(int prio, const char *fmt, ...) { __log_limiter_count = 0; } - when = (time_t) g_hash_table_lookup(__log_limiter, msg); + when = (time_t) g_hash_table_lookup(__log_limiter, &lle); if (!when || (poller_now - when) >= 15) { - g_hash_table_insert(__log_limiter, g_string_chunk_insert(__log_limiter_strings, - msg), (void *) poller_now); + lle.prefix = g_string_chunk_insert(__log_limiter_strings, prefix); + lle.msg = g_string_chunk_insert(__log_limiter_strings, msg); + llep = (void *) g_string_chunk_insert_len(__log_limiter_strings, + (void *) &lle, sizeof(lle)); + g_hash_table_insert(__log_limiter, llep, (void *) poller_now); __log_limiter_count++; when = 0; } @@ -167,8 +182,21 @@ void cdrlog(const char* cdrbuffer) { setlogmask(previous); } +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)) + return 0; + if (!g_str_equal(A->prefix, B->prefix)) + return 0; + return 1; +} + void log_init() { mutex_init(&__log_limiter_lock); - __log_limiter = g_hash_table_new(g_str_hash, g_str_equal); + __log_limiter = g_hash_table_new(log_limiter_entry_hash, log_limiter_entry_equal); __log_limiter_strings = g_string_chunk_new(1024); } From fb667a8dbae72d51df944d4e7d9b0cc32a11c58d Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Wed, 11 Feb 2015 12:31:40 -0500 Subject: [PATCH 17/82] turn tag and termination texts into array lookups --- daemon/aux.h | 15 +++++++++++++++ daemon/call.c | 34 ++++++++++++++++------------------ daemon/call.h | 2 +- daemon/cli.c | 3 +-- 4 files changed, 33 insertions(+), 21 deletions(-) diff --git a/daemon/aux.h b/daemon/aux.h index 0a9ad5893..099cffc92 100644 --- a/daemon/aux.h +++ b/daemon/aux.h @@ -605,4 +605,19 @@ INLINE void atomic64_local_copy_zero(atomic64 *dst, atomic64 *src) { + +INLINE const char *__get_enum_array_text(const char * const *array, unsigned int idx, + unsigned int len, const char *deflt) +{ + const char *ret; + if (idx >= len) + return deflt; + ret = array[idx]; + return ret ? : deflt; +} +#define get_enum_array_text(array, idx, deflt) \ + __get_enum_array_text(array, idx, G_N_ELEMENTS(array), deflt) + + + #endif diff --git a/daemon/call.c b/daemon/call.c index b3652e99e..76c406069 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -119,22 +119,22 @@ const struct transport_protocol transport_protocols[] = { }; const int num_transport_protocols = G_N_ELEMENTS(transport_protocols); -const char * get_term_reason_text(char *buf, enum termination_reason t) { - if (t==TIMEOUT) { buf = "TIMEOUT"; return buf; } - if (t==REGULAR) { buf = "REGULAR"; return buf; } - if (t==FORCED) { buf = "FORCED"; return buf; } - if (t==SILENT_TIMEOUT) { buf = "SILENT_TIMEOUT"; return buf; } +static const char * const __term_reason_texts[] = { + [TIMEOUT] = "TIMEOUT", + [REGULAR] = "REGULAR", + [FORCED] = "FORCED", + [SILENT_TIMEOUT] = "SILENT_TIMEOUT", +}; +static const char * const __tag_type_texts[] = { + [FROM_TAG] = "FROM_TAG", + [TO_TAG] = "TO_TAG", +}; - buf = "UNKNOWN"; - return buf; +static const char * get_term_reason_text(enum termination_reason t) { + return get_enum_array_text(__term_reason_texts, t, "UNKNOWN"); } - -const char * get_tag_type_text(char *buf, enum tag_type t) { - if (t==FROM_TAG) { buf = "FROM_TAG"; return buf; } - if (t==TO_TAG) { buf = "TO_TAG"; return buf; } - - buf = "UNKNOWN"; - return buf; +const char * get_tag_type_text(enum tag_type t) { + return get_enum_array_text(__tag_type_texts, t, "UNKNOWN"); } static void determine_handler(struct packet_stream *in, const struct packet_stream *out); @@ -2431,8 +2431,6 @@ void call_destroy(struct call *c) { GList *k, *o; struct timeval tim_result_duration; static const int CDRBUFLENGTH = 4096*2; - char reasonbuf[16]; memset(&reasonbuf,0,16); - char tagtypebuf[16]; memset(&tagtypebuf,0,16); char cdrbuffer[CDRBUFLENGTH]; memset(&cdrbuffer,0,CDRBUFLENGTH); char* cdrbufcur = cdrbuffer; int cdrlinecnt = 0; @@ -2474,9 +2472,9 @@ void call_destroy(struct call *c) { cdrlinecnt, ml->started.tv_sec, ml->started.tv_usec, cdrlinecnt, ml->terminated.tv_sec, ml->terminated.tv_usec, cdrlinecnt, tim_result_duration.tv_sec, tim_result_duration.tv_usec, - cdrlinecnt, get_term_reason_text(reasonbuf,ml->term_reason), + cdrlinecnt, get_term_reason_text(ml->term_reason), cdrlinecnt, ml->tag.s, - cdrlinecnt, get_tag_type_text(tagtypebuf,ml->tagtype), + cdrlinecnt, get_tag_type_text(ml->tagtype), cdrlinecnt, ml->active_dialogue ? ml->active_dialogue->tag.s : "(none)"); } diff --git a/daemon/call.h b/daemon/call.h index 67572d0de..aa6146505 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -534,6 +534,6 @@ INLINE struct packet_stream *packet_stream_sink(struct packet_stream *ps) { return ret; } -const char * get_tag_type_text(char *buf, enum tag_type t); +const char * get_tag_type_text(enum tag_type t); #endif diff --git a/daemon/cli.c b/daemon/cli.c index 57cf70b7e..1fd39ba05 100644 --- a/daemon/cli.c +++ b/daemon/cli.c @@ -100,7 +100,6 @@ static void cli_incoming_list_callid(char* buffer, int len, struct callmaster* m GSList *l; GList *k, *o; int printlen=0; - char tagtypebuf[16]; memset(&tagtypebuf,0,16); struct timeval tim_result_duration; memset(&tim_result_duration,0,sizeof(struct timeval)); struct timeval now; memset(&now,0,sizeof(struct timeval)); @@ -134,7 +133,7 @@ static void cli_incoming_list_callid(char* buffer, int len, struct callmaster* m timeval_subtract(&tim_result_duration,&now,&ml->started); printlen = snprintf(replybuffer,(outbufend-replybuffer), "--- Tag '"STR_FORMAT"' type: %s, callduration " "%ld.%06ld , in dialogue with '"STR_FORMAT"'\n", - STR_FMT(&ml->tag), get_tag_type_text(tagtypebuf,ml->tagtype), + STR_FMT(&ml->tag), get_tag_type_text(ml->tagtype), tim_result_duration.tv_sec, tim_result_duration.tv_usec, ml->active_dialogue ? ml->active_dialogue->tag.len : 6, From d79c68f5324430645a139eb40258211e513730ec Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Wed, 11 Feb 2015 12:50:42 -0500 Subject: [PATCH 18/82] remove some unnecessary memsets --- daemon/call.c | 3 +-- daemon/cli.c | 10 ++++------ daemon/graphite.c | 4 ++-- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index 76c406069..ef1d30f51 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -2431,11 +2431,10 @@ void call_destroy(struct call *c) { GList *k, *o; struct timeval tim_result_duration; static const int CDRBUFLENGTH = 4096*2; - char cdrbuffer[CDRBUFLENGTH]; memset(&cdrbuffer,0,CDRBUFLENGTH); + char cdrbuffer[CDRBUFLENGTH]; char* cdrbufcur = cdrbuffer; int cdrlinecnt = 0; int found = 0; - //char tmpstreampairstatus[2]; memset(&tmpstreampairstatus,0,2); rwlock_lock_w(&m->hashlock); ret = g_hash_table_remove(m->callhash, &c->callid); diff --git a/daemon/cli.c b/daemon/cli.c index 1fd39ba05..731b5d67f 100644 --- a/daemon/cli.c +++ b/daemon/cli.c @@ -100,8 +100,8 @@ static void cli_incoming_list_callid(char* buffer, int len, struct callmaster* m GSList *l; GList *k, *o; int printlen=0; - struct timeval tim_result_duration; memset(&tim_result_duration,0,sizeof(struct timeval)); - struct timeval now; memset(&now,0,sizeof(struct timeval)); + struct timeval tim_result_duration; + struct timeval now; if (len<=1) { printlen = snprintf(replybuffer,(outbufend-replybuffer), "%s\n", "More parameters required."); @@ -247,7 +247,6 @@ static void cli_incoming_terminate(char* buffer, int len, struct callmaster* m, if (!c->ml_deleted) { for (i = c->monologues; i; i = i->next) { ml = i->data; - memset(&ml->terminated,0,sizeof(struct timeval)); gettimeofday(&(ml->terminated), NULL); ml->term_reason = FORCED; } @@ -272,7 +271,6 @@ static void cli_incoming_terminate(char* buffer, int len, struct callmaster* m, if (!c->ml_deleted) { for (i = c->monologues; i; i = i->next) { ml = i->data; - memset(&ml->terminated,0,sizeof(struct timeval)); gettimeofday(&(ml->terminated), NULL); ml->term_reason = FORCED; } @@ -294,11 +292,11 @@ static void cli_incoming(int fd, void *p, uintptr_t u) { struct cli *cli = (void *) p; socklen_t sinl; static const int BUFLENGTH = 4096*1024; - char replybuffer[BUFLENGTH]; memset(&replybuffer,0,BUFLENGTH); + char replybuffer[BUFLENGTH]; char* outbuf = replybuffer; const char* outbufend = replybuffer+BUFLENGTH; static const int MAXINPUT = 1024; - char inbuf[MAXINPUT]; memset(&inbuf,0,MAXINPUT); + char inbuf[MAXINPUT]; int inlen = 0, readbytes = 0; int rc=0; diff --git a/daemon/graphite.c b/daemon/graphite.c index ba96e2e6a..f22c1bd9b 100644 --- a/daemon/graphite.c +++ b/daemon/graphite.c @@ -77,14 +77,14 @@ int send_graphite_data() { } // format hostname "." totals.subkey SPACE value SPACE timestamp - char hostname[256]; memset(&hostname,0,256); + char hostname[256]; rc = gethostname(hostname,256); if (rc<0) { ilog(LOG_ERROR, "Could not retrieve host name information."); goto error; } - char data_to_send[8192]; memset(&data_to_send,0,8192); + char data_to_send[8192]; char* ptr = data_to_send; struct totalstats ts; From a5f7315a72ea5fc94968362dc9392339684134e6 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Wed, 11 Feb 2015 12:51:47 -0500 Subject: [PATCH 19/82] string handling fixes and improvements --- daemon/cli.c | 9 +++++---- daemon/graphite.c | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/daemon/cli.c b/daemon/cli.c index 731b5d67f..711812531 100644 --- a/daemon/cli.c +++ b/daemon/cli.c @@ -306,7 +306,7 @@ next: nfd = accept(fd, (struct sockaddr *) &sin, &sinl); if (nfd == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { - sprintf(replybuffer, "Could currently not accept CLI commands. Reason:%s\n", strerror(errno)); + ilog(LOG_INFO, "Could currently not accept CLI commands. Reason:%s", strerror(errno)); goto cleanup; } ilog(LOG_INFO, "Accept error:%s", strerror(errno)); @@ -325,17 +325,18 @@ next: ilog(LOG_INFO, "Could currently not read CLI commands. Reason:%s", strerror(errno)); } inlen += readbytes; - } while (readbytes > 0); + } while (readbytes > 0 && inlen < sizeof(inbuf)-1); + inbuf[inlen] = 0; ilog(LOG_INFO, "Got CLI command:%s",inbuf); static const char* LIST = "list"; static const char* TERMINATE = "terminate"; - if (inlen>=strlen(LIST) && strncmp(inbuf,LIST,strlen(LIST)) == 0) { + if (strncmp(inbuf,LIST,strlen(LIST)) == 0) { cli_incoming_list(inbuf+strlen(LIST), inlen-strlen(LIST), cli->callmaster, outbuf, outbufend); - } else if (inlen>=strlen(TERMINATE) && strncmp(inbuf,TERMINATE,strlen(TERMINATE)) == 0) { + } else if (strncmp(inbuf,TERMINATE,strlen(TERMINATE)) == 0) { cli_incoming_terminate(inbuf+strlen(TERMINATE), inlen-strlen(TERMINATE), cli->callmaster, outbuf, outbufend); } else { sprintf(replybuffer, "%s:%s\n", "Unknown or incomplete command:", inbuf); diff --git a/daemon/graphite.c b/daemon/graphite.c index f22c1bd9b..8e5f8fca4 100644 --- a/daemon/graphite.c +++ b/daemon/graphite.c @@ -118,7 +118,7 @@ int send_graphite_data() { 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); 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); ptr += rc; - rc = write(graphite_sock, data_to_send, strlen(data_to_send)); + rc = write(graphite_sock, data_to_send, ptr - data_to_send); if (rc<0) { ilog(LOG_ERROR,"Could not write to graphite socket. Disconnecting graphite server."); goto error; From 037c6aa7dd998c45465d08d1521e699ea6eb1d9a Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Wed, 11 Feb 2015 14:02:29 -0500 Subject: [PATCH 20/82] shorter locking in stats loop --- daemon/call.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index ef1d30f51..b0720a228 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -1333,8 +1333,6 @@ static void callmaster_timer(void *ptr) { DS(bytes); DS(errors); - mutex_lock(&ps->in_lock); - if (ke->stats.packets != atomic64_get(&ps->kernel_stats.packets)) atomic64_set(&ps->last_packet, poller_now); @@ -1346,24 +1344,25 @@ static void callmaster_timer(void *ptr) { sink = packet_stream_sink(ps); - if (sink) - mutex_lock(&sink->out_lock); - /* XXX this only works if the kernel module actually gets to see the packets. */ - if (sink && sink->crypto.params.crypto_suite - && ke->target.encrypt.last_index - sink->crypto.last_index > 0x4000) { - sink->crypto.last_index = ke->target.encrypt.last_index; - update = 1; + if (sink) { + mutex_lock(&sink->out_lock); + if (sink->crypto.params.crypto_suite + && ke->target.encrypt.last_index - sink->crypto.last_index > 0x4000) { + sink->crypto.last_index = ke->target.encrypt.last_index; + update = 1; + } + mutex_unlock(&sink->out_lock); } + + mutex_lock(&ps->in_lock); if (sfd->crypto.params.crypto_suite && ke->target.decrypt.last_index - sfd->crypto.last_index > 0x4000) { sfd->crypto.last_index = ke->target.decrypt.last_index; update = 1; } - - if (sink) - mutex_unlock(&sink->out_lock); mutex_unlock(&ps->in_lock); + rwlock_unlock_r(&sfd->call->master_lock); if (update) From f7a15946dcad7aa189296bafe24ee0022c20f805 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Wed, 11 Feb 2015 14:09:32 -0500 Subject: [PATCH 21/82] more descriptive warning for failed kernelizing --- daemon/call.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/daemon/call.c b/daemon/call.c index b0720a228..a26715a4c 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -361,11 +361,13 @@ void kernelize(struct packet_stream *stream) { struct callmaster *cm = call->callmaster; struct packet_stream *sink = NULL; struct interface_address *ifa; + const char *nk_warn_msg; if (PS_ISSET(stream, KERNELIZED)) return; if (cm->conf.kernelid < 0) goto no_kernel; + nk_warn_msg = "interface to kernel module not open"; if (cm->conf.kernelfd < 0) goto no_kernel_warn; if (!PS_ISSET(stream, RTP)) @@ -386,6 +388,7 @@ void kernelize(struct packet_stream *stream) { if (is_addr_unspecified(&sink->advertised_endpoint.ip46) || !sink->advertised_endpoint.port) goto no_kernel; + nk_warn_msg = "protocol not supported by kernel module"; if (!stream->handler->in->kernel || !stream->handler->out->kernel) goto no_kernel_warn; @@ -426,8 +429,10 @@ void kernelize(struct packet_stream *stream) { mutex_unlock(&sink->out_lock); + nk_warn_msg = "encryption cipher or HMAC not supported by kernel module"; if (!reti.encrypt.cipher || !reti.encrypt.hmac) goto no_kernel_warn; + nk_warn_msg = "decryption cipher or HMAC not supported by kernel module"; if (!reti.decrypt.cipher || !reti.decrypt.hmac) goto no_kernel_warn; @@ -439,7 +444,7 @@ void kernelize(struct packet_stream *stream) { return; no_kernel_warn: - ilog(LOG_WARNING, "No support for kernel packet forwarding available"); + ilog(LOG_WARNING, "No support for kernel packet forwarding available (%s)", nk_warn_msg); no_kernel: PS_SET(stream, KERNELIZED); PS_SET(stream, NO_KERNEL_SUPPORT); From 5fa9902de93efde2b39748030afa67b1d0b1dfa5 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Wed, 11 Feb 2015 14:17:31 -0500 Subject: [PATCH 22/82] atomic vars should be volatile --- daemon/aux.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/aux.h b/daemon/aux.h index 099cffc92..80bf36c4b 100644 --- a/daemon/aux.h +++ b/daemon/aux.h @@ -515,7 +515,7 @@ int in6_addr_eq(const void *a, const void *b); #if GLIB_SIZEOF_VOID_P >= 8 typedef struct { - void *p; + volatile void *p; } atomic64; INLINE u_int64_t atomic64_get(const atomic64 *u) { From c21193a329f452446c69ed82e888ed83669d0a82 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Thu, 12 Feb 2015 11:22:16 -0500 Subject: [PATCH 23/82] fix byte stats in kernel module --- kernel-module/xt_RTPENGINE.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel-module/xt_RTPENGINE.c b/kernel-module/xt_RTPENGINE.c index 798653040..bc43b0324 100644 --- a/kernel-module/xt_RTPENGINE.c +++ b/kernel-module/xt_RTPENGINE.c @@ -2256,7 +2256,7 @@ out: g->stats.errors++; else { g->stats.packets++; - g->stats.bytes += skb->len; + g->stats.bytes += datalen; } spin_unlock_irqrestore(&g->stats_lock, flags); From c2ff5c3fd3821c74395d594c99d2069c651fc3fd Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Thu, 12 Feb 2015 12:10:15 -0500 Subject: [PATCH 24/82] MT#9935 understand, learn, remember and report RTP payload types (codecs) Squashed commit of the following: commit cca40e8e311e6884204289687ba2a05d0855720b Author: Richard Fuchs Date: Thu Feb 12 10:17:51 2015 -0500 support per-payload-type stats in kernel module commit dcc0dc0002bd552ae7c99aa58311af2f81336a8f Author: Richard Fuchs Date: Wed Feb 11 12:14:44 2015 -0500 count unknown rtp type as error commit 941bde0df59720d1d3ef6660096cf2532a5c7e1c Author: Richard Fuchs Date: Wed Feb 11 12:01:55 2015 -0500 use the list of rtp formats from the m= line This avoids dynamically altering the rtpstats hash table and makes keeping packet stats lock free. commit 9150fed671d8490f4c09fb3050002c7c558391df Author: Richard Fuchs Date: Fri Feb 6 15:06:04 2015 -0500 fix and simplify rtpmap hash table commit 1f73741cbf2ac7d6b8d0a54d9562e9a550678e7c Author: Richard Fuchs Date: Fri Feb 6 13:56:07 2015 -0500 MT#9935 understand, learn, remember and report RTP payload types (codecs) commit b0d690837c02989485cf73927a89ed860a299807 Author: Richard Fuchs Date: Thu Nov 20 13:36:25 2014 -0500 parse a=rtpmap attribute --- daemon/aux.h | 5 + daemon/call.c | 205 +++++++++++++++++++++++++++++++++- daemon/call.h | 18 +++ daemon/call_interfaces.c | 21 ++-- daemon/rtcp.c | 8 +- daemon/rtp.c | 64 ++++++++++- daemon/rtp.h | 11 ++ daemon/sdp.c | 206 ++++++++++++++++++++++++++++------- daemon/str.c | 4 + daemon/str.h | 4 + kernel-module/xt_RTPENGINE.c | 47 +++++++- kernel-module/xt_RTPENGINE.h | 15 +++ tests/simulator-ng.pl | 3 +- 13 files changed, 555 insertions(+), 56 deletions(-) diff --git a/daemon/aux.h b/daemon/aux.h index 80bf36c4b..51aab5bc3 100644 --- a/daemon/aux.h +++ b/daemon/aux.h @@ -86,6 +86,11 @@ INLINE void g_queue_truncate(GQueue *q, unsigned int len) { while (q->length > len) g_queue_pop_tail(q); } +INLINE void g_queue_clear_full(GQueue *q, GDestroyNotify free_func) { + void *p; + while ((p = g_queue_pop_head(q))) + free_func(p); +} INLINE void strmove(char **d, char **s) { diff --git a/daemon/call.c b/daemon/call.c index a26715a4c..1436f61a9 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -77,42 +77,49 @@ const struct transport_protocol transport_protocols[] = { [PROTO_RTP_AVP] = { .index = PROTO_RTP_AVP, .name = "RTP/AVP", + .rtp = 1, .srtp = 0, .avpf = 0, }, [PROTO_RTP_SAVP] = { .index = PROTO_RTP_SAVP, .name = "RTP/SAVP", + .rtp = 1, .srtp = 1, .avpf = 0, }, [PROTO_RTP_AVPF] = { .index = PROTO_RTP_AVPF, .name = "RTP/AVPF", + .rtp = 1, .srtp = 0, .avpf = 1, }, [PROTO_RTP_SAVPF] = { .index = PROTO_RTP_SAVPF, .name = "RTP/SAVPF", + .rtp = 1, .srtp = 1, .avpf = 1, }, [PROTO_UDP_TLS_RTP_SAVP] = { .index = PROTO_UDP_TLS_RTP_SAVP, .name = "UDP/TLS/RTP/SAVP", + .rtp = 1, .srtp = 1, .avpf = 0, }, [PROTO_UDP_TLS_RTP_SAVPF] = { .index = PROTO_UDP_TLS_RTP_SAVPF, .name = "UDP/TLS/RTP/SAVPF", + .rtp = 1, .srtp = 1, .avpf = 1, }, [PROTO_UDPTL] = { .index = PROTO_UDPTL, .name = "udptl", + .rtp = 0, .srtp = 0, .avpf = 0, }, @@ -354,6 +361,16 @@ INLINE void __re_address_translate(struct re_address *o, const struct endpoint * o->port = ep->port; } +static int __rtp_stats_pt_sort(const void *ap, const void *bp) { + const struct rtp_stats *a = ap, *b = bp; + + if (a->payload_type < b->payload_type) + return -1; + if (a->payload_type > b->payload_type) + return 1; + return 0; +} + /* called with in_lock held */ void kernelize(struct packet_stream *stream) { struct rtpengine_target_info reti; @@ -438,6 +455,23 @@ void kernelize(struct packet_stream *stream) { ZERO(stream->kernel_stats); + if (stream->media->protocol && stream->media->protocol->rtp) { + GList *values, *l; + struct rtp_stats *rs; + + reti.rtp = 1; + values = g_hash_table_get_values(stream->rtp_stats); + values = g_list_sort(values, __rtp_stats_pt_sort); + for (l = values; l; l = l->next) { + if (reti.num_payload_types >= G_N_ELEMENTS(reti.payload_types)) { + ilog(LOG_WARNING, "Too many RTP payload types for kernel module"); + break; + } + rs = l->data; + reti.payload_types[reti.num_payload_types++] = rs->payload_type; + } + } + kernel_add_stream(cm->conf.kernelfd, &reti, 0); PS_SET(stream, KERNELIZED); @@ -588,6 +622,7 @@ void stream_msg_mh_src(struct packet_stream *ps, struct msghdr *mh) { } } +/* XXX split this function into pieces */ /* called lock-free */ static int stream_packet(struct stream_fd *sfd, str *s, struct sockaddr_in6 *fsin, struct in6_addr *dst) { struct packet_stream *stream, @@ -608,6 +643,8 @@ static int stream_packet(struct stream_fd *sfd, str *s, struct sockaddr_in6 *fsi struct endpoint endpoint; rewrite_func rwf_in, rwf_out; struct interface_address *loc_addr; + struct rtp_header *rtp_h; + struct rtp_stats *rtp_s; call = sfd->call; cm = call->callmaster; @@ -626,6 +663,9 @@ static int stream_packet(struct stream_fd *sfd, str *s, struct sockaddr_in6 *fsi if (!stream->sfd) goto done; + + /* demux other protocols running on this port */ + if (MEDIA_ISSET(media, DTLS) && is_dtls(s)) { ret = dtls(stream, s, fsin); if (!ret) @@ -672,6 +712,9 @@ loop_ok: mutex_unlock(&stream->in_lock); + + /* demux RTCP */ + in_srtp = stream; sink = stream->rtp_sink; if (!sink && PS_ISSET(stream, RTCP)) { @@ -690,6 +733,29 @@ loop_ok: if (rtcp && sink && sink->rtcp_sibling) out_srtp = sink->rtcp_sibling; + + /* stats per RTP payload type */ + + if (media->protocol && media->protocol->rtp && !rtcp && !rtp_payload(&rtp_h, NULL, s)) { + i = (rtp_h->m_pt & 0x7f); + + rtp_s = g_hash_table_lookup(stream->rtp_stats, &i); + if (!rtp_s) { + ilog(LOG_WARNING | LOG_FLAG_LIMIT, + "RTP packet with unknown payload type %u received", i); + atomic64_inc(&stream->stats.errors); + atomic64_inc(&cm->statsps.errors); + } + + else { + atomic64_inc(&rtp_s->packets); + atomic64_add(&rtp_s->bytes, s->len); + } + } + + + /* do we have somewhere to forward it to? */ + if (!sink || !sink->sfd || !out_srtp->sfd || !in_srtp->sfd) { ilog(LOG_WARNING, "RTP packet from %s discarded", addr); atomic64_inc(&stream->stats.errors); @@ -697,6 +763,9 @@ loop_ok: goto unlock_out; } + + /* transcoding stuff, in and out */ + mutex_lock(&in_srtp->in_lock); determine_handler(in_srtp, sink); @@ -1303,6 +1372,8 @@ static void callmaster_timer(void *ptr) { struct stats tmpstats; int j, update; struct stream_fd *sfd; + struct rtp_stats *rs; + unsigned int pt; ZERO(hlp); @@ -1345,6 +1416,21 @@ static void callmaster_timer(void *ptr) { atomic64_set(&ps->kernel_stats.packets, ke->stats.packets); atomic64_set(&ps->kernel_stats.errors, ke->stats.errors); + for (j = 0; j < ke->target.num_payload_types; j++) { + pt = ke->target.payload_types[j]; + rs = g_hash_table_lookup(ps->rtp_stats, &pt); + if (!rs) + continue; + if (ke->rtp_stats[j].packets > atomic64_get(&rs->packets)) + atomic64_add(&rs->packets, + ke->rtp_stats[j].packets - atomic64_get(&rs->packets)); + if (ke->rtp_stats[j].bytes > atomic64_get(&rs->bytes)) + atomic64_add(&rs->bytes, + ke->rtp_stats[j].bytes - atomic64_get(&rs->bytes)); + atomic64_set(&rs->kernel_packets, ke->rtp_stats[j].packets); + atomic64_set(&rs->kernel_bytes, ke->rtp_stats[j].bytes); + } + update = 0; sink = packet_stream_sink(ps); @@ -1588,6 +1674,10 @@ fail: return -1; } +static void __payload_type_free(void *p) { + g_slice_free1(sizeof(struct rtp_payload_type), p); +} + static struct call_media *__get_media(struct call_monologue *ml, GList **it, const struct stream_params *sp) { struct call_media *med; @@ -1613,6 +1703,7 @@ static struct call_media *__get_media(struct call_monologue *ml, GList **it, con med->call = ml->call; med->index = sp->index; call_str_cpy(ml->call, &med->type, &sp->type); + med->rtp_payload_types = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, __payload_type_free); g_queue_push_tail(&ml->medias, med); *it = ml->medias.tail; @@ -1751,6 +1842,10 @@ static int __wildcard_endpoint_map(struct call_media *media, unsigned int num_po return 0; } +static void __rtp_stats_free(void *p) { + g_slice_free1(sizeof(struct rtp_stats), p); +} + struct packet_stream *__packet_stream_new(struct call *call) { struct packet_stream *stream; @@ -1759,6 +1854,8 @@ struct packet_stream *__packet_stream_new(struct call *call) { mutex_init(&stream->out_lock); stream->call = call; atomic64_set_na(&stream->last_packet, poller_now); + stream->rtp_stats = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, __rtp_stats_free); + call->streams = g_slist_prepend(call->streams, stream); return stream; @@ -1822,6 +1919,32 @@ static int __init_stream(struct packet_stream *ps) { return 0; } +static void __rtp_stats_update(GHashTable *dst, GHashTable *src) { + struct rtp_stats *rs; + struct rtp_payload_type *pt; + GList *values, *l; + + /* "src" is a call_media->rtp_payload_types table, while "dst" is a + * packet_stream->rtp_stats table */ + + values = g_hash_table_get_values(src); + + for (l = values; l; l = l->next) { + pt = l->data; + rs = g_hash_table_lookup(dst, &pt->payload_type); + if (rs) + continue; + + rs = g_slice_alloc0(sizeof(*rs)); + rs->payload_type = pt->payload_type; + g_hash_table_insert(dst, &rs->payload_type, rs); + } + + g_list_free(values); + + /* we leave previously added but now removed payload types in place */ +} + static int __init_streams(struct call_media *A, struct call_media *B, const struct stream_params *sp) { GList *la, *lb; struct packet_stream *a, *ax, *b; @@ -1837,7 +1960,9 @@ static int __init_streams(struct call_media *A, struct call_media *B, const stru /* RTP */ a->rtp_sink = b; - PS_SET(a, RTP); + PS_SET(a, RTP); /* XXX technically not correct, could be udptl too */ + + __rtp_stats_update(a->rtp_stats, A->rtp_payload_types); if (sp) { __fill_stream(a, &sp->rtp_endpoint, port_off); @@ -2199,6 +2324,19 @@ static void __dtls_logic(const struct sdp_ng_flags *flags, struct call_media *me MEDIA_SET(other_media, DTLS); } +static void __rtp_payload_types(struct call_media *media, GQueue *types) { + struct rtp_payload_type *pt; + struct call *call = media->call; + + /* we steal the entire list to avoid duplicate allocs */ + while ((pt = g_queue_pop_head(types))) { + /* but we must duplicate the contents */ + call_str_cpy(call, &pt->encoding, &pt->encoding); + call_str_cpy(call, &pt->encoding_parameters, &pt->encoding_parameters); + g_hash_table_replace(media->rtp_payload_types, &pt->payload_type, pt); + } +} + /* called with call->master_lock held in W */ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, const struct sdp_ng_flags *flags) @@ -2266,6 +2404,8 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, if (other_media->sdes_in.params.crypto_suite) MEDIA_SET(other_media, SDES); + __rtp_payload_types(media, &sp->rtp_payload_types); + /* send and recv are from our POV */ bf_copy_same(&media->media_flags, &sp->sp_flags, SP_FLAG_SEND | SP_FLAG_RECV); @@ -2422,6 +2562,47 @@ static void timeval_totalstats_average_add(struct totalstats *s, const struct ti mutex_unlock(&s->total_average_lock); } +static int __rtp_stats_sort(const void *ap, const void *bp) { + const struct rtp_stats *a = ap, *b = bp; + + /* descending order */ + if (atomic64_get(&a->packets) > atomic64_get(&b->packets)) + return -1; + if (atomic64_get(&a->packets) < atomic64_get(&b->packets)) + return 1; + return 0; +} + +static const struct rtp_payload_type *__rtp_stats_codec(struct call_media *m) { + struct packet_stream *ps; + GList *values; + struct rtp_stats *rtp_s; + const struct rtp_payload_type *rtp_pt = NULL; + + /* we only use the primary packet stream for the time being */ + if (!m->streams.head) + return NULL; + + ps = m->streams.head->data; + + values = g_hash_table_get_values(ps->rtp_stats); + if (!values) + return NULL; + + values = g_list_sort(values, __rtp_stats_sort); + + /* payload type with the most packets */ + rtp_s = values->data; + if (atomic64_get(&rtp_s->packets) == 0) + goto out; + + rtp_pt = rtp_payload_type(rtp_s->payload_type, m->rtp_payload_types); + +out: + g_list_free(values); + return rtp_pt; /* may be NULL */ +} + /* called lock-free, but must hold a reference to the call */ void call_destroy(struct call *c) { struct callmaster *m = c->callmaster; @@ -2439,6 +2620,7 @@ void call_destroy(struct call *c) { char* cdrbufcur = cdrbuffer; int cdrlinecnt = 0; int found = 0; + const struct rtp_payload_type *rtp_pt; rwlock_lock_w(&m->hashlock); ret = g_hash_table_remove(m->callhash, &c->callid); @@ -2492,6 +2674,22 @@ void call_destroy(struct call *c) { for (k = ml->medias.head; k; k = k->next) { md = k->data; + rtp_pt = __rtp_stats_codec(md); +#define MLL_PREFIX "------ Media #%u ("STR_FORMAT" over %s) using " /* media log line prefix */ +#define MLL_COMMON /* common args */ \ + md->index, \ + STR_FMT(&md->type), \ + md->protocol ? md->protocol->name : "(unknown)" + if (!rtp_pt) + ilog(LOG_INFO, MLL_PREFIX "unknown codec", MLL_COMMON); + else if (!rtp_pt->encoding_parameters.s) + ilog(LOG_INFO, MLL_PREFIX ""STR_FORMAT"/%u", MLL_COMMON, + STR_FMT(&rtp_pt->encoding), rtp_pt->clock_rate); + else + ilog(LOG_INFO, MLL_PREFIX ""STR_FORMAT"/%u/"STR_FORMAT"", MLL_COMMON, + STR_FMT(&rtp_pt->encoding), rtp_pt->clock_rate, + STR_FMT(&rtp_pt->encoding_parameters)); + for (o = md->streams.head; o; o = o->next) { ps = o->data; @@ -2523,9 +2721,8 @@ void call_destroy(struct call *c) { atomic64_get(&ps->last_packet)); } - ilog(LOG_INFO, "------ Media #%u, port %5u <> %15s:%-5hu%s, " + ilog(LOG_INFO, "--------- Port %5u <> %15s:%-5hu%s, " ""UINT64F" p, "UINT64F" b, "UINT64F" e, "UINT64F" last_packet", - md->index, (unsigned int) (ps->sfd ? ps->sfd->fd.localport : 0), addr, ps->endpoint.port, (!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) ? " (RTCP)" : "", @@ -2751,6 +2948,7 @@ static void __call_free(void *p) { g_queue_clear(&em->sfds); g_slice_free1(sizeof(*em), em); } + g_hash_table_destroy(md->rtp_payload_types); g_slice_free1(sizeof(*md), md); } g_queue_clear(&m->medias); @@ -2763,6 +2961,7 @@ static void __call_free(void *p) { while (c->streams) { ps = c->streams->data; c->streams = g_slist_delete_link(c->streams, c->streams); + g_hash_table_destroy(ps->rtp_stats); g_slice_free1(sizeof(*ps), ps); } diff --git a/daemon/call.h b/daemon/call.h index aa6146505..057d4a2be 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -3,6 +3,9 @@ +/* XXX split everything into call_signalling.[ch] and call_packets.[ch] or w/e */ + + #include #include @@ -66,6 +69,7 @@ struct call_monologue; #include "str.h" #include "crypto.h" #include "dtls.h" +#include "rtp.h" @@ -179,6 +183,7 @@ typedef bencode_buffer_t call_buffer_t; struct transport_protocol { enum transport_protocol_index index; const char *name; + int rtp:1; /* also set to 1 for SRTP */ int srtp:1; int avpf:1; }; @@ -230,6 +235,7 @@ struct stream_params { int desired_family; struct dtls_fingerprint fingerprint; unsigned int sp_flags; + GQueue rtp_payload_types; /* slice-alloc'd */ }; struct stream_fd { @@ -252,6 +258,14 @@ struct loop_protector { unsigned char buf[RTP_LOOP_PROTECT]; }; +struct rtp_stats { + unsigned int payload_type; + atomic64 packets; + atomic64 bytes; + atomic64 kernel_packets; + atomic64 kernel_bytes; +}; + struct packet_stream { mutex_t in_lock, out_lock; @@ -274,6 +288,7 @@ struct packet_stream { struct stats stats; struct stats kernel_stats; atomic64 last_packet; + GHashTable *rtp_stats; /* LOCK: call->master_lock */ #if RTP_LOOP_PROTECT /* LOCK: in_lock: */ @@ -317,6 +332,7 @@ struct call_media { GQueue streams; /* normally RTP + RTCP */ GSList *endpoint_maps; + GHashTable *rtp_payload_types; unsigned int media_flags; }; @@ -483,6 +499,8 @@ INLINE void *call_malloc(struct call *c, size_t l) { INLINE char *call_strdup_len(struct call *c, const char *s, unsigned int len) { char *r; + if (!s) + return NULL; r = call_malloc(c, len + 1); memcpy(r, s, len); r[len] = 0; diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index fa6af5243..ff82e153b 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -16,6 +16,7 @@ #include "str.h" #include "control_tcp.h" #include "control_udp.h" +#include "rtp.h" @@ -274,14 +275,20 @@ static void streams_parse(const char *s, struct callmaster *m, GQueue *q) { pcre_multi_match(m->streams_re, m->streams_ree, s, 3, streams_parse_func, &i, q); } -static void streams_free(GQueue *q) { - struct stream_params *s; +/* XXX move these somewhere else */ +static void rtp_pt_free(void *p) { + g_slice_free1(sizeof(struct rtp_payload_type), p); +} +static void sp_free(void *p) { + struct stream_params *s = p; - while ((s = g_queue_pop_head(q))) { - if (s->crypto.mki) - free(s->crypto.mki); - g_slice_free1(sizeof(*s), s); - } + if (s->crypto.mki) + free(s->crypto.mki); + g_queue_clear_full(&s->rtp_payload_types, rtp_pt_free); + g_slice_free1(sizeof(*s), s); +} +static void streams_free(GQueue *q) { + g_queue_clear_full(q, sp_free); } diff --git a/daemon/rtcp.c b/daemon/rtcp.c index aa9bc3e06..793bc39c5 100644 --- a/daemon/rtcp.c +++ b/daemon/rtcp.c @@ -240,11 +240,11 @@ static struct rtcp_chain_element *rtcp_psfb(str *s) { return rtcp_generic(s, RTCP_PT_PSFB); } +static void rtcp_ce_free(void *p) { + g_slice_free1(sizeof(struct rtcp_chain_element), p); +} static void rtcp_list_free(GQueue *q) { - struct rtcp_chain_element *el; - - while ((el = g_queue_pop_head(q))) - g_slice_free1(sizeof(*el), el); + g_queue_clear_full(q, rtcp_ce_free); } static int rtcp_parse(GQueue *q, str *_s) { diff --git a/daemon/rtp.c b/daemon/rtp.c index c7fd65d9b..a1ba9ebf4 100644 --- a/daemon/rtp.c +++ b/daemon/rtp.c @@ -20,6 +20,44 @@ struct rtp_extension { +#define RFC_TYPE(type, name, c_rate) \ + [type] = { \ + .payload_type = type, \ + .encoding = STR_CONST_INIT(#name), \ + .clock_rate = c_rate, \ + } + +static const struct rtp_payload_type __rfc_types[] = +{ + RFC_TYPE(0, PCMU, 8000), + RFC_TYPE(3, GSM, 8000), + RFC_TYPE(4, G723, 8000), + RFC_TYPE(5, DVI4, 8000), + RFC_TYPE(6, DVI4, 16000), + RFC_TYPE(7, LPC, 8000), + RFC_TYPE(8, PCMA, 8000), + RFC_TYPE(9, G722, 8000), + RFC_TYPE(10, L16, 44100), + RFC_TYPE(11, L16, 44100), + RFC_TYPE(12, QCELP, 8000), + RFC_TYPE(13, CN, 8000), + RFC_TYPE(14, MPA, 90000), + RFC_TYPE(15, G728, 8000), + RFC_TYPE(16, DVI4, 11025), + RFC_TYPE(17, DVI4, 22050), + RFC_TYPE(18, G729, 8000), + RFC_TYPE(25, CelB, 90000), + RFC_TYPE(26, JPEG, 90000), + RFC_TYPE(28, nv, 90000), + RFC_TYPE(31, H261, 90000), + RFC_TYPE(32, MPV, 90000), + RFC_TYPE(33, MP2T, 90000), + RFC_TYPE(34, H263, 90000), +}; + + + + INLINE int check_session_keys(struct crypto_context *c) { str s; const char *err; @@ -51,7 +89,7 @@ error: return -1; } -static int rtp_payload(struct rtp_header **out, str *p, const str *s) { +int rtp_payload(struct rtp_header **out, str *p, const str *s) { struct rtp_header *rtp; struct rtp_extension *ext; const char *err; @@ -65,6 +103,9 @@ static int rtp_payload(struct rtp_header **out, str *p, const str *s) { if ((rtp->v_p_x_cc & 0xc0) != 0x80) /* version 2 */ goto error; + if (!p) + goto done; + *p = *s; /* fixed header */ str_shift(p, sizeof(*rtp)); @@ -84,6 +125,7 @@ static int rtp_payload(struct rtp_header **out, str *p, const str *s) { goto error; } +done: *out = rtp; return 0; @@ -266,3 +308,23 @@ error: ilog(LOG_WARNING | LOG_FLAG_LIMIT, "Invalid SRTP/SRTCP packet received (short packet)"); return -1; } + +const struct rtp_payload_type *rtp_payload_type(unsigned int type, GHashTable *lookup) { + const struct rtp_payload_type *rtp_pt; + + if (!lookup) + goto rfc_types; + + rtp_pt = g_hash_table_lookup(lookup, &type); + if (rtp_pt) + return rtp_pt; + +rfc_types: + if (type >= G_N_ELEMENTS(__rfc_types)) + return NULL; + rtp_pt = &__rfc_types[type]; + if (!rtp_pt->encoding.s) + return NULL; + return rtp_pt; + +} diff --git a/daemon/rtp.h b/daemon/rtp.h index 1e14c6cc7..d5be29bd9 100644 --- a/daemon/rtp.h +++ b/daemon/rtp.h @@ -4,6 +4,7 @@ #include "str.h" +#include @@ -18,10 +19,20 @@ struct rtp_header { u_int32_t csrc[]; } __attribute__ ((packed)); +struct rtp_payload_type { + unsigned int payload_type; + str encoding; + unsigned int clock_rate; + str encoding_parameters; +}; + +int rtp_payload(struct rtp_header **out, str *p, const str *s); +const struct rtp_payload_type *rtp_payload_type(unsigned int, GHashTable *); + int rtp_avp2savp(str *, struct crypto_context *); int rtp_savp2avp(str *, struct crypto_context *); diff --git a/daemon/sdp.c b/daemon/sdp.c index f0aa3030c..e951ca4a5 100644 --- a/daemon/sdp.c +++ b/daemon/sdp.c @@ -13,6 +13,7 @@ #include "call.h" #include "crypto.h" #include "dtls.h" +#include "rtp.h" struct network_address { str network_type; @@ -58,7 +59,7 @@ struct sdp_media { str media_type; str port; str transport; - /* ... format list */ + str formats; /* space separated */ long int port_num; int port_count; @@ -66,6 +67,7 @@ struct sdp_media { struct sdp_connection connection; int rr, rs; struct sdp_attributes attributes; + GQueue format_list; /* list of slice-alloc'd str objects */ }; struct attribute_rtcp { @@ -143,6 +145,14 @@ struct attribute_setup { } value; }; +struct attribute_rtpmap { + str payload_type_str; + str encoding_str; + str clock_rate_str; + + struct rtp_payload_type rtp_pt; +}; + struct sdp_attribute { str full_line, /* including a= and \r\n */ line_value, /* without a= and without \r\n */ @@ -169,6 +179,7 @@ struct sdp_attribute { ATTR_MID, ATTR_FINGERPRINT, ATTR_SETUP, + ATTR_RTPMAP, } attr; union { @@ -179,6 +190,7 @@ struct sdp_attribute { struct attribute_group group; struct attribute_fingerprint fingerprint; struct attribute_setup setup; + struct attribute_rtpmap rtpmap; } u; }; @@ -190,13 +202,12 @@ static const char ice_chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJK -//static int has_rtcp(struct sdp_media *media); - - - INLINE struct sdp_attribute *attr_get_by_id(struct sdp_attributes *a, int id) { return g_hash_table_lookup(a->id_hash, &id); } +INLINE GQueue *attr_list_get_by_id(struct sdp_attributes *a, int id) { + return g_hash_table_lookup(a->id_lists_hash, &id); +} static struct sdp_attribute *attr_get_by_id_m_s(struct sdp_media *m, int id) { struct sdp_attribute *a; @@ -309,6 +320,9 @@ INLINE int extract_token(char **sp, char *end, str *out) { EXTRACT_NETWORK_ADDRESS_NP(field); \ if (parse_address(&output->field)) output->field.parsed.s6_addr32[0] = 0xfe +#define PARSE_DECL char *end, *start +#define PARSE_INIT start = output->value.s; end = start + output->value.len + static int parse_origin(char *start, char *end, struct sdp_origin *output) { if (output->parsed) return -1; @@ -334,10 +348,12 @@ static int parse_connection(char *start, char *end, struct sdp_connection *outpu static int parse_media(char *start, char *end, struct sdp_media *output) { char *ep; + str s, *sp; EXTRACT_TOKEN(media_type); EXTRACT_TOKEN(port); EXTRACT_TOKEN(transport); + str_init_len(&output->formats, start, end - start); output->port_num = strtol(output->port.s, &ep, 10); if (ep == output->port.s) @@ -355,6 +371,15 @@ static int parse_media(char *start, char *end, struct sdp_media *output) { else output->port_count = 1; + /* to split the "formats" list into tokens, we abuse some vars */ + start = output->formats.s; + end = start + output->formats.len; + while (!extract_token(&start, end, &s)) { + sp = g_slice_alloc(sizeof(*sp)); + *sp = s; + g_queue_push_tail(&output->format_list, sp); + } + return 0; } @@ -379,14 +404,12 @@ static int parse_attribute_group(struct sdp_attribute *output) { } static int parse_attribute_ssrc(struct sdp_attribute *output) { - char *start, *end; + PARSE_DECL; struct attribute_ssrc *s; output->attr = ATTR_SSRC; - start = output->value.s; - end = start + output->value.len; - + PARSE_INIT; EXTRACT_TOKEN(u.ssrc.id_str); EXTRACT_TOKEN(u.ssrc.attr_str); @@ -407,7 +430,8 @@ static int parse_attribute_ssrc(struct sdp_attribute *output) { } static int parse_attribute_crypto(struct sdp_attribute *output) { - char *start, *end, *endp; + PARSE_DECL; + char *endp; struct attribute_crypto *c; int salt_key_len, enc_salt_key_len; int b64_state = 0; @@ -419,9 +443,7 @@ static int parse_attribute_crypto(struct sdp_attribute *output) { output->attr = ATTR_CRYPTO; - start = output->value.s; - end = start + output->value.len; - + PARSE_INIT; EXTRACT_TOKEN(u.crypto.tag_str); EXTRACT_TOKEN(u.crypto.crypto_suite_str); EXTRACT_TOKEN(u.crypto.key_params_str); @@ -551,12 +573,12 @@ static int parse_attribute_rtcp(struct sdp_attribute *output) { } static int parse_attribute_candidate(struct sdp_attribute *output) { - char *end, *start, *ep; + PARSE_DECL; + char *ep; - start = output->value.s; - end = start + output->value.len; output->attr = ATTR_CANDIDATE; + PARSE_INIT; EXTRACT_TOKEN(u.candidate.foundation); EXTRACT_TOKEN(u.candidate.component_str); EXTRACT_TOKEN(u.candidate.transport); @@ -578,14 +600,13 @@ static int parse_attribute_candidate(struct sdp_attribute *output) { } static int parse_attribute_fingerprint(struct sdp_attribute *output) { - char *end, *start; + PARSE_DECL; unsigned char *c; int i; - start = output->value.s; - end = start + output->value.len; output->attr = ATTR_FINGERPRINT; + PARSE_INIT; EXTRACT_TOKEN(u.fingerprint.hash_func_str); EXTRACT_TOKEN(u.fingerprint.fingerprint_str); @@ -647,6 +668,49 @@ static int parse_attribute_setup(struct sdp_attribute *output) { return 0; } +static int parse_attribute_rtpmap(struct sdp_attribute *output) { + PARSE_DECL; + char *ep; + struct attribute_rtpmap *a; + struct rtp_payload_type *pt; + + output->attr = ATTR_RTPMAP; + + PARSE_INIT; + EXTRACT_TOKEN(u.rtpmap.payload_type_str); + EXTRACT_TOKEN(u.rtpmap.encoding_str); + + a = &output->u.rtpmap; + pt = &a->rtp_pt; + + pt->payload_type = strtoul(a->payload_type_str.s, &ep, 10); + if (ep == a->payload_type_str.s) + return -1; + + str_chr_str(&a->clock_rate_str, &a->encoding_str, '/'); + if (!a->clock_rate_str.s) + return -1; + + pt->encoding = a->encoding_str; + pt->encoding.len -= a->clock_rate_str.len; + str_shift(&a->clock_rate_str, 1); + + str_chr_str(&pt->encoding_parameters, &a->clock_rate_str, '/'); + if (pt->encoding_parameters.s) { + a->clock_rate_str.len -= pt->encoding_parameters.len; + str_shift(&pt->encoding_parameters, 1); + } + + if (!a->clock_rate_str.len) + return -1; + + pt->clock_rate = strtoul(a->clock_rate_str.s, &ep, 10); + if (ep && ep != a->clock_rate_str.s + a->clock_rate_str.len) + return -1; + + return 0; +} + static int parse_attribute(struct sdp_attribute *a) { int ret; @@ -696,6 +760,8 @@ static int parse_attribute(struct sdp_attribute *a) { ret = parse_attribute_crypto(a); else if (!str_cmp(&a->name, "extmap")) a->attr = ATTR_EXTMAP; + else if (!str_cmp(&a->name, "rtpmap")) + ret = parse_attribute_rtpmap(a); break; case 7: if (!str_cmp(&a->name, "ice-pwd")) @@ -915,30 +981,30 @@ error: return -1; } +static void attr_free(void *p) { + g_slice_free1(sizeof(struct sdp_attribute), p); +} static void free_attributes(struct sdp_attributes *a) { - struct sdp_attribute *attr; - /* g_hash_table_destroy(a->name_hash); */ g_hash_table_destroy(a->id_hash); /* g_hash_table_destroy(a->name_lists_hash); */ g_hash_table_destroy(a->id_lists_hash); - while ((attr = g_queue_pop_head(&a->list))) { - g_slice_free1(sizeof(*attr), attr); - } + g_queue_clear_full(&a->list, attr_free); +} +static void media_free(void *p) { + struct sdp_media *media = p; + free_attributes(&media->attributes); + g_queue_clear_full(&media->format_list, str_slice_free); + g_slice_free1(sizeof(*media), media); +} +static void session_free(void *p) { + struct sdp_session *session = p; + g_queue_clear_full(&session->media_streams, media_free); + free_attributes(&session->attributes); + g_slice_free1(sizeof(*session), session); } - void sdp_free(GQueue *sessions) { - struct sdp_session *session; - struct sdp_media *media; - - while ((session = g_queue_pop_head(sessions))) { - while ((media = g_queue_pop_head(&session->media_streams))) { - free_attributes(&media->attributes); - g_slice_free1(sizeof(*media), media); - } - free_attributes(&session->attributes); - g_slice_free1(sizeof(*session), session); - } + g_queue_clear_full(sessions, session_free); } static int fill_endpoint(struct endpoint *ep, const struct sdp_media *media, struct sdp_ng_flags *flags, @@ -968,6 +1034,66 @@ static int fill_endpoint(struct endpoint *ep, const struct sdp_media *media, str } + +static int __rtp_payload_types(struct stream_params *sp, struct sdp_media *media) +{ + GHashTable *ht; + GQueue *q; + GList *ql; + struct sdp_attribute *attr; + int ret = 0; + + if (!sp->protocol || !sp->protocol->rtp) + return 0; + + /* first go through a=rtpmap and build a hash table of attrs */ + ht = g_hash_table_new(g_int_hash, g_int_equal); + q = attr_list_get_by_id(&media->attributes, ATTR_RTPMAP); + for (ql = q->head; ql; ql = ql->next) { + struct rtp_payload_type *pt; + attr = ql->data; + pt = &attr->u.rtpmap.rtp_pt; + g_hash_table_insert(ht, &pt->payload_type, pt); + } + /* a=fmtp processing would go here */ + + /* then go through the format list and associate */ + for (ql = media->format_list.head; ql; ql = ql->next) { + char *ep; + str *s; + unsigned int i; + struct rtp_payload_type *pt; + const struct rtp_payload_type *ptl; + + s = ql->data; + i = (unsigned int) strtoul(s->s, &ep, 10); + if (ep == s->s || i > 127) + goto error; + + /* first look in rtpmap for a match, then check RFC types, + * else fall back to an "unknown" type */ + ptl = rtp_payload_type(i, ht); + + pt = g_slice_alloc0(sizeof(*pt)); + if (ptl) + *pt = *ptl; + else + pt->payload_type = i; + g_queue_push_tail(&sp->rtp_payload_types, pt); + } + + goto out; + +error: + ret = -1; + goto out; +out: + g_hash_table_destroy(ht); + return ret; +} + + +/* XXX split this function up */ int sdp_streams(const GQueue *sessions, GQueue *streams, struct sdp_ng_flags *flags) { struct sdp_session *session; struct sdp_media *media; @@ -1000,6 +1126,10 @@ int sdp_streams(const GQueue *sessions, GQueue *streams, struct sdp_ng_flags *fl bf_xset(&sp->sp_flags, SP_FLAG_STRICT_SOURCE, flags->strict_source); bf_xset(&sp->sp_flags, SP_FLAG_MEDIA_HANDOVER, flags->media_handover); + errstr = "Invalid RTP payload types"; + if (__rtp_payload_types(sp, media)) + goto error; + /* a=crypto */ attr = attr_get_by_id(&media->attributes, ATTR_CRYPTO); if (attr) { @@ -1451,7 +1581,6 @@ INLINE unsigned long type_from_prio(unsigned int prio) { } static unsigned long new_priority(struct sdp_media *media, int relay) { - int id; GQueue *cands; int pref; unsigned long prio, tpref; @@ -1468,8 +1597,7 @@ static unsigned long new_priority(struct sdp_media *media, int relay) { if (!media) goto out; - id = ATTR_CANDIDATE; - cands = g_hash_table_lookup(media->attributes.id_lists_hash, &id); + cands = attr_list_get_by_id(&media->attributes, ATTR_CANDIDATE); if (!cands) goto out; diff --git a/daemon/str.c b/daemon/str.c index e00aa01d2..ceada3781 100644 --- a/daemon/str.c +++ b/daemon/str.c @@ -29,3 +29,7 @@ str *__str_sprintf(const char *fmt, ...) { va_end(ap); return ret; } + +void str_slice_free(void *p) { + g_slice_free1(sizeof(str), p); +} diff --git a/daemon/str.h b/daemon/str.h index 099621144..20e1cda5b 100644 --- a/daemon/str.h +++ b/daemon/str.h @@ -25,6 +25,7 @@ typedef struct _str str; #define STR_FMT0(str) ((str) ? (str)->len : 6), ((str) ? (str)->s : "(NULL)") #define STR_NULL ((str) { NULL, 0 }) #define STR_EMPTY ((str) { "", 0 }) +#define STR_CONST_INIT(str) { str, sizeof(str)-1 } @@ -70,6 +71,9 @@ INLINE str *g_string_free_str(GString *gs); guint str_hash(gconstpointer s); gboolean str_equal(gconstpointer a, gconstpointer b); +/* destroy function, frees a slice-alloc'd str */ +void str_slice_free(void *); + diff --git a/kernel-module/xt_RTPENGINE.c b/kernel-module/xt_RTPENGINE.c index bc43b0324..65cd243a7 100644 --- a/kernel-module/xt_RTPENGINE.c +++ b/kernel-module/xt_RTPENGINE.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -148,6 +149,7 @@ struct rtpengine_target { spinlock_t stats_lock; struct rtpengine_stats stats; + struct rtpengine_rtp_stats rtp_stats[NUM_PAYLOAD_TYPES]; struct re_crypto_context decrypt; struct re_crypto_context encrypt; @@ -843,6 +845,7 @@ static ssize_t proc_blist_read(struct file *f, char __user *b, size_t l, loff_t spin_lock_irqsave(&g->stats_lock, flags); memcpy(&op.stats, &g->stats, sizeof(op.stats)); + memcpy(&op.rtp_stats, &g->rtp_stats, sizeof(op.rtp_stats)); spin_unlock_irqrestore(&g->stats_lock, flags); spin_lock_irqsave(&g->decrypt.lock, flags); @@ -971,6 +974,7 @@ static void proc_list_crypto_print(struct seq_file *f, struct re_crypto_context static int proc_list_show(struct seq_file *f, void *v) { struct rtpengine_target *g = v; unsigned long flags; + int i; seq_printf(f, "port %5u:\n", g->target.target_port); proc_list_addr_print(f, "src", &g->target.src_addr); @@ -982,6 +986,10 @@ static int proc_list_show(struct seq_file *f, void *v) { spin_lock_irqsave(&g->stats_lock, flags); seq_printf(f, " stats: %20llu bytes, %20llu packets, %20llu errors\n", g->stats.bytes, g->stats.packets, g->stats.errors); + for (i = 0; i < g->target.num_payload_types; i++) + seq_printf(f, " RTP payload type %3u: %20llu bytes, %20llu packets\n", + g->target.payload_types[i], + g->rtp_stats[i].bytes, g->rtp_stats[i].packets); spin_unlock_irqrestore(&g->stats_lock, flags); proc_list_crypto_print(f, &g->decrypt, &g->target.decrypt, "decryption (incoming)"); proc_list_crypto_print(f, &g->encrypt, &g->target.encrypt, "encryption (outgoing)"); @@ -1454,6 +1462,7 @@ static int table_new_target(struct rtpengine_table *t, struct rtpengine_target_i spin_lock(&og->stats_lock); /* nested lock! irqs are disabled already */ memcpy(&g->stats, &og->stats, sizeof(g->stats)); + memcpy(&g->rtp_stats, &og->rtp_stats, sizeof(g->rtp_stats)); spin_unlock(&og->stats_lock); } else { @@ -2141,11 +2150,30 @@ static inline int is_dtls(struct sk_buff *skb) { return 1; } +static int rtp_payload_match(const void *a, const void *b) { + const unsigned char *A = a, *B = b; + + if (*A < *B) + return -1; + if (*A > *B) + return 1; + return 0; +} +static inline int rtp_payload_type(const struct rtp_header *hdr, const struct rtpengine_target_info *tg) { + unsigned char pt, *match; + + pt = hdr->m_pt & 0x7f; + match = bsearch(&pt, tg->payload_types, tg->num_payload_types, sizeof(pt), rtp_payload_match); + if (!match) + return -1; + return match - tg->payload_types; +} + static unsigned int rtpengine46(struct sk_buff *skb, struct rtpengine_table *t, struct re_address *src) { struct udphdr *uh; struct rtpengine_target *g; struct sk_buff *skb2; - int err; + int err, rtp_pt_idx = -2; unsigned int datalen; unsigned long flags; u_int32_t *u32; @@ -2205,14 +2233,23 @@ not_stun: src_check_ok: if (g->target.dtls && is_dtls(skb)) goto skip1; + + rtp.ok = 0; + if (!g->target.rtp) + goto not_rtp; + parse_rtp(&rtp, skb); if (!rtp.ok) { if (g->target.rtp_only) goto skip1; goto not_rtp; } + if (g->target.rtcp_mux && is_muxed_rtcp(&rtp)) goto skip1; + + rtp_pt_idx = rtp_payload_type(rtp.header, &g->target); + pkt_idx_u = pkt_idx = packet_index(&g->decrypt, &g->target.decrypt, rtp.header); if (srtp_auth_validate(&g->decrypt, &g->target.decrypt, &rtp, &pkt_idx)) goto skip_error; @@ -2258,6 +2295,14 @@ out: g->stats.packets++; g->stats.bytes += datalen; } + if (rtp_pt_idx >= 0) { + g->rtp_stats[rtp_pt_idx].packets++; + g->rtp_stats[rtp_pt_idx].bytes += datalen; + } + else if (rtp_pt_idx == -2) + /* not RTP */ ; + else if (rtp_pt_idx == -1) + g->stats.errors++; spin_unlock_irqrestore(&g->stats_lock, flags); target_push(g); diff --git a/kernel-module/xt_RTPENGINE.h b/kernel-module/xt_RTPENGINE.h index 5f419a08c..361a56fb0 100644 --- a/kernel-module/xt_RTPENGINE.h +++ b/kernel-module/xt_RTPENGINE.h @@ -1,6 +1,12 @@ #ifndef XT_RTPPROXY_H #define XT_RTPPROXY_H + + +#define NUM_PAYLOAD_TYPES 16 + + + struct xt_rtpengine_info { u_int32_t id; }; @@ -10,6 +16,10 @@ struct rtpengine_stats { u_int64_t bytes; u_int64_t errors; }; +struct rtpengine_rtp_stats { + u_int64_t packets; + u_int64_t bytes; +}; struct re_address { int family; @@ -73,10 +83,14 @@ struct rtpengine_target_info { struct rtpengine_srtp decrypt; struct rtpengine_srtp encrypt; + unsigned char payload_types[NUM_PAYLOAD_TYPES]; /* must be sorted */ + unsigned int num_payload_types; + unsigned char tos; int rtcp_mux:1, dtls:1, stun:1, + rtp:1, rtp_only:1; }; @@ -94,6 +108,7 @@ struct rtpengine_message { struct rtpengine_list_entry { struct rtpengine_target_info target; struct rtpengine_stats stats; + struct rtpengine_rtp_stats rtp_stats[NUM_PAYLOAD_TYPES]; }; diff --git a/tests/simulator-ng.pl b/tests/simulator-ng.pl index 365d1f802..3c2e98cb9 100755 --- a/tests/simulator-ng.pl +++ b/tests/simulator-ng.pl @@ -558,8 +558,9 @@ t=0 0 and $cp = $p; $sdp .= <<"!"; -m=audio $p $$tr{name} 8 +m=audio $p $$tr{name} 0 8 111 a=rtpmap:8 PCMA/8000 +a=rtpmap:111 opus/48000/2 ! if ($$A{want_rtcpmux} && $op eq 'offer') { $sdp .= "a=rtcp-mux\n"; From b586fa703168010b94e86654c9c8c21396e091cd Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Thu, 12 Feb 2015 12:25:08 -0500 Subject: [PATCH 25/82] fix memory leak --- daemon/call.c | 3 +++ daemon/crypto.h | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/daemon/call.c b/daemon/call.c index 1436f61a9..041128100 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -2949,6 +2949,8 @@ static void __call_free(void *p) { g_slice_free1(sizeof(*em), em); } g_hash_table_destroy(md->rtp_payload_types); + crypto_params_cleanup(&md->sdes_in.params); + crypto_params_cleanup(&md->sdes_out.params); g_slice_free1(sizeof(*md), md); } g_queue_clear(&m->medias); @@ -2962,6 +2964,7 @@ static void __call_free(void *p) { ps = c->streams->data; c->streams = g_slist_delete_link(c->streams, c->streams); g_hash_table_destroy(ps->rtp_stats); + crypto_cleanup(&ps->crypto); g_slice_free1(sizeof(*ps), ps); } diff --git a/daemon/crypto.h b/daemon/crypto.h index b652a72cb..82e02dd47 100644 --- a/daemon/crypto.h +++ b/daemon/crypto.h @@ -127,12 +127,13 @@ INLINE void crypto_params_cleanup(struct crypto_params *p) { p->mki = NULL; } INLINE void crypto_cleanup(struct crypto_context *c) { + crypto_params_cleanup(&c->params); if (!c->params.crypto_suite) return; if (c->params.crypto_suite->session_key_cleanup) c->params.crypto_suite->session_key_cleanup(c); c->have_session_key = 0; - crypto_params_cleanup(&c->params); + c->params.crypto_suite = NULL; } INLINE void crypto_reset(struct crypto_context *c) { crypto_cleanup(c); From 65b3f66ee481de830238f8135082f33d13955d93 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Thu, 12 Feb 2015 13:39:41 -0500 Subject: [PATCH 26/82] use atomic64_t for kernel stats --- kernel-module/xt_RTPENGINE.c | 81 ++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/kernel-module/xt_RTPENGINE.c b/kernel-module/xt_RTPENGINE.c index 65cd243a7..713c34997 100644 --- a/kernel-module/xt_RTPENGINE.c +++ b/kernel-module/xt_RTPENGINE.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -142,14 +143,22 @@ struct re_crypto_context { const struct re_hmac *hmac; }; +struct rtpengine_stats_a { + atomic64_t packets; + atomic64_t bytes; + atomic64_t errors; +}; +struct rtpengine_rtp_stats_a { + atomic64_t packets; + atomic64_t bytes; +}; struct rtpengine_target { atomic_t refcnt; u_int32_t table; struct rtpengine_target_info target; - spinlock_t stats_lock; - struct rtpengine_stats stats; - struct rtpengine_rtp_stats rtp_stats[NUM_PAYLOAD_TYPES]; + struct rtpengine_stats_a stats; + struct rtpengine_rtp_stats_a rtp_stats[NUM_PAYLOAD_TYPES]; struct re_crypto_context decrypt; struct re_crypto_context encrypt; @@ -817,10 +826,9 @@ static ssize_t proc_blist_read(struct file *f, char __user *b, size_t l, loff_t u_int32_t id; struct rtpengine_table *t; struct rtpengine_list_entry op; - int err; + int err, port, i; struct rtpengine_target *g; unsigned long flags; - int port; if (l != sizeof(op)) return -EINVAL; @@ -843,10 +851,14 @@ static ssize_t proc_blist_read(struct file *f, char __user *b, size_t l, loff_t memset(&op, 0, sizeof(op)); memcpy(&op.target, &g->target, sizeof(op.target)); - spin_lock_irqsave(&g->stats_lock, flags); - memcpy(&op.stats, &g->stats, sizeof(op.stats)); - memcpy(&op.rtp_stats, &g->rtp_stats, sizeof(op.rtp_stats)); - spin_unlock_irqrestore(&g->stats_lock, flags); + op.stats.packets = atomic64_read(&g->stats.packets); + op.stats.bytes = atomic64_read(&g->stats.bytes); + op.stats.errors = atomic64_read(&g->stats.errors); + + for (i = 0; i < g->target.num_payload_types; i++) { + op.rtp_stats[i].packets = atomic64_read(&g->rtp_stats[i].packets); + op.rtp_stats[i].bytes = atomic64_read(&g->rtp_stats[i].bytes); + } spin_lock_irqsave(&g->decrypt.lock, flags); op.target.decrypt.last_index = g->target.decrypt.last_index; @@ -973,7 +985,6 @@ static void proc_list_crypto_print(struct seq_file *f, struct re_crypto_context static int proc_list_show(struct seq_file *f, void *v) { struct rtpengine_target *g = v; - unsigned long flags; int i; seq_printf(f, "port %5u:\n", g->target.target_port); @@ -983,14 +994,15 @@ static int proc_list_show(struct seq_file *f, void *v) { proc_list_addr_print(f, "expect", &g->target.expected_src); if (g->target.src_mismatch > 0 && g->target.src_mismatch <= ARRAY_SIZE(re_msm_strings)) seq_printf(f, " src mismatch action: %s\n", re_msm_strings[g->target.src_mismatch]); - spin_lock_irqsave(&g->stats_lock, flags); seq_printf(f, " stats: %20llu bytes, %20llu packets, %20llu errors\n", - g->stats.bytes, g->stats.packets, g->stats.errors); + (unsigned long long) atomic64_read(&g->stats.bytes), + (unsigned long long) atomic64_read(&g->stats.packets), + (unsigned long long) atomic64_read(&g->stats.errors)); for (i = 0; i < g->target.num_payload_types; i++) seq_printf(f, " RTP payload type %3u: %20llu bytes, %20llu packets\n", g->target.payload_types[i], - g->rtp_stats[i].bytes, g->rtp_stats[i].packets); - spin_unlock_irqrestore(&g->stats_lock, flags); + (unsigned long long) atomic64_read(&g->rtp_stats[i].bytes), + (unsigned long long) atomic64_read(&g->rtp_stats[i].packets)); proc_list_crypto_print(f, &g->decrypt, &g->target.decrypt, "decryption (incoming)"); proc_list_crypto_print(f, &g->encrypt, &g->target.encrypt, "encryption (outgoing)"); if (g->target.rtcp_mux) @@ -1381,7 +1393,7 @@ static int table_new_target(struct rtpengine_table *t, struct rtpengine_target_i struct rtpengine_target *g; struct re_bucket *b, *ba = NULL; struct rtpengine_target *og = NULL; - int err; + int err, j; unsigned long flags; if (!i->target_port) @@ -1412,7 +1424,6 @@ static int table_new_target(struct rtpengine_table *t, struct rtpengine_target_i memset(g, 0, sizeof(*g)); g->table = t->id; atomic_set(&g->refcnt, 1); - spin_lock_init(&g->stats_lock); spin_lock_init(&g->decrypt.lock); spin_lock_init(&g->encrypt.lock); memcpy(&g->target, i, sizeof(*i)); @@ -1460,10 +1471,14 @@ static int table_new_target(struct rtpengine_table *t, struct rtpengine_target_i if (!og) goto fail4; - spin_lock(&og->stats_lock); /* nested lock! irqs are disabled already */ - memcpy(&g->stats, &og->stats, sizeof(g->stats)); - memcpy(&g->rtp_stats, &og->rtp_stats, sizeof(g->rtp_stats)); - spin_unlock(&og->stats_lock); + 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)); + + for (j = 0; j < NUM_PAYLOAD_TYPES; j++) { + atomic64_set(&g->rtp_stats[j].packets, atomic64_read(&og->rtp_stats[j].packets)); + atomic64_set(&g->rtp_stats[j].bytes, atomic64_read(&og->rtp_stats[j].bytes)); + } } else { err = -EEXIST; @@ -2175,7 +2190,6 @@ static unsigned int rtpengine46(struct sk_buff *skb, struct rtpengine_table *t, struct sk_buff *skb2; int err, rtp_pt_idx = -2; unsigned int datalen; - unsigned long flags; u_int32_t *u32; struct rtp_parsed rtp; u_int64_t pkt_idx = 0, pkt_idx_u; @@ -2272,11 +2286,8 @@ not_rtp: DBG("sending mirror packet to dst "MIPF"\n", MIPP(g->target.mirror_addr)); skb2 = skb_copy(skb, GFP_ATOMIC); err = send_proxy_packet(skb2, &g->target.src_addr, &g->target.mirror_addr, g->target.tos); - if (err) { - spin_lock_irqsave(&g->stats_lock, flags); - g->stats.errors++; - spin_unlock_irqrestore(&g->stats_lock, flags); - } + if (err) + atomic64_inc(&g->stats.errors); } if (rtp.ok) { @@ -2288,22 +2299,20 @@ not_rtp: err = send_proxy_packet(skb, &g->target.src_addr, &g->target.dst_addr, g->target.tos); out: - spin_lock_irqsave(&g->stats_lock, flags); if (err) - g->stats.errors++; + atomic64_inc(&g->stats.errors); else { - g->stats.packets++; - g->stats.bytes += datalen; + atomic64_inc(&g->stats.packets); + atomic64_add(datalen, &g->stats.bytes); } if (rtp_pt_idx >= 0) { - g->rtp_stats[rtp_pt_idx].packets++; - g->rtp_stats[rtp_pt_idx].bytes += datalen; + atomic64_inc(&g->rtp_stats[rtp_pt_idx].packets); + atomic64_add(datalen, &g->rtp_stats[rtp_pt_idx].bytes); } else if (rtp_pt_idx == -2) /* not RTP */ ; else if (rtp_pt_idx == -1) - g->stats.errors++; - spin_unlock_irqrestore(&g->stats_lock, flags); + atomic64_inc(&g->stats.errors); target_push(g); table_push(t); @@ -2311,9 +2320,7 @@ out: return NF_DROP; skip_error: - spin_lock_irqsave(&g->stats_lock, flags); - g->stats.errors++; - spin_unlock_irqrestore(&g->stats_lock, flags); + atomic64_inc(&g->stats.errors); skip1: target_push(g); skip2: From 4a97bb167ac876a701a4154b1b6ec32b9758951f Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 6 Feb 2015 16:09:38 -0500 Subject: [PATCH 27/82] attempt to work around trickle ice --- daemon/call.c | 10 ++++++---- daemon/call.h | 3 +++ daemon/sdp.c | 15 ++++++++++++--- daemon/sdp.h | 6 ++++++ daemon/str.h | 19 +++++++++++++++++++ 5 files changed, 46 insertions(+), 7 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index 041128100..7144ee70a 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -2397,7 +2397,8 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, /* copy parameters advertised by the sender of this message */ bf_copy_same(&other_media->media_flags, &sp->sp_flags, - SHARED_FLAG_RTCP_MUX | SHARED_FLAG_ASYMMETRIC | SHARED_FLAG_ICE); + SHARED_FLAG_RTCP_MUX | SHARED_FLAG_ASYMMETRIC | SHARED_FLAG_ICE + | SHARED_FLAG_TRICKLE_ICE); crypto_params_copy(&other_media->sdes_in.params, &sp->crypto); other_media->sdes_in.tag = sp->sdes_tag; @@ -2457,7 +2458,7 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, __disable_streams(other_media, num_ports); goto init; } - if (is_addr_unspecified(&sp->rtp_endpoint.ip46)) { + if (is_addr_unspecified(&sp->rtp_endpoint.ip46) && !is_trickle_ice_address(&sp->rtp_endpoint)) { /* Zero endpoint address, equivalent to setting the media stream * to sendonly or inactive */ MEDIA_CLEAR(media, RECV); @@ -2856,7 +2857,7 @@ static int call_stream_address4(char *o, struct packet_stream *ps, enum stream_a l = 4; } - if (!in6_to_4(&ps->advertised_endpoint.ip46)) { + if (!in6_to_4(&ps->advertised_endpoint.ip46) && !is_trickle_ice_address(&ps->advertised_endpoint)) { strcpy(o + l, "0.0.0.0"); l += 7; } @@ -2879,7 +2880,8 @@ static int call_stream_address6(char *o, struct packet_stream *ps, enum stream_a l += 4; } - if (is_addr_unspecified(&ps->advertised_endpoint.ip46)) { + if (is_addr_unspecified(&ps->advertised_endpoint.ip46) + && !is_trickle_ice_address(&ps->advertised_endpoint)) { strcpy(o + l, "::"); l += 2; } diff --git a/daemon/call.h b/daemon/call.h index 057d4a2be..05c0321f1 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -104,6 +104,7 @@ struct call_monologue; #define SHARED_FLAG_ICE 0x00000080 #define SHARED_FLAG_STRICT_SOURCE 0x00000100 #define SHARED_FLAG_MEDIA_HANDOVER 0x00000200 +#define SHARED_FLAG_TRICKLE_ICE 0x00000400 /* struct stream_params */ #define SP_FLAG_NO_RTCP 0x00010000 @@ -117,6 +118,7 @@ struct call_monologue; #define SP_FLAG_ICE SHARED_FLAG_ICE #define SP_FLAG_STRICT_SOURCE SHARED_FLAG_STRICT_SOURCE #define SP_FLAG_MEDIA_HANDOVER SHARED_FLAG_MEDIA_HANDOVER +#define SP_FLAG_TRICKLE_ICE SHARED_FLAG_TRICKLE_ICE /* struct packet_stream */ #define PS_FLAG_RTP 0x00010000 @@ -146,6 +148,7 @@ struct call_monologue; #define MEDIA_FLAG_SETUP_PASSIVE SHARED_FLAG_SETUP_PASSIVE #define MEDIA_FLAG_PASSTHRU 0x00100000 #define MEDIA_FLAG_ICE SHARED_FLAG_ICE +#define MEDIA_FLAG_TRICKLE_ICE SHARED_FLAG_TRICKLE_ICE /* access macros */ #define SP_ISSET(p, f) bf_isset(&(p)->sp_flags, SP_FLAG_ ## f) diff --git a/daemon/sdp.c b/daemon/sdp.c index e951ca4a5..45820d790 100644 --- a/daemon/sdp.c +++ b/daemon/sdp.c @@ -166,6 +166,7 @@ struct sdp_attribute { ATTR_RTCP, ATTR_CANDIDATE, ATTR_ICE, + ATTR_ICE_OPTIONS, ATTR_ICE_UFRAG, ATTR_CRYPTO, ATTR_SSRC, @@ -797,7 +798,7 @@ static int parse_attribute(struct sdp_attribute *a) { break; case 11: if (!str_cmp(&a->name, "ice-options")) - a->attr = ATTR_ICE; + a->attr = ATTR_ICE_OPTIONS; else if (!str_cmp(&a->name, "fingerprint")) ret = parse_attribute_fingerprint(a); break; @@ -1180,9 +1181,15 @@ int sdp_streams(const GQueue *sessions, GQueue *streams, struct sdp_ng_flags *fl sp->fingerprint.hash_func->num_bytes); } - /* a=candidate */ + /* ICE stuff */ if (attr_get_by_id(&media->attributes, ATTR_CANDIDATE)) SP_SET(sp, ICE); + if ((attr = attr_get_by_id(&media->attributes, ATTR_ICE_OPTIONS))) { + if (str_str(&attr->value, "trickle") >= 0) + SP_SET(sp, TRICKLE_ICE); + } + else if (is_trickle_ice_address(&sp->rtp_endpoint)) + SP_SET(sp, TRICKLE_ICE); /* determine RTCP endpoint */ @@ -1386,8 +1393,10 @@ static int replace_network_address(struct sdp_chopper *chop, struct network_addr { char buf[64]; int len; + struct packet_stream *sink = packet_stream_sink(ps); - if (is_addr_unspecified(&address->parsed)) + if (is_addr_unspecified(&address->parsed) + && !(sink && is_trickle_ice_address(&sink->advertised_endpoint))) return 0; if (copy_up_to(chop, &address->address_type)) diff --git a/daemon/sdp.h b/daemon/sdp.h index 272c4eae1..ab659f17b 100644 --- a/daemon/sdp.h +++ b/daemon/sdp.h @@ -57,4 +57,10 @@ void sdp_chopper_destroy(struct sdp_chopper *chop); int address_family(const str *s); +INLINE int is_trickle_ice_address(const struct endpoint *ep) { + if (is_addr_unspecified(&ep->ip46) && ep->port == 9) + return 1; + return 0; +} + #endif diff --git a/daemon/str.h b/daemon/str.h index 20e1cda5b..2225767c0 100644 --- a/daemon/str.h +++ b/daemon/str.h @@ -57,6 +57,8 @@ INLINE str *str_chunk_insert(GStringChunk *c, const str *s); INLINE int str_shift(str *s, int len); /* binary compares str object with memory chunk of equal size */ INLINE int str_memcmp(const str *s, void *m); +/* locate a substring within a string, returns character index or -1 */ +INLINE int str_str(const str *s, const char *sub); /* asprintf() analogs */ #define str_sprintf(fmt, a...) __str_sprintf(STR_MALLOC_PADDING fmt, a) @@ -212,5 +214,22 @@ INLINE str *g_string_free_str(GString *gs) { INLINE int str_memcmp(const str *s, void *m) { return memcmp(s->s, m, s->len); } +INLINE int str_str(const str *s, const char *sub) { + int len = strlen(sub); + void *p, *e; + + p = s->s; + e = p + (s->len - len); + while (p < e) { + p = memchr(p, sub[0], e - p); + if (!p) + return -1; + if (!memcmp(p, sub, len)) + return p - (void *) s->s; + p++; + } + + return -1; +} #endif From b44bb288c94b423e1da0a862b60deb0288a71d4a Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 13 Feb 2015 11:53:06 -0500 Subject: [PATCH 28/82] segfault fix --- daemon/call.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index 7144ee70a..f386c47fa 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -2800,19 +2800,21 @@ void call_destroy(struct call *c) { atomic64_add(&m->totalstats.total_nopacket_relayed_sess, total_nopacket_relayed_sess / 2); atomic64_add(&m->totalstats_interval.total_nopacket_relayed_sess, total_nopacket_relayed_sess / 2); - ml = c->monologues->data; - if (ml->term_reason==TIMEOUT) { - atomic64_inc(&m->totalstats.total_timeout_sess); - atomic64_inc(&m->totalstats_interval.total_timeout_sess); - } else if (ml->term_reason==SILENT_TIMEOUT) { - atomic64_inc(&m->totalstats.total_silent_timeout_sess); - atomic64_inc(&m->totalstats_interval.total_silent_timeout_sess); - } else if (ml->term_reason==REGULAR) { - atomic64_inc(&m->totalstats.total_regular_term_sess); - atomic64_inc(&m->totalstats_interval.total_regular_term_sess); - } else if (ml->term_reason==FORCED) { - atomic64_inc(&m->totalstats.total_forced_term_sess); - atomic64_inc(&m->totalstats_interval.total_forced_term_sess); + if (c->monologues) { + ml = c->monologues->data; + if (ml->term_reason==TIMEOUT) { + atomic64_inc(&m->totalstats.total_timeout_sess); + atomic64_inc(&m->totalstats_interval.total_timeout_sess); + } else if (ml->term_reason==SILENT_TIMEOUT) { + atomic64_inc(&m->totalstats.total_silent_timeout_sess); + atomic64_inc(&m->totalstats_interval.total_silent_timeout_sess); + } else if (ml->term_reason==REGULAR) { + atomic64_inc(&m->totalstats.total_regular_term_sess); + atomic64_inc(&m->totalstats_interval.total_regular_term_sess); + } else if (ml->term_reason==FORCED) { + atomic64_inc(&m->totalstats.total_forced_term_sess); + atomic64_inc(&m->totalstats_interval.total_forced_term_sess); + } } timeval_totalstats_average_add(&m->totalstats, &tim_result_duration); From 015b2a9bc821b9ef750b27974e60810b2d0919b5 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 13 Feb 2015 11:54:13 -0500 Subject: [PATCH 29/82] legacy tcp protocol fix --- daemon/call_interfaces.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index ff82e153b..e4a0dd6bc 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -222,7 +222,7 @@ str *call_lookup_udp(char **out, struct callmaster *m) { static int info_parse_func(char **a, void **ret, void *p) { GHashTable *ih = p; - g_hash_table_replace(ih, a[0], a[1]); + g_hash_table_replace(ih, strdup(a[0]), strdup(a[1])); return -1; } @@ -301,7 +301,7 @@ static str *call_request_lookup_tcp(char **out, struct callmaster *m, enum call_ GHashTable *infohash; str_init(&callid, out[RE_TCP_RL_CALLID]); - infohash = g_hash_table_new(g_str_hash, g_str_equal); + infohash = g_hash_table_new_full(g_str_hash, g_str_equal, free, free); c = call_get_opmode(&callid, m, opmode); if (!c) { ilog(LOG_WARNING, "["STR_FORMAT"] Got LOOKUP for unknown call-id", STR_FMT(&callid)); From 016f8b3b53f4b5fa7390906b6c3cd9e78e838862 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 13 Feb 2015 11:54:44 -0500 Subject: [PATCH 30/82] Rework logging a bit (primarily in NG) Creates a preliminary log tag with the call-id whenever possible. Changes log level of underlying NG messages to DEBUG. Does a pretty-printed dump of NG responses. Closes #73 --- daemon/call.c | 5 ++--- daemon/control_ng.c | 39 ++++++++++++++++++++++++++++++--------- daemon/control_tcp.c | 6 ++++++ daemon/control_udp.c | 5 +++++ daemon/log.c | 12 +++++++++++- daemon/log.h | 32 ++++++++++++++++++++++++++++---- 6 files changed, 82 insertions(+), 17 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index f386c47fa..8701dcf2b 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -2978,8 +2978,7 @@ static void __call_free(void *p) { static struct call *call_create(const str *callid, struct callmaster *m) { struct call *c; - ilog(LOG_NOTICE, "["STR_FORMAT"] Creating new call", - STR_FMT(callid)); + ilog(LOG_NOTICE, "Creating new call"); c = obj_alloc0("call", sizeof(*c), __call_free); c->callmaster = m; mutex_init(&c->buffer_lock); @@ -3231,7 +3230,7 @@ int call_delete_branch(struct callmaster *m, const str *callid, const str *branc c = call_get(callid, m); if (!c) { - ilog(LOG_INFO, "["STR_FORMAT"] Call-ID to delete not found", STR_FMT(callid)); + ilog(LOG_INFO, "Call-ID to delete not found"); goto err; } diff --git a/daemon/control_ng.c b/daemon/control_ng.c index dcee3c4f5..ed5fcf8b0 100644 --- a/daemon/control_ng.c +++ b/daemon/control_ng.c @@ -76,7 +76,7 @@ static void control_ng_incoming(struct obj *obj, str *buf, struct sockaddr_in6 * struct control_ng *c = (void *) obj; bencode_buffer_t bencbuf; bencode_item_t *dict, *resp; - str cmd, cookie, data, reply, *to_send; + str cmd, cookie, data, reply, *to_send, callid; const char *errstr; struct msghdr mh; struct iovec iov[3]; @@ -119,11 +119,18 @@ static void control_ng_incoming(struct obj *obj, str *buf, struct sockaddr_in6 * if (!cmd.s) goto err_send; - log_str = g_string_sized_new(256); - g_string_append_printf(log_str, "Got valid command from %s: %.*s - ", addr, STR_FMT(&cmd)); - pretty_print(dict, log_str); - ilog(LOG_INFO, "%.*s", (int) log_str->len, log_str->str); - g_string_free(log_str, TRUE); + bencode_dictionary_get_str(dict, "call-id", &callid); + log_info_str(&callid); + + ilog(LOG_INFO, "Received command '"STR_FORMAT"' from %s", STR_FMT(&cmd), addr); + + if (get_log_level() >= LOG_DEBUG) { + log_str = g_string_sized_new(256); + g_string_append_printf(log_str, "Dump for '"STR_FORMAT"' from %s: ", STR_FMT(&cmd), addr); + pretty_print(dict, log_str); + ilog(LOG_DEBUG, "%.*s", (int) log_str->len, log_str->str); + g_string_free(log_str, TRUE); + } errstr = NULL; if (!str_cmp(&cmd, "ping")) { @@ -165,15 +172,29 @@ err_send: bencode_dictionary_add_string(resp, "result", "error"); bencode_dictionary_add_string(resp, "error-reason", errstr); g_atomic_int_inc(&cur->errors); - goto send_resp; + cmd = STR_NULL; send_resp: bencode_collapse_str(resp, &reply); to_send = &reply; -send_only: - ilog(LOG_INFO, "Returning to SIP proxy: "STR_FORMAT, STR_FMT(to_send)); + if (cmd.s) { + ilog(LOG_INFO, "Replying to '"STR_FORMAT"' from %s", STR_FMT(&cmd), addr); + + if (get_log_level() >= LOG_DEBUG) { + dict = bencode_decode_expect_str(&bencbuf, to_send, BENCODE_DICTIONARY); + if (dict) { + log_str = g_string_sized_new(256); + g_string_append_printf(log_str, "Response dump for '"STR_FORMAT"' to %s: ", + STR_FMT(&cmd), addr); + pretty_print(dict, log_str); + ilog(LOG_DEBUG, "%.*s", (int) log_str->len, log_str->str); + g_string_free(log_str, TRUE); + } + } + } +send_only: ZERO(mh); mh.msg_name = sin; mh.msg_namelen = sizeof(*sin); diff --git a/daemon/control_tcp.c b/daemon/control_tcp.c index 1a707aacb..06ff565dd 100644 --- a/daemon/control_tcp.c +++ b/daemon/control_tcp.c @@ -115,6 +115,12 @@ static int control_stream_parse(struct control_stream *s, char *line) { pcre_get_substring_list(line, ovec, ret, (const char ***) &out); + if (out[RE_TCP_RL_CALLID]) + log_info_c_string(out[RE_TCP_RL_CALLID]); + else if (out[RE_TCP_D_CALLID]) + log_info_c_string(out[RE_TCP_D_CALLID]); + + if (!strcmp(out[RE_TCP_RL_CMD], "request")) output = call_request_tcp(out, c->callmaster); else if (!strcmp(out[RE_TCP_RL_CMD], "lookup")) diff --git a/daemon/control_udp.c b/daemon/control_udp.c index 1e4f3b214..0fcf218ed 100644 --- a/daemon/control_udp.c +++ b/daemon/control_udp.c @@ -82,6 +82,11 @@ static void control_udp_incoming(struct obj *obj, str *buf, struct sockaddr_in6 goto out; } + if (out[RE_UDP_UL_CALLID]) + log_info_c_string(out[RE_UDP_UL_CALLID]); + else if (out[RE_UDP_DQ_CALLID]) + log_info_c_string(out[RE_UDP_DQ_CALLID]); + if (chrtoupper(out[RE_UDP_UL_CMD][0]) == 'U') reply = call_update_udp(out, u->callmaster, addr, sin); else if (chrtoupper(out[RE_UDP_UL_CMD][0]) == 'L') diff --git a/daemon/log.c b/daemon/log.c index f8e4b6854..adfdcf750 100644 --- a/daemon/log.c +++ b/daemon/log.c @@ -116,6 +116,13 @@ void __ilog(int prio, const char *fmt, ...) { STR_FMT(&log_info.u.stream_fd->call->callid), log_info.u.stream_fd->fd.localport); break; + case LOG_INFO_STR: + snprintf(prefix, sizeof(prefix), "["STR_FORMAT"] ", + STR_FMT(log_info.u.str)); + break; + case LOG_INFO_C_STRING: + snprintf(prefix, sizeof(prefix), "[%s] ", log_info.u.cstr); + break; } va_start(ap, fmt); @@ -127,6 +134,9 @@ void __ilog(int prio, const char *fmt, ...) { return; } + while (ret > 0 && msg[ret-1] == '\n') + ret--; + if ((prio & LOG_FLAG_LIMIT)) { time_t when; struct log_limiter_entry lle, *llep; @@ -168,7 +178,7 @@ void __ilog(int prio, const char *fmt, ...) { infix = "... "; } - write_log(xprio, "%s%s%s", prefix, infix, piece); + write_log(xprio, "%s%s%.*s", prefix, infix, ret, piece); out: free(msg); diff --git a/daemon/log.h b/daemon/log.h index 73d49ec23..8ee9e9081 100644 --- a/daemon/log.h +++ b/daemon/log.h @@ -5,18 +5,24 @@ #include #include #include "compat.h" +#include "str.h" struct log_info { union { - struct call *call; - struct stream_fd *stream_fd; + const struct call *call; + const struct stream_fd *stream_fd; + const str *str; + const char *cstr; + const void *ptr; } u; enum { LOG_INFO_NONE = 0, LOG_INFO_CALL, LOG_INFO_STREAM_FD, + LOG_INFO_STR, + LOG_INFO_C_STRING, } e; }; @@ -75,23 +81,41 @@ INLINE void log_info_clear() { case LOG_INFO_STREAM_FD: __obj_put((void *) log_info.u.stream_fd); break; + case LOG_INFO_STR: + case LOG_INFO_C_STRING: + break; } log_info.e = LOG_INFO_NONE; + log_info.u.ptr = NULL; } -INLINE void log_info_call(struct call *c) { +INLINE void log_info_call(const struct call *c) { log_info_clear(); if (!c) return; log_info.e = LOG_INFO_CALL; log_info.u.call = __obj_get((void *) c); } -INLINE void log_info_stream_fd(struct stream_fd *sfd) { +INLINE void log_info_stream_fd(const struct stream_fd *sfd) { log_info_clear(); if (!sfd) return; log_info.e = LOG_INFO_STREAM_FD; log_info.u.stream_fd = __obj_get((void *) sfd); } +INLINE void log_info_str(const str *s) { + log_info_clear(); + if (!s || !s->s) + return; + log_info.e = LOG_INFO_STR; + log_info.u.str = s; +} +INLINE void log_info_c_string(const char *s) { + log_info_clear(); + if (!s) + return; + log_info.e = LOG_INFO_C_STRING; + log_info.u.cstr = s; +} INLINE int get_log_level(void) { return g_atomic_int_get(&log_level); } From 91a85e6cd8e05a6ad3619ecf48f9e902e1f72355 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 13 Feb 2015 12:17:52 -0500 Subject: [PATCH 31/82] fix for skewed stats --- daemon/call.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index 8701dcf2b..74d2ea0bb 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -2646,7 +2646,14 @@ void call_destroy(struct call *c) { cdrbufcur += sprintf(cdrbufcur,"tos=%u, ", (unsigned int)c->tos); for (l = c->monologues; l; l = l->next) { ml = l->data; + + if (!ml->terminated.tv_sec) { + gettimeofday(&ml->terminated, NULL); + ml->term_reason = UNKNOWN; + } + timeval_subtract(&tim_result_duration,&ml->terminated,&ml->started); + if (_log_facility_cdr) { cdrbufcur += sprintf(cdrbufcur, "ml%i_start_time=%ld.%06lu, " "ml%i_end_time=%ld.%06ld, " @@ -2815,10 +2822,11 @@ void call_destroy(struct call *c) { atomic64_inc(&m->totalstats.total_forced_term_sess); atomic64_inc(&m->totalstats_interval.total_forced_term_sess); } + + timeval_totalstats_average_add(&m->totalstats, &tim_result_duration); + timeval_totalstats_average_add(&m->totalstats_interval, &tim_result_duration); } - timeval_totalstats_average_add(&m->totalstats, &tim_result_duration); - timeval_totalstats_average_add(&m->totalstats_interval, &tim_result_duration); if (_log_facility_cdr) /* log it */ From 6173a7a2dc2e053947fd495e3816e6a04a51a727 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 13 Feb 2015 16:34:00 -0500 Subject: [PATCH 32/82] implement atomic bitfield ops --- daemon/aux.h | 42 ++++++++++++++++++++++++------------------ daemon/call.h | 5 ++--- daemon/sdp.c | 6 +++--- 3 files changed, 29 insertions(+), 24 deletions(-) diff --git a/daemon/aux.h b/daemon/aux.h index 51aab5bc3..6eaa2305a 100644 --- a/daemon/aux.h +++ b/daemon/aux.h @@ -474,34 +474,40 @@ INLINE int family_from_address(const struct in6_addr *a) { } /* checks if at least one of the flags is set */ -INLINE int bf_isset(const unsigned int *u, unsigned int f) { - if ((*u & f)) +INLINE int bf_isset(const volatile unsigned int *u, unsigned int f) { + if ((g_atomic_int_get(u) & f)) return -1; return 0; } -INLINE void bf_set(unsigned int *u, unsigned int f) { - *u |= f; +INLINE void bf_set(volatile unsigned int *u, unsigned int f) { + g_atomic_int_or(u, f); } -INLINE void bf_clear(unsigned int *u, unsigned int f) { - *u &= ~f; +INLINE void bf_clear(volatile unsigned int *u, unsigned int f) { + g_atomic_int_and(u, ~f); } -INLINE void bf_xset(unsigned int *u, unsigned int f, int cond) { - bf_clear(u, f); - bf_set(u, cond ? f : 0); +INLINE void bf_set_clear(volatile unsigned int *u, unsigned int f, int cond) { + if (cond) + bf_set(u, f); + else + bf_clear(u, f); } /* works only for single flags */ -INLINE void bf_copy(unsigned int *u, unsigned int f, const unsigned int *s, unsigned int g) { - /* a good compiler will optimize out the calls to log2() */ - *u &= ~f; - if (f >= g) - *u |= (*s & g) << (int) (log2(f) - log2(g)); - else - *u |= (*s & g) >> (int) (log2(g) - log2(f)); +INLINE void bf_copy(volatile unsigned int *u, unsigned int f, + const volatile unsigned int *s, unsigned int g) +{ + bf_set_clear(u, f, bf_isset(s, g)); } /* works for multiple flags */ -INLINE void bf_copy_same(unsigned int *u, const unsigned int *s, unsigned int g) { - bf_copy(u, g, s, g); +INLINE void bf_copy_same(volatile unsigned int *u, const volatile unsigned int *s, unsigned int g) { + unsigned int old, set, clear; + old = g_atomic_int_get(s); + set = old & g; + clear = ~old & g; + bf_set(u, set); + bf_clear(u, clear); } + + INLINE void g_queue_append(GQueue *dst, const GQueue *src) { GList *l; if (!src || !dst) diff --git a/daemon/call.h b/daemon/call.h index 05c0321f1..3bd59810b 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -303,8 +303,7 @@ struct packet_stream { X509 *dtls_cert; /* LOCK: in_lock */ /* in_lock must be held for SETTING these: */ - /* (XXX replace with atomic ops where appropriate) */ - unsigned int ps_flags; + volatile unsigned int ps_flags; }; /* protected by call->master_lock, except the RO elements */ @@ -337,7 +336,7 @@ struct call_media { GSList *endpoint_maps; GHashTable *rtp_payload_types; - unsigned int media_flags; + volatile unsigned int media_flags; }; /* half a dialogue */ diff --git a/daemon/sdp.c b/daemon/sdp.c index 45820d790..2a14fe6e9 100644 --- a/daemon/sdp.c +++ b/daemon/sdp.c @@ -1123,9 +1123,9 @@ int sdp_streams(const GQueue *sessions, GQueue *streams, struct sdp_ng_flags *fl sp->type = media->media_type; memcpy(sp->direction, flags->direction, sizeof(sp->direction)); sp->desired_family = flags->address_family; - bf_xset(&sp->sp_flags, SP_FLAG_ASYMMETRIC, flags->asymmetric); - bf_xset(&sp->sp_flags, SP_FLAG_STRICT_SOURCE, flags->strict_source); - bf_xset(&sp->sp_flags, SP_FLAG_MEDIA_HANDOVER, flags->media_handover); + bf_set_clear(&sp->sp_flags, SP_FLAG_ASYMMETRIC, flags->asymmetric); + bf_set_clear(&sp->sp_flags, SP_FLAG_STRICT_SOURCE, flags->strict_source); + bf_set_clear(&sp->sp_flags, SP_FLAG_MEDIA_HANDOVER, flags->media_handover); errstr = "Invalid RTP payload types"; if (__rtp_payload_types(sp, media)) From 1f7c8a228a85704f7428a0a165debd34edc4c407 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 13 Feb 2015 16:39:01 -0500 Subject: [PATCH 33/82] remove obsolete HAS_HANDLER flag --- daemon/call.c | 8 +++----- daemon/call.h | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index 74d2ea0bb..f0be4a5f4 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -551,7 +551,7 @@ static void determine_handler(struct packet_stream *in, const struct packet_stre const struct streamhandler **sh_pp, *sh; const struct streamhandler ***matrix; - if (PS_ISSET(in, HAS_HANDLER)) + if (in->handler) return; if (MEDIA_ISSET(in->media, PASSTHRU)) goto noop; @@ -573,15 +573,13 @@ static void determine_handler(struct packet_stream *in, const struct packet_stre goto err; in->handler = sh; -done: - PS_SET(in, HAS_HANDLER); return; err: ilog(LOG_WARNING, "Unknown transport protocol encountered"); noop: in->handler = &__sh_noop; - goto done; + return; } void stream_msg_mh_src(struct packet_stream *ps, struct msghdr *mh) { @@ -3088,7 +3086,7 @@ void __monologue_tag(struct call_monologue *ml, const str *tag) { static void __stream_unkernelize(struct packet_stream *ps) { unkernelize(ps); PS_CLEAR(ps, CONFIRMED); - PS_CLEAR(ps, HAS_HANDLER); + ps->handler = NULL; } static void stream_unkernelize(struct packet_stream *ps) { if (!ps) diff --git a/daemon/call.h b/daemon/call.h index 3bd59810b..8be6893eb 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -130,7 +130,7 @@ struct call_monologue; #define PS_FLAG_CONFIRMED 0x00200000 #define PS_FLAG_KERNELIZED 0x00400000 #define PS_FLAG_NO_KERNEL_SUPPORT 0x00800000 -#define PS_FLAG_HAS_HANDLER 0x01000000 +#define PS_FLAG_UNUSED 0x01000000 #define PS_FLAG_FINGERPRINT_VERIFIED 0x02000000 #define PS_FLAG_STRICT_SOURCE SHARED_FLAG_STRICT_SOURCE #define PS_FLAG_MEDIA_HANDOVER SHARED_FLAG_MEDIA_HANDOVER From ec5a543f0f9803c94433bf79cfe1ad6a54eb5b78 Mon Sep 17 00:00:00 2001 From: Frederic-Philippe Metz Date: Wed, 18 Feb 2015 07:36:25 +0100 Subject: [PATCH 34/82] Added prefix config parameter to graphite acc. to OPS --- README.md | 5 +++++ daemon/graphite.c | 17 +++++++++++++++++ daemon/graphite.h | 1 + daemon/main.c | 19 ++++++++++++------- debian/ngcp-rtpengine-daemon.default | 3 ++- debian/ngcp-rtpengine-daemon.init | 1 + el/rtpengine.init | 5 +++++ 7 files changed, 43 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 9727cef63..b878745d7 100644 --- a/README.md +++ b/README.md @@ -186,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. @@ -364,6 +365,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/graphite.c b/daemon/graphite.c index f886584af..8a3ebd088 100644 --- a/daemon/graphite.c +++ b/daemon/graphite.c @@ -12,6 +12,7 @@ #include "log.h" #include "call.h" +#include "graphite.h" static int graphite_sock=-1; static u_int32_t graphite_ipaddress; @@ -19,6 +20,11 @@ static int graphite_port=0; static struct callmaster* cm=0; //struct totalstats totalstats_prev; static time_t g_now, next_run; +static char* graphite_prefix = NULL; + +void set_prefix(char* prefix) { + graphite_prefix = prefix; +} int connect_to_graphite_server(u_int32_t ipaddress, int port) { @@ -89,16 +95,27 @@ int send_graphite_data() { mutex_lock(&cm->totalstats_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) cm->totalstats_interval.total_average_call_dur.tv_sec,(unsigned long long)g_now); ptr += rc; + if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; } rc = sprintf(ptr,"%s.totals.average_call_dur.tv_usec %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_average_call_dur.tv_usec,(unsigned long long)g_now); ptr += rc; + if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; } rc = sprintf(ptr,"%s.totals.forced_term_sess %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_forced_term_sess,(unsigned long long)g_now); ptr += rc; + if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; } rc = sprintf(ptr,"%s.totals.managed_sess %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_managed_sess,(unsigned long long)g_now); ptr += rc; + if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; } rc = sprintf(ptr,"%s.totals.nopacket_relayed_sess %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_nopacket_relayed_sess,(unsigned long long)g_now); ptr += rc; + if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; } rc = sprintf(ptr,"%s.totals.oneway_stream_sess %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_oneway_stream_sess,(unsigned long long)g_now); ptr += rc; + if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; } rc = sprintf(ptr,"%s.totals.regular_term_sess %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_regular_term_sess,(unsigned long long)g_now); ptr += rc; + if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; } rc = sprintf(ptr,"%s.totals.relayed_errors %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_relayed_errors,(unsigned long long)g_now); ptr += rc; + if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; } rc = sprintf(ptr,"%s.totals.relayed_packets %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_relayed_packets,(unsigned long long)g_now); ptr += rc; + if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; } rc = sprintf(ptr,"%s.totals.silent_timeout_sess %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_silent_timeout_sess,(unsigned long long)g_now); ptr += rc; + if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; } rc = sprintf(ptr,"%s.totals.timeout_sess %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_timeout_sess,(unsigned long long)g_now); ptr += rc; ZERO(cm->totalstats_interval); diff --git a/daemon/graphite.h b/daemon/graphite.h index 0b51e7b9a..a1a89267f 100644 --- a/daemon/graphite.h +++ b/daemon/graphite.h @@ -13,5 +13,6 @@ 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); #endif /* GRAPHITE_H_ */ diff --git a/daemon/main.c b/daemon/main.c index 50da00044..4f0c29aa7 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -257,9 +257,10 @@ 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; int version = 0; int sip_source = 0; @@ -274,6 +275,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" }, @@ -340,6 +342,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 +374,12 @@ 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_stderr) { write_log = log_to_stderr; diff --git a/debian/ngcp-rtpengine-daemon.default b/debian/ngcp-rtpengine-daemon.default index 20b10c3d5..3f259c0c6 100644 --- a/debian/ngcp-rtpengine-daemon.default +++ b/debian/ngcp-rtpengine-daemon.default @@ -24,4 +24,5 @@ TABLE=0 # 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..64ce42902 100755 --- a/debian/ngcp-rtpengine-daemon.init +++ b/debian/ngcp-rtpengine-daemon.init @@ -74,6 +74,7 @@ OPTIONS="$OPTIONS --table=$TABLE" [ -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 1fb33ce62..48b2c215c 100644 --- a/el/rtpengine.init +++ b/el/rtpengine.init @@ -153,6 +153,11 @@ 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" From f224babc54bc0bf8db97ee96d74987225fa26301 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Wed, 18 Feb 2015 08:55:58 -0500 Subject: [PATCH 35/82] segfault fix if no rtpmap attribute is present --- daemon/sdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/sdp.c b/daemon/sdp.c index 2a14fe6e9..e869d621b 100644 --- a/daemon/sdp.c +++ b/daemon/sdp.c @@ -1050,7 +1050,7 @@ static int __rtp_payload_types(struct stream_params *sp, struct sdp_media *media /* first go through a=rtpmap and build a hash table of attrs */ ht = g_hash_table_new(g_int_hash, g_int_equal); q = attr_list_get_by_id(&media->attributes, ATTR_RTPMAP); - for (ql = q->head; ql; ql = ql->next) { + for (ql = q ? q->head : NULL; ql; ql = ql->next) { struct rtp_payload_type *pt; attr = ql->data; pt = &attr->u.rtpmap.rtp_pt; From 26ca844e7388cd197b02d8f91688e1ab4fab84da Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Tue, 24 Feb 2015 08:23:05 -0500 Subject: [PATCH 36/82] drop support for glib versions older than 2.30 --- daemon/aux.c | 32 -------------------------------- daemon/aux.h | 6 ------ daemon/call_interfaces.c | 4 ---- daemon/call_interfaces.h | 2 -- daemon/control_ng.c | 2 -- daemon/cookie_cache.c | 6 ------ debian/control | 2 +- 7 files changed, 1 insertion(+), 53 deletions(-) diff --git a/daemon/aux.c b/daemon/aux.c index 3331dd2f0..840f75e4a 100644 --- a/daemon/aux.c +++ b/daemon/aux.c @@ -83,38 +83,6 @@ int pcre_multi_match(pcre *re, pcre_extra *ree, const char *s, unsigned int num, -#if !GLIB_CHECK_VERSION(2,14,0) - -void g_string_vprintf(GString *string, const gchar *format, va_list args) { - char *s; - int r; - - r = vasprintf(&s, format, args); - if (r < 0) - return; - - g_string_assign(string, s); - free(s); -} - -void g_queue_clear(GQueue *q) { - GList *l, *n; - - if (!q) - return; - - for (l = q->head; l; l = n) { - n = l->next; - g_list_free_1(l); - } - - q->head = q->tail = NULL; - q->length = 0; -} - -#endif - - static void thread_join_me() { pthread_t *me; diff --git a/daemon/aux.h b/daemon/aux.h index 6eaa2305a..05c7671c1 100644 --- a/daemon/aux.h +++ b/daemon/aux.h @@ -65,12 +65,6 @@ INLINE void strdupfree(char **, const char *); char *get_thread_buf(void); -#if !GLIB_CHECK_VERSION(2,14,0) -#define G_QUEUE_INIT { NULL, NULL, 0 } -void g_string_vprintf(GString *string, const gchar *format, va_list args); -void g_queue_clear(GQueue *); -#endif - #if !GLIB_CHECK_VERSION(2,32,0) INLINE int g_hash_table_contains(GHashTable *h, const void *k) { return g_hash_table_lookup(h, k) ? 1 : 0; diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index e4a0dd6bc..4fabe1b8d 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -919,7 +919,6 @@ stats: ng_stats(bencode_dictionary_add_dictionary(dict, "RTCP"), &totals->totals[1], NULL); } -#if GLIB_CHECK_VERSION(2,16,0) static void ng_list_calls( struct callmaster *m, bencode_item_t *output, long long int limit) { GHashTableIter iter; gpointer key, value; @@ -933,7 +932,6 @@ static void ng_list_calls( struct callmaster *m, bencode_item_t *output, long lo rwlock_unlock_r(&m->hashlock); } -#endif @@ -958,7 +956,6 @@ const char *call_query_ng(bencode_item_t *input, struct callmaster *m, bencode_i } -#if GLIB_CHECK_VERSION(2,16,0) const char *call_list_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output) { bencode_item_t *calls = NULL; long long int limit; @@ -975,4 +972,3 @@ const char *call_list_ng(bencode_item_t *input, struct callmaster *m, bencode_it return NULL; } -#endif diff --git a/daemon/call_interfaces.h b/daemon/call_interfaces.h index 5c1e68601..12db76fab 100644 --- a/daemon/call_interfaces.h +++ b/daemon/call_interfaces.h @@ -35,9 +35,7 @@ const char *call_offer_ng(bencode_item_t *, struct callmaster *, bencode_item_t const char *call_answer_ng(bencode_item_t *, struct callmaster *, bencode_item_t *); const char *call_delete_ng(bencode_item_t *, struct callmaster *, bencode_item_t *); const char *call_query_ng(bencode_item_t *, struct callmaster *, bencode_item_t *); -#if GLIB_CHECK_VERSION(2,16,0) const char *call_list_ng(bencode_item_t *, struct callmaster *, bencode_item_t *); -#endif void ng_call_stats(struct call *call, const str *fromtag, const str *totag, bencode_item_t *output, struct call_stats *totals); diff --git a/daemon/control_ng.c b/daemon/control_ng.c index ed5fcf8b0..dc0c1d1e5 100644 --- a/daemon/control_ng.c +++ b/daemon/control_ng.c @@ -153,12 +153,10 @@ static void control_ng_incoming(struct obj *obj, str *buf, struct sockaddr_in6 * errstr = call_query_ng(dict, c->callmaster, resp); g_atomic_int_inc(&cur->query); } -#if GLIB_CHECK_VERSION(2,16,0) else if (!str_cmp(&cmd, "list")) { errstr = call_list_ng(dict, c->callmaster, resp); g_atomic_int_inc(&cur->list); } -#endif else errstr = "Unrecognized command"; diff --git a/daemon/cookie_cache.c b/daemon/cookie_cache.c index b90de96af..8f5b2052d 100644 --- a/daemon/cookie_cache.c +++ b/daemon/cookie_cache.c @@ -27,14 +27,8 @@ void cookie_cache_init(struct cookie_cache *c) { static void __cookie_cache_check_swap(struct cookie_cache *c) { if (poller_now - c->swap_time >= 30) { g_hash_table_remove_all(c->old.cookies); -#if GLIB_CHECK_VERSION(2,14,0) g_string_chunk_clear(c->old.chunks); swap_ptrs(&c->old.chunks, &c->current.chunks); -#else - g_string_chunk_free(c->old.chunks); - c->old.chunks = c->current.chunks; - c->current.chunks = g_string_chunk_new(4 * 1024); -#endif swap_ptrs(&c->old.cookies, &c->current.cookies); c->swap_time = poller_now; } diff --git a/debian/control b/debian/control index 9a21efdd1..14eba8d66 100644 --- a/debian/control +++ b/debian/control @@ -6,7 +6,7 @@ Build-Depends: debhelper (>= 5), iptables-dev (>= 1.4), libcurl4-openssl-dev | libcurl4-gnutls-dev | libcurl3-openssl-dev | libcurl3-gnutls-dev, - libglib2.0-dev, + libglib2.0-dev (>= 2.30), libpcre3-dev, libssl-dev (>= 1.0.1), libxmlrpc-c3-dev (>= 1.16.07) | libxmlrpc-core-c3-dev (>= 1.16.07), From b83e80c8118d7d7d2663358a135d44b08ea8a84c Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Tue, 24 Feb 2015 11:16:38 -0500 Subject: [PATCH 37/82] add makefile dependency to itself --- daemon/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/daemon/Makefile b/daemon/Makefile index cd2f1e670..d1488770b 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -83,8 +83,10 @@ clean: .depend: $(SRCS) Makefile $(CC) $(CFLAGS) -M $(SRCS) | sed -e 's/:/ .depend:/' > .depend -rtpengine: $(OBJS) .depend +rtpengine: $(OBJS) .depend Makefile $(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS) +$(OBJS): Makefile + include .depend From 956d07d42e106231aa4cae13d706b30e7833ae89 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Wed, 25 Feb 2015 08:29:08 -0500 Subject: [PATCH 38/82] fix erroneously reported 0.0.0.0 address when bridging from ipv6 --- daemon/call.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/daemon/call.c b/daemon/call.c index f0be4a5f4..d0b8ab84f 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -2865,7 +2865,8 @@ static int call_stream_address4(char *o, struct packet_stream *ps, enum stream_a l = 4; } - if (!in6_to_4(&ps->advertised_endpoint.ip46) && !is_trickle_ice_address(&ps->advertised_endpoint)) { + if (is_addr_unspecified(&ps->advertised_endpoint.ip46) + && !is_trickle_ice_address(&ps->advertised_endpoint)) { strcpy(o + l, "0.0.0.0"); l += 7; } From fca4500dd06d3fb9b2acbbe60ed64c51f4346f77 Mon Sep 17 00:00:00 2001 From: Michael Prokop Date: Wed, 25 Feb 2015 16:29:09 +0100 Subject: [PATCH 39/82] MT#9127 Bump Standards-Version to 3.9.6 --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 14eba8d66..146e2d6ed 100644 --- a/debian/control +++ b/debian/control @@ -12,7 +12,7 @@ Build-Depends: debhelper (>= 5), libxmlrpc-c3-dev (>= 1.16.07) | libxmlrpc-core-c3-dev (>= 1.16.07), markdown, zlib1g-dev -Standards-Version: 3.9.5 +Standards-Version: 3.9.6 Homepage: http://sipwise.com/ Package: ngcp-rtpengine-daemon From d98d17b92f84df550eedadf5406ec2c46d96de18 Mon Sep 17 00:00:00 2001 From: Frederic-Philippe Metz Date: Wed, 25 Feb 2015 17:25:43 +0100 Subject: [PATCH 40/82] Implemented non blocking connect to graphite due to firewall issues. As requested, the connect call blocks if a firewall discards the TCP SYN and the rtpengine could not be terminated. This Impl. fixes the behaviour. --- daemon/graphite.c | 110 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 96 insertions(+), 14 deletions(-) diff --git a/daemon/graphite.c b/daemon/graphite.c index 8a3ebd088..979f08467 100644 --- a/daemon/graphite.c +++ b/daemon/graphite.c @@ -9,12 +9,18 @@ #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; @@ -26,10 +32,34 @@ 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)); @@ -38,9 +68,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; } @@ -54,18 +84,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); @@ -137,27 +175,71 @@ 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 + } + } g_now = time(NULL); - if (g_now < next_run) - goto sleep; + if (g_now < next_run) { + usleep(100000); + return; + } next_run = g_now + 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); } From 6e7a287f54cac080d552d90266033bdb57266841 Mon Sep 17 00:00:00 2001 From: Frederic-Philippe Metz Date: Thu, 26 Feb 2015 18:59:06 +0100 Subject: [PATCH 41/82] Modified average_dur format for graphite. --- daemon/graphite.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/daemon/graphite.c b/daemon/graphite.c index 979f08467..f8cb79ab2 100644 --- a/daemon/graphite.c +++ b/daemon/graphite.c @@ -134,9 +134,7 @@ int send_graphite_data() { mutex_lock(&cm->totalstats_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) cm->totalstats_interval.total_average_call_dur.tv_sec,(unsigned long long)g_now); ptr += rc; - if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"%s.totals.average_call_dur.tv_usec %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_average_call_dur.tv_usec,(unsigned long long)g_now); ptr += rc; + rc = sprintf(ptr,"%s.totals.average_call_dur %llu.%06llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_average_call_dur.tv_sec,(unsigned long long) cm->totalstats_interval.total_average_call_dur.tv_usec,(unsigned long long)g_now); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; } rc = sprintf(ptr,"%s.totals.forced_term_sess %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_forced_term_sess,(unsigned long long)g_now); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; } From f99d6d4f4259a873d9125fe552ee358a31d5eed8 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 27 Feb 2015 09:47:06 -0500 Subject: [PATCH 42/82] implement full ICE support Squashed commit of the following: commit 00213e66c74d402d2c3045eabeb5a10fa68f10c7 Author: Richard Fuchs Date: Fri Feb 27 09:40:04 2015 -0500 perform ICE restart if we change ports commit 27fbcbd6cbebdd071c1fa960c6e55f016534d171 Author: Richard Fuchs Date: Thu Feb 26 15:58:12 2015 -0500 locking fixes commit 60c1c5ae13b2c11720ee099daea1e3cdebea6317 Author: Richard Fuchs Date: Thu Feb 26 15:58:06 2015 -0500 unkernelize when ICE completes commit 1d816f9864ce31ea9a55a0bdf243a9900e597cdf Author: Richard Fuchs Date: Thu Feb 26 15:54:44 2015 -0500 relaxed locking where not needed commit 75b58a9093d97daa5b2f4f8c1790f3b0f64117f8 Author: Richard Fuchs Date: Thu Feb 26 11:46:08 2015 -0500 use atomic bitfield instead of bitfield+mutex commit 03552eeed92aad419c1ad3cb9cfe5a7bded7f601 Author: Richard Fuchs Date: Thu Feb 26 11:24:59 2015 -0500 shuffle around aux.h a bit more commit b9b8a3aa5ef4d1a9026977465716fdbadf96917a Author: Richard Fuchs Date: Thu Feb 26 11:16:12 2015 -0500 remove some code redundancy through a "state machine" commit 0b4bfef1b18b75e22611f18e510209c743708cd6 Author: Richard Fuchs Date: Thu Feb 26 10:10:41 2015 -0500 reorder to match struct commit a2a51d81a8f02dbb0fd8afe103ae0b0fa2a28dcf Author: Richard Fuchs Date: Thu Feb 26 10:10:03 2015 -0500 clear more states for ICE restart commit d554a2b858dc3655d72d037a16c12e7eea243e1b Author: Richard Fuchs Date: Thu Feb 26 09:58:29 2015 -0500 dont duplicate candidates on re-invite commit 4c804652b7985046d0214e4a90e67da9cce8fd03 Author: Richard Fuchs Date: Thu Feb 26 09:58:20 2015 -0500 retain ICE role across re-invites commit 4a586dd72d9f31ba87fb82e1e0e3602b467c252a Author: Richard Fuchs Date: Thu Feb 26 09:42:09 2015 -0500 eliminate duplicate log messages commit ef0be2e308e1e3c3ce063afd2854e3fb749d813a Author: Richard Fuchs Date: Thu Feb 26 09:32:36 2015 -0500 fix incorrect log message commit 2544b60f00b3b03fd64ac6e4b4e8ec20423b7745 Author: Richard Fuchs Date: Wed Feb 25 15:26:48 2015 -0500 better logging for ICE agents commit c42807384efa167afa08e10296b5177526efb9ba Author: Richard Fuchs Date: Wed Feb 25 15:18:27 2015 -0500 dont run ICE checks if we dont have a password commit 1b56cb75b5912fd5aa6e3c66bf792e56f706d249 Author: Richard Fuchs Date: Wed Feb 25 15:16:19 2015 -0500 ICE pairs should go in triggered queue only once commit d10c56f3ae6172240c2a786fcacb14e5df177f13 Author: Richard Fuchs Date: Wed Feb 25 14:04:00 2015 -0500 obsolete the ICE agent running flag commit 52237e33995d8b6ff25e898daee7d46af37992ed Author: Richard Fuchs Date: Wed Feb 25 14:02:06 2015 -0500 cease checks once ICE is completed commit 5332d18612d5c8e068590a6b4e708b969d385844 Author: Richard Fuchs Date: Wed Feb 25 13:53:57 2015 -0500 fix ICE completion logging commit 85f5fd63aa24998d206657cabeaf3fecbe4d187a Author: Richard Fuchs Date: Wed Feb 25 13:53:33 2015 -0500 make better use of bit flags commit 796b48bb78601e98270c182e2ad60f1c5278091a Author: Richard Fuchs Date: Wed Feb 25 12:09:46 2015 -0500 improved learned candidate pairing and completion logic commit d15561072e24a842e4fc7f074ec6ecadcf96cd8e Author: Richard Fuchs Date: Wed Feb 25 11:21:45 2015 -0500 support upper case transport strings commit 557da7b1c39807bad119f23e57c4d31d77962103 Author: Richard Fuchs Date: Wed Feb 25 10:43:57 2015 -0500 use struct endpoint in ice candidates commit 951040bfd62eb52bccb091d99b7b8289ccef2cc9 Author: Richard Fuchs Date: Wed Feb 25 10:31:13 2015 -0500 more meaningful ICE log messages commit 8ec2426bd3101c4f381e95cd0686b6b1a6fec658 Author: Richard Fuchs Date: Wed Feb 25 09:54:49 2015 -0500 shut down agent if no components - limit number of candidates commit 149260f3a6eaa5f58a6f9071e0b05a18914b987d Author: Richard Fuchs Date: Wed Feb 25 09:44:13 2015 -0500 handle ICE restarts commit 6a18c31f81ab94df1f5d5a701339b14be0e10ee5 Author: Richard Fuchs Date: Tue Feb 24 16:29:01 2015 -0500 dont clear succeeded flag when nominating commit 93e0861d0250f9190b16b14c3f4fe33a359d5d47 Author: Richard Fuchs Date: Tue Feb 24 16:21:40 2015 -0500 use correct pwd in stun binding response commit 32ba3ea406e30d168834f93d3e454d812820cf55 Author: Richard Fuchs Date: Tue Feb 24 16:03:09 2015 -0500 use deterministic foundation for prflx cands commit 2f2dc9151566d0bf2eebbaf02845718229be0e3d Author: Richard Fuchs Date: Tue Feb 24 15:21:13 2015 -0500 handle initial ICE role commit a6b8ad25e6c326f2a0edda12db5dff910bc3d771 Author: Richard Fuchs Date: Tue Feb 24 14:43:47 2015 -0500 another ICE scheduling fix commit c572b04e55b695af28efed4fc4dc8c798989f02d Author: Richard Fuchs Date: Tue Feb 24 14:14:29 2015 -0500 make ICE aware of rtcp-mux commit 93cd2d2560809f82e8ab9ffb2f5e2725fb439f4c Author: Richard Fuchs Date: Tue Feb 24 14:13:27 2015 -0500 print timestamp when logging to stderr commit 22a52ffda2f21dba37c44c9e148a4cbe830be52e Author: Richard Fuchs Date: Tue Feb 24 14:13:13 2015 -0500 ICE scheduling fixes commit 5d2d1a7739ef7d41514352fbf84deddcbd4500af Author: Richard Fuchs Date: Tue Feb 24 13:27:59 2015 -0500 increase ICE pwd length to make chrome happy commit ceff6698db33fa7a4500cb94a4fa377a7629b8aa Author: Richard Fuchs Date: Tue Feb 24 13:09:54 2015 -0500 dont discard RTP if ICE hasnt finished yet commit e809877d0ee0e7d60f3826be091c59145a2a9e19 Author: Richard Fuchs Date: Tue Feb 24 13:06:31 2015 -0500 implement remote-candidates (untested) commit 41670eadbbbe99a35a70158cf054b20a84c9c51b Author: Richard Fuchs Date: Tue Feb 24 11:58:13 2015 -0500 shut down ICE agent when everything fails commit 1ca26c4a815c3f78b0c97cc6b5a5d794a64926f4 Author: Richard Fuchs Date: Tue Feb 24 11:10:46 2015 -0500 fix up SDP output for ICE candidates commit 0287d68f3330d9b2b7e61ea77ce9c77dec83d217 Author: Richard Fuchs Date: Tue Feb 24 10:09:49 2015 -0500 process ICE completion and fire up DTLS if desired commit 5b6386036b05f43ccbdbeb63ea49d8585498cfd0 Author: Richard Fuchs Date: Tue Feb 24 09:37:52 2015 -0500 use a btree to schedule ice checks commit 2bc25f1e0f5acee9262186a866fcc851d2e911ba Author: Richard Fuchs Date: Tue Feb 24 09:37:02 2015 -0500 convert shutdown condition into global var commit ecf0c5587c62fcb39c8a67e510bcc7e6e3c10162 Author: Richard Fuchs Date: Mon Feb 23 14:50:46 2015 -0500 replace poller_now with timeval g_now commit 164ecdd7ac5d37641de32c98bba7db2e23446b91 Author: Richard Fuchs Date: Mon Feb 23 13:00:15 2015 -0500 handle nominations if we're controlling commit d013659365c33a3e802e1ec734dc45361531071d Author: Richard Fuchs Date: Mon Feb 23 12:11:40 2015 -0500 copy controlling/ed role into agent commit 09f1cae14a2dd1aaf06b1c81ebe30ad198b96ec6 Author: Richard Fuchs Date: Mon Feb 23 12:02:54 2015 -0500 separate nominated pairs from valid pairs commit f75f338cded413617318288ceb6a664771e9434c Author: Richard Fuchs Date: Mon Feb 23 11:55:30 2015 -0500 organize aux.h into sections commit d6acee1392a9cb34bd51524d11fc3d1e40f788cd Author: Richard Fuchs Date: Mon Feb 23 11:55:17 2015 -0500 use b-tree for various ICE lists commit af9804d139cf50c6908ac760c9179685b3a872f0 Author: Richard Fuchs Date: Fri Feb 20 16:21:09 2015 -0500 prepare to finalize ice processing commit 18df118375cb30015f703cfbd9ed5c5020ff122e Author: Richard Fuchs Date: Fri Feb 20 16:19:50 2015 -0500 prettier logging commit 280755c61a5e014422acfd45a30e98b2ec6b9efa Author: Richard Fuchs Date: Fri Feb 20 15:02:14 2015 -0500 implement unfreezing of other components on success commit 5d13657d5b7a962ebbbd96677909ba49f59492dc Author: Richard Fuchs Date: Fri Feb 20 15:00:24 2015 -0500 bitfield access macros commit 71746ad6a1171234adbb274ad67159a890c59c3c Author: Richard Fuchs Date: Fri Feb 20 14:15:05 2015 -0500 handle ice updates and duplicate candidates commit 02309d1b5b39333778e04ef61b61be3ba81cb9d3 Author: Richard Fuchs Date: Fri Feb 20 12:22:29 2015 -0500 handle role conflicts commit 52acf54ba5b2fe694f647e828ab92b0bf82bdce1 Author: Richard Fuchs Date: Fri Feb 20 10:24:01 2015 -0500 proper pair priorities calculation commit 307af79e8d347d703b065c5c7b7b0321f1ca82fb Author: Richard Fuchs Date: Fri Feb 20 09:54:18 2015 -0500 fix address family mixups commit 7cbfd4d36a21ad917974259bcb37c5cea340bf4e Author: Richard Fuchs Date: Fri Feb 20 09:14:49 2015 -0500 delay dtls startup and timeout checks while ice is running commit 2a8ab752280ebf9961137414c88c6afbf03b0df2 Author: Richard Fuchs Date: Thu Feb 19 16:47:56 2015 -0500 process ice/stun responses commit 92da323dcff4f725e29ab378ede1aab68d9900a1 Author: Richard Fuchs Date: Thu Feb 19 15:14:30 2015 -0500 adding stun server script for testing commit c5cfeb122cc8503a8f4c901e279eac2932feb3f7 Author: Richard Fuchs Date: Thu Feb 19 13:42:40 2015 -0500 act on stun requests, learn prflx candidates commit 1cafd35e7a5105214aa4af5b03d14054f02c668e Author: Richard Fuchs Date: Thu Feb 19 13:30:14 2015 -0500 fix pktinfo for ipv4 packets commit 8e338b842606a910a34a7ced09516a0cb097b449 Author: Richard Fuchs Date: Thu Feb 19 11:48:55 2015 -0500 rework interface handling to prepare to learn ICE candidate commit 09e365c1429d2a1d81126661df4749567f1e109d Author: Richard Fuchs Date: Wed Feb 18 16:24:42 2015 -0500 add some locking commit 8fc7b75095d09f118a4febf1f3f7b75ed333751d Author: Richard Fuchs Date: Wed Feb 18 16:05:20 2015 -0500 extend logic in response to stun req and implement triggered checks commit 35eeb04376c5a590f8fc42fcb1083a2fa7126e13 Author: Richard Fuchs Date: Wed Feb 18 12:46:42 2015 -0500 handle ice/stun retransmits and timeouts commit b5637565b659350fefb63034e4146ace8c6019b0 Author: Richard Fuchs Date: Wed Feb 18 11:19:19 2015 -0500 first implementation of sending ICE checks commit f0c1928c05a2c87fe23494367670207805c0b096 Author: Richard Fuchs Date: Tue Feb 17 14:39:11 2015 -0500 preliminary list and loop for ICE checks commit c38d6e22c18285a7fb87e5d7a00ac61cb5b7df02 Author: Richard Fuchs Date: Tue Feb 17 12:00:24 2015 -0500 pair up candidates and prepare to run checks commit d9559b4c5935ec602fdb801ecb0bd2158b39db65 Author: Richard Fuchs Date: Fri Feb 13 15:36:29 2015 -0500 parse and remeber basic ICE attributes --- daemon/Makefile | 2 +- daemon/aux.c | 21 + daemon/aux.h | 339 +++++++--- daemon/call.c | 444 ++++++++----- daemon/call.h | 70 +- daemon/call_interfaces.c | 2 + daemon/dtls.c | 10 +- daemon/graphite.c | 30 +- daemon/ice.c | 1357 ++++++++++++++++++++++++++++++++++++++ daemon/ice.h | 208 ++++++ daemon/log.c | 10 +- daemon/log.h | 14 +- daemon/main.c | 19 +- daemon/poller.c | 12 +- daemon/poller.h | 6 +- daemon/sdp.c | 368 ++++++----- daemon/sdp.h | 1 - daemon/stun.c | 285 ++++++-- daemon/stun.h | 24 +- tests/stun-client | 91 +++ tests/stun-server | 204 ++++++ 21 files changed, 2960 insertions(+), 557 deletions(-) create mode 100644 daemon/ice.c create mode 100644 daemon/ice.h create mode 100755 tests/stun-client create mode 100755 tests/stun-server diff --git a/daemon/Makefile b/daemon/Makefile index d1488770b..235526e14 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -63,7 +63,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 + crypto.c rtp.c call_interfaces.c dtls.c log.c cli.c graphite.c ice.c OBJS= $(SRCS:.c=.o) diff --git a/daemon/aux.c b/daemon/aux.c index 840f75e4a..cbb645024 100644 --- a/daemon/aux.c +++ b/daemon/aux.c @@ -34,6 +34,9 @@ static cond_t threads_cond = COND_STATIC_INIT; static struct thread_buf __thread t_bufs[NUM_THREAD_BUFS]; static int __thread t_buf_idx; +__thread struct timeval g_now; +volatile int g_shutdown; + #ifdef NEED_ATOMIC64_MUTEX mutex_t __atomic64_mutex = MUTEX_STATIC_INIT; #endif @@ -191,3 +194,21 @@ char *get_thread_buf(void) { t_buf_idx = 0; return ret; } + +int g_tree_find_first_cmp(void *k, void *v, void *d) { + void **p = d; + GEqualFunc f = p[1]; + if (!f || f(v, p[0])) { + p[2] = v; + return TRUE; + } + return FALSE; +} +int g_tree_find_all_cmp(void *k, void *v, void *d) { + void **p = d; + GEqualFunc f = p[1]; + GQueue *q = p[2]; + if (!f || f(v, p[0])) + g_queue_push_tail(q, v); + return FALSE; +} diff --git a/daemon/aux.h b/daemon/aux.h index 05c7671c1..4b0643349 100644 --- a/daemon/aux.h +++ b/daemon/aux.h @@ -27,27 +27,11 @@ +/*** HELPER MACROS ***/ + #define OFFSET_OF(t,e) ((unsigned int) (unsigned long) &(((t *) 0)->e)) #define ZERO(x) memset(&(x), 0, sizeof(x)) -#define IPF "%u.%u.%u.%u" -#define IPP(x) ((unsigned char *) (&(x)))[0], ((unsigned char *) (&(x)))[1], ((unsigned char *) (&(x)))[2], ((unsigned char *) (&(x)))[3] -#define IP6F "%x:%x:%x:%x:%x:%x:%x:%x" -#define IP6P(x) ntohs(((u_int16_t *) (x))[0]), \ - ntohs(((u_int16_t *) (x))[1]), \ - ntohs(((u_int16_t *) (x))[2]), \ - ntohs(((u_int16_t *) (x))[3]), \ - ntohs(((u_int16_t *) (x))[4]), \ - ntohs(((u_int16_t *) (x))[5]), \ - ntohs(((u_int16_t *) (x))[6]), \ - ntohs(((u_int16_t *) (x))[7]) -#define D6F "["IP6F"]:%u" -#define D6P(x) IP6P((x).sin6_addr.s6_addr), ntohs((x).sin6_port) -#define DF IPF ":%u" -#define DP(x) IPP((x).sin_addr.s_addr), ntohs((x).sin_port) - -#define BIT_ARRAY_DECLARE(name, size) unsigned long name[((size) + sizeof(long) * 8 - 1) / (sizeof(long) * 8)] - #define UINT64F "%" G_GUINT64_FORMAT #define THREAD_BUF_SIZE 64 @@ -56,21 +40,50 @@ +/*** TYPES ***/ + +struct endpoint { + struct in6_addr ip46; + u_int16_t port; +}; + + + +/*** GLOBALS ***/ + +extern __thread struct timeval g_now; +extern volatile int g_shutdown; + + + + +/*** PROTOTYPES ***/ + typedef int (*parse_func)(char **, void **, void *); -GList *g_list_link(GList *, GList *); int pcre_multi_match(pcre *, pcre_extra *, const char *, unsigned int, parse_func, void *, GQueue *); INLINE void strmove(char **, char **); INLINE void strdupfree(char **, const char *); char *get_thread_buf(void); +unsigned int in6_addr_hash(const void *p); +int in6_addr_eq(const void *a, const void *b); + +/*** GLIB HELPERS ***/ + +GList *g_list_link(GList *, GList *); + #if !GLIB_CHECK_VERSION(2,32,0) INLINE int g_hash_table_contains(GHashTable *h, const void *k) { return g_hash_table_lookup(h, k) ? 1 : 0; } #endif + + +/* GQUEUE */ + INLINE void g_queue_move(GQueue *dst, GQueue *src) { GList *l; while ((l = g_queue_pop_head_link(src))) @@ -85,8 +98,55 @@ INLINE void g_queue_clear_full(GQueue *q, GDestroyNotify free_func) { while ((p = g_queue_pop_head(q))) free_func(p); } +INLINE void g_queue_append(GQueue *dst, const GQueue *src) { + GList *l; + if (!src || !dst) + return; + for (l = src->head; l; l = l->next) + g_queue_push_tail(dst, l->data); +} + + +/* GTREE */ + +int g_tree_find_first_cmp(void *, void *, void *); +int g_tree_find_all_cmp(void *, void *, void *); +INLINE void *g_tree_find_first(GTree *t, GEqualFunc f, void *data) { + void *p[3]; + p[0] = data; + p[1] = f; + p[2] = NULL; + g_tree_foreach(t, g_tree_find_first_cmp, p); + return p[2]; +} +INLINE void g_tree_find_all(GQueue *out, GTree *t, GEqualFunc f, void *data) { + void *p[3]; + p[0] = data; + p[1] = f; + p[2] = out; + g_tree_foreach(t, g_tree_find_all_cmp, p); +} +INLINE void g_tree_get_values(GQueue *out, GTree *t) { + g_tree_find_all(out, t, NULL, NULL); +} +INLINE void g_tree_remove_all(GQueue *out, GTree *t) { + GList *l; + g_queue_init(out); + g_tree_find_all(out, t, NULL, NULL); + for (l = out->head; l; l = l->next) + g_tree_remove(t, l->data); +} +INLINE void g_tree_add_all(GTree *t, GQueue *q) { + GList *l; + for (l = q->head; l; l = l->next) + g_tree_insert(t, l->data, l->data); + g_queue_clear(q); +} + +/*** STRING HELPERS ***/ + INLINE void strmove(char **d, char **s) { if (*d) free(*d); @@ -100,6 +160,36 @@ INLINE void strdupfree(char **d, const char *s) { *d = strdup(s); } +INLINE int strmemcmp(const void *mem, int len, const char *str) { + int l = strlen(str); + if (l < len) + return -1; + if (l > len) + return 1; + return memcmp(mem, str, len); +} + +INLINE void random_string(unsigned char *buf, int len) { + RAND_bytes(buf, len); +} + +INLINE const char *__get_enum_array_text(const char * const *array, unsigned int idx, + unsigned int len, const char *deflt) +{ + const char *ret; + if (idx >= len) + return deflt; + ret = array[idx]; + return ret ? : deflt; +} +#define get_enum_array_text(array, idx, deflt) \ + __get_enum_array_text(array, idx, G_N_ELEMENTS(array), deflt) + + + + + +/*** SOCKET/FD HELPERS ***/ INLINE void nonblock(int fd) { fcntl(fd, F_SETFL, O_NONBLOCK); @@ -112,17 +202,10 @@ INLINE void ipv6only(int fd, int yn) { setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yn, sizeof(yn)); } -INLINE unsigned long bit_array_isset(unsigned long *name, unsigned int bit) { - return name[(bit) / (sizeof(long) * 8)] & (1UL << ((bit) % (sizeof(long) * 8))); -} -INLINE void bit_array_set(unsigned long *name, unsigned int bit) { - name[(bit) / (sizeof(long) * 8)] |= 1UL << ((bit) % (sizeof(long) * 8)); -} -INLINE void bit_array_clear(unsigned long *name, unsigned int bit) { - name[(bit) / (sizeof(long) * 8)] &= ~(1UL << ((bit) % (sizeof(long) * 8))); -} + +/*** GENERIC HELPERS ***/ INLINE char chrtoupper(char x) { return x & 0xdf; @@ -137,6 +220,34 @@ INLINE void swap_ptrs(void *a, void *b) { *bb = t; } +INLINE int rlim(int res, rlim_t val) { + struct rlimit rlim; + + ZERO(rlim); + rlim.rlim_cur = rlim.rlim_max = val; + return setrlimit(res, &rlim); +} + + + +/*** INET ADDRESS HELPERS ***/ + +#define IPF "%u.%u.%u.%u" +#define IPP(x) ((unsigned char *) (&(x)))[0], ((unsigned char *) (&(x)))[1], ((unsigned char *) (&(x)))[2], ((unsigned char *) (&(x)))[3] +#define IP6F "%x:%x:%x:%x:%x:%x:%x:%x" +#define IP6P(x) ntohs(((u_int16_t *) (x))[0]), \ + ntohs(((u_int16_t *) (x))[1]), \ + ntohs(((u_int16_t *) (x))[2]), \ + ntohs(((u_int16_t *) (x))[3]), \ + ntohs(((u_int16_t *) (x))[4]), \ + ntohs(((u_int16_t *) (x))[5]), \ + ntohs(((u_int16_t *) (x))[6]), \ + ntohs(((u_int16_t *) (x))[7]) +#define D6F "["IP6F"]:%u" +#define D6P(x) IP6P((x).sin6_addr.s6_addr), ntohs((x).sin6_port) +#define DF IPF ":%u" +#define DP(x) IPP((x).sin_addr.s_addr), ntohs((x).sin_port) + INLINE void in4_to_6(struct in6_addr *o, u_int32_t ip) { o->s6_addr32[0] = 0; o->s6_addr32[1] = 0; @@ -193,15 +304,19 @@ INLINE char *smart_ntop_p_buf(const struct in6_addr *a) { return buf; } -INLINE void smart_ntop_port(char *o, const struct sockaddr_in6 *a, size_t len) { +INLINE void smart_ntop_ap(char *o, const struct in6_addr *a, unsigned int port, size_t len) { char *e; - e = smart_ntop_p(o, &a->sin6_addr, len); + e = smart_ntop_p(o, a, len); if (!e) return; if (len - (e - o) < 7) return; - sprintf(e, ":%hu", ntohs(a->sin6_port)); + sprintf(e, ":%u", port); +} + +INLINE void smart_ntop_port(char *o, const struct sockaddr_in6 *a, size_t len) { + return smart_ntop_ap(o, &a->sin6_addr, ntohs(a->sin6_port), len); } INLINE char *smart_ntop_port_buf(const struct sockaddr_in6 *a) { @@ -210,6 +325,18 @@ INLINE char *smart_ntop_port_buf(const struct sockaddr_in6 *a) { return buf; } +INLINE char *smart_ntop_ap_buf(const struct in6_addr *a, unsigned int port) { + char *buf = get_thread_buf(); + smart_ntop_ap(buf, a, port, THREAD_BUF_SIZE); + return buf; +} + +INLINE char *smart_ntop_ep_buf(const struct endpoint *ep) { + char *buf = get_thread_buf(); + smart_ntop_ap(buf, &ep->ip46, ep->port, THREAD_BUF_SIZE); + return buf; +} + INLINE int smart_pton(int af, char *src, void *dst) { char *p; int ret; @@ -302,22 +429,62 @@ fail: return -1; } - -INLINE int strmemcmp(const void *mem, int len, const char *str) { - int l = strlen(str); - if (l < len) - return -1; - if (l > len) +INLINE int is_addr_unspecified(const struct in6_addr *a) { + if (a->s6_addr32[0]) + return 0; + if (a->s6_addr32[1]) + return 0; + if (a->s6_addr32[3]) + return 0; + if (a->s6_addr32[2] == 0 || a->s6_addr32[2] == htonl(0xffff)) return 1; - return memcmp(mem, str, len); + return 0; } -INLINE void random_string(unsigned char *buf, int len) { - RAND_bytes(buf, len); +INLINE int family_from_address(const struct in6_addr *a) { + if (IN6_IS_ADDR_V4MAPPED(a)) + return AF_INET; + return AF_INET6; +} + +INLINE void msg_mh_src(const struct in6_addr *src, struct msghdr *mh) { + struct cmsghdr *ch; + struct in_pktinfo *pi; + struct in6_pktinfo *pi6; + struct sockaddr_in6 *sin6; + + sin6 = mh->msg_name; + ch = CMSG_FIRSTHDR(mh); + ZERO(*ch); + + if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { + ch->cmsg_len = CMSG_LEN(sizeof(*pi)); + ch->cmsg_level = IPPROTO_IP; + ch->cmsg_type = IP_PKTINFO; + + pi = (void *) CMSG_DATA(ch); + ZERO(*pi); + pi->ipi_spec_dst.s_addr = in6_to_4(src); + + mh->msg_controllen = CMSG_SPACE(sizeof(*pi)); + } + else { + ch->cmsg_len = CMSG_LEN(sizeof(*pi6)); + ch->cmsg_level = IPPROTO_IPV6; + ch->cmsg_type = IPV6_PKTINFO; + + pi6 = (void *) CMSG_DATA(ch); + ZERO(*pi6); + pi6->ipi6_addr = *src; + + mh->msg_controllen = CMSG_SPACE(sizeof(*pi6)); + } } +/*** MUTEX ABSTRACTION ***/ + typedef pthread_mutex_t mutex_t; typedef pthread_rwlock_t rwlock_t; typedef pthread_cond_t cond_t; @@ -338,10 +505,18 @@ typedef pthread_cond_t cond_t; #define cond_init(c) __debug_cond_init(c, __FILE__, __LINE__) #define cond_wait(c,m) __debug_cond_wait(c,m, __FILE__, __LINE__) +#define cond_timedwait(c,m,t) __debug_cond_timedwait(c,m,t, __FILE__, __LINE__) #define cond_signal(c) __debug_cond_signal(c, __FILE__, __LINE__) #define cond_broadcast(c) __debug_cond_broadcast(c, __FILE__, __LINE__) #define COND_STATIC_INIT PTHREAD_COND_INITIALIZER +INLINE int __cond_timedwait_tv(cond_t *c, mutex_t *m, const struct timeval *tv) { + struct timespec ts; + ts.tv_sec = tv->tv_sec; + ts.tv_nsec = tv->tv_usec * 1000; + return pthread_cond_timedwait(c, m, &ts); +} + #ifndef __THREAD_DEBUG #define __debug_mutex_init(m, F, L) pthread_mutex_init(m, NULL) @@ -359,6 +534,7 @@ typedef pthread_cond_t cond_t; #define __debug_cond_init(c, F, L) pthread_cond_init(c, NULL) #define __debug_cond_wait(c, m, F, L) pthread_cond_wait(c,m) +#define __debug_cond_timedwait(c, m, t, F, L) __cond_timedwait_tv(c,m,t) #define __debug_cond_signal(c, F, L) pthread_cond_signal(c) #define __debug_cond_broadcast(c, F, L) pthread_cond_broadcast(c) @@ -429,6 +605,7 @@ INLINE int __debug_rwlock_unlock_w(rwlock_t *m, const char *file, unsigned int l #define __debug_cond_init(c, F, L) pthread_cond_init(c, NULL) #define __debug_cond_wait(c, m, F, L) pthread_cond_wait(c,m) +#define __debug_cond_timedwait(c, m, t, F, L) __cond_timedwait_tv(c,m,t) #define __debug_cond_signal(c, F, L) pthread_cond_signal(c) #define __debug_cond_broadcast(c, F, L) pthread_cond_broadcast(c) @@ -436,36 +613,16 @@ INLINE int __debug_rwlock_unlock_w(rwlock_t *m, const char *file, unsigned int l -void threads_join_all(int); -void thread_create_detach(void (*)(void *), void *); +/*** THREAD HELPERS ***/ +void threads_join_all(int); +void thread_create_detach(void (*)(void *), void *); -INLINE int rlim(int res, rlim_t val) { - struct rlimit rlim; - ZERO(rlim); - rlim.rlim_cur = rlim.rlim_max = val; - return setrlimit(res, &rlim); -} -INLINE int is_addr_unspecified(const struct in6_addr *a) { - if (a->s6_addr32[0]) - return 0; - if (a->s6_addr32[1]) - return 0; - if (a->s6_addr32[3]) - return 0; - if (a->s6_addr32[2] == 0 || a->s6_addr32[2] == htonl(0xffff)) - return 1; - return 0; -} -INLINE int family_from_address(const struct in6_addr *a) { - if (IN6_IS_ADDR_V4MAPPED(a)) - return AF_INET; - return AF_INET6; -} +/*** ATOMIC BITFIELD OPERATIONS ***/ /* checks if at least one of the flags is set */ INLINE int bf_isset(const volatile unsigned int *u, unsigned int f) { @@ -473,11 +630,19 @@ INLINE int bf_isset(const volatile unsigned int *u, unsigned int f) { return -1; return 0; } -INLINE void bf_set(volatile unsigned int *u, unsigned int f) { - g_atomic_int_or(u, f); +/* checks if all of the flags are set */ +INLINE int bf_areset(const volatile unsigned int *u, unsigned int f) { + if ((g_atomic_int_get(u) & f) == f) + return -1; + return 0; +} +/* returns true if at least one of the flags was set already */ +INLINE int bf_set(volatile unsigned int *u, unsigned int f) { + return (g_atomic_int_or(u, f) & f) ? -1 : 0; } -INLINE void bf_clear(volatile unsigned int *u, unsigned int f) { - g_atomic_int_and(u, ~f); +/* returns true if at least one of the flags was set */ +INLINE int bf_clear(volatile unsigned int *u, unsigned int f) { + return (g_atomic_int_and(u, ~f) & f) ? -1 : 0; } INLINE void bf_set_clear(volatile unsigned int *u, unsigned int f, int cond) { if (cond) @@ -502,21 +667,27 @@ INLINE void bf_copy_same(volatile unsigned int *u, const volatile unsigned int * } -INLINE void g_queue_append(GQueue *dst, const GQueue *src) { - GList *l; - if (!src || !dst) - return; - for (l = src->head; l; l = l->next) - g_queue_push_tail(dst, l->data); -} +/*** BIT ARRAY FUNCTIONS ***/ +#define BIT_ARRAY_DECLARE(name, size) \ + volatile unsigned int name[((size) + sizeof(int) * 8 - 1) / (sizeof(int) * 8)] -unsigned int in6_addr_hash(const void *p); -int in6_addr_eq(const void *a, const void *b); +INLINE int bit_array_isset(const volatile unsigned int *name, unsigned int bit) { + return bf_isset(&name[bit / (sizeof(int) * 8)], 1U << (bit % (sizeof(int) * 8))); +} +INLINE int bit_array_set(volatile unsigned int *name, unsigned int bit) { + return bf_set(&name[bit / (sizeof(int) * 8)], 1U << (bit % (sizeof(int) * 8))); +} +INLINE int bit_array_clear(volatile unsigned int *name, unsigned int bit) { + return bf_clear(&name[bit / (sizeof(int) * 8)], 1U << (bit % (sizeof(int) * 8))); +} + +/*** ATOMIC64 ***/ + #if GLIB_SIZEOF_VOID_P >= 8 typedef struct { @@ -611,18 +782,4 @@ INLINE void atomic64_local_copy_zero(atomic64 *dst, atomic64 *src) { -INLINE const char *__get_enum_array_text(const char * const *array, unsigned int idx, - unsigned int len, const char *deflt) -{ - const char *ret; - if (idx >= len) - return deflt; - ret = array[idx]; - return ret ? : deflt; -} -#define get_enum_array_text(array, idx, deflt) \ - __get_enum_array_text(array, idx, G_N_ELEMENTS(array), deflt) - - - #endif diff --git a/daemon/call.c b/daemon/call.c index d0b8ab84f..0f90b4c89 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -32,6 +32,7 @@ #include "rtcp.h" #include "rtp.h" #include "call_interfaces.h" +#include "ice.h" @@ -320,12 +321,12 @@ static const struct rtpengine_srtp __res_null = { -static void unkernelize(struct packet_stream *); -static void __stream_unkernelize(struct packet_stream *ps); -static void stream_unkernelize(struct packet_stream *ps); +static void __unkernelize(struct packet_stream *); +static void __stream_unconfirm(struct packet_stream *ps); +static void stream_unconfirm(struct packet_stream *ps); static void __monologue_destroy(struct call_monologue *monologue); static struct interface_address *get_interface_address(struct local_interface *lif, int family); -static const GQueue *get_interface_addresses(struct local_interface *lif, int family); +static struct interface_address *get_any_interface_address(struct local_interface *lif, int family); @@ -412,7 +413,7 @@ void kernelize(struct packet_stream *stream) { ZERO(reti); - if (PS_ISSET(stream, STRICT_SOURCE) || PS_ISSET(stream, MEDIA_HANDOVER)) { + if (PS_ISSET2(stream, STRICT_SOURCE, MEDIA_HANDOVER)) { mutex_lock(&stream->out_lock); __re_address_translate(&reti.expected_src, &stream->endpoint); mutex_unlock(&stream->out_lock); @@ -428,7 +429,7 @@ void kernelize(struct packet_stream *stream) { reti.tos = call->tos; reti.rtcp_mux = MEDIA_ISSET(stream->media, RTCP_MUX); reti.dtls = MEDIA_ISSET(stream->media, DTLS); - reti.stun = PS_ISSET(stream, STUN); + reti.stun = stream->media->ice_agent ? 1 : 0; __re_address_translate(&reti.dst_addr, &sink->endpoint); @@ -583,41 +584,10 @@ noop: } void stream_msg_mh_src(struct packet_stream *ps, struct msghdr *mh) { - struct cmsghdr *ch; - struct in_pktinfo *pi; - struct in6_pktinfo *pi6; - struct sockaddr_in6 *sin6; struct interface_address *ifa; - - sin6 = mh->msg_name; ifa = g_atomic_pointer_get(&ps->media->local_address); - - ch = CMSG_FIRSTHDR(mh); - ZERO(*ch); - - if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { - ch->cmsg_len = CMSG_LEN(sizeof(*pi)); - ch->cmsg_level = IPPROTO_IP; - ch->cmsg_type = IP_PKTINFO; - - pi = (void *) CMSG_DATA(ch); - ZERO(*pi); - pi->ipi_spec_dst.s_addr = in6_to_4(&ifa->addr); - - mh->msg_controllen = CMSG_SPACE(sizeof(*pi)); - } - else { - ch->cmsg_len = CMSG_LEN(sizeof(*pi6)); - ch->cmsg_level = IPPROTO_IPV6; - ch->cmsg_type = IPV6_PKTINFO; - - pi6 = (void *) CMSG_DATA(ch); - ZERO(*pi6); - pi6->ipi6_addr = ifa->addr; - - mh->msg_controllen = CMSG_SPACE(sizeof(*pi6)); - } + msg_mh_src(&ifa->addr, mh); } /* XXX split this function into pieces */ @@ -654,35 +624,39 @@ static int stream_packet(struct stream_fd *sfd, str *s, struct sockaddr_in6 *fsi if (!stream) goto unlock_out; - mutex_lock(&stream->in_lock); media = stream->media; if (!stream->sfd) - goto done; + goto unlock_out; /* demux other protocols running on this port */ if (MEDIA_ISSET(media, DTLS) && is_dtls(s)) { + mutex_lock(&stream->in_lock); ret = dtls(stream, s, fsin); + mutex_unlock(&stream->in_lock); if (!ret) - goto done; + goto unlock_out; } - if (PS_ISSET(stream, STUN) && is_stun(s)) { - stun_ret = stun(s, stream, fsin); + if (media->ice_agent && is_stun(s)) { + stun_ret = stun(s, stream, fsin, dst); if (!stun_ret) - goto done; + goto unlock_out; if (stun_ret == 1) { - ilog(LOG_INFO, "STUN: using this candidate"); - goto use_cand; + call_stream_state_machine(stream); + mutex_lock(&stream->in_lock); /* for the jump */ + goto kernel_check; } else /* not an stun packet */ stun_ret = 0; } #if RTP_LOOP_PROTECT + mutex_lock(&stream->in_lock); + for (i = 0; i < RTP_LOOP_PACKETS; i++) { if (stream->lp_buf[i].len != s->len) continue; @@ -706,9 +680,8 @@ static int stream_packet(struct stream_fd *sfd, str *s, struct sockaddr_in6 *fsi memcpy(stream->lp_buf[stream->lp_idx].buf, s->s, MIN(s->len, RTP_LOOP_PROTECT)); stream->lp_idx = (stream->lp_idx + 1) % RTP_LOOP_PACKETS; loop_ok: -#endif - mutex_unlock(&stream->in_lock); +#endif /* demux RTCP */ @@ -792,9 +765,11 @@ loop_ok: mutex_unlock(&out_srtp->out_lock); mutex_unlock(&in_srtp->in_lock); + + /* endpoint address handling */ + mutex_lock(&stream->in_lock); -use_cand: /* we're OK to (potentially) use the source address of this packet as destination * in the other direction. */ /* if the other side hasn't been signalled yet, just forward the packet */ @@ -808,7 +783,7 @@ use_cand: /* if we have already updated the endpoint in the past ... */ if (PS_ISSET(stream, CONFIRMED)) { /* see if we need to compare the source address with the known endpoint */ - if (PS_ISSET(stream, STRICT_SOURCE) || PS_ISSET(stream, MEDIA_HANDOVER)) { + if (PS_ISSET2(stream, STRICT_SOURCE, MEDIA_HANDOVER)) { endpoint.ip46 = fsin->sin6_addr; endpoint.port = ntohs(fsin->sin6_port); mutex_lock(&stream->out_lock); @@ -874,7 +849,7 @@ kernel_check: if (PS_ISSET(stream, NO_KERNEL_SUPPORT)) goto forward; - if (PS_ISSET(stream, CONFIRMED) && sink && PS_ISSET(sink, CONFIRMED) && PS_ISSET(sink, FILLED)) + if (PS_ISSET(stream, CONFIRMED) && sink && PS_ARESET2(sink, CONFIRMED, FILLED)) kernelize(stream); forward: @@ -936,11 +911,11 @@ out: done: if (unk) - __stream_unkernelize(stream); + __stream_unconfirm(stream); mutex_unlock(&stream->in_lock); if (unk) { - stream_unkernelize(stream->rtp_sink); - stream_unkernelize(stream->rtcp_sink); + stream_unconfirm(stream->rtp_sink); + stream_unconfirm(stream->rtcp_sink); } unlock_out: rwlock_unlock_r(&call->master_lock); @@ -964,7 +939,8 @@ static void stream_fd_readable(int fd, void *p, uintptr_t u) { char control[128]; struct cmsghdr *cmh; struct in6_pktinfo *pi6; - struct in6_addr *dst; + struct in6_addr dst_buf, *dst; + struct in_pktinfo *pi; if (sfd->fd.fd != fd) goto out; @@ -1003,20 +979,30 @@ static void stream_fd_readable(int fd, void *p, uintptr_t u) { if (ret >= MAX_RTP_PACKET_SIZE) ilog(LOG_WARNING, "UDP packet possibly truncated"); - dst = NULL; for (cmh = CMSG_FIRSTHDR(&mh); cmh; cmh = CMSG_NXTHDR(&mh, cmh)) { if (cmh->cmsg_level == IPPROTO_IPV6 && cmh->cmsg_type == IPV6_PKTINFO) { pi6 = (void *) CMSG_DATA(cmh); dst = &pi6->ipi6_addr; + goto got_dst; + } + if (cmh->cmsg_level == IPPROTO_IP && cmh->cmsg_type == IP_PKTINFO) { + pi = (void *) CMSG_DATA(cmh); + in4_to_6(&dst_buf, pi->ipi_addr.s_addr); + dst = &dst_buf; + goto got_dst; } } + ilog(LOG_WARNING, "No pkt_info present in received UDP packet, cannot handle packet"); + goto done; + +got_dst: str_init_len(&s, buf + RTP_BUFFER_HEAD_ROOM, ret); ret = stream_packet(sfd, &s, &sin6_src, dst); if (ret < 0) { ilog(LOG_WARNING, "Write error on RTP socket: %s", strerror(-ret)); call_destroy(sfd->call); - return; + goto done; } if (ret == 1) update = 1; @@ -1096,6 +1082,7 @@ static void call_timer_iterator(void *key, void *val, void *ptr) { int tmp_t_reason=0; struct call_monologue *ml; GSList *i; + enum call_stream_state css; rwlock_lock_r(&c->master_lock); log_info_call(c); @@ -1116,7 +1103,6 @@ static void call_timer_iterator(void *key, void *val, void *ptr) { for (it = c->streams; it; it = it->next) { ps = it->data; - mutex_lock(&ps->in_lock); if (!ps->media) goto next; @@ -1124,8 +1110,14 @@ static void call_timer_iterator(void *key, void *val, void *ptr) { if (!sfd) goto no_sfd; - if (MEDIA_ISSET(ps->media, DTLS) && sfd->dtls.init && !sfd->dtls.connected) - dtls(ps, NULL, NULL); + /* valid stream */ + + css = call_stream_state_machine(ps); + + if (css == CSS_ICE) { + good = 1; + goto next; + } if (hlp->ports[sfd->fd.localport]) goto next; @@ -1147,7 +1139,7 @@ no_sfd: good = 1; next: - mutex_unlock(&ps->in_lock); + ; } if (good) @@ -1532,6 +1524,7 @@ static void __get_pktinfo(int fd) { int x; x = 1; setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &x, sizeof(x)); + setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &x, sizeof(x)); } static int get_port6(struct udp_fd *r, u_int16_t p, const struct call *c) { @@ -1571,23 +1564,17 @@ static int get_port(struct udp_fd *r, u_int16_t p, const struct call *c) { __C_DBG("attempting to open port %u", p); - mutex_lock(&m->portlock); - if (bit_array_isset(m->ports_used, p)) { - mutex_unlock(&m->portlock); + if (bit_array_set(m->ports_used, p)) { __C_DBG("port in use"); return -1; } - bit_array_set(m->ports_used, p); - mutex_unlock(&m->portlock); __C_DBG("port locked"); ret = get_port6(r, p, c); if (ret) { __C_DBG("couldn't open port"); - mutex_lock(&m->portlock); bit_array_clear(m->ports_used, p); - mutex_unlock(&m->portlock); return ret; } @@ -1600,9 +1587,7 @@ static void release_port(struct udp_fd *r, struct callmaster *m) { if (r->fd == -1 || !r->localport) return; __C_DBG("releasing port %u", r->localport); - mutex_lock(&m->portlock); bit_array_clear(m->ports_used, r->localport); - mutex_unlock(&m->portlock); close(r->fd); r->fd = -1; r->localport = 0; @@ -1619,9 +1604,7 @@ int __get_consecutive_ports(struct udp_fd *array, int array_len, int wanted_star if (wanted_start_port > 0) port = wanted_start_port; else { - mutex_lock(&m->portlock); - port = m->lastport; - mutex_unlock(&m->portlock); + port = g_atomic_int_get(&m->lastport); #if PORT_RANDOM_MIN && PORT_RANDOM_MAX port += PORT_RANDOM_MIN + (random() % (PORT_RANDOM_MAX - PORT_RANDOM_MIN)); #endif @@ -1658,9 +1641,7 @@ release_restart: } /* success */ - mutex_lock(&m->portlock); - m->lastport = port; - mutex_unlock(&m->portlock); + g_atomic_int_set(&m->lastport, port); ilog(LOG_DEBUG, "Opened ports %u..%u for media relay", array[0].localport, array[array_len - 1].localport); @@ -1810,22 +1791,27 @@ static void __assign_stream_fds(struct call_media *media, GList *sfds) { GList *l; struct packet_stream *ps; struct stream_fd *sfd; + int reset = 0; for (l = media->streams.head; l; l = l->next) { assert(sfds != NULL); ps = l->data; sfd = sfds->data; - /* if we switch local ports, we reset crypto params */ + /* if we switch local ports, we reset crypto params and ICE */ if (ps->sfd && ps->sfd != sfd) { dtls_shutdown(ps); crypto_reset(&ps->sfd->crypto); + reset = 1; } ps->sfd = sfd; sfd->stream = ps; sfds = sfds->next; } + + if (reset && media->ice_agent) + ice_restart(media->ice_agent); } static int __wildcard_endpoint_map(struct call_media *media, unsigned int num_ports) { @@ -1869,6 +1855,7 @@ static int __num_media_streams(struct call_media *media, unsigned int num_ports) stream = __packet_stream_new(call); stream->media = media; g_queue_push_tail(&media->streams, stream); + stream->component = media->streams.length; ret++; } @@ -1889,6 +1876,29 @@ static void __fill_stream(struct packet_stream *ps, const struct endpoint *ep, u PS_SET(ps, FILLED); } +/* called with call locked in R or W, but ps not locked */ +enum call_stream_state call_stream_state_machine(struct packet_stream *ps) { + struct call_media *media = ps->media; + + if (!ps->sfd) + return CSS_SHUTDOWN; + + if (MEDIA_ISSET(media, ICE) && !ice_has_finished(media)) + return CSS_ICE; /* handled by ICE timer */ + + if (MEDIA_ISSET(media, DTLS)) { + mutex_lock(&ps->in_lock); + if (ps->sfd->dtls.init && !ps->sfd->dtls.connected) { + dtls(ps, NULL, NULL); + mutex_unlock(&ps->in_lock); + return CSS_DTLS; + } + mutex_unlock(&ps->in_lock); + } + + return CSS_RUNNING; +} + static int __init_stream(struct packet_stream *ps) { struct call_media *media = ps->media; struct call *call = ps->call; @@ -1908,6 +1918,8 @@ static int __init_stream(struct packet_stream *ps) { if (dtls_verify_cert(ps)) return -1; } + + call_stream_state_machine(ps); } } @@ -1967,6 +1979,8 @@ static int __init_streams(struct call_media *A, struct call_media *B, const stru bf_copy_same(&a->ps_flags, &sp->sp_flags, SHARED_FLAG_STRICT_SOURCE | SHARED_FLAG_MEDIA_HANDOVER); } + bf_copy_same(&a->ps_flags, &A->media_flags, SHARED_FLAG_ICE); + if (__init_stream(a)) return -1; @@ -2016,6 +2030,8 @@ static int __init_streams(struct call_media *A, struct call_media *B, const stru bf_copy_same(&a->ps_flags, &sp->sp_flags, SHARED_FLAG_STRICT_SOURCE | SHARED_FLAG_MEDIA_HANDOVER); } + bf_copy_same(&a->ps_flags, &A->media_flags, SHARED_FLAG_ICE); + if (__init_stream(a)) return -1; @@ -2046,6 +2062,29 @@ static void __ice_offer(const struct sdp_ng_flags *flags, struct call_media *thi ilog(LOG_DEBUG, "enabling passthrough mode"); MEDIA_SET(this, PASSTHRU); MEDIA_SET(other, PASSTHRU); + return; + } + + /* determine roles (even if we don't actually do ICE) */ + /* this = receiver, other = sender */ + /* ICE_CONTROLLING is from our POV, the other ICE flags are from peer's POV */ + if (MEDIA_ISSET(this, ICE_LITE)) + MEDIA_SET(this, ICE_CONTROLLING); + else if (!MEDIA_ISSET(this, INITIALIZED)) { + if (flags->opmode == OP_OFFER) + MEDIA_SET(this, ICE_CONTROLLING); + else + MEDIA_CLEAR(this, ICE_CONTROLLING); + } + + /* roles are reversed for the other side */ + if (MEDIA_ISSET(other, ICE_LITE)) + MEDIA_SET(other, ICE_CONTROLLING); + else if (!MEDIA_ISSET(other, INITIALIZED)) { + if (flags->opmode == OP_OFFER) + MEDIA_CLEAR(other, ICE_CONTROLLING); + else + MEDIA_SET(other, ICE_CONTROLLING); } } @@ -2265,9 +2304,8 @@ static void __init_interface(struct call_media *media, const str *ifname) { if (!str_cmp_str(&media->interface->name, ifname)) return; get: - media->interface = get_local_interface(media->call->callmaster, ifname); + media->interface = get_local_interface(media->call->callmaster, ifname, media->desired_family); if (!media->interface) { - media->interface = get_local_interface(media->call->callmaster, NULL); /* legacy support */ if (!str_cmp(ifname, "internal")) media->desired_family = AF_INET; @@ -2275,6 +2313,7 @@ get: media->desired_family = AF_INET6; else ilog(LOG_WARNING, "Interface '"STR_FORMAT"' not found, using default", STR_FMT(ifname)); + media->interface = get_local_interface(media->call->callmaster, NULL, media->desired_family); } media->local_address = ifa = get_interface_address(media->interface, media->desired_family); if (!ifa) { @@ -2317,7 +2356,7 @@ static void __dtls_logic(const struct sdp_ng_flags *flags, struct call_media *me other_media->fingerprint = sp->fingerprint; } MEDIA_CLEAR(other_media, DTLS); - if ((MEDIA_ISSET(other_media, SETUP_PASSIVE) || MEDIA_ISSET(other_media, SETUP_ACTIVE)) + if (MEDIA_ISSET2(other_media, SETUP_PASSIVE, SETUP_ACTIVE) && other_media->fingerprint.hash_func) MEDIA_SET(other_media, DTLS); } @@ -2335,6 +2374,15 @@ static void __rtp_payload_types(struct call_media *media, GQueue *types) { } } +static void __ice_start(struct call_media *media) { + if (!MEDIA_ISSET(media, ICE) || MEDIA_ISSET(media, PASSTHRU)) { + ice_shutdown(&media->ice_agent); + return; + } + + ice_agent_init(&media->ice_agent, media); +} + /* called with call->master_lock held in W */ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, const struct sdp_ng_flags *flags) @@ -2396,7 +2444,7 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, /* copy parameters advertised by the sender of this message */ bf_copy_same(&other_media->media_flags, &sp->sp_flags, SHARED_FLAG_RTCP_MUX | SHARED_FLAG_ASYMMETRIC | SHARED_FLAG_ICE - | SHARED_FLAG_TRICKLE_ICE); + | SHARED_FLAG_TRICKLE_ICE | SHARED_FLAG_ICE_LITE); crypto_params_copy(&other_media->sdes_in.params, &sp->crypto); other_media->sdes_in.tag = sp->sdes_tag; @@ -2414,11 +2462,9 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, /* DTLS stuff */ __dtls_logic(flags, media, other_media, sp); - /* ICE negotiation */ - __ice_offer(flags, media, other_media); - /* control rtcp-mux */ __rtcp_mux_logic(flags, media, other_media); + /* XXX update ICE if rtcp-mux changes */ /* SDES and DTLS */ __generate_crypto(flags, media, other_media); @@ -2431,11 +2477,16 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, if (sp->desired_family) media->desired_family = sp->desired_family; - /* local interface selection */ __init_interface(media, &sp->direction[1]); __init_interface(other_media, &sp->direction[0]); + /* ICE stuff - must come after interface and address family selection */ + __ice_offer(flags, media, other_media); + __ice_start(other_media); + __ice_start(media); + + /* we now know what's being advertised by the other side */ MEDIA_SET(other_media, INITIALIZED); @@ -2486,6 +2537,9 @@ init: return -1; if (__init_streams(other_media, media, sp)) return -1; + + /* we are now ready to fire up ICE if so desired and requested */ + ice_update(other_media->ice_agent, sp); } return 0; @@ -2496,7 +2550,7 @@ error: } /* must be called with in_lock held or call->master_lock held in W */ -static void unkernelize(struct packet_stream *p) { +static void __unkernelize(struct packet_stream *p) { if (!PS_ISSET(p, KERNELIZED)) return; if (PS_ISSET(p, NO_KERNEL_SUPPORT)) @@ -2508,33 +2562,57 @@ static void unkernelize(struct packet_stream *p) { PS_CLEAR(p, KERNELIZED); } -void timeval_subtract (struct timeval *result, const struct timeval *a, const struct timeval *b) { - u_int64_t microseconds=0; +/* XXX move these somewhere else */ +u_int64_t timeval_diff(const struct timeval *a, const struct timeval *b) { + u_int64_t microseconds; microseconds = ((u_int64_t)a->tv_sec - (u_int64_t)b->tv_sec) * 1000000LLU + (a->tv_usec - b->tv_usec); + return microseconds; +} +void timeval_subtract (struct timeval *result, const struct timeval *a, const struct timeval *b) { + u_int64_t microseconds; + microseconds = timeval_diff(a, b); result->tv_sec = microseconds/1000000LLU; result->tv_usec = microseconds%1000000LLU; } void timeval_multiply(struct timeval *result, const struct timeval *a, const long multiplier) { - u_int64_t microseconds=0; + u_int64_t microseconds; microseconds = (((u_int64_t)a->tv_sec * 1000000LLU) + a->tv_usec) * multiplier; result->tv_sec = microseconds/1000000LLU; result->tv_usec = microseconds%1000000LLU; } void timeval_divide(struct timeval *result, const struct timeval *a, const long divisor) { - u_int64_t microseconds=0; + u_int64_t microseconds; microseconds = (((u_int64_t)a->tv_sec * 1000000LLU) + a->tv_usec) / divisor; result->tv_sec = microseconds/1000000LLU; result->tv_usec = microseconds%1000000LLU; } void timeval_add(struct timeval *result, const struct timeval *a, const struct timeval *b) { - u_int64_t microseconds=0; + u_int64_t microseconds; microseconds = ((u_int64_t)a->tv_sec + (u_int64_t)b->tv_sec) * 1000000LLU + (a->tv_usec + b->tv_usec); result->tv_sec = microseconds/1000000LLU; result->tv_usec = microseconds%1000000LLU; } +void timeval_add_usec(struct timeval *tv, long usec) { + struct timeval a; + a.tv_sec = usec / 1000000LLU; + a.tv_usec = usec % 1000000LLU; + timeval_add(tv, tv, &a); +} + +int timeval_cmp(const struct timeval *a, const struct timeval *b) { + if (a->tv_sec < b->tv_sec) + return -1; + if (a->tv_sec > b->tv_sec) + return 1; + if (a->tv_usec < b->tv_usec) + return -1; + if (a->tv_usec > b->tv_usec) + return 1; + return 0; +} static void timeval_totalstats_average_add(struct totalstats *s, const struct timeval *add) { struct timeval dp, oa; @@ -2746,6 +2824,8 @@ void call_destroy(struct call *c) { atomic64_add(&m->totalstats_interval.total_relayed_errors, atomic64_get(&ps->stats.errors)); } + + ice_shutdown(&md->ice_agent); } if (_log_facility_cdr) ++cdrlinecnt; @@ -2833,7 +2913,7 @@ void call_destroy(struct call *c) { for (l = c->streams; l; l = l->next) { ps = l->data; - unkernelize(ps); + __unkernelize(ps); dtls_shutdown(ps); ps->sfd = NULL; crypto_cleanup(&ps->crypto); @@ -3084,16 +3164,23 @@ void __monologue_tag(struct call_monologue *ml, const str *tag) { g_hash_table_insert(call->tags, &ml->tag, ml); } -static void __stream_unkernelize(struct packet_stream *ps) { - unkernelize(ps); +static void __stream_unconfirm(struct packet_stream *ps) { + __unkernelize(ps); PS_CLEAR(ps, CONFIRMED); ps->handler = NULL; } -static void stream_unkernelize(struct packet_stream *ps) { +static void stream_unconfirm(struct packet_stream *ps) { if (!ps) return; mutex_lock(&ps->in_lock); - __stream_unkernelize(ps); + __stream_unconfirm(ps); + mutex_unlock(&ps->in_lock); +} +static void unkernelize(struct packet_stream *ps) { + if (!ps) + return; + mutex_lock(&ps->in_lock); + __unkernelize(ps); mutex_unlock(&ps->in_lock); } @@ -3114,15 +3201,28 @@ static void __monologue_unkernelize(struct call_monologue *monologue) { for (m = media->streams.head; m; m = m->next) { stream = m->data; - __stream_unkernelize(stream); + __stream_unconfirm(stream); if (stream->rtp_sink) - __stream_unkernelize(stream->rtp_sink); + __stream_unconfirm(stream->rtp_sink); if (stream->rtcp_sink) - __stream_unkernelize(stream->rtcp_sink); + __stream_unconfirm(stream->rtcp_sink); } } } +/* call locked in R */ +void call_media_unkernelize(struct call_media *media) { + GList *m; + struct packet_stream *stream; + + for (m = media->streams.head; m; m = m->next) { + stream = m->data; + unkernelize(stream); + unkernelize(stream->rtp_sink); + unkernelize(stream->rtcp_sink); + } +} + /* must be called with call->master_lock held in W */ static void __monologue_destroy(struct call_monologue *monologue) { struct call *call; @@ -3357,102 +3457,106 @@ out: return NULL; } +static unsigned int __local_interface_hash(const void *p) { + const struct local_interface *lif = p; + return str_hash(&lif->name) ^ lif->preferred_family; +} +static int __local_interface_eq(const void *a, const void *b) { + const struct local_interface *A = a, *B = b; + return str_equal(&A->name, &B->name) && A->preferred_family == B->preferred_family; +} +static GQueue *__interface_list_for_family(struct callmaster *m, int family) { + return (family == AF_INET6) ? &m->interface_list_v6 : &m->interface_list_v4; +} +static void __interface_append(struct callmaster *m, struct interface_address *ifa, int family) { + struct local_interface *lif; + GQueue *q; + struct interface_address *ifc; + + lif = get_local_interface(m, &ifa->interface_name, family); + + if (!lif) { + lif = g_slice_alloc0(sizeof(*lif)); + lif->name = ifa->interface_name; + lif->preferred_family = family; + lif->addr_hash = g_hash_table_new(in6_addr_hash, in6_addr_eq); + g_hash_table_insert(m->interfaces, lif, lif); + if (ifa->family == family) { + q = __interface_list_for_family(m, family); + g_queue_push_tail(q, lif); + } + } + + if (!ifa->ice_foundation.s) + ice_foundation(ifa); + + ifc = g_slice_alloc(sizeof(*ifc)); + *ifc = *ifa; + ifc->preference = lif->list.length; + + g_queue_push_tail(&lif->list, ifc); + g_hash_table_insert(lif->addr_hash, &ifc->addr, ifc); +} + +/* XXX interface handling should go somewhere else */ void callmaster_config_init(struct callmaster *m) { GList *l; struct interface_address *ifa; - struct local_interface *lif; - m->interfaces = g_hash_table_new(str_hash, str_equal); + m->interfaces = g_hash_table_new(__local_interface_hash, __local_interface_eq); + /* build primary lists first */ for (l = m->conf.interfaces->head; l; l = l->next) { ifa = l->data; + __interface_append(m, ifa, ifa->family); + } - lif = g_hash_table_lookup(m->interfaces, &ifa->interface_name); - if (!lif) { - lif = g_slice_alloc0(sizeof(*lif)); - lif->name = ifa->interface_name; - g_hash_table_insert(m->interfaces, &lif->name, lif); - g_queue_push_tail(&m->interface_list, lif); - } - - if (IN6_IS_ADDR_V4MAPPED(&ifa->addr)) - g_queue_push_tail(&lif->ipv4, ifa); + /* then append to each other as lower-preference alternatives */ + for (l = m->conf.interfaces->head; l; l = l->next) { + ifa = l->data; + if (ifa->family == AF_INET) + __interface_append(m, ifa, AF_INET6); + else if (ifa->family == AF_INET6) + __interface_append(m, ifa, AF_INET); else - g_queue_push_tail(&lif->ipv6, ifa); - - sdp_ice_foundation(ifa); + abort(); } } -struct local_interface *get_local_interface(struct callmaster *m, const str *name) { - struct local_interface *lif; - - if (!name || !name->s) - return m->interface_list.head->data; +struct local_interface *get_local_interface(struct callmaster *m, const str *name, int family) { + struct local_interface d, *lif; - lif = g_hash_table_lookup(m->interfaces, name); - return lif; -} + if (!name || !name->s) { + GQueue *q; + q = __interface_list_for_family(m, family); + return q->head ? q->head->data : NULL; + } -static const GQueue *get_interface_addresses(struct local_interface *lif, int family) { - if (!lif) - return NULL; + d.name = *name; + d.preferred_family = family; - switch (family) { - case AF_INET: - return &lif->ipv4; - break; - case AF_INET6: - return &lif->ipv6; - break; - default: - return NULL; - } + lif = g_hash_table_lookup(m->interfaces, &d); + return lif; } static struct interface_address *get_interface_address(struct local_interface *lif, int family) { const GQueue *q; - q = get_interface_addresses(lif, family); - if (!q || !q->head) + q = &lif->list; + if (!q->head) return NULL; return q->head->data; } /* safety fallback */ -struct interface_address *get_any_interface_address(struct local_interface *lif, int family) { +static struct interface_address *get_any_interface_address(struct local_interface *lif, int family) { struct interface_address *ifa; - GQueue q = G_QUEUE_INIT; - get_all_interface_addresses(&q, lif, family); - ifa = q.head->data; - g_queue_clear(&q); - return ifa; -} - -void get_all_interface_addresses(GQueue *q, struct local_interface *lif, int family) { - g_queue_append(q, get_interface_addresses(lif, family)); - if (family == AF_INET) - g_queue_append(q, get_interface_addresses(lif, AF_INET6)); - else - g_queue_append(q, get_interface_addresses(lif, AF_INET)); -} - -struct interface_address *get_interface_from_address(struct local_interface *lif, const struct in6_addr *addr) { - GQueue *q; - GList *l; - struct interface_address *ifa; - - if (IN6_IS_ADDR_V4MAPPED(addr)) - q = &lif->ipv4; - else - q = &lif->ipv6; - - for (l = q->head; l; l = l->next) { - ifa = l->data; - if (!memcmp(&ifa->addr, addr, sizeof(*addr))) - return ifa; - } - - return NULL; + ifa = get_interface_address(lif, family); + if (ifa) + return ifa; + ifa = get_interface_address(lif, AF_INET); + if (ifa) + return ifa; + return get_interface_address(lif, AF_INET6); } diff --git a/daemon/call.h b/daemon/call.h index 8be6893eb..3ca148c14 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -58,7 +58,14 @@ enum xmlrpc_format { XF_CALLID, }; -struct call_monologue; +enum call_stream_state { + CSS_UNKNOWN = 0, + CSS_SHUTDOWN, + CSS_ICE, + CSS_DTLS, + CSS_RUNNING, +}; + @@ -105,6 +112,7 @@ struct call_monologue; #define SHARED_FLAG_STRICT_SOURCE 0x00000100 #define SHARED_FLAG_MEDIA_HANDOVER 0x00000200 #define SHARED_FLAG_TRICKLE_ICE 0x00000400 +#define SHARED_FLAG_ICE_LITE 0x00000800 /* struct stream_params */ #define SP_FLAG_NO_RTCP 0x00010000 @@ -119,13 +127,14 @@ struct call_monologue; #define SP_FLAG_STRICT_SOURCE SHARED_FLAG_STRICT_SOURCE #define SP_FLAG_MEDIA_HANDOVER SHARED_FLAG_MEDIA_HANDOVER #define SP_FLAG_TRICKLE_ICE SHARED_FLAG_TRICKLE_ICE +#define SP_FLAG_ICE_LITE SHARED_FLAG_ICE_LITE /* struct packet_stream */ #define PS_FLAG_RTP 0x00010000 #define PS_FLAG_RTCP 0x00020000 #define PS_FLAG_IMPLICIT_RTCP SHARED_FLAG_IMPLICIT_RTCP #define PS_FLAG_FALLBACK_RTCP 0x00040000 -#define PS_FLAG_STUN 0x00080000 +#define PS_FLAG_UNUSED2 0x00080000 #define PS_FLAG_FILLED 0x00100000 #define PS_FLAG_CONFIRMED 0x00200000 #define PS_FLAG_KERNELIZED 0x00400000 @@ -134,6 +143,7 @@ struct call_monologue; #define PS_FLAG_FINGERPRINT_VERIFIED 0x02000000 #define PS_FLAG_STRICT_SOURCE SHARED_FLAG_STRICT_SOURCE #define PS_FLAG_MEDIA_HANDOVER SHARED_FLAG_MEDIA_HANDOVER +#define PS_FLAG_ICE SHARED_FLAG_ICE /* struct call_media */ #define MEDIA_FLAG_INITIALIZED 0x00010000 @@ -149,15 +159,21 @@ struct call_monologue; #define MEDIA_FLAG_PASSTHRU 0x00100000 #define MEDIA_FLAG_ICE SHARED_FLAG_ICE #define MEDIA_FLAG_TRICKLE_ICE SHARED_FLAG_TRICKLE_ICE +#define MEDIA_FLAG_ICE_LITE SHARED_FLAG_ICE_LITE +#define MEDIA_FLAG_ICE_CONTROLLING 0x00200000 /* access macros */ #define SP_ISSET(p, f) bf_isset(&(p)->sp_flags, SP_FLAG_ ## f) #define SP_SET(p, f) bf_set(&(p)->sp_flags, SP_FLAG_ ## f) #define SP_CLEAR(p, f) bf_clear(&(p)->sp_flags, SP_FLAG_ ## f) #define PS_ISSET(p, f) bf_isset(&(p)->ps_flags, PS_FLAG_ ## f) +#define PS_ISSET2(p, f, g) bf_isset(&(p)->ps_flags, PS_FLAG_ ## f | PS_FLAG_ ## g) +#define PS_ARESET2(p, f, g) bf_areset(&(p)->ps_flags, PS_FLAG_ ## f | PS_FLAG_ ## g) #define PS_SET(p, f) bf_set(&(p)->ps_flags, PS_FLAG_ ## f) #define PS_CLEAR(p, f) bf_clear(&(p)->ps_flags, PS_FLAG_ ## f) #define MEDIA_ISSET(p, f) bf_isset(&(p)->media_flags, MEDIA_FLAG_ ## f) +#define MEDIA_ISSET2(p, f, g) bf_isset(&(p)->media_flags, MEDIA_FLAG_ ## f | MEDIA_FLAG_ ## g) +#define MEDIA_ARESET2(p, f, g) bf_areset(&(p)->media_flags, MEDIA_FLAG_ ## f | MEDIA_FLAG_ ## g) #define MEDIA_SET(p, f) bf_set(&(p)->media_flags, MEDIA_FLAG_ ## f) #define MEDIA_CLEAR(p, f) bf_clear(&(p)->media_flags, MEDIA_FLAG_ ## f) @@ -173,6 +189,8 @@ struct rtpengine_srtp; struct streamhandler; struct sdp_ng_flags; struct local_interface; +struct call_monologue; +struct ice_agent; typedef bencode_buffer_t call_buffer_t; @@ -221,10 +239,6 @@ struct udp_fd { int fd; u_int16_t localport; }; -struct endpoint { - struct in6_addr ip46; - u_int16_t port; -}; struct stream_params { unsigned int index; /* starting with 1 */ str type; @@ -239,6 +253,9 @@ struct stream_params { struct dtls_fingerprint fingerprint; unsigned int sp_flags; GQueue rtp_payload_types; /* slice-alloc'd */ + GQueue ice_candidates; /* slice-alloc'd */ + str ice_ufrag; + str ice_pwd; }; struct stream_fd { @@ -278,6 +295,7 @@ struct packet_stream { struct call_media *media; /* RO */ struct call *call; /* RO */ + unsigned int component; /* RO, starts with 1 */ struct stream_fd *sfd; /* LOCK: call->master_lock */ struct packet_stream *rtp_sink; /* LOCK: call->master_lock */ @@ -322,8 +340,8 @@ struct call_media { * atomic ops to access it when holding an R lock. */ volatile struct interface_address *local_address; - str ice_ufrag; - str ice_pwd; + struct ice_agent *ice_agent; + struct { struct crypto_params params; unsigned int tag; @@ -386,8 +404,9 @@ struct call { struct local_interface { str name; - GQueue ipv4; /* struct interface_address */ - GQueue ipv6; /* struct interface_address */ + int preferred_family; + GQueue list; /* struct interface_address */ + GHashTable *addr_hash; }; struct interface_address { str interface_name; @@ -396,6 +415,7 @@ struct interface_address { struct in6_addr advertised; str ice_foundation; char foundation_buf[16]; + unsigned int preference; /* starting with 0 */ }; struct callmaster_config { @@ -420,10 +440,10 @@ struct callmaster { GHashTable *callhash; GHashTable *interfaces; /* struct local_interface */ - GQueue interface_list; /* ditto */ + GQueue interface_list_v4; /* ditto */ + GQueue interface_list_v6; /* ditto */ - mutex_t portlock; - u_int16_t lastport; + volatile unsigned int lastport; BIT_ARRAY_DECLARE(ports_used, 0x10000); /* XXX rework these */ @@ -473,15 +493,19 @@ int monologue_offer_answer(struct call_monologue *monologue, GQueue *streams, co int call_delete_branch(struct callmaster *m, const str *callid, const str *branch, const str *fromtag, const str *totag, bencode_item_t *output); void call_destroy(struct call *); +enum call_stream_state call_stream_state_machine(struct packet_stream *); +void call_media_unkernelize(struct call_media *media); void kernelize(struct packet_stream *); int call_stream_address(char *, struct packet_stream *, enum stream_address_format, int *); int call_stream_address46(char *o, struct packet_stream *ps, enum stream_address_format format, int *len, struct interface_address *ifa); -void get_all_interface_addresses(GQueue *q, struct local_interface *lif, int family); -struct local_interface *get_local_interface(struct callmaster *m, const str *name); -struct interface_address *get_any_interface_address(struct local_interface *lif, int family); -struct interface_address *get_interface_from_address(struct local_interface *lif, const struct in6_addr *addr); +struct local_interface *get_local_interface(struct callmaster *m, const str *name, int familiy); +INLINE struct interface_address *get_interface_from_address(struct local_interface *lif, + const struct in6_addr *addr) +{ + return g_hash_table_lookup(lif->addr_hash, addr); +} const struct transport_protocol *transport_protocol(const str *s); @@ -489,6 +513,15 @@ void timeval_subtract (struct timeval *result, const struct timeval *a, const st void timeval_multiply(struct timeval *result, const struct timeval *a, const long multiplier); void timeval_divide(struct timeval *result, const struct timeval *a, const long divisor); void timeval_add(struct timeval *result, const struct timeval *a, const struct timeval *b); +int timeval_cmp(const struct timeval *a, const struct timeval *b); +void timeval_add_usec(struct timeval *tv, long usec); +u_int64_t timeval_diff(const struct timeval *a, const struct timeval *b); +INLINE void timeval_lowest(struct timeval *l, const struct timeval *n) { + if (!n->tv_sec) + return; + if (!l->tv_sec || timeval_cmp(l, n) == 1) + *l = *n; +} INLINE void *call_malloc(struct call *c, size_t l) { @@ -541,10 +574,7 @@ INLINE str *call_str_init_dup(struct call *c, char *s) { return call_str_dup(c, &t); } INLINE void callmaster_exclude_port(struct callmaster *m, u_int16_t p) { - /* XXX atomic bit field? */ - mutex_lock(&m->portlock); bit_array_set(m->ports_used, p); - mutex_unlock(&m->portlock); } INLINE struct packet_stream *packet_stream_sink(struct packet_stream *ps) { struct packet_stream *ret; diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 4fabe1b8d..f9f495803 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -17,6 +17,7 @@ #include "control_tcp.h" #include "control_udp.h" #include "rtp.h" +#include "ice.h" @@ -285,6 +286,7 @@ static void sp_free(void *p) { if (s->crypto.mki) free(s->crypto.mki); g_queue_clear_full(&s->rtp_payload_types, rtp_pt_free); + ice_candidates_free(&s->ice_candidates); g_slice_free1(sizeof(*s), s); } static void streams_free(GQueue *q) { diff --git a/daemon/dtls.c b/daemon/dtls.c index e4b987ca1..3e25bd09b 100644 --- a/daemon/dtls.c +++ b/daemon/dtls.c @@ -16,6 +16,7 @@ #include "log.h" #include "call.h" #include "poller.h" +#include "ice.h" @@ -483,7 +484,7 @@ int dtls_connection_init(struct packet_stream *ps, int active, struct dtls_cert if (d->init) { if ((d->active && active) || (!d->active && !active)) - goto connect; + goto done; dtls_connection_cleanup(d); } @@ -522,9 +523,7 @@ int dtls_connection_init(struct packet_stream *ps, int active, struct dtls_cert d->init = 1; d->active = active ? -1 : 0; -connect: - dtls(ps, NULL, NULL); - +done: return 0; error: @@ -628,6 +627,7 @@ error: return -1; } +/* called with call locked in W or R with ps->in_lock held */ int dtls(struct packet_stream *ps, const str *s, struct sockaddr_in6 *fsin) { struct dtls_connection *d; int ret; @@ -638,6 +638,8 @@ int dtls(struct packet_stream *ps, const str *s, struct sockaddr_in6 *fsin) { if (!ps || !ps->sfd) return 0; + if (!MEDIA_ISSET(ps->media, DTLS)) + return 0; d = &ps->sfd->dtls; diff --git a/daemon/graphite.c b/daemon/graphite.c index 8e5f8fca4..c571bcf8d 100644 --- a/daemon/graphite.c +++ b/daemon/graphite.c @@ -18,7 +18,7 @@ static u_int32_t graphite_ipaddress; static int graphite_port=0; static struct callmaster* cm=0; //struct totalstats totalstats_prev; -static time_t g_now, next_run; +static time_t next_run; int connect_to_graphite_server(u_int32_t ipaddress, int port) { @@ -106,17 +106,17 @@ int send_graphite_data() { ZERO(ts.total_managed_sess); mutex_unlock(&cm->totalstats_interval.total_average_lock); - 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); 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); 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); ptr += rc; - rc = sprintf(ptr,"%s.totals.managed_sess "UINT64F" %llu\n",hostname, ts.total_managed_sess,(unsigned long long)g_now); 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); 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); 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); 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); 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); 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); 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); 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; + 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; + 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; + rc = sprintf(ptr,"%s.totals.managed_sess "UINT64F" %llu\n",hostname, ts.total_managed_sess,(unsigned long long)g_now.tv_sec); 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; + 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; + 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; + 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; + 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; + 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; + 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); if (rc<0) { @@ -134,11 +134,11 @@ void graphite_loop_run(struct callmaster* callmaster, int seconds) { int rc=0; - g_now = time(NULL); - if (g_now < next_run) + gettimeofday(&g_now, NULL); + if (g_now.tv_sec < next_run) goto sleep; - next_run = g_now + seconds; + next_run = g_now.tv_sec + seconds; if (!cm) cm = callmaster; diff --git a/daemon/ice.c b/daemon/ice.c new file mode 100644 index 000000000..9af547a33 --- /dev/null +++ b/daemon/ice.c @@ -0,0 +1,1357 @@ +#include "ice.h" +#include +#include +#include +#include "str.h" +#include "call.h" +#include "aux.h" +#include "log.h" +#include "obj.h" +#include "stun.h" + + + +#if __DEBUG +#define ICE_DEBUG 1 +#else +#define ICE_DEBUG 0 +#endif + +#if ICE_DEBUG +#define __DBG(x...) ilog(LOG_DEBUG, x) +#else +#define __DBG(x...) ((void)0) +#endif + + + + +#define PAIR_FORMAT STR_FORMAT":"STR_FORMAT":%lu" +#define PAIR_FMT(p) \ + STR_FMT(&(p)->local_address->ice_foundation), \ + STR_FMT(&(p)->remote_candidate->foundation), \ + (p)->remote_candidate->component_id + + + + +static void __ice_agent_free(void *p); +static void create_random_ice_string(struct call *call, str *s, int len); +static void __do_ice_checks(struct ice_agent *ag); +static struct ice_candidate_pair *__pair_lookup(struct ice_agent *, struct ice_candidate *cand, + struct interface_address *ifa); +static void __recalc_pair_prios(struct ice_agent *ag); +static void __role_change(struct ice_agent *ag, int new_controlling); +static void __get_complete_components(GQueue *out, struct ice_agent *ag, GTree *t, unsigned int); +static void __agent_schedule(struct ice_agent *ag, unsigned long); +static void __agent_schedule_abs(struct ice_agent *ag, const struct timeval *tv); +static void __agent_deschedule(struct ice_agent *ag); +static void __ice_agent_free_components(struct ice_agent *ag); +static void __agent_shutdown(struct ice_agent *ag); + + + +static u_int64_t tie_breaker; + +static mutex_t ice_agents_timers_lock = MUTEX_STATIC_INIT; +static cond_t ice_agents_timers_cond = COND_STATIC_INIT; +static GTree *ice_agents_timers; + +static const char ice_chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +const unsigned int ice_type_preferences[] = { + [ICT_UNKNOWN] = 0, + [ICT_HOST] = 126, + [ICT_SRFLX] = 100, + [ICT_PRFLX] = 110, + [ICT_RELAY] = 0, +}; + +const char * const ice_type_strings[] = { + [ICT_UNKNOWN] = "unknown", + [ICT_HOST] = "host", + [ICT_SRFLX] = "srflx", + [ICT_PRFLX] = "prflx", + [ICT_RELAY] = "relay", +}; + + + + + +enum ice_candidate_type ice_candidate_type(const str *s) { + int i; + for (i = 1; i < G_N_ELEMENTS(ice_type_strings); i++) { + if (!str_cmp(s, ice_type_strings[i])) + return i; + } + return ICT_UNKNOWN; +} + +enum ice_transport ice_transport(const str *s) { + if (!str_cmp(s, "udp")) + return ITP_UDP; +// if (!str_cmp(s, "tcp")) +// return ITP_TCP; + if (!str_cmp(s, "UDP")) + return ITP_UDP; +// if (!str_cmp(s, "TCP")) +// return ITP_TCP; + return ITP_UNKNOWN; +} + +int ice_has_related(enum ice_candidate_type t) { + if (t == ICT_HOST) + return 0; + /* ignoring ICT_UNKNOWN */ + return 1; +} + + + +static u_int64_t __ice_pair_priority(struct interface_address *ifa, struct ice_candidate *cand, + int controlling) +{ + u_int64_t g, d; + + g = ice_priority(ICT_HOST, ifa->preference, cand->component_id); + d = cand->priority; + + if (!controlling) { + u_int64_t t = g; + g = d; + d = t; + } + + return (MIN(g,d) << 32) + (MAX(g,d) << 1) + (g > d ? 1 : 0); +} +static void __do_ice_pair_priority(struct ice_candidate_pair *pair) { + pair->pair_priority = __ice_pair_priority(pair->local_address, pair->remote_candidate, + AGENT_ISSET(pair->agent, CONTROLLING)); +} +static void __new_stun_transaction(struct ice_candidate_pair *pair) { + struct ice_agent *ag = pair->agent; + + g_hash_table_remove(ag->transaction_hash, pair->stun_transaction); + random_string((void *) pair->stun_transaction, sizeof(pair->stun_transaction)); + g_hash_table_insert(ag->transaction_hash, pair->stun_transaction, pair); +} + +/* agent must be locked */ +static void __all_pairs_list(struct ice_agent *ag) { + g_queue_clear(&ag->all_pairs_list); + g_tree_get_values(&ag->all_pairs_list, ag->all_pairs); +} + +/* agent must be locked */ +static struct ice_candidate_pair *__pair_candidate(struct interface_address *addr, struct ice_agent *ag, + struct ice_candidate *cand, struct packet_stream *ps) +{ + struct ice_candidate_pair *pair; + + if (addr->family != family_from_address(&cand->endpoint.ip46)) + return NULL; + + pair = g_slice_alloc0(sizeof(*pair)); + + pair->agent = ag; + pair->remote_candidate = cand; + pair->local_address = addr; + pair->packet_stream = ps; + if (cand->component_id != 1) + PAIR_SET(pair, FROZEN); + __do_ice_pair_priority(pair); + __new_stun_transaction(pair); + + g_queue_push_tail(&ag->candidate_pairs, pair); + g_hash_table_insert(ag->pair_hash, pair, pair); + g_tree_insert(ag->all_pairs, pair, pair); + + ilog(LOG_DEBUG, "Created candidate pair "PAIR_FORMAT" between %s and %s, type %s", PAIR_FMT(pair), + smart_ntop_buf(&addr->addr), smart_ntop_ep_buf(&cand->endpoint), + ice_candidate_type_str(cand->type)); + + return pair; +} + +static unsigned int __pair_hash(const void *p) { + const struct ice_candidate_pair *pair = p; + return g_direct_hash(pair->local_address) ^ g_direct_hash(pair->remote_candidate); +} +static int __pair_equal(const void *a, const void *b) { + const struct ice_candidate_pair *A = a, *B = b; + return A->local_address == B->local_address + && A->remote_candidate == B->remote_candidate; +} +static unsigned int __cand_hash(const void *p) { + const struct ice_candidate *cand = p; + return in6_addr_hash(&cand->endpoint.ip46) ^ cand->endpoint.port ^ cand->component_id; +} +static int __cand_equal(const void *a, const void *b) { + const struct ice_candidate *A = a, *B = b; + return A->endpoint.port == B->endpoint.port + && A->component_id == B->component_id + && in6_addr_eq(&A->endpoint.ip46, &B->endpoint.ip46); +} +static unsigned int __found_hash(const void *p) { + const struct ice_candidate *cand = p; + return str_hash(&cand->foundation) ^ cand->component_id; +} +static int __found_equal(const void *a, const void *b) { + const struct ice_candidate *A = a, *B = b; + return str_equal(&A->foundation, &B->foundation) + && A->component_id == B->component_id; +} +static unsigned int __trans_hash(const void *p) { + const u_int32_t *tp = p; + return tp[0] ^ tp[1] ^ tp[2]; +} +static int __trans_equal(const void *a, const void *b) { + const u_int32_t *A = a, *B = b; + return A[0] == B[0] && A[1] == B[1] && A[2] == B[2]; +} +static int __pair_prio_cmp(const void *a, const void *b) { + const struct ice_candidate_pair *A = a, *B = b; + /* highest priority first */ + if (A->pair_priority < B->pair_priority) + return 1; + if (A->pair_priority > B->pair_priority) + return -1; + /* lowest component first */ + if (A->remote_candidate->component_id < B->remote_candidate->component_id) + return -1; + if (A->remote_candidate->component_id > B->remote_candidate->component_id) + return 1; + /* highest local preference first, which is lowest number first */ + if (A->local_address->preference < B->local_address->preference) + return -1; + if (A->local_address->preference > B->local_address->preference) + return 1; + return 0; +} + +static void __ice_agent_initialize(struct ice_agent *ag) { + struct call_media *media = ag->media; + struct call *call = ag->call; + + ag->candidate_hash = g_hash_table_new(__cand_hash, __cand_equal); + ag->pair_hash = g_hash_table_new(__pair_hash, __pair_equal); + ag->transaction_hash = g_hash_table_new(__trans_hash, __trans_equal); + ag->foundation_hash = g_hash_table_new(__found_hash, __found_equal); + ag->agent_flags = 0; + bf_copy(&ag->agent_flags, ICE_AGENT_CONTROLLING, &media->media_flags, MEDIA_FLAG_ICE_CONTROLLING); + ag->local_interface = media->interface; + ag->desired_family = media->desired_family; + ag->nominated_pairs = g_tree_new(__pair_prio_cmp); + ag->valid_pairs = g_tree_new(__pair_prio_cmp); + ag->succeeded_pairs = g_tree_new(__pair_prio_cmp); + ag->all_pairs = g_tree_new(__pair_prio_cmp); + + create_random_ice_string(call, &ag->ufrag[1], 8); + create_random_ice_string(call, &ag->pwd[1], 26); +} + +static struct ice_agent *__ice_agent_new(struct call_media *media) { + struct ice_agent *ag; + struct call *call = media->call; + + ag = obj_alloc0("ice_agent", sizeof(*ag), __ice_agent_free); + ag->call = obj_get(call); + ag->media = media; + mutex_init(&ag->lock); + + __ice_agent_initialize(ag); + + return ag; +} + +/* called with the call lock held in W */ +void ice_agent_init(struct ice_agent **agp, struct call_media *media) { + struct ice_agent *ag; + + if (*agp) + ag = *agp; + else + *agp = ag = __ice_agent_new(media); +} + +static int __copy_cand(struct call *call, struct ice_candidate *dst, const struct ice_candidate *src) { + int eq = (dst->priority == src->priority); + *dst = *src; + call_str_cpy(call, &dst->foundation, &src->foundation); + return eq ? 0 : 1; +} + +static void __ice_reset(struct ice_agent *ag) { + AGENT_CLEAR2(ag, COMPLETED, NOMINATING); + __ice_agent_free_components(ag); + ZERO(ag->active_components); + ZERO(ag->start_nominating); + ZERO(ag->next_check); + ZERO(ag->last_run); + __ice_agent_initialize(ag); +} + +/* if the other side did a restart */ +static void __ice_restart(struct ice_agent *ag) { + ilog(LOG_DEBUG, "ICE restart, resetting ICE agent"); + + ag->ufrag[0] = STR_NULL; + ag->pwd[0] = STR_NULL; + __ice_reset(ag); +} + +/* if we're doing a restart */ +void ice_restart(struct ice_agent *ag) { + ag->ufrag[1] = STR_NULL; + ag->pwd[1] = STR_NULL; + __ice_reset(ag); +} + +/* called with the call lock held in W, hence agent doesn't need to be locked */ +void ice_update(struct ice_agent *ag, struct stream_params *sp) { + GList *l, *k; + struct ice_candidate *cand, *dup; + struct call_media *media; + struct call *call; + int recalc = 0; + unsigned int comps; + struct packet_stream *components[MAX_COMPONENTS], *ps; + + if (!ag || !sp) + return; + + media = ag->media; + call = media->call; + + __role_change(ag, MEDIA_ISSET(media, ICE_CONTROLLING)); + + /* check for ICE restarts */ + if (ag->ufrag[0].s && sp->ice_ufrag.s && str_cmp_str(&ag->ufrag[0], &sp->ice_ufrag)) + __ice_restart(ag); + else if (ag->pwd[0].s && sp->ice_pwd.s && str_cmp_str(&ag->pwd[0], &sp->ice_pwd)) + __ice_restart(ag); + else if (ag->local_interface != media->interface) + __ice_restart(ag); + + /* update remote info */ + if (sp->ice_ufrag.s) + call_str_cpy(call, &ag->ufrag[0], &sp->ice_ufrag); + if (sp->ice_pwd.s) + call_str_cpy(call, &ag->pwd[0], &sp->ice_pwd); + + /* get our component streams */ + ZERO(components); + comps = 0; + for (l = media->streams.head; l; l = l->next) + components[comps++] = l->data; + if (comps == 2 && MEDIA_ISSET(media, RTCP_MUX)) + components[1] = NULL; + + comps = 0; + for (l = sp->ice_candidates.head; l; l = l->next) { + if (ag->remote_candidates.length >= MAX_ICE_CANDIDATES) { + ilog(LOG_WARNING, "Maxmimum number of ICE candidates exceeded"); + break; + } + + cand = l->data; + + /* skip invalid */ + if (!cand->component_id || cand->component_id > G_N_ELEMENTS(components)) + continue; + /* skip if we don't have a candidate of our own */ + ps = components[cand->component_id - 1]; + if (!ps) + continue; + + comps = MAX(comps, cand->component_id); + + /* check for duplicates */ + dup = g_hash_table_lookup(ag->candidate_hash, cand); + if (dup) { + /* if this is peer reflexive, we've learned it through STUN. + * otherwise it's simply one we've seen before. */ + if (dup->type == ICT_PRFLX) { + ilog(LOG_DEBUG, "Replacing previously learned prflx ICE candidate with " + STR_FORMAT":%lu", STR_FMT(&cand->foundation), + cand->component_id); + } + else { + /* if the new one has higher priority then the old one, then we + * update it, otherwise we just drop it */ + if (cand->priority <= dup->priority) { + ilog(LOG_DEBUG, "Dropping new ICE candidate "STR_FORMAT" in favour of " + STR_FORMAT":%lu", + STR_FMT(&cand->foundation), + STR_FMT(&dup->foundation), cand->component_id); + continue; + } + + ilog(LOG_DEBUG, "Replacing known ICE candidate "STR_FORMAT" with higher " + "priority " + STR_FORMAT":%lu", + STR_FMT(&dup->foundation), + STR_FMT(&cand->foundation), cand->component_id); + } + + /* priority and foundation may change */ + g_hash_table_remove(ag->foundation_hash, dup); + recalc += __copy_cand(call, dup, cand); + } + else { + ilog(LOG_DEBUG, "Learning new ICE candidate "STR_FORMAT":%lu", + STR_FMT(&cand->foundation), cand->component_id); + dup = g_slice_alloc(sizeof(*dup)); + __copy_cand(call, dup, cand); + g_hash_table_insert(ag->candidate_hash, dup, dup); + g_queue_push_tail(&ag->remote_candidates, dup); + } + + g_hash_table_insert(ag->foundation_hash, dup, dup); + + for (k = ag->local_interface->list.head; k; k = k->next) { + /* skip duplicates here also */ + if (__pair_lookup(ag, dup, k->data)) + continue; + __pair_candidate(k->data, ag, dup, ps); + } + } + + ag->active_components = comps; + + /* if we're here, we can start our ICE checks */ + if (recalc) + __recalc_pair_prios(ag); + else + __all_pairs_list(ag); + + if (comps) + __do_ice_checks(ag); + else + __agent_shutdown(ag); +} + + +static void ice_candidate_free(void *p) { + g_slice_free1(sizeof(struct ice_candidate), p); +} +void ice_candidates_free(GQueue *q) { + g_queue_clear_full(q, ice_candidate_free); +} +static void ice_candidate_pair_free(void *p) { + g_slice_free1(sizeof(struct ice_candidate_pair), p); +} +static void ice_candidate_pairs_free(GQueue *q) { + g_queue_clear_full(q, ice_candidate_pair_free); +} + + +/* call must be locked */ +void ice_shutdown(struct ice_agent **agp) { + struct ice_agent *ag = *agp; + if (!ag) + return; + + __agent_deschedule(ag); + + *agp = NULL; + obj_put(ag); +} +static void __ice_agent_free_components(struct ice_agent *ag) { + ice_candidates_free(&ag->remote_candidates); + ice_candidate_pairs_free(&ag->candidate_pairs); + g_queue_clear(&ag->triggered); + g_hash_table_destroy(ag->candidate_hash); + g_hash_table_destroy(ag->pair_hash); + g_hash_table_destroy(ag->transaction_hash); + g_hash_table_destroy(ag->foundation_hash); + g_tree_destroy(ag->all_pairs); + g_queue_clear(&ag->all_pairs_list); + g_tree_destroy(ag->nominated_pairs); + g_tree_destroy(ag->succeeded_pairs); + g_tree_destroy(ag->valid_pairs); +} +static void __ice_agent_free(void *p) { + struct ice_agent *ag = p; + + __DBG("freeing ice_agent"); + + __ice_agent_free_components(ag); + mutex_destroy(&ag->lock); + + obj_put(ag->call); +} + + +static void __agent_schedule(struct ice_agent *ag, unsigned long usec) { + struct timeval nxt; + + nxt = g_now; + timeval_add_usec(&nxt, usec); + __agent_schedule_abs(ag, &nxt); +} +static void __agent_schedule_abs(struct ice_agent *ag, const struct timeval *tv) { + struct timeval nxt; + unsigned long diff; + + nxt = *tv; + + mutex_lock(&ice_agents_timers_lock); + if (ag->last_run.tv_sec) { + /* make sure we don't run more often than we should */ + diff = timeval_diff(&nxt, &ag->last_run); + if (diff < TIMER_RUN_INTERVAL * 1000) + timeval_add_usec(&nxt, TIMER_RUN_INTERVAL * 1000 - diff); + } + if (ag->next_check.tv_sec && timeval_cmp(&ag->next_check, &nxt) <= 0) + goto nope; /* already scheduled sooner */ + if (!g_tree_remove(ice_agents_timers, ag)) + obj_hold(ag); /* if it wasn't removed (should never happen), we make a new reference */ + ag->next_check = nxt; + g_tree_insert(ice_agents_timers, ag, ag); + cond_broadcast(&ice_agents_timers_cond); +nope: + mutex_unlock(&ice_agents_timers_lock); +} +static void __agent_deschedule(struct ice_agent *ag) { + int ret; + mutex_lock(&ice_agents_timers_lock); + if (!ag->next_check.tv_sec) + goto nope; /* already descheduled */ + ret = g_tree_remove(ice_agents_timers, ag); + ZERO(ag->next_check); + if (ret) /* should always be true */ + obj_put(ag); +nope: + mutex_unlock(&ice_agents_timers_lock); +} + +static int __ice_agent_timer_cmp(const void *a, const void *b) { + const struct ice_agent *A = a, *B = b; + int ret; + /* zero timevals go last */ + if (A->next_check.tv_sec == 0 && B->next_check.tv_sec != 0) + return 1; + if (B->next_check.tv_sec == 0 && A->next_check.tv_sec == 0) + return -1; + if (A->next_check.tv_sec == 0 && B->next_check.tv_sec == 0) + goto ptr; + /* earlier timevals go first */ + ret = timeval_cmp(&A->next_check, &B->next_check); + if (ret) + return ret; + /* equal timeval, so use pointer as tie breaker */ +ptr: + if (A < B) + return -1; + if (A > B) + return 1; + return 0; +} +void ice_init(void) { + random_string((void *) &tie_breaker, sizeof(tie_breaker)); + ice_agents_timers = g_tree_new(__ice_agent_timer_cmp); +} + + + +static void __fail_pair(struct ice_candidate_pair *pair) { + ilog(LOG_DEBUG, "Setting ICE candidate pair "PAIR_FORMAT" as failed", PAIR_FMT(pair)); + PAIR_SET(pair, FAILED); +} + +/* agent must NOT be locked, but call must be locked in R */ +static void __do_ice_check(struct ice_candidate_pair *pair) { + struct sockaddr_in6 dst; + struct packet_stream *ps = pair->packet_stream; + struct ice_agent *ag = pair->agent; + u_int32_t prio, transact[3]; + + if (PAIR_ISSET(pair, SUCCEEDED) && !PAIR_ISSET(pair, TO_USE)) + return; + + if (!ag->pwd[0].s) + return; + + ZERO(dst); + dst.sin6_port = htons(pair->remote_candidate->endpoint.port); + dst.sin6_addr = pair->remote_candidate->endpoint.ip46; + dst.sin6_family = AF_INET6; + + prio = ice_priority(ICT_PRFLX, pair->local_address->preference, + pair->remote_candidate->component_id); + + mutex_lock(&ag->lock); + + pair->retransmit = g_now; + if (!PAIR_SET(pair, IN_PROGRESS)) { + PAIR_CLEAR2(pair, FROZEN, FAILED); + pair->retransmit_ms = STUN_RETRANSMIT_INTERVAL; + pair->retransmits = 0; + } + else if (pair->retransmits > STUN_MAX_RETRANSMITS) { + __fail_pair(pair); + mutex_unlock(&ag->lock); + return; + } + else { + pair->retransmit_ms *= 2; + pair->retransmits++; + } + timeval_add_usec(&pair->retransmit, pair->retransmit_ms * 1000); + __agent_schedule_abs(pair->agent, &pair->retransmit); + memcpy(transact, pair->stun_transaction, sizeof(transact)); + + pair->was_controlling = AGENT_ISSET(ag, CONTROLLING); + pair->was_nominated = PAIR_ISSET(pair, TO_USE); + + mutex_unlock(&ag->lock); + + ilog(LOG_DEBUG, "Sending %sICE/STUN request for candidate pair "PAIR_FORMAT" from %s to %s", + PAIR_ISSET(pair, TO_USE) ? "nominating " : "", + PAIR_FMT(pair), smart_ntop_buf(&pair->local_address->addr), + smart_ntop_ep_buf(&pair->remote_candidate->endpoint)); + + stun_binding_request(&dst, transact, &ag->pwd[0], ag->ufrag, + AGENT_ISSET(ag, CONTROLLING), tie_breaker, + prio, &pair->local_address->addr, ps->sfd->fd.fd, + PAIR_ISSET(pair, TO_USE)); + +} + +static int __component_find(const void *a, const void *b) { + const struct ice_candidate_pair *A = a; + unsigned int comp = GPOINTER_TO_UINT(b); + if (A->remote_candidate->component_id == comp) + return TRUE; + return FALSE; +} +static struct ice_candidate_pair *__get_pair_by_component(GTree *t, unsigned int component) { + return g_tree_find_first(t, __component_find, GUINT_TO_POINTER(component)); +} +static void __get_pairs_by_component(GQueue *out, GTree *t, unsigned int component) { + g_tree_find_all(out, t, __component_find, GUINT_TO_POINTER(component)); +} + +static void __get_complete_succeeded_pairs(GQueue *out, struct ice_agent *ag) { + __get_complete_components(out, ag, ag->succeeded_pairs, ICE_PAIR_SUCCEEDED); +} +static void __get_complete_valid_pairs(GQueue *out, struct ice_agent *ag) { + __get_complete_components(out, ag, ag->valid_pairs, ICE_PAIR_VALID); +} + +static void __nominate_pairs(struct ice_agent *ag) { + GQueue complete; + GList *l; + struct ice_candidate_pair *pair; + + ilog(LOG_DEBUG, "Start nominating ICE pairs"); + + AGENT_SET(ag, NOMINATING); + ZERO(ag->start_nominating); + + __get_complete_succeeded_pairs(&complete, ag); + + for (l = complete.head; l; l = l->next) { + pair = l->data; + ilog(LOG_DEBUG, "Nominating ICE pair "PAIR_FORMAT, PAIR_FMT(pair)); + PAIR_CLEAR(pair, IN_PROGRESS); + PAIR_SET2(pair, NOMINATED, TO_USE); + pair->retransmits = 0; + __new_stun_transaction(pair); + g_queue_push_tail(&ag->triggered, pair); + } + + g_queue_clear(&complete); +} + +/* call must be locked R or W, agent must not be locked */ +static void __do_ice_checks(struct ice_agent *ag) { + GList *l; + struct ice_candidate_pair *pair, *highest = NULL, *frozen = NULL, *valid; + struct packet_stream *ps; + GQueue retransmits = G_QUEUE_INIT; + struct timeval next_run = {0,0}; + int have_more = 0; + + if (!ag->pwd[0].s) + return; + + __DBG("running checks, call "STR_FORMAT" tag "STR_FORMAT"", STR_FMT(&ag->call->callid), + STR_FMT(&ag->media->monologue->tag)); + + mutex_lock(&ag->lock); + + /* check if we're done and should start nominating pairs */ + if (AGENT_ISSET(ag, CONTROLLING) && !AGENT_ISSET(ag, NOMINATING) && ag->start_nominating.tv_sec) { + if (timeval_cmp(&g_now, &ag->start_nominating) >= 0) + __nominate_pairs(ag); + timeval_lowest(&next_run, &ag->start_nominating); + } + + /* triggered checks are preferred */ + pair = g_queue_pop_head(&ag->triggered); + if (pair) { + PAIR_CLEAR(pair, TRIGGERED); + next_run = g_now; + goto check; + } + + /* find the highest-priority non-frozen non-in-progress pair */ + for (l = ag->all_pairs_list.head; l; l = l->next) { + pair = l->data; + + /* skip dead streams */ + ps = pair->packet_stream; + if (!ps || !ps->sfd) + continue; + if (PAIR_ISSET(pair, FAILED)) + continue; + if (PAIR_ISSET(pair, SUCCEEDED) && !PAIR_ISSET(pair, TO_USE)) + continue; + + valid = __get_pair_by_component(ag->valid_pairs, pair->remote_candidate->component_id); + + if (PAIR_ISSET(pair, IN_PROGRESS)) { + /* handle retransmits */ + /* but only if our priority is lower than any valid pair */ + if (valid && valid->pair_priority > pair->pair_priority) + continue; + + if (timeval_cmp(&pair->retransmit, &g_now) <= 0) + g_queue_push_tail(&retransmits, pair); /* can't run check directly + due to locks */ + else + timeval_lowest(&next_run, &pair->retransmit); + continue; + } + + /* don't do anything else if we already have a valid pair */ + if (valid) + continue; + /* or if we're in or past the final phase */ + if (AGENT_ISSET2(ag, NOMINATING, COMPLETED)) + continue; + + have_more = 1; + + /* remember the first frozen pair in case we find nothing else */ + if (PAIR_ISSET(pair, FROZEN)) { + if (!frozen) + frozen = pair; + continue; + } + + if (!highest) + highest = pair; + } + + if (highest) + pair = highest; + else if (frozen) + pair = frozen; + else + pair = NULL; + +check: + mutex_unlock(&ag->lock); + + if (pair) + __do_ice_check(pair); + + while ((pair = g_queue_pop_head(&retransmits))) + __do_ice_check(pair); + + + /* determine when to run next */ + if (have_more) + __agent_schedule(ag, 0); + else if (next_run.tv_sec) + __agent_schedule_abs(ag, &next_run); /* for retransmits */ +} + +static void __agent_shutdown(struct ice_agent *ag) { + ilog(LOG_DEBUG, "Shutting down ICE agent (nothing to do)"); + __agent_deschedule(ag); +} + +/* agent must be locked for these */ +static struct ice_candidate *__cand_lookup(struct ice_agent *ag, const struct sockaddr_in6 *sin, + unsigned int component) +{ + struct ice_candidate d; + + d.endpoint.port = ntohs(sin->sin6_port); + d.endpoint.ip46 = sin->sin6_addr; + d.component_id = component; + return g_hash_table_lookup(ag->candidate_hash, &d); +} +static struct ice_candidate *__foundation_lookup(struct ice_agent *ag, const str *foundation, + unsigned int component) +{ + struct ice_candidate d; + + d.foundation = *foundation; + d.component_id = component; + return g_hash_table_lookup(ag->foundation_hash, &d); +} +static struct ice_candidate_pair *__pair_lookup(struct ice_agent *ag, struct ice_candidate *cand, + struct interface_address *ifa) +{ + struct ice_candidate_pair p; + + p.local_address = ifa; + p.remote_candidate = cand; + return g_hash_table_lookup(ag->pair_hash, &p); +} + +static void __cand_ice_foundation(struct call *call, struct ice_candidate *cand) { + char buf[64]; + int len; + + len = sprintf(buf, "%lx%lx%lx%lx%x%x", + (long unsigned) cand->endpoint.ip46.s6_addr32[0], + (long unsigned) cand->endpoint.ip46.s6_addr32[1], + (long unsigned) cand->endpoint.ip46.s6_addr32[2], + (long unsigned) cand->endpoint.ip46.s6_addr32[3], + cand->type, cand->transport); + call_str_cpy_len(call, &cand->foundation, buf, len); +} + +/* agent must be locked */ +static struct ice_candidate_pair *__learned_candidate(struct ice_agent *ag, struct packet_stream *ps, + struct sockaddr_in6 *src, struct interface_address *ifa, unsigned long priority) +{ + struct ice_candidate *cand, *old_cand; + struct ice_candidate_pair *pair; + struct call *call = ag->call; + + cand = g_slice_alloc0(sizeof(*cand)); + cand->component_id = ps->component; + cand->transport = ITP_UDP; + cand->priority = priority; + cand->endpoint.ip46 = src->sin6_addr; + cand->endpoint.port = ntohs(src->sin6_port); + cand->type = ICT_PRFLX; + __cand_ice_foundation(call, cand); + + old_cand = __foundation_lookup(ag, &cand->foundation, ps->component); + if (old_cand && old_cand->priority > priority) { + /* this is possible if two distinct requests are received from the same NAT IP + * address, but from different ports. we cannot distinguish such candidates and + * will drop the one with the lower priority */ + g_slice_free1(sizeof(*cand), cand); + pair = __pair_lookup(ag, old_cand, ifa); + if (pair) + goto out; /* nothing to do */ + cand = old_cand; + goto pair; + } + + g_queue_push_tail(&ag->remote_candidates, cand); + g_hash_table_insert(ag->candidate_hash, cand, cand); + g_hash_table_insert(ag->foundation_hash, cand, cand); + +pair: + pair = __pair_candidate(ifa, ag, cand, ps); + PAIR_SET(pair, LEARNED); + __all_pairs_list(ag); + +out: + return pair; +} + +/* agent must NOT be locked */ +static void __trigger_check(struct ice_candidate_pair *pair) { + struct ice_agent *ag = pair->agent; + + ilog(LOG_DEBUG, "Triggering check for "PAIR_FORMAT, PAIR_FMT(pair)); + + mutex_lock(&ag->lock); + PAIR_CLEAR(pair, FAILED); + if (ag->triggered.length < 4 * MAX_ICE_CANDIDATES && !PAIR_SET(pair, TRIGGERED)) + g_queue_push_tail(&ag->triggered, pair); + mutex_unlock(&ag->lock); + + __agent_schedule(ag, 0); +} + +/* agent must be locked */ +/* also regenerates all_pairs_list */ +static void __recalc_pair_prios(struct ice_agent *ag) { + struct ice_candidate_pair *pair; + GList *l; + GQueue nominated, valid, succ, all; + + ilog(LOG_DEBUG, "Recalculating all ICE pair priorities"); + + g_tree_remove_all(&nominated, ag->nominated_pairs); + g_tree_remove_all(&succ, ag->succeeded_pairs); + g_tree_remove_all(&valid, ag->valid_pairs); + g_tree_remove_all(&all, ag->all_pairs); + + for (l = ag->candidate_pairs.head; l; l = l->next) { + pair = l->data; + __do_ice_pair_priority(pair); + /* this changes the packets, so we must keep these from being seen as retransmits */ + __new_stun_transaction(pair); + } + + g_tree_add_all(ag->nominated_pairs, &nominated); + g_tree_add_all(ag->succeeded_pairs, &succ); + g_tree_add_all(ag->valid_pairs, &valid); + g_tree_add_all(ag->all_pairs, &all); + __all_pairs_list(ag); +} + +/* agent must NOT be locked */ +static void __role_change(struct ice_agent *ag, int new_controlling) { + if (new_controlling && !AGENT_SET(ag, CONTROLLING)) + ; + else if (!new_controlling && AGENT_CLEAR(ag, CONTROLLING)) + ; + else + return; + + ilog(LOG_DEBUG, "ICE role change, now %s", new_controlling ? "controlling" : "controlled"); + + /* recalc priorities and resort list */ + + mutex_lock(&ag->lock); + __recalc_pair_prios(ag); + mutex_unlock(&ag->lock); +} + +/* initializes "out" */ +static void __get_complete_components(GQueue *out, struct ice_agent *ag, GTree *t, unsigned int flag) { + GQueue compo1 = G_QUEUE_INIT; + GList *l; + struct ice_candidate_pair *pair1, *pairX; + struct ice_candidate *cand; + unsigned int i; + + __get_pairs_by_component(&compo1, t, 1); + + g_queue_init(out); + + for (l = compo1.head; l; l = l->next) { + pair1 = l->data; + + g_queue_clear(out); + g_queue_push_tail(out, pair1); + + for (i = 2; i <= ag->active_components; i++) { + cand = __foundation_lookup(ag, &pair1->remote_candidate->foundation, i); + if (!cand) + goto next_foundation; + pairX = __pair_lookup(ag, cand, pair1->local_address); + if (!pairX) + goto next_foundation; + if (!bf_isset(&pairX->pair_flags, flag)) + goto next_foundation; + g_queue_push_tail(out, pairX); + } + goto found; + +next_foundation: + ; + } + + /* nothing found */ + g_queue_clear(out); + +found: + g_queue_clear(&compo1); +} + +/* call(W) or call(R)+agent must be locked - no in_lock or out_lock must be held */ +static int __check_valid(struct ice_agent *ag) { + struct call_media *media = ag->media; + struct packet_stream *ps; + GList *l, *k; + GQueue all_compos; + struct ice_candidate_pair *pair; + struct interface_address *ifa; + + __get_complete_valid_pairs(&all_compos, ag); + + if (!all_compos.length) { + ilog(LOG_DEBUG, "ICE not completed yet"); + return 0; + } + + pair = all_compos.head->data; + ilog(LOG_DEBUG, "ICE completed, using pair "PAIR_FORMAT, PAIR_FMT(pair)); + AGENT_SET(ag, COMPLETED); + + ifa = g_atomic_pointer_get(&media->local_address); + if (ifa != pair->local_address + && g_atomic_pointer_compare_and_exchange(&media->local_address, ifa, + pair->local_address)) + ilog(LOG_INFO, "ICE negotiated: local interface %s", smart_ntop_buf(&pair->local_address->addr)); + + for (l = media->streams.head, k = all_compos.head; l && k; l = l->next, k = k->next) { + ps = l->data; + pair = k->data; + + mutex_lock(&ps->out_lock); + if (memcmp(&ps->endpoint, &pair->remote_candidate->endpoint, sizeof(ps->endpoint))) { + ilog(LOG_INFO, "ICE negotiated: peer for component %u is %s", ps->component, + smart_ntop_ep_buf(&pair->remote_candidate->endpoint)); + ps->endpoint = pair->remote_candidate->endpoint; + } + mutex_unlock(&ps->out_lock); + } + + call_media_unkernelize(media); + + g_queue_clear(&all_compos); + return 1; +} + + +/* call is locked in R */ +/* return values: + * 1 = ICE completed, interfaces selected + * 0 = packet processed + * -1 = generic error, process packet as normal + * -2 = role conflict + */ +int ice_request(struct packet_stream *ps, struct sockaddr_in6 *src, struct in6_addr *dst, + struct stun_attrs *attrs) +{ + struct call_media *media = ps->media; + struct ice_agent *ag; + struct interface_address *ifa; + const char *err; + struct ice_candidate *cand; + struct ice_candidate_pair *pair; + int ret; + + __DBG("received ICE request from %s on %s", smart_ntop_port_buf(src), smart_ntop_buf(dst)); + + ag = media->ice_agent; + if (!ag) + return -1; + + ifa = get_interface_from_address(ag->local_interface, dst); + err = "ICE/STUN binding request received on unknown local interface address"; + if (!ifa) + goto err; + + /* determine candidate pair */ + mutex_lock(&ag->lock); + + cand = __cand_lookup(ag, src, ps->component); + + if (!cand) + pair = __learned_candidate(ag, ps, src, ifa, attrs->priority); + else + pair = __pair_lookup(ag, cand, ifa); + + err = "Failed to determine ICE candidate from STUN request"; + if (!pair) + goto err_unlock; + + mutex_unlock(&ag->lock); + + /* determine role conflict */ + if (attrs->controlling && AGENT_ISSET(ag, CONTROLLING)) { + if (tie_breaker >= attrs->tiebreaker) + return -2; + else + __role_change(ag, 0); + } + else if (attrs->controlled && !AGENT_ISSET(ag, CONTROLLING)) { + if (tie_breaker >= attrs->tiebreaker) + __role_change(ag, 1); + else + return -2; + } + + + if (PAIR_ISSET(pair, SUCCEEDED)) + ; + else + __trigger_check(pair); + + ret = 0; + + if (attrs->use) { + ilog(LOG_DEBUG, "ICE pair "PAIR_FORMAT" has been nominated by peer", PAIR_FMT(pair)); + + PAIR_SET(pair, NOMINATED); + + mutex_lock(&ag->lock); + + g_tree_insert(ag->nominated_pairs, pair, pair); + + if (PAIR_ISSET(pair, SUCCEEDED)) + g_tree_insert(ag->valid_pairs, pair, pair); + + if (!AGENT_ISSET(ag, CONTROLLING)) + ret = __check_valid(ag); + + mutex_unlock(&ag->lock); + } + + return ret; + +err_unlock: + mutex_unlock(&ag->lock); +err: + ilog(LOG_NOTICE, "%s (from %s on interface %s)", err, smart_ntop_port_buf(src), smart_ntop_buf(dst)); + return 0; +} + + +static int __check_succeeded_complete(struct ice_agent *ag) { + GQueue complete; + int ret; + + __get_complete_succeeded_pairs(&complete, ag); + if (complete.length) { + struct ice_candidate_pair *pair = complete.head->data; + ilog(LOG_DEBUG, "Best succeeded ICE pair with all components is "PAIR_FORMAT, PAIR_FMT(pair)); + ret = 1; + } + else { + ilog(LOG_DEBUG, "No succeeded ICE pairs with all components yet"); + ret = 0; + } + g_queue_clear(&complete); + return ret; +} + +/* call is locked in R */ +int ice_response(struct packet_stream *ps, struct sockaddr_in6 *src, struct in6_addr *dst, + struct stun_attrs *attrs, u_int32_t transaction[3]) +{ + struct ice_candidate_pair *pair; + struct ice_agent *ag; + struct call_media *media = ps->media; + const char *err; + unsigned int component; + struct ice_candidate *cand; + struct interface_address *ifa; + int ret, was_ctl; + + __DBG("received ICE response from %s on %s", smart_ntop_port_buf(src), smart_ntop_buf(dst)); + + ag = media->ice_agent; + if (!ag) + return -1; + + mutex_lock(&ag->lock); + + pair = g_hash_table_lookup(ag->transaction_hash, transaction); + err = "ICE/STUN response with unknown transaction received"; + if (!pair) + goto err_unlock; + was_ctl = pair->was_controlling; + + mutex_unlock(&ag->lock); + + ifa = pair->local_address; + + ilog(LOG_DEBUG, "Received ICE/STUN response code %u for candidate pair "PAIR_FORMAT" from %s to %s", + attrs->error_code, PAIR_FMT(pair), + smart_ntop_ep_buf(&pair->remote_candidate->endpoint), + smart_ntop_buf(&ifa->addr)); + + /* verify endpoints */ + err = "ICE/STUN response received, but source address didn't match remote candidate address"; + if (memcmp(&src->sin6_addr, &pair->remote_candidate->endpoint.ip46, sizeof(src->sin6_addr))) + goto err; + if (ntohs(src->sin6_port) != pair->remote_candidate->endpoint.port) + goto err; + + err = "ICE/STUN response received, but destination address didn't match local interface address"; + if (memcmp(dst, &ifa->addr, sizeof(*dst))) + goto err; + if (pair->packet_stream != ps) + goto err; + + PAIR_CLEAR(pair, IN_PROGRESS); + ret = 0; + + /* handle all errors */ + if (attrs->error_code) { + err = "ICE/STUN error received"; + if (attrs->error_code != 487) + goto err; + __role_change(ag, !was_ctl); + __trigger_check(pair); + goto out; + } + + /* we don't discover peer reflexive here (RFC 5245 7.1.3.2.1) as we don't expect to be behind NAT */ + /* we also skip parts of 7.1.3.2.2 as we don't do server reflexive */ + + mutex_lock(&ag->lock); + + /* check if we're in the final (controlling) phase */ + if (pair->was_nominated && PAIR_CLEAR(pair, TO_USE)) { + ilog(LOG_DEBUG, "Setting nominated ICE candidate pair "PAIR_FORMAT" as valid", PAIR_FMT(pair)); + PAIR_SET(pair, VALID); + g_tree_insert(ag->valid_pairs, pair, pair); + ret = __check_valid(ag); + goto out_unlock; + } + + if (PAIR_SET(pair, SUCCEEDED)) + goto out_unlock; + + ilog(LOG_DEBUG, "Setting ICE candidate pair "PAIR_FORMAT" as succeeded", PAIR_FMT(pair)); + g_tree_insert(ag->succeeded_pairs, pair, pair); + + if (!ag->start_nominating.tv_sec) { + if (__check_succeeded_complete(ag)) { + ag->start_nominating = g_now; + timeval_add_usec(&ag->start_nominating, 100000); + __agent_schedule_abs(ag, &ag->start_nominating); + } + } + + /* now unfreeze all other pairs from the same foundation */ + for (component = 1; component <= MAX_COMPONENTS; component++) { + if (component == ps->component) + continue; + cand = __foundation_lookup(ag, &pair->remote_candidate->foundation, component); + if (!cand) + continue; + pair = __pair_lookup(ag, cand, ifa); + if (!pair) + continue; + + if (PAIR_ISSET(pair, FAILED)) + continue; + if (!PAIR_CLEAR(pair, FROZEN)) + continue; + + ilog(LOG_DEBUG, "Unfreezing related ICE pair "PAIR_FORMAT, PAIR_FMT(pair)); + } + + /* if this was previously nominated by the peer, it's now valid */ + if (PAIR_ISSET(pair, NOMINATED)) { + g_tree_insert(ag->valid_pairs, pair, pair); + + if (!AGENT_ISSET(ag, CONTROLLING)) + ret = __check_valid(ag); + } + +out_unlock: + mutex_unlock(&ag->lock); +out: + return ret; + +err_unlock: + mutex_unlock(&ag->lock); +err: + if (err) + ilog(LOG_NOTICE, "%s (from %s on interface %s)", + err, smart_ntop_port_buf(src), smart_ntop_buf(dst)); + + if (pair && attrs->error_code) + __fail_pair(pair); + + return 0; +} + + + +void ice_thread_run(void *p) { + struct ice_agent *ag; + struct call *call; + unsigned long sleeptime; + struct timeval tv; + + mutex_lock(&ice_agents_timers_lock); + + while (!g_shutdown) { + gettimeofday(&g_now, NULL); + + /* lock our list and get the first element */ + ag = g_tree_find_first(ice_agents_timers, NULL, NULL); + /* scheduled to run? if not, we just go to sleep, otherwise we remove it from the tree, + * steal the reference and run it */ + if (!ag) + goto sleep; + if (timeval_cmp(&g_now, &ag->next_check) < 0) + goto sleep; + + g_tree_remove(ice_agents_timers, ag); + ZERO(ag->next_check); + ag->last_run = g_now; + mutex_unlock(&ice_agents_timers_lock); + + /* this agent is scheduled to run right now */ + + /* lock the call */ + call = ag->call; + log_info_ice_agent(ag); + rwlock_lock_r(&call->master_lock); + + /* and run our checks */ + __do_ice_checks(ag); + + /* finally, release our reference and start over */ + log_info_clear(); + rwlock_unlock_r(&call->master_lock); + obj_put(ag); + mutex_lock(&ice_agents_timers_lock); + continue; + +sleep: + /* figure out how long we should sleep */ + sleeptime = ag ? timeval_diff(&ag->next_check, &g_now) : 100000; + sleeptime = MIN(100000, sleeptime); /* 100 ms at the most */ + tv = g_now; + timeval_add_usec(&tv, sleeptime); + cond_timedwait(&ice_agents_timers_cond, &ice_agents_timers_lock, &tv); + continue; + } + + mutex_unlock(&ice_agents_timers_lock); +} + +static void random_ice_string(char *buf, int len) { + while (len--) + *buf++ = ice_chars[random() % strlen(ice_chars)]; +} + +static void create_random_ice_string(struct call *call, str *s, int len) { + char buf[30]; + + assert(len < sizeof(buf)); + if (s->s) + return; + + random_ice_string(buf, len); + call_str_cpy_len(call, s, buf, len); +} + +void ice_foundation(struct interface_address *ifa) { + random_ice_string(ifa->foundation_buf, sizeof(ifa->foundation_buf)); + str_init_len(&ifa->ice_foundation, ifa->foundation_buf, sizeof(ifa->foundation_buf)); +} + +void ice_remote_candidates(GQueue *out, struct ice_agent *ag) { + GQueue all_compos; + GList *l; + struct ice_candidate_pair *pair; + + g_queue_init(out); + + mutex_lock(&ag->lock); + __get_complete_valid_pairs(&all_compos, ag); + mutex_unlock(&ag->lock); + + for (l = all_compos.head; l; l = l->next) { + pair = l->data; + g_queue_push_tail(out, pair->remote_candidate); + } + + g_queue_clear(&all_compos); +} diff --git a/daemon/ice.h b/daemon/ice.h new file mode 100644 index 000000000..44fe15adc --- /dev/null +++ b/daemon/ice.h @@ -0,0 +1,208 @@ +#ifndef __ICE_H__ +#define __ICE_H__ + + + +#include +#include +#include +#include +#include "str.h" +#include "obj.h" +#include "aux.h" +#include "call.h" + + + + +#define MAX_COMPONENTS 2 +#define TIMER_RUN_INTERVAL 20 /* ms */ +#define STUN_RETRANSMIT_INTERVAL 100 /* ms, with exponential backoff */ +#define STUN_MAX_RETRANSMITS 7 +#define MAX_ICE_CANDIDATES 100 + + + +#define ICE_AGENT_COMPLETED 0x0002 +#define ICE_AGENT_CONTROLLING 0x0004 +#define ICE_AGENT_NOMINATING 0x0008 + +#define ICE_PAIR_FROZEN 0x0001 +#define ICE_PAIR_IN_PROGRESS 0x0002 +#define ICE_PAIR_FAILED 0x0004 +#define ICE_PAIR_SUCCEEDED 0x0008 +#define ICE_PAIR_NOMINATED 0x0010 +#define ICE_PAIR_LEARNED 0x0020 +#define ICE_PAIR_VALID 0x0040 +#define ICE_PAIR_TO_USE 0x0080 +#define ICE_PAIR_TRIGGERED 0x0100 + +#define PAIR_ISSET(p, f) bf_isset(&(p)->pair_flags, ICE_PAIR_ ## f) +#define PAIR_SET(p, f) bf_set(&(p)->pair_flags, ICE_PAIR_ ## f) +#define PAIR_SET2(p, f, g) bf_set(&(p)->pair_flags, ICE_PAIR_ ## f | ICE_PAIR_ ## g) +#define PAIR_CLEAR(p, f) bf_clear(&(p)->pair_flags, ICE_PAIR_ ## f) +#define PAIR_CLEAR2(p, f, g) bf_clear(&(p)->pair_flags, ICE_PAIR_ ## f | ICE_PAIR_ ## g) + +#define AGENT_ISSET(p, f) bf_isset(&(p)->agent_flags, ICE_AGENT_ ## f) +#define AGENT_ISSET2(p, f, g) bf_isset(&(p)->agent_flags, ICE_AGENT_ ## f | ICE_AGENT_ ## g) +#define AGENT_SET(p, f) bf_set(&(p)->agent_flags, ICE_AGENT_ ## f) +#define AGENT_SET2(p, f, g) bf_set(&(p)->agent_flags, ICE_AGENT_ ## f | ICE_AGENT_ ## g) +#define AGENT_CLEAR(p, f) bf_clear(&(p)->agent_flags, ICE_AGENT_ ## f) +#define AGENT_CLEAR2(p, f, g) bf_clear(&(p)->agent_flags, ICE_AGENT_ ## f | ICE_AGENT_ ## g) + + + +struct local_interface; +struct interface_address; +struct packet_stream; +struct call_media; +struct call; +struct stream_params; +struct stun_attrs; + + + + +enum ice_candidate_type { + ICT_UNKNOWN = 0, + ICT_HOST, + ICT_SRFLX, + ICT_PRFLX, + ICT_RELAY, + __ICT_LAST, +}; + +enum ice_transport { + ITP_UNKNOWN = 0, + ITP_UDP, +// ITP_TCP, +}; + +struct ice_candidate { + str foundation; + unsigned long component_id; + enum ice_transport transport; + unsigned long priority; + struct endpoint endpoint; + enum ice_candidate_type type; + struct in6_addr related_address; + unsigned int related_port; +}; + +struct ice_candidate_pair { + struct ice_candidate *remote_candidate; + struct interface_address *local_address; + struct packet_stream *packet_stream; + volatile unsigned int pair_flags; + u_int32_t stun_transaction[3]; /* belongs to transaction_hash, thus agent->lock */ + unsigned int retransmit_ms; + struct timeval retransmit; + unsigned int retransmits; + struct ice_agent *agent; + u_int64_t pair_priority; + int was_controlling:1, + was_nominated:1; +}; + +/* these are protected by the call's master_lock */ +struct ice_agent { + struct obj obj; + struct call *call; /* main reference */ + struct call_media *media; + struct local_interface *local_interface; + int desired_family; + + mutex_t lock; /* for elements below. and call must be locked in R */ + /* lock order: in_lock first, then agent->lock */ + GQueue remote_candidates; + GQueue candidate_pairs; /* for storage */ + GQueue triggered; + GHashTable *candidate_hash; + GHashTable *pair_hash; + GHashTable *transaction_hash; + GHashTable *foundation_hash; + GTree *all_pairs; + GQueue all_pairs_list; /* sorted through gtree */ + GTree *nominated_pairs; /* nominated by peer */ + GTree *succeeded_pairs; /* checked by us */ + GTree *valid_pairs; /* succeeded and nominated */ + unsigned int active_components; + struct timeval start_nominating; + + str ufrag[2]; /* 0 = remote, 1 = local */ + str pwd[2]; /* ditto */ + volatile unsigned int agent_flags; + + struct timeval next_check; /* protected by ice_agents_timers_lock */ + struct timeval last_run; /* ditto */ +}; + + + + +extern const unsigned int ice_type_preferences[]; +extern const char * const ice_type_strings[]; + + + + +void ice_init(void); + +enum ice_candidate_type ice_candidate_type(const str *s); +enum ice_transport ice_transport(const str *s); +int ice_has_related(enum ice_candidate_type); +void ice_foundation(struct interface_address *ifa); + +void ice_agent_init(struct ice_agent **agp, struct call_media *media); +void ice_update(struct ice_agent *, struct stream_params *); +void ice_shutdown(struct ice_agent **); +void ice_restart(struct ice_agent *); + +void ice_candidates_free(GQueue *); +void ice_remote_candidates(GQueue *, struct ice_agent *); + +void ice_thread_run(void *); + +int ice_request(struct packet_stream *, struct sockaddr_in6 *, struct in6_addr *, struct stun_attrs *); +int ice_response(struct packet_stream *ps, struct sockaddr_in6 *src, struct in6_addr *dst, + struct stun_attrs *attrs, u_int32_t transaction[3]); + +/* returns 0 if ICE still has work to do, 1 otherwise */ +INLINE int ice_has_finished(struct call_media *media) { + if (!media) + return 1; + if (!MEDIA_ISSET(media, ICE)) + return 1; + if (!media->ice_agent) + return 1; + if (AGENT_ISSET(media->ice_agent, COMPLETED)) + return 1; + return 0; +} +INLINE unsigned int ice_type_preference(enum ice_candidate_type type) { + if (type >= __ICT_LAST) + return 0; + return ice_type_preferences[type]; +} +/* local_pref starts with 0 */ +INLINE u_int32_t ice_priority_pref(unsigned int type_pref, unsigned int local_pref, unsigned int component) { + return type_pref << 24 | (65535 - local_pref) << 8 | (256 - component); +} +INLINE u_int32_t ice_priority(enum ice_candidate_type type, unsigned int local_pref, unsigned int component) { + return ice_priority_pref(ice_type_preference(type), local_pref, component); +} +INLINE unsigned int ice_type_pref_from_prio(u_int32_t prio) { + return (prio & 0xff000000) >> 24; +} +INLINE unsigned int ice_local_pref_from_prio(u_int32_t prio) { + return 65535 - ((prio & 0xffff00) >> 8); +} +INLINE const char *ice_candidate_type_str(enum ice_candidate_type type) { + if (type >= __ICT_LAST) + return 0; + return ice_type_strings[type]; +} + + + +#endif diff --git a/daemon/log.c b/daemon/log.c index adfdcf750..7bd4c2e84 100644 --- a/daemon/log.c +++ b/daemon/log.c @@ -6,6 +6,7 @@ #include "str.h" #include "call.h" #include "poller.h" +#include "ice.h" @@ -88,7 +89,8 @@ void log_to_stderr(int facility_priority, char *format, ...) { return; } - fprintf(stderr, "%s: %s\n", prio_str[facility_priority & LOG_PRIMASK], msg); + fprintf(stderr, "[%lu.%06lu] %s: %s\n", (unsigned long) g_now.tv_sec, (unsigned long) g_now.tv_usec, + prio_str[facility_priority & LOG_PRIMASK], msg); free(msg); } @@ -123,6 +125,12 @@ void __ilog(int prio, const char *fmt, ...) { case LOG_INFO_C_STRING: snprintf(prefix, sizeof(prefix), "[%s] ", log_info.u.cstr); break; + case LOG_INFO_ICE_AGENT: + snprintf(prefix, sizeof(prefix), "["STR_FORMAT"/"STR_FORMAT"/%u] ", + STR_FMT(&log_info.u.ice_agent->call->callid), + STR_FMT(&log_info.u.ice_agent->media->monologue->tag), + log_info.u.ice_agent->media->index); + break; } va_start(ap, fmt); diff --git a/daemon/log.h b/daemon/log.h index 8ee9e9081..ffe3f941e 100644 --- a/daemon/log.h +++ b/daemon/log.h @@ -15,6 +15,7 @@ struct log_info { const struct stream_fd *stream_fd; const str *str; const char *cstr; + const struct ice_agent *ice_agent; const void *ptr; } u; enum { @@ -23,6 +24,7 @@ struct log_info { LOG_INFO_STREAM_FD, LOG_INFO_STR, LOG_INFO_C_STRING, + LOG_INFO_ICE_AGENT, } e; }; @@ -76,10 +78,9 @@ INLINE void log_info_clear() { case LOG_INFO_NONE: return; case LOG_INFO_CALL: - __obj_put((void *) log_info.u.call); - break; case LOG_INFO_STREAM_FD: - __obj_put((void *) log_info.u.stream_fd); + case LOG_INFO_ICE_AGENT: + __obj_put((void *) log_info.u.ptr); break; case LOG_INFO_STR: case LOG_INFO_C_STRING: @@ -116,6 +117,13 @@ INLINE void log_info_c_string(const char *s) { log_info.e = LOG_INFO_C_STRING; log_info.u.cstr = s; } +INLINE void log_info_ice_agent(const struct ice_agent *ag) { + log_info_clear(); + if (!ag) + return; + log_info.e = LOG_INFO_ICE_AGENT; + log_info.u.ice_agent = __obj_get((void *) ag); +} INLINE int get_log_level(void) { return g_atomic_int_get(&log_level); } diff --git a/daemon/main.c b/daemon/main.c index b7e1aa575..97b835c33 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -27,6 +27,7 @@ #include "call_interfaces.h" #include "cli.h" #include "graphite.h" +#include "ice.h" @@ -80,7 +81,6 @@ struct main_context { -static int global_shutdown; static mutex_t *openssl_locks; static char *pidfile; @@ -126,7 +126,7 @@ static void sighandler(gpointer x) { ts.tv_sec = 0; ts.tv_nsec = 100000000; /* 0.1 sec */ - while (!global_shutdown) { + while (!g_shutdown) { ret = sigtimedwait(&ss, NULL, &ts); if (ret == -1) { if (errno == EAGAIN || errno == EINTR) @@ -135,7 +135,7 @@ static void sighandler(gpointer x) { } if (ret == SIGINT || ret == SIGTERM) - global_shutdown = 1; + g_shutdown = 1; else if (ret == SIGUSR1) { if (get_log_level() > 0) { g_atomic_int_add(&log_level, -1); @@ -237,7 +237,7 @@ static struct interface_address *if_addr_parse(char *s) { return NULL; } - ifa = g_slice_alloc(sizeof(*ifa)); + ifa = g_slice_alloc0(sizeof(*ifa)); ifa->interface_name = name; ifa->addr = addr; ifa->advertised = adv; @@ -458,6 +458,7 @@ static void init_everything() { resources(); sdp_init(); dtls_init(); + ice_init(); } void redis_mod_verify(void *dlh) { @@ -622,10 +623,11 @@ no_kernel: die("Refusing to continue without working Redis database"); } +/* XXX move loop functions */ static void timer_loop(void *d) { struct poller *p = d; - while (!global_shutdown) + while (!g_shutdown) poller_timers_wait_run(p, 100); } @@ -639,14 +641,14 @@ static void graphite_loop(void *d) { connect_to_graphite_server(graphite_ip,graphite_port); - while (!global_shutdown) + while (!g_shutdown) graphite_loop_run(cm,graphite_interval); // time in seconds } static void poller_loop(void *d) { struct poller *p = d; - while (!global_shutdown) + while (!g_shutdown) poller_poll(p, 100); } @@ -664,6 +666,7 @@ int main(int argc, char **argv) { thread_create_detach(timer_loop, ctx.p); if (graphite_ip) thread_create_detach(graphite_loop, ctx.m); + thread_create_detach(ice_thread_run, NULL); if (num_threads < 1) { #ifdef _SC_NPROCESSORS_ONLN @@ -677,7 +680,7 @@ int main(int argc, char **argv) { thread_create_detach(poller_loop, ctx.p); } - while (!global_shutdown) { + while (!g_shutdown) { usleep(100000); threads_join_all(0); } diff --git a/daemon/poller.c b/daemon/poller.c index 96e83e1d9..5d0830a6d 100644 --- a/daemon/poller.c +++ b/daemon/poller.c @@ -50,18 +50,12 @@ struct poller { -__thread time_t poller_now; - - - - - struct poller *poller_new(void) { struct poller *p; p = malloc(sizeof(*p)); memset(p, 0, sizeof(*p)); - poller_now = time(NULL); + gettimeofday(&g_now, NULL); p->fd = epoll_create1(0); if (p->fd == -1) abort(); @@ -315,7 +309,7 @@ int poller_poll(struct poller *p, int timeout) { if (ret <= 0) goto out; - poller_now = time(NULL); + gettimeofday(&g_now, NULL); for (i = 0; i < ret; i++) { ev = &evs[i]; @@ -502,6 +496,6 @@ retry: goto retry; now: - poller_now = tv.tv_sec; + gettimeofday(&g_now, NULL); poller_timers_run(p); } diff --git a/daemon/poller.h b/daemon/poller.h index 6c0003248..0bc6d9485 100644 --- a/daemon/poller.h +++ b/daemon/poller.h @@ -30,10 +30,8 @@ struct poller_item { struct poller; - -extern __thread time_t poller_now; - - +/* XXX replace all occurrences with g_now */ +#define poller_now g_now.tv_sec struct poller *poller_new(void); diff --git a/daemon/sdp.c b/daemon/sdp.c index e869d621b..84e71241e 100644 --- a/daemon/sdp.c +++ b/daemon/sdp.c @@ -14,6 +14,7 @@ #include "crypto.h" #include "dtls.h" #include "rtp.h" +#include "ice.h" struct network_address { str network_type; @@ -76,17 +77,17 @@ struct attribute_rtcp { }; struct attribute_candidate { - str foundation; str component_str; - str transport; + str transport_str; str priority_str; - str ip_str; + str address_str; str port_str; str typ_str; str type_str; + str related_address_str; + str related_port_str; - unsigned long component; - unsigned long priority; + struct ice_candidate cand_parsed; int parsed:1; }; @@ -166,8 +167,10 @@ struct sdp_attribute { ATTR_RTCP, ATTR_CANDIDATE, ATTR_ICE, + ATTR_ICE_LITE, ATTR_ICE_OPTIONS, ATTR_ICE_UFRAG, + ATTR_ICE_PWD, ATTR_CRYPTO, ATTR_SSRC, ATTR_INACTIVE, @@ -198,11 +201,6 @@ struct sdp_attribute { -static const char ice_chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - - - INLINE struct sdp_attribute *attr_get_by_id(struct sdp_attributes *a, int id) { return g_hash_table_lookup(a->id_hash, &id); } @@ -576,27 +574,59 @@ static int parse_attribute_rtcp(struct sdp_attribute *output) { static int parse_attribute_candidate(struct sdp_attribute *output) { PARSE_DECL; char *ep; + struct attribute_candidate *c; output->attr = ATTR_CANDIDATE; + c = &output->u.candidate; PARSE_INIT; - EXTRACT_TOKEN(u.candidate.foundation); + EXTRACT_TOKEN(u.candidate.cand_parsed.foundation); EXTRACT_TOKEN(u.candidate.component_str); - EXTRACT_TOKEN(u.candidate.transport); + EXTRACT_TOKEN(u.candidate.transport_str); EXTRACT_TOKEN(u.candidate.priority_str); - EXTRACT_TOKEN(u.candidate.ip_str); + EXTRACT_TOKEN(u.candidate.address_str); EXTRACT_TOKEN(u.candidate.port_str); EXTRACT_TOKEN(u.candidate.typ_str); EXTRACT_TOKEN(u.candidate.type_str); - output->u.candidate.component = strtoul(output->u.candidate.component_str.s, &ep, 10); - if (ep == output->u.candidate.component_str.s) + c->cand_parsed.component_id = strtoul(c->component_str.s, &ep, 10); + if (ep == c->component_str.s) + return -1; + + c->cand_parsed.transport = ice_transport(&c->transport_str); + if (!c->cand_parsed.transport) + return 0; + + c->cand_parsed.priority = strtoul(c->priority_str.s, &ep, 10); + if (ep == c->priority_str.s) + return -1; + + if (__parse_address(&c->cand_parsed.endpoint.ip46, NULL, NULL, &c->address_str)) + return 0; + + c->cand_parsed.endpoint.port = strtoul(c->port_str.s, &ep, 10); + if (ep == c->port_str.s) return -1; - output->u.candidate.priority = strtoul(output->u.candidate.priority_str.s, &ep, 10); - if (ep == output->u.candidate.priority_str.s) + + if (str_cmp(&c->typ_str, "typ")) + return -1; + + c->cand_parsed.type = ice_candidate_type(&c->type_str); + if (!c->cand_parsed.type) + return 0; + + if (!ice_has_related(c->cand_parsed.type)) + goto done; + + if (__parse_address(&c->cand_parsed.related_address, NULL, NULL, &c->related_address_str)) + return 0; + + c->cand_parsed.related_port = strtoul(c->related_port_str.s, &ep, 10); + if (ep == c->related_port_str.s) return -1; - output->u.candidate.parsed = 1; +done: + c->parsed = 1; return 0; } @@ -766,13 +796,13 @@ static int parse_attribute(struct sdp_attribute *a) { break; case 7: if (!str_cmp(&a->name, "ice-pwd")) - a->attr = ATTR_ICE; + a->attr = ATTR_ICE_PWD; break; case 8: switch (a->name.s[0]) { case 'i': if (!str_cmp(&a->name, "ice-lite")) - a->attr = ATTR_ICE; + a->attr = ATTR_ICE_LITE; else if (!str_cmp(&a->name, "inactive")) a->attr = ATTR_INACTIVE; break; @@ -1093,6 +1123,48 @@ out: return ret; } +static void __sdp_ice(struct stream_params *sp, struct sdp_media *media) { + struct sdp_attribute *attr; + struct attribute_candidate *ac; + struct ice_candidate *cand; + GQueue *q; + GList *ql; + + q = attr_list_get_by_id(&media->attributes, ATTR_CANDIDATE); + if (!q) + return; + + SP_SET(sp, ICE); + + for (ql = q->head; ql; ql = ql->next) { + attr = ql->data; + ac = &attr->u.candidate; + if (!ac->parsed) + continue; + cand = g_slice_alloc(sizeof(*cand)); + *cand = ac->cand_parsed; + g_queue_push_tail(&sp->ice_candidates, cand); + } + + if ((attr = attr_get_by_id(&media->attributes, ATTR_ICE_OPTIONS))) { + if (str_str(&attr->value, "trickle") >= 0) + SP_SET(sp, TRICKLE_ICE); + } + else if (is_trickle_ice_address(&sp->rtp_endpoint)) + SP_SET(sp, TRICKLE_ICE); + + if (attr_get_by_id(&media->attributes, ATTR_ICE_LITE)) + SP_SET(sp, ICE_LITE); + + attr = attr_get_by_id_m_s(media, ATTR_ICE_UFRAG); + if (attr) + sp->ice_ufrag = attr->value; + + attr = attr_get_by_id_m_s(media, ATTR_ICE_PWD); + if (attr) + sp->ice_pwd = attr->value; +} + /* XXX split this function up */ int sdp_streams(const GQueue *sessions, GQueue *streams, struct sdp_ng_flags *flags) { @@ -1181,15 +1253,7 @@ int sdp_streams(const GQueue *sessions, GQueue *streams, struct sdp_ng_flags *fl sp->fingerprint.hash_func->num_bytes); } - /* ICE stuff */ - if (attr_get_by_id(&media->attributes, ATTR_CANDIDATE)) - SP_SET(sp, ICE); - if ((attr = attr_get_by_id(&media->attributes, ATTR_ICE_OPTIONS))) { - if (str_str(&attr->value, "trickle") >= 0) - SP_SET(sp, TRICKLE_ICE); - } - else if (is_trickle_ice_address(&sp->rtp_endpoint)) - SP_SET(sp, TRICKLE_ICE); + __sdp_ice(sp, media); /* determine RTCP endpoint */ @@ -1430,22 +1494,6 @@ void sdp_chopper_destroy(struct sdp_chopper *chop) { g_slice_free1(sizeof(*chop), chop); } -static void random_ice_string(char *buf, int len) { - while (len--) - *buf++ = ice_chars[random() % strlen(ice_chars)]; -} - -static void create_random_ice_string(struct call *call, str *s, int len) { - char buf[30]; - - assert(len < sizeof(buf)); - if (s->s) - return; - - random_ice_string(buf, len); - call_str_cpy_len(call, s, buf, len); -} - static int process_session_attributes(struct sdp_chopper *chop, struct sdp_attributes *attrs, struct sdp_ng_flags *flags) { @@ -1458,6 +1506,9 @@ static int process_session_attributes(struct sdp_chopper *chop, struct sdp_attri switch (attr->attr) { case ATTR_ICE: case ATTR_ICE_UFRAG: + case ATTR_ICE_PWD: + case ATTR_ICE_OPTIONS: + case ATTR_ICE_LITE: if (!flags->ice_remove && !flags->ice_force) break; goto strip; @@ -1517,6 +1568,9 @@ static int process_media_attributes(struct sdp_chopper *chop, struct sdp_media * switch (attr->attr) { case ATTR_ICE: case ATTR_ICE_UFRAG: + case ATTR_ICE_PWD: + case ATTR_ICE_OPTIONS: + case ATTR_ICE_LITE: if (MEDIA_ISSET(media, PASSTHRU)) break; if (!flags->ice_remove && !flags->ice_force) @@ -1579,32 +1633,19 @@ strip: return 0; } -INLINE unsigned long prio_calc(unsigned int pref, unsigned int tpref) { - return (1 << 24) * tpref + (1 << 8) * pref + (256 - 1); -} -INLINE unsigned long pref_from_prio(unsigned int prio) { - return (prio & 0xffff00) >> 8; -} -INLINE unsigned long type_from_prio(unsigned int prio) { - return (prio & 0xff000000) >> 24; -} - -static unsigned long new_priority(struct sdp_media *media, int relay) { +static void new_priority(struct sdp_media *media, enum ice_candidate_type type, unsigned int *tprefp, + unsigned int *lprefp) +{ GQueue *cands; - int pref; - unsigned long prio, tpref; + unsigned int lpref, tpref; + u_int32_t prio; GList *l; struct sdp_attribute *a; struct attribute_candidate *c; - tpref = 126; - if (relay) - tpref = 0; - pref = 65535; - prio = prio_calc(pref, tpref); - - if (!media) - goto out; + lpref = 0; + tpref = ice_type_preference(type); + prio = ice_priority_pref(tpref, lpref, 1); cands = attr_list_get_by_id(&media->attributes, ATTR_CANDIDATE); if (!cands) @@ -1613,88 +1654,111 @@ static unsigned long new_priority(struct sdp_media *media, int relay) { for (l = cands->head; l; l = l->next) { a = l->data; c = &a->u.candidate; - if (c->priority <= prio && !str_cmp(&c->type_str, "host")) { - /* tpref should come out as 126 here, unless the client isn't following + if (c->cand_parsed.priority <= prio && c->cand_parsed.type == type + && c->cand_parsed.component_id == 1) + { + /* tpref should come out as 126 (if host) here, unless the client isn't following * the RFC, in which case we must adapt */ - tpref = type_from_prio(c->priority); + tpref = ice_type_pref_from_prio(c->cand_parsed.priority); - pref = pref_from_prio(c->priority); - if (pref) - pref--; + lpref = ice_local_pref_from_prio(c->cand_parsed.priority); + if (lpref) + lpref--; else { /* we must deviate from the RFC recommended values */ if (tpref) tpref--; - pref = 65535; + lpref = 65535; } - prio = prio_calc(pref, tpref); + prio = ice_priority_pref(tpref, lpref, 1); } } out: - return prio; + *tprefp = tpref; + *lprefp = lpref; +} + +static void insert_candidate(struct sdp_chopper *chop, struct packet_stream *ps, unsigned int component, + unsigned int type_pref, unsigned int local_pref, enum ice_candidate_type type, + struct interface_address *ifa) +{ + unsigned long priority; + + priority = ice_priority_pref(type_pref, local_pref, component); + chopper_append_c(chop, "a=candidate:"); + chopper_append_str(chop, &ifa->ice_foundation); + chopper_append_printf(chop, " %u UDP %lu ", component, priority); + insert_ice_address(chop, ps, ifa); + chopper_append_c(chop, " typ "); + chopper_append_c(chop, ice_candidate_type_str(type)); + chopper_append_c(chop, "\r\n"); } static void insert_candidates(struct sdp_chopper *chop, struct packet_stream *rtp, struct packet_stream *rtcp, - unsigned long priority, struct sdp_media *media, - unsigned int relay) + struct sdp_ng_flags *flags, struct sdp_media *sdp_media) { - GQueue addrs = G_QUEUE_INIT; GList *l; struct interface_address *ifa; + unsigned int pref; + struct call_media *media; + struct local_interface *lif; + struct ice_agent *ag; + unsigned int type_pref, local_pref; + enum ice_candidate_type cand_type; + struct ice_candidate *cand; + + media = rtp->media; + + cand_type = ICT_HOST; + if (flags->ice_force_relay) + cand_type = ICT_RELAY; + if (MEDIA_ISSET(media, PASSTHRU)) + new_priority(sdp_media, cand_type, &type_pref, &local_pref); + else { + type_pref = ice_type_preference(cand_type); + local_pref = -1; + } - get_all_interface_addresses(&addrs, rtp->media->interface, rtp->media->desired_family); - - for (l = addrs.head; l; l = l->next) { - ifa = l->data; - - chopper_append_c(chop, "a=candidate:"); - chopper_append_str(chop, &ifa->ice_foundation); - chopper_append_printf(chop, " 1 UDP %lu ", priority); - insert_ice_address(chop, rtp, ifa); - if (relay) - chopper_append_c(chop, " typ relay\r\n"); - else - chopper_append_c(chop, " typ host\r\n"); - - if (rtcp) { - /* rtcp-mux only possible in answer */ - chopper_append_c(chop, "a=candidate:"); - chopper_append_str(chop, &ifa->ice_foundation); - chopper_append_printf(chop, " 2 UDP %lu ", priority - 1); - insert_ice_address(chop, rtcp, ifa); - if (relay) - chopper_append_c(chop, " typ relay\r\n"); - else - chopper_append_c(chop, " typ host\r\n"); + ag = media->ice_agent; + lif = ag ? ag->local_interface : media->interface; + + if (ag && AGENT_ISSET(ag, COMPLETED)) { + ifa = g_atomic_pointer_get(&media->local_address); + insert_candidate(chop, rtp, 1, type_pref, ifa->preference, cand_type, ifa); + if (rtcp) /* rtcp-mux only possible in answer */ + insert_candidate(chop, rtcp, 2, type_pref, ifa->preference, cand_type, ifa); + + if (flags->opmode == OP_OFFER && AGENT_ISSET(ag, CONTROLLING)) { + GQueue rc; + GList *l; + chopper_append_c(chop, "a=remote-candidates:"); + ice_remote_candidates(&rc, ag); + for (l = rc.head; l; l = l->next) { + if (l != rc.head) + chopper_append_c(chop, " "); + cand = l->data; + chopper_append_printf(chop, "%lu %s %u", cand->component_id, + smart_ntop_buf(&cand->endpoint.ip46), cand->endpoint.port); + } + chopper_append_c(chop, "\r\n"); + g_queue_clear(&rc); } - - priority -= 256; + return; } - g_queue_clear(&addrs); -} - -static int has_ice(GQueue *sessions) { - GList *l, *m; - struct sdp_session *session; - struct sdp_media *media; - - for (l = sessions->head; l; l = l->next) { - session = l->data; + for (l = lif->list.head; l; l = l->next) { + ifa = l->data; + pref = (local_pref == -1) ? ifa->preference : local_pref; - if (attr_get_by_id(&session->attributes, ATTR_ICE_UFRAG)) - return 1; + insert_candidate(chop, rtp, 1, type_pref, pref, cand_type, ifa); - for (m = session->media_streams.head; m; m = m->next) { - media = m->data; + if (rtcp) /* rtcp-mux only possible in answer */ + insert_candidate(chop, rtcp, 2, type_pref, pref, cand_type, ifa); - if (attr_get_by_id(&media->attributes, ATTR_ICE_UFRAG)) - return 1; - } + if (local_pref != -1) + local_pref++; } - - return 0; } static void insert_dtls(struct call_media *media, struct sdp_chopper *chop) { @@ -1720,12 +1784,10 @@ static void insert_dtls(struct call_media *media, struct sdp_chopper *chop) { *(--o) = '\0'; actpass = "holdconn"; - if (MEDIA_ISSET(media, SETUP_PASSIVE)) { - if (MEDIA_ISSET(media, SETUP_ACTIVE)) - actpass = "actpass"; - else - actpass = "passive"; - } + if (MEDIA_ARESET2(media, SETUP_PASSIVE, SETUP_ACTIVE)) + actpass = "actpass"; + else if (MEDIA_ISSET(media, SETUP_PASSIVE)) + actpass = "passive"; else if (MEDIA_ISSET(media, SETUP_ACTIVE)) actpass = "active"; @@ -1779,16 +1841,11 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call_monologu struct sdp_session *session; struct sdp_media *sdp_media; GList *l, *k, *m, *j; - int do_ice, media_index, sess_conn; - unsigned long priority; + int media_index, sess_conn; struct call_media *call_media; struct packet_stream *ps, *ps_rtcp; - struct call *call; m = monologue->medias.head; - do_ice = (flags->ice_force || flags->ice_force_relay || - (!has_ice(sessions) && !flags->ice_remove)) ? 1 : 0; - call = monologue->call; for (l = sessions->head; l; l = l->next) { session = l->data; @@ -1829,11 +1886,6 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call_monologu if (!MEDIA_ISSET(call_media, PASSTHRU)) { if (process_session_attributes(chop, &session->attributes, flags)) goto error; - - if (do_ice) { - copy_up_to_end_of(chop, &session->s); - chopper_append_c(chop, "a=ice-lite\r\n"); - } } media_index = 1; @@ -1881,11 +1933,11 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call_monologu if (!sdp_media->port_num || !ps->sfd) goto next; - if (MEDIA_ISSET(call_media, SEND) && MEDIA_ISSET(call_media, RECV)) + if (MEDIA_ARESET2(call_media, SEND, RECV)) chopper_append_c(chop, "a=sendrecv\r\n"); - else if (MEDIA_ISSET(call_media, SEND) && !MEDIA_ISSET(call_media, RECV)) + else if (MEDIA_ISSET(call_media, SEND)) chopper_append_c(chop, "a=sendonly\r\n"); - else if (!MEDIA_ISSET(call_media, SEND) && MEDIA_ISSET(call_media, RECV)) + else if (MEDIA_ISSET(call_media, RECV)) chopper_append_c(chop, "a=recvonly\r\n"); else chopper_append_c(chop, "a=inactive\r\n"); @@ -1908,31 +1960,16 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call_monologu insert_crypto(call_media, chop); insert_dtls(call_media, chop); - if (do_ice && !MEDIA_ISSET(call_media, PASSTHRU)) { - if (!call_media->ice_ufrag.s) { - create_random_ice_string(call, &call_media->ice_ufrag, 8); - create_random_ice_string(call, &call_media->ice_pwd, 28); - } - PS_SET(ps, STUN); - if (ps_rtcp) - PS_SET(ps_rtcp, STUN); - + if (call_media->ice_agent) { chopper_append_c(chop, "a=ice-ufrag:"); - chopper_append_str(chop, &call_media->ice_ufrag); + chopper_append_str(chop, &call_media->ice_agent->ufrag[1]); chopper_append_c(chop, "\r\na=ice-pwd:"); - chopper_append_str(chop, &call_media->ice_pwd); + chopper_append_str(chop, &call_media->ice_agent->pwd[1]); chopper_append_c(chop, "\r\n"); } - if (!flags->ice_remove) { - priority = new_priority( - (flags->ice_force || flags->ice_force_relay) ? NULL : sdp_media, - flags->ice_force_relay - ); - - insert_candidates(chop, ps, ps_rtcp, - priority, sdp_media, flags->ice_force_relay); - } + if (!flags->ice_remove) + insert_candidates(chop, ps, ps_rtcp, flags, sdp_media); next: media_index++; @@ -1950,8 +1987,3 @@ error: void sdp_init() { } - -void sdp_ice_foundation(struct interface_address *ifa) { - random_ice_string(ifa->foundation_buf, sizeof(ifa->foundation_buf)); - str_init_len(&ifa->ice_foundation, ifa->foundation_buf, sizeof(ifa->foundation_buf)); -} diff --git a/daemon/sdp.h b/daemon/sdp.h index ab659f17b..5d20f7ec6 100644 --- a/daemon/sdp.h +++ b/daemon/sdp.h @@ -45,7 +45,6 @@ struct sdp_chopper { }; void sdp_init(void); -void sdp_ice_foundation(struct interface_address *ifa); int sdp_parse(str *body, GQueue *sessions); int sdp_streams(const GQueue *sessions, GQueue *streams, struct sdp_ng_flags *); diff --git a/daemon/stun.c b/daemon/stun.c index d0826f95c..85ac584ea 100644 --- a/daemon/stun.c +++ b/daemon/stun.c @@ -6,11 +6,13 @@ #include #include #include +#include #include "compat.h" #include "str.h" #include "aux.h" #include "log.h" +#include "ice.h" @@ -21,7 +23,11 @@ #define STUN_ERROR_CODE 0x0009 #define STUN_UNKNOWN_ATTRIBUTES 0x000a #define STUN_XOR_MAPPED_ADDRESS 0x0020 +#define STUN_PRIORITY 0x0024 +#define STUN_USE_CANDIDATE 0x0025 #define STUN_FINGERPRINT 0x8028 +#define STUN_ICE_CONTROLLED 0x8029 +#define STUN_ICE_CONTROLLING 0x802a #define STUN_CLASS_REQUEST 0x00 #define STUN_CLASS_INDICATION 0x01 @@ -35,6 +41,8 @@ | (((method) & 0x0f80) << 2) | (((class) & 0x1) << 4) \ | (((class) & 0x2) << 7)) +#define STUN_BINDING_REQUEST \ + STUN_MESSAGE_TYPE(STUN_METHOD_BINDING, STUN_CLASS_REQUEST) #define STUN_BINDING_SUCCESS_RESPONSE \ STUN_MESSAGE_TYPE(STUN_METHOD_BINDING, STUN_CLASS_SUCCESS) #define STUN_BINDING_ERROR_RESPONSE \ @@ -44,18 +52,6 @@ -struct stun_attrs { - str username; - char *msg_integrity_attr; - str msg_integrity; - u_int32_t priority; - char *fingerprint_attr; - u_int32_t fingerprint; - int use:1, - controlled:1, - controlling:1; -}; - struct header { u_int16_t msg_type; u_int16_t msg_len; @@ -94,9 +90,23 @@ struct xor_mapped_address { u_int32_t address[4]; } __attribute__ ((packed)); +struct controlled_ing { + struct tlv tlv; + u_int64_t tiebreaker; +} __attribute__ ((packed)); + +struct priority { + struct tlv tlv; + u_int32_t priority; +} __attribute__ ((packed)); + + + +/* XXX add const in functions */ -static int stun_attributes(struct stun_attrs *out, str *s, u_int16_t *unknowns) { + +static int stun_attributes(struct stun_attrs *out, str *s, u_int16_t *unknowns, struct header *req) { struct tlv *tlv; int len, type, uc; str attr; @@ -129,12 +139,14 @@ static int stun_attributes(struct stun_attrs *out, str *s, u_int16_t *unknowns) case STUN_USERNAME: out->username = attr; break; + case STUN_MESSAGE_INTEGRITY: if (attr.len != 20) return -1; out->msg_integrity_attr = (void *) tlv; out->msg_integrity = attr; break; + case STUN_FINGERPRINT: if (attr.len != 4) return -1; @@ -142,17 +154,29 @@ static int stun_attributes(struct stun_attrs *out, str *s, u_int16_t *unknowns) out->fingerprint = ntohl(*(u_int32_t *) attr.s); goto out; - case 0x0025: /* use-candidate */ + case STUN_USE_CANDIDATE: out->use = 1; break; - case 0x8029: /* ice-controlled */ + + case STUN_ICE_CONTROLLED: + if (out->controlling) + return -1; + if (attr.len != 8) + return -1; + out->tiebreaker = be64toh(*((u_int64_t *) attr.s)); out->controlled = 1; break; - case 0x802a: /* ice-controlling */ + + case STUN_ICE_CONTROLLING: + if (out->controlled) + return -1; + if (attr.len != 8) + return -1; + out->tiebreaker = be64toh(*((u_int64_t *) attr.s)); out->controlling = 1; break; - case 0x0024: /* priority */ + case STUN_PRIORITY: if (attr.len != 4) return -1; out->priority = ntohl(*((u_int32_t *) attr.s)); @@ -161,6 +185,33 @@ static int stun_attributes(struct stun_attrs *out, str *s, u_int16_t *unknowns) case 0x8022: /* software */ break; /* ignore but suppress warning message */ + case STUN_XOR_MAPPED_ADDRESS: + if (attr.len < 8) + return -1; + out->mapped_port = ntohs(*((u_int16_t *) (&attr.s[2]))) ^ (STUN_COOKIE >> 16); + if (attr.len == 8 && ntohs(*((u_int16_t *) attr.s)) == 1) + in4_to_6(&out->mapped_address, + ntohl(*((u_int32_t *) (&attr.s[4]))) ^ STUN_COOKIE); + else if (attr.len == 20 && ntohs(*((u_int16_t *) attr.s)) == 1) { + out->mapped_address.s6_addr32[0] + = *((u_int32_t *) (&attr.s[4])) ^ htonl(STUN_COOKIE); + out->mapped_address.s6_addr32[1] + = *((u_int32_t *) (&attr.s[8])) ^ req->transaction[0]; + out->mapped_address.s6_addr32[2] + = *((u_int32_t *) (&attr.s[12])) ^ req->transaction[1]; + out->mapped_address.s6_addr32[3] + = *((u_int32_t *) (&attr.s[16])) ^ req->transaction[2]; + } + break; + + case STUN_ERROR_CODE: + if (attr.len < 4) + return -1; + out->error_code = ntohl(*((u_int32_t *) attr.s)); + out->error_code = ((out->error_code & 0x700) >> 8) * 100 + + (out->error_code & 0x0ff); + break; + default: ilog(LOG_NOTICE, "Unknown STUN attribute: 0x%04x", type); if ((type & 0x8000)) @@ -231,13 +282,19 @@ INLINE void __output_add(struct msghdr *mh, struct tlv *tlv, unsigned int len, u __output_add(mh, &(attr)->tlv, sizeof(*(attr)), code, data, len) -static void output_finish(struct msghdr *mh, struct packet_stream *ps) { +static void __output_finish(struct msghdr *mh) { struct header *hdr; hdr = mh->msg_iov->iov_base; hdr->msg_len = htons(hdr->msg_len); - - stream_msg_mh_src(ps, mh); +} +//static void output_finish_ps(struct msghdr *mh, struct packet_stream *ps) { +// __output_finish(mh); +// stream_msg_mh_src(ps, mh); +//} +static void output_finish_src(struct msghdr *mh, const struct in6_addr *src) { + __output_finish(mh); + msg_mh_src(src, mh); } static void fingerprint(struct msghdr *mh, struct fingerprint *fp) { @@ -277,6 +334,9 @@ static void integrity(struct msghdr *mh, struct msg_integrity *mi, str *pwd) { struct iovec *iov; struct header *hdr; + if (!pwd || !pwd->s) + return; + output_add(mh, mi, STUN_MESSAGE_INTEGRITY); iov = mh->msg_iov; hdr = iov->iov_base; @@ -287,16 +347,18 @@ static void integrity(struct msghdr *mh, struct msg_integrity *mi, str *pwd) { hdr->msg_len = ntohs(hdr->msg_len); } -static void stun_error_len(struct packet_stream *ps, struct sockaddr_in6 *sin, struct header *req, +static void stun_error_len(struct packet_stream *ps, struct sockaddr_in6 *sin, struct in6_addr *dst, + struct header *req, int code, char *reason, int len, u_int16_t add_attr, void *attr_cont, int attr_len) { struct header hdr; struct error_code ec; + struct msg_integrity mi; struct fingerprint fp; struct generic aa; struct msghdr mh; - struct iovec iov[6]; /* hdr, ec, reason, aa, attr_cont, fp */ + struct iovec iov[7]; /* hdr, ec, reason, aa, attr_cont, mi, fp */ unsigned char buf[256]; output_init(&mh, iov, sin, &hdr, STUN_BINDING_ERROR_RESPONSE, req->transaction, buf, sizeof(buf)); @@ -306,17 +368,18 @@ static void stun_error_len(struct packet_stream *ps, struct sockaddr_in6 *sin, s if (attr_cont) output_add_data(&mh, &aa, add_attr, attr_cont, attr_len); + integrity(&mh, &mi, &ps->media->ice_agent->pwd[0]); fingerprint(&mh, &fp); - output_finish(&mh, ps); + output_finish_src(&mh, dst); sendmsg(ps->sfd->fd.fd, &mh, 0); } -#define stun_error(ps, sin, str, code, reason) \ - stun_error_len(ps, sin, str, code, reason "\0\0\0", strlen(reason), \ +#define stun_error(ps, sin, dst, req, code, reason) \ + stun_error_len(ps, sin, dst, req, code, reason "\0\0\0", strlen(reason), \ 0, NULL, 0) -#define stun_error_attrs(ps, sin, str, code, reason, type, content, len) \ - stun_error_len(ps, sin, str, code, reason "\0\0\0", strlen(reason), \ +#define stun_error_attrs(ps, sin, dst, req, code, reason, type, content, len) \ + stun_error_len(ps, sin, dst, req, code, reason "\0\0\0", strlen(reason), \ type, content, len) @@ -334,28 +397,35 @@ static int check_fingerprint(str *msg, struct stun_attrs *attrs) { return 0; } -static int check_auth(str *msg, struct stun_attrs *attrs, struct call_media *media) { +static int check_auth(str *msg, struct stun_attrs *attrs, struct call_media *media, int dst, int src) { u_int16_t lenX; char digest[20]; str ufrag[2]; struct iovec iov[3]; + struct ice_agent *ag; - if (!media->ice_ufrag.s || !media->ice_ufrag.len) + ag = media->ice_agent; + if (!ag) return -1; - if (!media->ice_pwd.s || !media->ice_pwd.len) + if (!ag->ufrag[dst].s || !ag->ufrag[dst].len) return -1; - - ufrag[0] = attrs->username; - str_chr_str(&ufrag[1], &ufrag[0], ':'); - if (!ufrag[1].s) + if (!ag->pwd[dst].s || !ag->pwd[dst].len) return -1; - ufrag[0].len -= ufrag[1].len; - str_shift(&ufrag[1], 1); - if (!ufrag[0].len || !ufrag[1].len) - return -1; - if (str_cmp_str(&ufrag[0], &media->ice_ufrag)) - return -1; + if (attrs->username.s) { + /* request */ + ufrag[dst] = attrs->username; + str_chr_str(&ufrag[src], &ufrag[dst], ':'); + if (!ufrag[src].s) + return -1; + ufrag[dst].len -= ufrag[src].len; + str_shift(&ufrag[src], 1); + + if (!ufrag[src].len || !ufrag[dst].len) + return -1; + if (str_cmp_str(&ufrag[dst], &ag->ufrag[dst])) + return -1; + } lenX = htons((attrs->msg_integrity_attr - msg->s) - 20 + 24); iov[0].iov_base = msg->s; @@ -365,13 +435,14 @@ static int check_auth(str *msg, struct stun_attrs *attrs, struct call_media *med iov[2].iov_base = msg->s + OFFSET_OF(struct header, cookie); iov[2].iov_len = ntohs(lenX) + - 24 + 20 - OFFSET_OF(struct header, cookie); - __integrity(iov, G_N_ELEMENTS(iov), &media->ice_pwd, digest); + __integrity(iov, G_N_ELEMENTS(iov), &ag->pwd[dst], digest); return memcmp(digest, attrs->msg_integrity.s, 20) ? -1 : 0; } +/* XXX way too many parameters being passed around here, unify into a struct */ static int stun_binding_success(struct packet_stream *ps, struct header *req, struct stun_attrs *attrs, - struct sockaddr_in6 *sin) + struct sockaddr_in6 *sin, struct in6_addr *dst) { struct header hdr; struct xor_mapped_address xma; @@ -398,10 +469,10 @@ static int stun_binding_success(struct packet_stream *ps, struct header *req, st output_add(&mh, &xma, STUN_XOR_MAPPED_ADDRESS); } - integrity(&mh, &mi, &ps->media->ice_pwd); + integrity(&mh, &mi, &ps->media->ice_agent->pwd[1]); fingerprint(&mh, &fp); - output_finish(&mh, ps); + output_finish_src(&mh, dst); sendmsg(ps->sfd->fd.fd, &mh, 0); return 0; @@ -415,20 +486,56 @@ INLINE int u_int16_t_arr_len(u_int16_t *arr) { } + #define SLF " from %s" #define SLP smart_ntop_port_buf(sin) +static int __stun_request(struct packet_stream *ps, struct sockaddr_in6 *sin, + struct in6_addr *dst, struct header *req, struct stun_attrs *attrs) +{ + int ret; + + ret = ice_request(ps, sin, dst, attrs); + + if (ret == -2) { + ilog(LOG_DEBUG, "ICE role conflict detected"); + stun_error(ps, sin, dst, req, 487, "Role conflict"); + return 0; + } + if (ret < 0) + return -1; + + ilog(LOG_DEBUG, "Successful STUN binding request" SLF, SLP); + stun_binding_success(ps, req, attrs, sin, dst); + + return ret; +} +static int __stun_success(struct packet_stream *ps, struct sockaddr_in6 *sin, + struct in6_addr *dst, struct header *req, struct stun_attrs *attrs) +{ + return ice_response(ps, sin, dst, attrs, req->transaction); +} +static int __stun_error(struct packet_stream *ps, struct sockaddr_in6 *sin, + struct in6_addr *dst, struct header *req, struct stun_attrs *attrs) +{ + return ice_response(ps, sin, dst, attrs, req->transaction); +} + + /* return values: * 0 = stun packet processed successfully * -1 = stun packet not processed, processing should continue as non-stun packet - * 1 = stun packet processed and "use candidate" was set + * 1 = stun packet processed and ICE has completed + * + * call is locked in R */ -int stun(str *b, struct packet_stream *ps, struct sockaddr_in6 *sin) { +int stun(str *b, struct packet_stream *ps, struct sockaddr_in6 *sin, struct in6_addr *dst) { struct header *req = (void *) b->s; int msglen, method, class; str attr_str; struct stun_attrs attrs; u_int16_t unknowns[UNKNOWNS_COUNT]; const char *err; + int dst_idx, src_idx; msglen = ntohs(req->msg_len); err = "message-length mismatch"; @@ -446,51 +553,107 @@ int stun(str *b, struct packet_stream *ps, struct sockaddr_in6 *sin) { attr_str.s = &b->s[20]; attr_str.len = b->len - 20; - if (stun_attributes(&attrs, &attr_str, unknowns)) { + if (stun_attributes(&attrs, &attr_str, unknowns, req)) { err = "failed to parse attributes"; if (unknowns[0] == 0xffff) goto ignore; ilog(LOG_WARNING, "STUN packet contained unknown " "\"comprehension required\" attribute(s)" SLF, SLP); - stun_error_attrs(ps, sin, req, 420, "Unknown attribute", + stun_error_attrs(ps, sin, dst, req, 420, "Unknown attribute", STUN_UNKNOWN_ATTRIBUTES, unknowns, u_int16_t_arr_len(unknowns) * 2); return 0; } - if (class != STUN_CLASS_REQUEST) - return -1; - err = "FINGERPRINT attribute missing"; if (!attrs.fingerprint_attr) goto ignore; - err = "USERNAME attribute missing"; - if (!attrs.username.s) - goto bad_req; err = "MESSAGE_INTEGRITY attribute missing"; if (!attrs.msg_integrity.s) goto bad_req; + if (class == STUN_CLASS_REQUEST) { + err = "USERNAME attribute missing"; + if (!attrs.username.s) + goto bad_req; + dst_idx = 1; + src_idx = 0; + } + else { + dst_idx = 0; + src_idx = 1; + } + err = "FINGERPRINT mismatch"; if (check_fingerprint(b, &attrs)) goto ignore; - if (check_auth(b, &attrs, ps->media)) + if (check_auth(b, &attrs, ps->media, dst_idx, src_idx)) goto unauth; - ilog(LOG_INFO, "Successful STUN binding request" SLF, SLP); - stun_binding_success(ps, req, &attrs, sin); - - return attrs.use ? 1 : 0; + switch (class) { + case STUN_CLASS_REQUEST: + return __stun_request(ps, sin, dst, req, &attrs); + case STUN_CLASS_SUCCESS: + return __stun_success(ps, sin, dst, req, &attrs); + case STUN_CLASS_ERROR: + return __stun_error(ps, sin, dst, req, &attrs); + default: + return -1; + } + /* notreached */ bad_req: ilog(LOG_NOTICE, "Received invalid STUN packet" SLF ": %s", SLP, err); - stun_error(ps, sin, req, 400, "Bad request"); + stun_error(ps, sin, dst, req, 400, "Bad request"); return 0; unauth: ilog(LOG_NOTICE, "STUN authentication mismatch" SLF, SLP); - stun_error(ps, sin, req, 401, "Unauthorized"); + stun_error(ps, sin, dst, req, 401, "Unauthorized"); return 0; ignore: ilog(LOG_NOTICE, "Not handling potential STUN packet" SLF ": %s", SLP, err); return -1; } + +int stun_binding_request(struct sockaddr_in6 *dst, u_int32_t transaction[3], str *pwd, + str ufrags[2], int controlling, u_int64_t tiebreaker, u_int32_t priority, + struct in6_addr *src, int fd, int to_use) +{ + struct header hdr; + struct msghdr mh; + struct iovec iov[8]; /* hdr, username x2, ice_controlled/ing, priority, uc, fp, mi */ + unsigned char buf[256]; + char username_buf[256]; + int i; + struct generic un_attr; + struct controlled_ing cc; + struct priority prio; + struct generic uc; + struct fingerprint fp; + struct msg_integrity mi; + + output_init(&mh, iov, dst, &hdr, STUN_BINDING_REQUEST, transaction, buf, sizeof(buf)); + + i = snprintf(username_buf, sizeof(username_buf), STR_FORMAT":"STR_FORMAT, + STR_FMT(&ufrags[0]), STR_FMT(&ufrags[1])); + if (i <= 0 || i >= sizeof(username_buf)) + return -1; + output_add_data(&mh, &un_attr, STUN_USERNAME, username_buf, i); + + cc.tiebreaker = htobe64(tiebreaker); + output_add(&mh, &cc, controlling ? STUN_ICE_CONTROLLING : STUN_ICE_CONTROLLED); + + prio.priority = htonl(priority); + output_add(&mh, &prio, STUN_PRIORITY); + + if (to_use) + output_add(&mh, &uc, STUN_USE_CANDIDATE); + + integrity(&mh, &mi, pwd); + fingerprint(&mh, &fp); + + output_finish_src(&mh, src); + sendmsg(fd, &mh, 0); + + return 0; +} diff --git a/daemon/stun.h b/daemon/stun.h index c0e1b14b8..9a52336fb 100644 --- a/daemon/stun.h +++ b/daemon/stun.h @@ -4,6 +4,7 @@ #include #include +#include #include "compat.h" #include "call.h" #include "str.h" @@ -12,6 +13,24 @@ #define STUN_COOKIE 0x2112A442UL + +struct stun_attrs { + str username; + char *msg_integrity_attr; + str msg_integrity; + u_int32_t priority; + char *fingerprint_attr; + u_int32_t fingerprint; + u_int64_t tiebreaker; + struct in6_addr mapped_address; + unsigned int mapped_port; /* XXX use struct endpoint */ + unsigned int error_code; + int use:1, + controlled:1, + controlling:1; +}; + + INLINE int is_stun(const str *s) { const unsigned char *b = (const void *) s->s; const u_int32_t *u; @@ -30,7 +49,10 @@ INLINE int is_stun(const str *s) { } -int stun(str *, struct packet_stream *, struct sockaddr_in6 *); +int stun(str *, struct packet_stream *, struct sockaddr_in6 *, struct in6_addr *); +int stun_binding_request(struct sockaddr_in6 *dst, u_int32_t transaction[3], str *pwd, + str ufrags[2], int controlling, u_int64_t tiebreaker, u_int32_t priority, + struct in6_addr *src, int fd, int); #endif diff --git a/tests/stun-client b/tests/stun-client new file mode 100755 index 000000000..72c2dd881 --- /dev/null +++ b/tests/stun-client @@ -0,0 +1,91 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Socket; +use Socket6; +use Digest::SHA qw(hmac_sha1); +use Digest::CRC qw(crc32); + +my ($prio, $ip, $port, $username, $pwd) = @ARGV; + +my $fd; +my @dests = getaddrinfo($ip, $port, AF_UNSPEC, SOCK_DGRAM); +while (@dests >= 5) { + my ($fam, $type, $prot, $addr, $canon, @dests) = @dests; + socket($fd, $fam, $type, $prot) or undef($fd), next; + connect($fd, $addr) or undef($fd), next; + last; +} +$fd or die($!); + +my @rand = ('A' .. 'Z', 'a' .. 'z'); +my $ufrag = join('', (map {$rand[rand($#rand)]} (1 .. 10))); +my $tract = join('', (map {$rand[rand($#rand)]} (1 .. 12))); +my $control = rand() < .5; +my $tbreak = int(rand(0xffffffff)) * int(rand(0xffffffff)); + +print("transaction: $tract\n"); +print("my username fragment: $ufrag\n"); +print("controll".($control?"ing":'ed')."\n"); +print("tie breaker: $tbreak\n"); + + +my $packet = ''; +$packet .= attr(6, "$username:$ufrag"); +$packet .= attr($control ? 0x802a : 0x8029, pack('Q', $tbreak)); +$packet .= attr(0x24, pack('N', $prio)); +$packet .= integrity(); +$packet .= fingerprint(); +$packet = header() . $packet; + +send($fd, $packet, 0) or die $!; +my $buf; +recv($fd, $buf, 200, 0) or die; + +my ($code, $length, $cookie, $tract2, $attrs) = unpack('nnN a12 a*', $buf); + +if ($cookie == 0x2112A442 || $tract2 ne $tract) { + printf("code: \%x\n", $code); + while ($attrs ne '') { + my ($type, $len, $cont); + ($type, $len, $attrs) = unpack('nn a*', $attrs); + my $pad = 0; + while ((($len + $pad) % 4) != 0) { + $pad++; + } + ($cont, $pad, $attrs) = unpack("a$len a$pad a*", $attrs); + printf(" attr type: \%x\n", $type); + print(" content: $cont\n"); + } +} +else { + print("not stun: ".unpack('H*', $buf)."\n"); +} + +exit; + + +sub attr { + my ($type, $data) = @_; + my $len = length($data); + while ((length($data) % 4) != 0) { + $data .= "\0"; + } + return pack('nn a*', $type, $len, $data); +} +sub header { + my ($add_length) = @_; + $add_length ||= 0; + return pack('nnN a12', 1, length($packet) + $add_length, 0x2112A442, $tract); +} +sub integrity { + my $h = header(24); + my $hmac = hmac_sha1($h.$packet, $pwd); + return attr(8, $hmac); +} +sub fingerprint { + my $h = header(8); + my $crc = crc32($h.$packet); + return attr(0x8028, pack('N', ($crc ^ 0x5354554e))); +} diff --git a/tests/stun-server b/tests/stun-server new file mode 100755 index 000000000..b4446774b --- /dev/null +++ b/tests/stun-server @@ -0,0 +1,204 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Socket; +use Socket6; +use Digest::SHA qw(hmac_sha1); +use Digest::CRC qw(crc32); + +my ($controlling, $port, $username, $pwd, @addresses) = @ARGV; + +my %attrs = ( + 6 => \&attr_un, + 8 => \&attr_mi, + 0x8028 => \&attr_fp, + 0x25 => \&attr_use, + 0x8029 => \&attr_controlled, + 0x802a => \&attr_controlling, +); + +my (@sockets, $packet, $tract, $code); + +for my $addr (@addresses) { + my ($fam, $pka, $sin, $meth) = addrparse($addr, $port); + socket(my $fd, $fam, SOCK_DGRAM, 0) or die $!; + bind($fd, $sin) or die $!; + push(@sockets, {fd => $fd, fam => $fam, addr => $pka, sin => $sin, xormethod => $meth}); +} + +while (1) { + my $rin = ''; + for my $s (@sockets) { + vec($rin, fileno($$s{fd}), 1) = 1; + } + + select($rin, undef, undef, 1); + + for my $s (@sockets) { + vec($rin, fileno($$s{fd}), 1) or next; + + my $src = recv($$s{fd}, my $buf, 200, 0) or die $!; + print("\npacket from " . addrdeparse($$s{fam}, $src) . " on ". + addrdeparse($$s{fam}, $$s{sin}) . "\n"); + + my ($cmd, $len, $cookie, $attrs); + ($cmd, $len, $cookie, $tract, $attrs) = unpack('nnN a12 a*', $buf); + + if ($cookie != 0x2112A442) { + if ($buf =~ /^[\x14-\x3f]/s) { + print("DTLS\n"); + } + else { + print("not stun: " . unpack("H*", $buf)."\n"); + } + next; + } + $cmd == 1 or print("not stun request\n"), next; + length($attrs) == $len or print("length mismatch\n"), next; + + my ($list, $hash) = unpack_attrs($attrs); + + $$list[$#$list]{name} eq 'fingerprint' or print("last attr not fingerprint\n"), next; + $$list[$#$list-1]{name} eq 'message-integrity' or print("last but one attr not MI\n"), next; + $$hash{username} or print("no username\n"), next; + + $$hash{controlling} and print("is controlling\n"); + $$hash{controlled} and print("is controlled\n"); + $$hash{'use-candidate'} and print("nominated\n"); + + print("local username is $$hash{username}{split}[0], remote is $$hash{username}{split}[1]\n"); + + if ($$hash{controlling} && $controlling || $$hash{controlled} && !$controlling) { + print("role conflict, replying with 487"); + $code = 0x0111; # binding error + $packet = attr(0x9, pack('CCCC a16', 0, 0, 4, 87, 'Role conflict')); + $packet .= integrity(); + $packet .= fingerprint(); + } + else { + $code = 0x101; # binding success + my $xorattr = $$s{xormethod}($src); + $packet = attr(0x20, $xorattr); + $packet .= integrity(); + $packet .= fingerprint(); + } + + $packet = header() . $packet; + + print("sending reply\n"); + send($$s{fd}, $packet, 0, $src); + } +} + + + +exit; + +sub xor4 { + my ($src) = @_; + my @a = unpack_sockaddr_in($src); + return pack('nna4', 1, $a[0] ^ 0x2112, $a[1] ^ "\x21\x12\xa4\x42"); +} +sub xor6 { + my ($src) = @_; + my @a = unpack_sockaddr_in6($src); + return pack('nna16', 2, $a[0] ^ 0x2112, + $a[1] ^ ("\x21\x12\xa4\x42" . $tract)); +} +sub addrparse { + my ($addr, $port) = @_; + my $r = inet_pton(AF_INET, $addr); + $r and return (AF_INET, $r, pack_sockaddr_in($port, $r), \&xor4); + $r = inet_pton(AF_INET6, $addr); + $r and return (AF_INET6, $r, pack_sockaddr_in6($port, $r), \&xor6); + die; +} +sub addrdeparse { + my ($fam, $sin) = @_; + if ($fam == AF_INET) { + my @up = unpack_sockaddr_in($sin); + return inet_ntop(AF_INET, $up[1]) . ":$up[0]"; + } + if ($fam == AF_INET6) { + my @up = unpack_sockaddr_in6($sin); + return '['.inet_ntop(AF_INET6, $up[1]) . "]:$up[0]"; + } + die; +} + +sub attr_un { + my ($cont) = @_; + return {name => 'username', split => [$cont =~ /(.*):(.*)/]}; +} +sub attr_mi { + return {name => 'message-integrity'}; +} +sub attr_fp { + my ($cont) = @_; + return {name => 'fingerprint', value => unpack('N', $cont)}; +} +sub attr_use { + return {name => 'use-candidate'}; +} +sub attr_controlling { + my ($cont) = @_; + return {name => 'controlling', tiebreaker => unpack('Q', $cont)}; +} +sub attr_controlled { + my ($cont) = @_; + return {name => 'controlled', tiebreaker => unpack('Q', $cont)}; +} + + +sub unpack_attrs { + my ($s) = @_; + + my (@out, %out); + while ($s ne '') { + my ($type, $len, $cont); + ($type, $len, $s) = unpack('nn a*', $s); + my $pad = 0; + while ((($len + $pad) % 4) != 0) { + $pad++; + } + ($cont, $pad, $s) = unpack("a$len a$pad a*", $s); + my $ins = {type => $type, len => $len, content => $cont, + raw => pack('nna*a*', $type, $len, $cont, $pad)}; + push(@out, $ins); + $out{$type} = $ins; + + my $pars = $attrs{$type}; + $pars or next; + + my $ai = $pars->($cont); + %$ins = (%$ins, %$ai); + $out{$$ins{name}} = $ins; + } + return (\@out, \%out); +} + + +sub attr { + my ($type, $data) = @_; + my $len = length($data); + while ((length($data) % 4) != 0) { + $data .= "\0"; + } + return pack('nn a*', $type, $len, $data); +} +sub header { + my ($add_length) = @_; + $add_length ||= 0; + return pack('nnN a12', $code, length($packet) + $add_length, 0x2112A442, $tract); +} +sub integrity { + my $h = header(24); + my $hmac = hmac_sha1($h.$packet, $pwd); + return attr(8, $hmac); +} +sub fingerprint { + my $h = header(8); + my $crc = crc32($h.$packet); + return attr(0x8028, pack('N', ($crc ^ 0x5354554e))); +} From 98e0b6dea525f9c5939c279ce66fe458833f67fe Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 27 Feb 2015 09:58:38 -0500 Subject: [PATCH 43/82] update for redis plugin --- daemon/call.c | 3 +-- daemon/call.h | 1 + daemon/main.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index 0f90b4c89..f880b639e 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -326,7 +326,6 @@ static void __stream_unconfirm(struct packet_stream *ps); static void stream_unconfirm(struct packet_stream *ps); static void __monologue_destroy(struct call_monologue *monologue); static struct interface_address *get_interface_address(struct local_interface *lif, int family); -static struct interface_address *get_any_interface_address(struct local_interface *lif, int family); @@ -3549,7 +3548,7 @@ static struct interface_address *get_interface_address(struct local_interface *l } /* safety fallback */ -static struct interface_address *get_any_interface_address(struct local_interface *lif, int family) { +struct interface_address *get_any_interface_address(struct local_interface *lif, int family) { struct interface_address *ifa; ifa = get_interface_address(lif, family); diff --git a/daemon/call.h b/daemon/call.h index 3ca148c14..390fa2cd8 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -506,6 +506,7 @@ INLINE struct interface_address *get_interface_from_address(struct local_interfa { return g_hash_table_lookup(lif->addr_hash, addr); } +struct interface_address *get_any_interface_address(struct local_interface *lif, int family); const struct transport_protocol *transport_protocol(const str *s); diff --git a/daemon/main.c b/daemon/main.c index 97b835c33..699e6cbab 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -31,7 +31,7 @@ -#define REDIS_MODULE_VERSION "redis/7" +#define REDIS_MODULE_VERSION "redis/8" From 0e4e6f2f3cadd0852a8205459af0a433747be1c8 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 27 Feb 2015 11:57:55 -0500 Subject: [PATCH 44/82] revamp and move timeval functions --- daemon/aux.h | 43 ++++++++++++++++++++++++++++++++++++++++++ daemon/call.c | 52 --------------------------------------------------- daemon/call.h | 13 ------------- daemon/ice.c | 4 ++-- 4 files changed, 45 insertions(+), 67 deletions(-) diff --git a/daemon/aux.h b/daemon/aux.h index 4b0643349..80a731457 100644 --- a/daemon/aux.h +++ b/daemon/aux.h @@ -782,4 +782,47 @@ INLINE void atomic64_local_copy_zero(atomic64 *dst, atomic64 *src) { +/*** TIMEVAL FUNCTIONS ***/ + +INLINE long long timeval_ms(const struct timeval *t) { + return (long long) ((long long) t->tv_sec * 1000000LL) + t->tv_usec; +} +INLINE void timeval_from_ms(struct timeval *t, long long ms) { + t->tv_sec = ms/1000000LL; + t->tv_usec = ms%1000000LL; +} +INLINE long long timeval_diff(const struct timeval *a, const struct timeval *b) { + return timeval_ms(a) - timeval_ms(b); +} +INLINE void timeval_subtract(struct timeval *result, const struct timeval *a, const struct timeval *b) { + timeval_from_ms(result, timeval_diff(a, b)); +} +INLINE void timeval_multiply(struct timeval *result, const struct timeval *a, const long multiplier) { + timeval_from_ms(result, timeval_ms(a) * multiplier); +} +INLINE void timeval_divide(struct timeval *result, const struct timeval *a, const long divisor) { + timeval_from_ms(result, timeval_ms(a) / divisor); +} +INLINE void timeval_add(struct timeval *result, const struct timeval *a, const struct timeval *b) { + timeval_from_ms(result, timeval_ms(a) + timeval_ms(b)); +} +INLINE void timeval_add_usec(struct timeval *tv, long usec) { + timeval_from_ms(tv, timeval_ms(tv) + usec); +} +INLINE int timeval_cmp(const struct timeval *a, const struct timeval *b) { + long long diff; + diff = timeval_diff(a, b); + if (diff < 0) + return -1; + if (diff > 0) + return 1; + return 0; +} +INLINE void timeval_lowest(struct timeval *l, const struct timeval *n) { + if (!n->tv_sec) + return; + if (!l->tv_sec || timeval_cmp(l, n) == 1) + *l = *n; +} + #endif diff --git a/daemon/call.c b/daemon/call.c index f880b639e..ceb1258bf 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -2561,58 +2561,6 @@ static void __unkernelize(struct packet_stream *p) { PS_CLEAR(p, KERNELIZED); } -/* XXX move these somewhere else */ -u_int64_t timeval_diff(const struct timeval *a, const struct timeval *b) { - u_int64_t microseconds; - microseconds = ((u_int64_t)a->tv_sec - (u_int64_t)b->tv_sec) * 1000000LLU + (a->tv_usec - b->tv_usec); - return microseconds; -} -void timeval_subtract (struct timeval *result, const struct timeval *a, const struct timeval *b) { - u_int64_t microseconds; - microseconds = timeval_diff(a, b); - result->tv_sec = microseconds/1000000LLU; - result->tv_usec = microseconds%1000000LLU; -} - -void timeval_multiply(struct timeval *result, const struct timeval *a, const long multiplier) { - u_int64_t microseconds; - microseconds = (((u_int64_t)a->tv_sec * 1000000LLU) + a->tv_usec) * multiplier; - result->tv_sec = microseconds/1000000LLU; - result->tv_usec = microseconds%1000000LLU; -} - -void timeval_divide(struct timeval *result, const struct timeval *a, const long divisor) { - u_int64_t microseconds; - microseconds = (((u_int64_t)a->tv_sec * 1000000LLU) + a->tv_usec) / divisor; - result->tv_sec = microseconds/1000000LLU; - result->tv_usec = microseconds%1000000LLU; -} - -void timeval_add(struct timeval *result, const struct timeval *a, const struct timeval *b) { - u_int64_t microseconds; - microseconds = ((u_int64_t)a->tv_sec + (u_int64_t)b->tv_sec) * 1000000LLU + (a->tv_usec + b->tv_usec); - result->tv_sec = microseconds/1000000LLU; - result->tv_usec = microseconds%1000000LLU; -} -void timeval_add_usec(struct timeval *tv, long usec) { - struct timeval a; - a.tv_sec = usec / 1000000LLU; - a.tv_usec = usec % 1000000LLU; - timeval_add(tv, tv, &a); -} - -int timeval_cmp(const struct timeval *a, const struct timeval *b) { - if (a->tv_sec < b->tv_sec) - return -1; - if (a->tv_sec > b->tv_sec) - return 1; - if (a->tv_usec < b->tv_usec) - return -1; - if (a->tv_usec > b->tv_usec) - return 1; - return 0; -} - static void timeval_totalstats_average_add(struct totalstats *s, const struct timeval *add) { struct timeval dp, oa; diff --git a/daemon/call.h b/daemon/call.h index 390fa2cd8..3849308ba 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -510,19 +510,6 @@ struct interface_address *get_any_interface_address(struct local_interface *lif, const struct transport_protocol *transport_protocol(const str *s); -void timeval_subtract (struct timeval *result, const struct timeval *a, const struct timeval *b); -void timeval_multiply(struct timeval *result, const struct timeval *a, const long multiplier); -void timeval_divide(struct timeval *result, const struct timeval *a, const long divisor); -void timeval_add(struct timeval *result, const struct timeval *a, const struct timeval *b); -int timeval_cmp(const struct timeval *a, const struct timeval *b); -void timeval_add_usec(struct timeval *tv, long usec); -u_int64_t timeval_diff(const struct timeval *a, const struct timeval *b); -INLINE void timeval_lowest(struct timeval *l, const struct timeval *n) { - if (!n->tv_sec) - return; - if (!l->tv_sec || timeval_cmp(l, n) == 1) - *l = *n; -} INLINE void *call_malloc(struct call *c, size_t l) { diff --git a/daemon/ice.c b/daemon/ice.c index 9af547a33..dd5c09495 100644 --- a/daemon/ice.c +++ b/daemon/ice.c @@ -493,7 +493,7 @@ static void __agent_schedule(struct ice_agent *ag, unsigned long usec) { } static void __agent_schedule_abs(struct ice_agent *ag, const struct timeval *tv) { struct timeval nxt; - unsigned long diff; + long long diff; nxt = *tv; @@ -1264,7 +1264,7 @@ err: void ice_thread_run(void *p) { struct ice_agent *ag; struct call *call; - unsigned long sleeptime; + long long sleeptime; struct timeval tv; mutex_lock(&ice_agents_timers_lock); From 26137d2b4d2e4b4eed5027b1f772451fc987f4f7 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 27 Feb 2015 12:09:35 -0500 Subject: [PATCH 45/82] move thread loop functions out of main.c --- daemon/call.h | 3 +++ daemon/graphite.c | 14 ++++++++++++++ daemon/graphite.h | 1 + daemon/main.c | 34 ++++------------------------------ daemon/poller.c | 38 ++++++++++++++++++++------------------ daemon/poller.h | 3 ++- 6 files changed, 44 insertions(+), 49 deletions(-) diff --git a/daemon/call.h b/daemon/call.h index 3849308ba..f70d7e032 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -431,6 +431,9 @@ struct callmaster_config { char *b2b_url; unsigned char default_tos; enum xmlrpc_format fmt; + u_int32_t graphite_ip; + u_int16_t graphite_port; + int graphite_interval; }; struct callmaster { diff --git a/daemon/graphite.c b/daemon/graphite.c index c571bcf8d..efba17e4e 100644 --- a/daemon/graphite.c +++ b/daemon/graphite.c @@ -157,3 +157,17 @@ void graphite_loop_run(struct callmaster* callmaster, int seconds) { sleep: usleep(100000); } + +void graphite_loop(void *d) { + struct callmaster *cm = d; + + if (!cm->conf.graphite_interval) { + ilog(LOG_WARNING,"Graphite send interval was not set. Setting it to 1 second."); + cm->conf.graphite_interval=1; + } + + connect_to_graphite_server(cm->conf.graphite_ip,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 0b51e7b9a..f00532a8f 100644 --- a/daemon/graphite.h +++ b/daemon/graphite.h @@ -13,5 +13,6 @@ 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 graphite_loop(void *d); #endif /* GRAPHITE_H_ */ diff --git a/daemon/main.c b/daemon/main.c index 699e6cbab..99afb65f1 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -564,6 +564,9 @@ no_kernel: mc.default_tos = tos; mc.b2b_url = b2b_url; mc.fmt = xmlrpc_fmt; + mc.graphite_port = graphite_port; + mc.graphite_ip = graphite_ip; + mc.graphite_interval = graphite_interval; ct = NULL; if (listenport) { @@ -623,35 +626,6 @@ no_kernel: die("Refusing to continue without working Redis database"); } -/* XXX move loop functions */ -static void timer_loop(void *d) { - struct poller *p = d; - - while (!g_shutdown) - poller_timers_wait_run(p, 100); -} - -static void graphite_loop(void *d) { - struct callmaster *cm = d; - - if (!graphite_interval) { - ilog(LOG_WARNING,"Graphite send interval was not set. Setting it to 1 second."); - graphite_interval=1; - } - - connect_to_graphite_server(graphite_ip,graphite_port); - - while (!g_shutdown) - graphite_loop_run(cm,graphite_interval); // time in seconds -} - -static void poller_loop(void *d) { - struct poller *p = d; - - while (!g_shutdown) - poller_poll(p, 100); -} - int main(int argc, char **argv) { struct main_context ctx; int idx=0; @@ -663,7 +637,7 @@ int main(int argc, char **argv) { ilog(LOG_INFO, "Startup complete, version %s", RTPENGINE_VERSION); thread_create_detach(sighandler, NULL); - thread_create_detach(timer_loop, ctx.p); + 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/poller.c b/daemon/poller.c index 5d0830a6d..d437e0f4f 100644 --- a/daemon/poller.c +++ b/daemon/poller.c @@ -474,28 +474,30 @@ int poller_add_timer(struct poller *p, void (*f)(void *), struct obj *o) { } /* run in thread separate from poller_poll() */ -void poller_timers_wait_run(struct poller *p, int max) { +void poller_timer_loop(void *d) { + struct poller *p = d; struct timeval tv; int wt; - int i = 0; - max *= 1000; + while (!g_shutdown) { + gettimeofday(&tv, NULL); + if (tv.tv_sec != poller_now) + goto now; -retry: - gettimeofday(&tv, NULL); - if (tv.tv_sec != poller_now) - goto now; - if (i) - return; - - wt = 1000000 - tv.tv_usec; - if (max >= 0 && max < wt) - wt = max; - usleep(wt); - i = 1; - goto retry; + wt = 1000000 - tv.tv_usec; + wt = MIN(wt, 100000); + usleep(wt); + continue; now: - gettimeofday(&g_now, NULL); - poller_timers_run(p); + gettimeofday(&g_now, NULL); + poller_timers_run(p); + } +} + +void poller_loop(void *d) { + struct poller *p = d; + + while (!g_shutdown) + poller_poll(p, 100); } diff --git a/daemon/poller.h b/daemon/poller.h index 0bc6d9485..41a29e751 100644 --- a/daemon/poller.h +++ b/daemon/poller.h @@ -43,7 +43,8 @@ int poller_isblocked(struct poller *, int); void poller_error(struct poller *, int); int poller_poll(struct poller *, int); -void poller_timers_wait_run(struct poller *, int max); +void poller_timer_loop(void *); +void poller_loop(void *); int poller_add_timer(struct poller *, void (*)(void *), struct obj *); int poller_del_timer(struct poller *, void (*)(void *), struct obj *); From 5fca71d4b08996bd4f991583879ef12ddf9fcdfb Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Mon, 2 Mar 2015 09:48:38 -0500 Subject: [PATCH 46/82] fix segfault when parsing server reflexive ICE candidates fixes #78 --- daemon/sdp.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/daemon/sdp.c b/daemon/sdp.c index 84e71241e..21af6d49f 100644 --- a/daemon/sdp.c +++ b/daemon/sdp.c @@ -84,7 +84,9 @@ struct attribute_candidate { str port_str; str typ_str; str type_str; + str raddr_str; str related_address_str; + str rport_str; str related_port_str; struct ice_candidate cand_parsed; @@ -618,6 +620,16 @@ static int parse_attribute_candidate(struct sdp_attribute *output) { if (!ice_has_related(c->cand_parsed.type)) goto done; + EXTRACT_TOKEN(u.candidate.raddr_str); + EXTRACT_TOKEN(u.candidate.related_address_str); + EXTRACT_TOKEN(u.candidate.rport_str); + EXTRACT_TOKEN(u.candidate.related_port_str); + + if (str_cmp(&c->raddr_str, "raddr")) + return -1; + if (str_cmp(&c->rport_str, "rport")) + return -1; + if (__parse_address(&c->cand_parsed.related_address, NULL, NULL, &c->related_address_str)) return 0; From 506076b3b7dc0b730cebd69bb26409bac2e1f5b1 Mon Sep 17 00:00:00 2001 From: Eric Tamme Date: Tue, 10 Mar 2015 14:21:19 -0600 Subject: [PATCH 47/82] add raddr and rport on all non host type candidates per rfc5245 section-15.1 --- daemon/sdp.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/daemon/sdp.c b/daemon/sdp.c index 21af6d49f..20ef59320 100644 --- a/daemon/sdp.c +++ b/daemon/sdp.c @@ -1464,6 +1464,20 @@ static int insert_ice_address(struct sdp_chopper *chop, struct packet_stream *ps return 0; } +static int insert_raddr_rport(struct sdp_chopper *chop, struct packet_stream *ps, struct interface_address *ifa) { + char buf[64]; + int len; + + chopper_append_c(chop, " raddr "); + call_stream_address46(buf, ps, SAF_ICE, &len, ifa); + chopper_append_dup(chop, buf, len); + chopper_append_c(chop, " rport "); + chopper_append_printf(chop, "%hu", ps->sfd->fd.localport); + + return 0; +} + + static int replace_network_address(struct sdp_chopper *chop, struct network_address *address, struct packet_stream *ps, struct sdp_ng_flags *flags) { @@ -1704,6 +1718,9 @@ static void insert_candidate(struct sdp_chopper *chop, struct packet_stream *ps, insert_ice_address(chop, ps, ifa); chopper_append_c(chop, " typ "); chopper_append_c(chop, ice_candidate_type_str(type)); + /* raddr and rport are required for non-host candidates: rfc5245 section-15.1 */ + if(type != ICT_HOST) + insert_raddr_rport(chop, ps, ifa); chopper_append_c(chop, "\r\n"); } From 3520617364f478de23e19c93f54db58135a4bd40 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Thu, 12 Mar 2015 11:04:48 -0400 Subject: [PATCH 48/82] remove ice-lite mention from readme --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 9727cef63..fa0129a88 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,6 @@ the following additional features are available: + Bridging between ICE-enabled and ICE-unaware user agents + Optionally acting only as additional ICE relay/candidate + Optionally forcing relay of media streams by removing other ICE candidates - + Supports ice-lite only - SRTP (RFC 3711) support: + Support for SDES (RFC 4568) and DTLS-SRTP (RFC 5764) + AES-CM and AES-F8 ciphers, both in userspace and in kernel From 95935194838dc895d35ef2d828c2a3a50d3e34d0 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Mon, 16 Mar 2015 08:50:05 -0400 Subject: [PATCH 49/82] Leave unsupported (non-RTP) protocols alone when translation is requested Fixes #80 --- daemon/call.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index ceb1258bf..d67ad1370 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -2434,11 +2434,14 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, * but also lets endpoints re-negotiate. */ media->protocol = NULL; } + /* default is to leave the protocol unchanged */ + if (!media->protocol) + media->protocol = other_media->protocol; /* allow override of outgoing protocol even if we know it already */ - if (flags && flags->transport_protocol) + /* but only if this is an RTP-based protocol */ + if (flags && flags->transport_protocol + && other_media->protocol && other_media->protocol->rtp) media->protocol = flags->transport_protocol; - else if (!media->protocol) - media->protocol = other_media->protocol; /* copy parameters advertised by the sender of this message */ bf_copy_same(&other_media->media_flags, &sp->sp_flags, From 2103f4137d855c5e25cdd191927b75b5fbbff090 Mon Sep 17 00:00:00 2001 From: Jenkins User Date: Wed, 18 Mar 2015 14:15:06 +0100 Subject: [PATCH 50/82] Release new version 3.3.0.0+0~mr4.0.0.0 --- debian/changelog | 90 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/debian/changelog b/debian/changelog index 2983b8e55..ffa836f4b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,93 @@ +ngcp-rtpengine (3.3.0.0+0~mr4.0.0.0) unstable; urgency=low + + [ Sergey Lavrov ] + * [bb14eba] Fix %post/%preun directives. + * [fe5e129] Added rtpengine-ctl and dependency to spec file. + + [ Richard Fuchs ] + * [a0068f4] relax sdes key lifetime validation check + * [caba5be] MT#10583 remember the "created from" address in raw format + * [6e32379] MT#10583 support ipv6 control connection in simulator + * [02d27d5] MT#10583 support b2bua callback to sip proxy address + * [43cd3f5] reset other side's crypto params only in passthru mode + * [2af682b] use SSL_CTX_set_read_ahead to fix for openssl 1.0.1k + * [e24253a] move parse_ip(6)_port into aux.h + * [a81588e] provide convenience function get_log_level() + * [37d98ad] dump DTLS cert and keys + * [57c0a84] add locking to totalstats + * [f544471] fix compiler warnings + * [36c7141] change control_ng_stats into a hash and use locking and atomic ops + * [c0b2f3d] fix graphite code not to leak fds in error cases + * [965d989] static'ize graphite global vars + * [7175a26] decrease sleep time of graphite loop + * [3882285] decrease calls to time() in graphite code + * [de37ac0] use TLS buffer array for temporary strings + * [ee4f2d2] convert ilog() into macro + * [411a888] use atomic ops for stats + * [28c6db9] fix references and locking in CLI + * [20fd255] use UINT64F instead of %llu where appropriate + * [3bc1672] more atomic stats, and fix average length calc + * [fedaadd] remove superfluous \n from log messages + * [1e3f06a] split some perl SRTP stuff into module and add debug script + * [deba231] rename an old MP_ variable to RE_ + * [fb9ed6e] use trial & error approach to guess ROC in case of SRTP auth failures + * [f6dee07] fix initial seqnum in simulator + * [787d90f] rename atomic_uint64 to atomic64 for brevity + * [49328cd] implement atomic64 for non-64-bit platforms + * [da6b6a1] fix compiler warning on 32bit + * [ca149e6] "long" can be 32 bits only and may truncate math + * [b023d6c] update redis mod version to 7 + * [adeb0b3] the log rate limiter must take the prefix into account + * [fb667a8] turn tag and termination texts into array lookups + * [d79c68f] remove some unnecessary memsets + * [a5f7315] string handling fixes and improvements + * [037c6aa] shorter locking in stats loop + * [f7a1594] more descriptive warning for failed kernelizing + * [5fa9902] atomic vars should be volatile + * [c21193a] fix byte stats in kernel module + * [c2ff5c3] MT#9935 understand, learn, remember and report RTP payload types (codecs) + * [b586fa7] fix memory leak + * [65b3f66] use atomic64_t for kernel stats + * [4a97bb1] attempt to work around trickle ice + * [b44bb28] segfault fix + * [015b2a9] legacy tcp protocol fix + * [016f8b3] Rework logging a bit (primarily in NG) + * [91a85e6] fix for skewed stats + * [6173a7a] implement atomic bitfield ops + * [1f7c8a2] remove obsolete HAS_HANDLER flag + * [f224bab] segfault fix if no rtpmap attribute is present + * [26ca844] drop support for glib versions older than 2.30 + * [b83e80c] add makefile dependency to itself + * [956d07d] fix erroneously reported 0.0.0.0 address when bridging from ipv6 + * [f99d6d4] implement full ICE support + * [98e0b6d] update for redis plugin + * [0e4e6f2] revamp and move timeval functions + * [26137d2] move thread loop functions out of main.c + * [5fca71d] fix segfault when parsing server reflexive ICE candidates + * [3520617] remove ice-lite mention from readme + * [9593519] Leave unsupported (non-RTP) protocols alone when translation is requested + + [ Eric Tamme ] + * [b8b17bf] clear crypto for BOTH legs when in passthrough mode + * [506076b] add raddr and rport on all non host type candidates per rfc5245 section-15.1 + + [ Carsten Bock ] + * [9a2da87] Fix randomness source for key generation. + + [ Frederic-Philippe Metz ] + * [2ae0e35] Added functionality to report statistics to graphite + * [7446822] Forgot the files .. + * [61a72b1] Added some more statistics. + * [0ccb11a] Added statistics on control ng interface. + * [794709f] Changed retry behaviour for connecting to graphite server. + + [ Michael Prokop ] + * [fca4500] MT#9127 Bump Standards-Version to 3.9.6 + + [ Sipwise Jenkins Builder ] + + -- Sipwise Jenkins Builder Wed, 18 Mar 2015 14:15:06 +0100 + ngcp-rtpengine (3.3.0.0+0~mr3.8.0.0) unstable; urgency=low [ Richard Fuchs ] From 2db33efeccc3ebf05a44c6255f92dae19785a7e4 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Wed, 18 Mar 2015 09:23:13 -0400 Subject: [PATCH 51/82] reset ICE/STUN retransmit data on triggered checks fixes #84 --- daemon/ice.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/daemon/ice.c b/daemon/ice.c index dd5c09495..8119da3e6 100644 --- a/daemon/ice.c +++ b/daemon/ice.c @@ -559,6 +559,7 @@ void ice_init(void) { static void __fail_pair(struct ice_candidate_pair *pair) { ilog(LOG_DEBUG, "Setting ICE candidate pair "PAIR_FORMAT" as failed", PAIR_FMT(pair)); PAIR_SET(pair, FAILED); + PAIR_CLEAR(pair, IN_PROGRESS); } /* agent must NOT be locked, but call must be locked in R */ @@ -869,7 +870,9 @@ static void __trigger_check(struct ice_candidate_pair *pair) { ilog(LOG_DEBUG, "Triggering check for "PAIR_FORMAT, PAIR_FMT(pair)); mutex_lock(&ag->lock); - PAIR_CLEAR(pair, FAILED); + pair->retransmits = 0; + if (PAIR_CLEAR(pair, FAILED)) + PAIR_CLEAR(pair, IN_PROGRESS); if (ag->triggered.length < 4 * MAX_ICE_CANDIDATES && !PAIR_SET(pair, TRIGGERED)) g_queue_push_tail(&ag->triggered, pair); mutex_unlock(&ag->lock); From 20ac7cd5f7da6d26cfdb0d6b489812eee09fd68a Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 20 Mar 2015 11:49:57 -0400 Subject: [PATCH 52/82] process nominating ICE request only once per pair --- daemon/ice.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/daemon/ice.c b/daemon/ice.c index 8119da3e6..4b43ae6b9 100644 --- a/daemon/ice.c +++ b/daemon/ice.c @@ -1081,11 +1081,9 @@ int ice_request(struct packet_stream *ps, struct sockaddr_in6 *src, struct in6_a ret = 0; - if (attrs->use) { + if (attrs->use && !PAIR_SET(pair, NOMINATED)) { ilog(LOG_DEBUG, "ICE pair "PAIR_FORMAT" has been nominated by peer", PAIR_FMT(pair)); - PAIR_SET(pair, NOMINATED); - mutex_lock(&ag->lock); g_tree_insert(ag->nominated_pairs, pair, pair); From eba4414fe9ed14f95782d153d17c46fd9d3f36c5 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Mon, 23 Mar 2015 13:34:00 +0300 Subject: [PATCH 53/82] Remove BuildRoot This is no longer required even for EL5. Signed-off-by: Peter Lemenkov --- el/rtpengine.spec | 1 - 1 file changed, 1 deletion(-) diff --git a/el/rtpengine.spec b/el/rtpengine.spec index dba62c65b..21c0ad090 100644 --- a/el/rtpengine.spec +++ b/el/rtpengine.spec @@ -8,7 +8,6 @@ License: GPLv3 URL: https://github.com/sipwise/rtpengine Source0: https://github.com/sipwise/rtpengine/archive/%{version}/%{name}-%{version}.tar.gz Conflicts: %{name}-kernel < %{version} -BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) BuildRequires: gcc make pkgconfig redhat-rpm-config BuildRequires: glib2-devel libcurl-devel openssl-devel pcre-devel From 597c35aae0906ad9c42a2b36c00eb7a4cbc4b111 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Mon, 23 Mar 2015 13:36:48 +0300 Subject: [PATCH 54/82] Remove duplicated text in descriptions Signed-off-by: Peter Lemenkov --- el/rtpengine.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/el/rtpengine.spec b/el/rtpengine.spec index 21c0ad090..321236246 100644 --- a/el/rtpengine.spec +++ b/el/rtpengine.spec @@ -29,7 +29,7 @@ Requires: iptables iptables-ipv6 ngcp-rtpengine = %{version} Requires: ngcp-rtpengine-dkms = %{version} %description kernel -NGCP rtpengine in-kernel packet forwarding +%{summary}. %package dkms @@ -42,7 +42,7 @@ Requires(post): epel-release dkms Requires(preun): epel-release dkms %description dkms -Kernel module for rtpengine in-kernel packet forwarding +%{summary}. %prep From 49ed2a826c3427e40382657ad2e05686a275997f Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Mon, 23 Mar 2015 13:40:20 +0300 Subject: [PATCH 55/82] Remove autoadded deps Signed-off-by: Peter Lemenkov --- el/rtpengine.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/el/rtpengine.spec b/el/rtpengine.spec index 321236246..6b6238cb1 100644 --- a/el/rtpengine.spec +++ b/el/rtpengine.spec @@ -12,7 +12,7 @@ Conflicts: %{name}-kernel < %{version} BuildRequires: gcc make pkgconfig redhat-rpm-config BuildRequires: glib2-devel libcurl-devel openssl-devel pcre-devel BuildRequires: xmlrpc-c-devel zlib-devel -Requires: glibc libcurl openssl pcre xmlrpc-c nmap-ncat +Requires: nmap-ncat %description From b0240eecaa10d753484330b7209c3123831fd821 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Mon, 23 Mar 2015 13:40:47 +0300 Subject: [PATCH 56/82] Use generic dependency name for nc Signed-off-by: Peter Lemenkov --- el/rtpengine.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/el/rtpengine.spec b/el/rtpengine.spec index 6b6238cb1..439e0196b 100644 --- a/el/rtpengine.spec +++ b/el/rtpengine.spec @@ -12,7 +12,7 @@ Conflicts: %{name}-kernel < %{version} BuildRequires: gcc make pkgconfig redhat-rpm-config BuildRequires: glib2-devel libcurl-devel openssl-devel pcre-devel BuildRequires: xmlrpc-c-devel zlib-devel -Requires: nmap-ncat +Requires: nc %description From cd70a8f8a5f8e0b0d729fe77feafeda4fb91f060 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Mon, 23 Mar 2015 13:48:05 +0300 Subject: [PATCH 57/82] Use proper versionin scheme Signed-off-by: Peter Lemenkov --- el/rtpengine.spec | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/el/rtpengine.spec b/el/rtpengine.spec index 439e0196b..8719ab679 100644 --- a/el/rtpengine.spec +++ b/el/rtpengine.spec @@ -7,7 +7,7 @@ Group: System Environment/Daemons License: GPLv3 URL: https://github.com/sipwise/rtpengine Source0: https://github.com/sipwise/rtpengine/archive/%{version}/%{name}-%{version}.tar.gz -Conflicts: %{name}-kernel < %{version} +Conflicts: %{name}-kernel < %{version}-%{release} BuildRequires: gcc make pkgconfig redhat-rpm-config BuildRequires: glib2-devel libcurl-devel openssl-devel pcre-devel @@ -25,8 +25,9 @@ drop-in replacement for any of the other available RTP and media proxies. Summary: NGCP rtpengine in-kernel packet forwarding Group: System Environment/Daemons BuildRequires: gcc make redhat-rpm-config iptables-devel -Requires: iptables iptables-ipv6 ngcp-rtpengine = %{version} -Requires: ngcp-rtpengine-dkms = %{version} +Requires: iptables iptables-ipv6 +Requires: %{name}%{?_isa} = %{version}-%{release} +Requires: %{name}-dkms%{?_isa} = %{version}-%{release} %description kernel %{summary}. From 8d56ed506730441c21d96ecb0bfb8544eade53c2 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Mon, 23 Mar 2015 14:10:41 +0300 Subject: [PATCH 58/82] Typo fix in Source0 url Signed-off-by: Peter Lemenkov --- el/rtpengine.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/el/rtpengine.spec b/el/rtpengine.spec index 8719ab679..00473c759 100644 --- a/el/rtpengine.spec +++ b/el/rtpengine.spec @@ -6,7 +6,7 @@ Summary: The Sipwise NGCP rtpengine Group: System Environment/Daemons License: GPLv3 URL: https://github.com/sipwise/rtpengine -Source0: https://github.com/sipwise/rtpengine/archive/%{version}/%{name}-%{version}.tar.gz +Source0: https://github.com/sipwise/rtpengine/archive/mr%{version}/%{name}-%{version}.tar.gz Conflicts: %{name}-kernel < %{version}-%{release} BuildRequires: gcc make pkgconfig redhat-rpm-config From 9890371a894f03bf1dce2587c18afab268e110b6 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Mon, 23 Mar 2015 14:11:10 +0300 Subject: [PATCH 59/82] Remove no longer required section Signed-off-by: Peter Lemenkov --- el/rtpengine.spec | 4 ---- 1 file changed, 4 deletions(-) diff --git a/el/rtpengine.spec b/el/rtpengine.spec index 00473c759..26c6b6239 100644 --- a/el/rtpengine.spec +++ b/el/rtpengine.spec @@ -86,10 +86,6 @@ sed "s/__VERSION__/%{version}-%{release}/g" debian/dkms.conf.in > \ %{buildroot}/%{_usrsrc}/%{name}-%{version}-%{release}/dkms.conf -%clean -rm -rf %{buildroot} - - %pre /usr/sbin/groupadd -r rtpengine 2> /dev/null || : /usr/sbin/useradd -r -g rtpengine -s /sbin/nologin -c "rtpengine daemon" \ From 234b45441ac26d66d4164fbfcd71c4d1fb85a030 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Mon, 23 Mar 2015 14:16:41 +0300 Subject: [PATCH 60/82] Use macro instead of /etc/rc.d/init.d Signed-off-by: Peter Lemenkov --- el/rtpengine.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/el/rtpengine.spec b/el/rtpengine.spec index 26c6b6239..601802752 100644 --- a/el/rtpengine.spec +++ b/el/rtpengine.spec @@ -66,7 +66,7 @@ install -D -p -m755 utils/rtpengine-ctl %{buildroot}/%{_sbindir}/rtpengine-ctl ## Install the init.d script and configuration file install -D -p -m755 el/rtpengine.init \ - %{buildroot}/%{_sysconfdir}/rc.d/init.d/rtpengine + %{buildroot}/%{_initrddir}/rtpengine install -D -p -m644 el/rtpengine.sysconfig \ %{buildroot}/%{_sysconfdir}/sysconfig/rtpengine mkdir -p %{buildroot}/%{_sharedstatedir}/rtpengine @@ -127,7 +127,7 @@ true %{_sbindir}/rtpengine-ctl # init.d script and configuration file -%{_sysconfdir}/rc.d/init.d/rtpengine +%{_initrddir}/rtpengine %config(noreplace) %{_sysconfdir}/sysconfig/rtpengine %dir %{_sharedstatedir}/rtpengine From 882731b8142a2dda8ddeb3817874267c6a12dcc2 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Mon, 23 Mar 2015 14:18:42 +0300 Subject: [PATCH 61/82] Remove double slash Signed-off-by: Peter Lemenkov --- el/rtpengine.spec | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/el/rtpengine.spec b/el/rtpengine.spec index 601802752..b5bb3f65c 100644 --- a/el/rtpengine.spec +++ b/el/rtpengine.spec @@ -60,16 +60,16 @@ cd .. %install # Install the userspace daemon -install -D -p -m755 daemon/rtpengine %{buildroot}/%{_sbindir}/rtpengine +install -D -p -m755 daemon/rtpengine %{buildroot}%{_sbindir}/rtpengine # Install CLI (command line interface) -install -D -p -m755 utils/rtpengine-ctl %{buildroot}/%{_sbindir}/rtpengine-ctl +install -D -p -m755 utils/rtpengine-ctl %{buildroot}%{_sbindir}/rtpengine-ctl ## Install the init.d script and configuration file install -D -p -m755 el/rtpengine.init \ - %{buildroot}/%{_initrddir}/rtpengine + %{buildroot}%{_initrddir}/rtpengine install -D -p -m644 el/rtpengine.sysconfig \ - %{buildroot}/%{_sysconfdir}/sysconfig/rtpengine -mkdir -p %{buildroot}/%{_sharedstatedir}/rtpengine + %{buildroot}%{_sysconfdir}/sysconfig/rtpengine +mkdir -p %{buildroot}%{_sharedstatedir}/rtpengine # Install the iptables plugin install -D -p -m755 iptables-extension/libxt_RTPENGINE.so \ @@ -77,13 +77,13 @@ install -D -p -m755 iptables-extension/libxt_RTPENGINE.so \ ## DKMS module source install install -D -p -m644 kernel-module/Makefile \ - %{buildroot}/%{_usrsrc}/%{name}-%{version}-%{release}/Makefile + %{buildroot}%{_usrsrc}/%{name}-%{version}-%{release}/Makefile install -D -p -m644 kernel-module/xt_RTPENGINE.c \ - %{buildroot}/%{_usrsrc}/%{name}-%{version}-%{release}/xt_RTPENGINE.c + %{buildroot}%{_usrsrc}/%{name}-%{version}-%{release}/xt_RTPENGINE.c install -D -p -m644 kernel-module/xt_RTPENGINE.h \ - %{buildroot}/%{_usrsrc}/%{name}-%{version}-%{release}/xt_RTPENGINE.h + %{buildroot}%{_usrsrc}/%{name}-%{version}-%{release}/xt_RTPENGINE.h sed "s/__VERSION__/%{version}-%{release}/g" debian/dkms.conf.in > \ - %{buildroot}/%{_usrsrc}/%{name}-%{version}-%{release}/dkms.conf + %{buildroot}%{_usrsrc}/%{name}-%{version}-%{release}/dkms.conf %pre From d25b697f6aab867732077d6ae07aad5c276fd0ec Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Mon, 23 Mar 2015 14:29:23 +0300 Subject: [PATCH 62/82] Check for username,group before creating Just a cosmetic actually. Earlier we created user,group anyway and suppress error message if user or group already exists. Signed-off-by: Peter Lemenkov --- el/rtpengine.spec | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/el/rtpengine.spec b/el/rtpengine.spec index b5bb3f65c..8ded7d64c 100644 --- a/el/rtpengine.spec +++ b/el/rtpengine.spec @@ -87,10 +87,10 @@ sed "s/__VERSION__/%{version}-%{release}/g" debian/dkms.conf.in > \ %pre -/usr/sbin/groupadd -r rtpengine 2> /dev/null || : -/usr/sbin/useradd -r -g rtpengine -s /sbin/nologin -c "rtpengine daemon" \ - -d %{_sharedstatedir}/rtpengine rtpengine \ - 2> /dev/null || : +getent group rtpengine >/dev/null || /usr/sbin/groupadd -r rtpengine +getent passwd rtpengine >/dev/null || /usr/sbin/useradd -r -g rtpengine \ + -s /sbin/nologin -c "rtpengine daemon" -d %{_sharedstatedir}/rtpengine \ + rtpengine %post From e71a7e15d8f9dace65f632b74067d93a100421e2 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Mon, 23 Mar 2015 14:34:14 +0300 Subject: [PATCH 63/82] Consistently use rtpengine name everywhere Signed-off-by: Peter Lemenkov --- el/rtpengine.init | 2 +- el/rtpengine.spec | 43 ++++++++++++++++++++++--------------------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/el/rtpengine.init b/el/rtpengine.init index 1fb33ce62..c6056242d 100644 --- a/el/rtpengine.init +++ b/el/rtpengine.init @@ -39,7 +39,7 @@ MODULE=0 build_opts() { shopt -s nocasematch - RPMS=`rpm -qa | grep ngcp-rtpengine-kernel` + RPMS=`rpm -qa | grep rtpengine-kernel` if [[ "$KERNEL" == "yes" && -n "$TABLE" && -n "$RPMS" ]] then MODULE=1 diff --git a/el/rtpengine.spec b/el/rtpengine.spec index 8ded7d64c..48f152c48 100644 --- a/el/rtpengine.spec +++ b/el/rtpengine.spec @@ -1,4 +1,4 @@ -Name: ngcp-rtpengine +Name: rtpengine Version: 2.3.6 Release: 0%{?dist} Summary: The Sipwise NGCP rtpengine @@ -13,6 +13,8 @@ BuildRequires: gcc make pkgconfig redhat-rpm-config BuildRequires: glib2-devel libcurl-devel openssl-devel pcre-devel BuildRequires: xmlrpc-c-devel zlib-devel Requires: nc +# Remain compat with other installations +Provides: ngcp-rtpengine = %{version}-%{release} %description @@ -60,16 +62,16 @@ cd .. %install # Install the userspace daemon -install -D -p -m755 daemon/rtpengine %{buildroot}%{_sbindir}/rtpengine +install -D -p -m755 daemon/%{name} %{buildroot}%{_sbindir}/%{name} # Install CLI (command line interface) -install -D -p -m755 utils/rtpengine-ctl %{buildroot}%{_sbindir}/rtpengine-ctl +install -D -p -m755 utils/%{name}-ctl %{buildroot}%{_sbindir}/%{name}-ctl ## Install the init.d script and configuration file -install -D -p -m755 el/rtpengine.init \ - %{buildroot}%{_initrddir}/rtpengine -install -D -p -m644 el/rtpengine.sysconfig \ - %{buildroot}%{_sysconfdir}/sysconfig/rtpengine -mkdir -p %{buildroot}%{_sharedstatedir}/rtpengine +install -D -p -m755 el/%{name}.init \ + %{buildroot}%{_initrddir}/%{name} +install -D -p -m644 el/%{name}.sysconfig \ + %{buildroot}%{_sysconfdir}/sysconfig/%{name} +mkdir -p %{buildroot}%{_sharedstatedir}/%{name} # Install the iptables plugin install -D -p -m755 iptables-extension/libxt_RTPENGINE.so \ @@ -82,20 +84,19 @@ install -D -p -m644 kernel-module/xt_RTPENGINE.c \ %{buildroot}%{_usrsrc}/%{name}-%{version}-%{release}/xt_RTPENGINE.c install -D -p -m644 kernel-module/xt_RTPENGINE.h \ %{buildroot}%{_usrsrc}/%{name}-%{version}-%{release}/xt_RTPENGINE.h -sed "s/__VERSION__/%{version}-%{release}/g" debian/dkms.conf.in > \ +sed "s/__VERSION__/%{version}-%{release}/g;s/ngcp-rtpengine/rtpengine/g" debian/dkms.conf.in > \ %{buildroot}%{_usrsrc}/%{name}-%{version}-%{release}/dkms.conf %pre -getent group rtpengine >/dev/null || /usr/sbin/groupadd -r rtpengine -getent passwd rtpengine >/dev/null || /usr/sbin/useradd -r -g rtpengine \ - -s /sbin/nologin -c "rtpengine daemon" -d %{_sharedstatedir}/rtpengine \ - rtpengine +getent group %{name} >/dev/null || /usr/sbin/groupadd -r %{name} +getent passwd %{name} >/dev/null || /usr/sbin/useradd -r -g %{name} \ + -s /sbin/nologin -c "%{name} daemon" -d %{_sharedstatedir}/%{name} %{name} %post if [ $1 -eq 1 ]; then - /sbin/chkconfig --add rtpengine || : + /sbin/chkconfig --add %{name} || : fi @@ -109,8 +110,8 @@ true %preun if [ $1 = 0 ] ; then - /sbin/service rtpengine stop >/dev/null 2>&1 - /sbin/chkconfig --del rtpengine + /sbin/service %{name} stop >/dev/null 2>&1 + /sbin/chkconfig --del %{name} fi @@ -122,14 +123,14 @@ true %files # Userspace daemon -%{_sbindir}/rtpengine +%{_sbindir}/%{name} # CLI (command line interface) -%{_sbindir}/rtpengine-ctl +%{_sbindir}/%{name}-ctl # init.d script and configuration file -%{_initrddir}/rtpengine -%config(noreplace) %{_sysconfdir}/sysconfig/rtpengine -%dir %{_sharedstatedir}/rtpengine +%{_initrddir}/%{name} +%config(noreplace) %{_sysconfdir}/sysconfig/%{name} +%dir %{_sharedstatedir}/%{name} # Documentation %doc LICENSE README.md el/README.el.md debian/changelog debian/copyright From a6ab5b5ebce32da4fb285189f28f50a29d461645 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Mon, 23 Mar 2015 14:36:18 +0300 Subject: [PATCH 64/82] Restrict access to a working directory Signed-off-by: Peter Lemenkov --- el/rtpengine.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/el/rtpengine.spec b/el/rtpengine.spec index 48f152c48..70ec79399 100644 --- a/el/rtpengine.spec +++ b/el/rtpengine.spec @@ -130,7 +130,7 @@ true # init.d script and configuration file %{_initrddir}/%{name} %config(noreplace) %{_sysconfdir}/sysconfig/%{name} -%dir %{_sharedstatedir}/%{name} +%attr(0750,%{name},%{name}) %dir %{_sharedstatedir}/%{name} # Documentation %doc LICENSE README.md el/README.el.md debian/changelog debian/copyright From 9a12b4915a66c28d9fade1568704b95efb256bab Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Mon, 23 Mar 2015 15:31:46 +0300 Subject: [PATCH 65/82] Don't specify access rights for _usrsrc dir Signed-off-by: Peter Lemenkov --- el/rtpengine.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/el/rtpengine.spec b/el/rtpengine.spec index 70ec79399..784fffa97 100644 --- a/el/rtpengine.spec +++ b/el/rtpengine.spec @@ -141,7 +141,7 @@ true %files dkms -%attr(0755,root,root) %{_usrsrc}/%{name}-%{version}-%{release}/ +%{_usrsrc}/%{name}-%{version}-%{release}/ %changelog From 3b270318283738f04ed65761804d1ca1daddc030 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Mon, 23 Mar 2015 15:33:17 +0300 Subject: [PATCH 66/82] Explicitly set rights on dkms.conf as 644 Signed-off-by: Peter Lemenkov --- el/rtpengine.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/el/rtpengine.spec b/el/rtpengine.spec index 784fffa97..493f6a038 100644 --- a/el/rtpengine.spec +++ b/el/rtpengine.spec @@ -84,8 +84,8 @@ install -D -p -m644 kernel-module/xt_RTPENGINE.c \ %{buildroot}%{_usrsrc}/%{name}-%{version}-%{release}/xt_RTPENGINE.c install -D -p -m644 kernel-module/xt_RTPENGINE.h \ %{buildroot}%{_usrsrc}/%{name}-%{version}-%{release}/xt_RTPENGINE.h -sed "s/__VERSION__/%{version}-%{release}/g;s/ngcp-rtpengine/rtpengine/g" debian/dkms.conf.in > \ - %{buildroot}%{_usrsrc}/%{name}-%{version}-%{release}/dkms.conf +sed -i -e "s/__VERSION__/%{version}-%{release}/g;s/ngcp-rtpengine/rtpengine/g" debian/dkms.conf.in +install -D -p -m644 debian/dkms.conf.in %{buildroot}%{_usrsrc}/%{name}-%{version}-%{release}/dkms.conf %pre From 93294f8c5f8aae3c93804e363a5827062c80aae3 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Mon, 23 Mar 2015 10:00:09 -0400 Subject: [PATCH 67/82] don't relearn addresses if the endpoint hasn't changed --- daemon/call.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index d67ad1370..b5a27e9c7 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -1863,15 +1863,21 @@ static int __num_media_streams(struct call_media *media, unsigned int num_ports) return ret; } -static void __fill_stream(struct packet_stream *ps, const struct endpoint *ep, unsigned int port_off) { - ps->endpoint = *ep; - ps->endpoint.port += port_off; +static void __fill_stream(struct packet_stream *ps, const struct endpoint *epp, unsigned int port_off) { + struct endpoint ep; + + ep = *epp; + ep.port += port_off; + + /* if the endpoint hasn't changed, we do nothing */ + if (PS_ISSET(ps, FILLED) && !memcmp(&ps->advertised_endpoint, &ep, sizeof(ep))) + return; + + ps->endpoint = ep; + ps->advertised_endpoint = ep; /* we reset crypto params whenever the endpoint changes */ - if (PS_ISSET(ps, FILLED) && memcmp(&ps->advertised_endpoint, &ps->endpoint, sizeof(ps->endpoint))) { - crypto_reset(&ps->crypto); - dtls_shutdown(ps); - } - ps->advertised_endpoint = ps->endpoint; + crypto_reset(&ps->crypto); + dtls_shutdown(ps); PS_SET(ps, FILLED); } From 0b202d821f0c386e9f4632fbbc983adc0bc5a41c Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Tue, 24 Mar 2015 10:47:09 -0400 Subject: [PATCH 68/82] Fall back to a different address family if the requested one is unavailable Fixes #90 --- daemon/call.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/daemon/call.c b/daemon/call.c index b5a27e9c7..b9227e09a 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -3485,7 +3485,15 @@ struct local_interface *get_local_interface(struct callmaster *m, const str *nam if (!name || !name->s) { GQueue *q; q = __interface_list_for_family(m, family); - return q->head ? q->head->data : NULL; + if (q->head) + return q->head->data; + q = __interface_list_for_family(m, AF_INET); + if (q->head) + return q->head->data; + q = __interface_list_for_family(m, AF_INET6); + if (q->head) + return q->head->data; + return NULL; } d.name = *name; From fd99ecf98c8daffb0a059d3dee859e9cf4ef08d2 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Thu, 26 Mar 2015 16:04:49 -0400 Subject: [PATCH 69/82] don't close call on UDP send error --- daemon/call.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/call.c b/daemon/call.c index b9227e09a..c92d192e7 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -885,7 +885,7 @@ forward: ret = sendmsg(sink->sfd->fd.fd, &mh, 0); if (ret == -1) { - ret = -errno; + ret = 0; /* temp for address family mismatches */ ilog(LOG_DEBUG,"Error when sending message. Error: %s",strerror(errno)); atomic64_inc(&stream->stats.errors); atomic64_inc(&cm->statsps.errors); From c0fab9b47d0d1ee45ca1a9359ae6ad10288fb97b Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Thu, 26 Mar 2015 16:06:06 -0400 Subject: [PATCH 70/82] various fixes related to ICE negotation and rtcp-mux closes #92 --- daemon/call.c | 12 +++++++-- daemon/ice.c | 68 +++++++++++++++++++++++++++++++-------------------- 2 files changed, 51 insertions(+), 29 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index c92d192e7..34255e1d9 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -146,6 +146,7 @@ const char * get_tag_type_text(enum tag_type t) { } static void determine_handler(struct packet_stream *in, const struct packet_stream *out); +static void __call_media_state_machine(struct call_media *m); static int __k_null(struct rtpengine_srtp *s, struct packet_stream *); static int __k_srtp_encrypt(struct rtpengine_srtp *s, struct packet_stream *); @@ -645,7 +646,7 @@ static int stream_packet(struct stream_fd *sfd, str *s, struct sockaddr_in6 *fsi if (!stun_ret) goto unlock_out; if (stun_ret == 1) { - call_stream_state_machine(stream); + __call_media_state_machine(media); mutex_lock(&stream->in_lock); /* for the jump */ goto kernel_check; } @@ -1904,6 +1905,13 @@ enum call_stream_state call_stream_state_machine(struct packet_stream *ps) { return CSS_RUNNING; } +static void __call_media_state_machine(struct call_media *m) { + GList *l; + + for (l = m->streams.head; l; l = l->next) + call_stream_state_machine(l->data); +} + static int __init_stream(struct packet_stream *ps) { struct call_media *media = ps->media; struct call *call = ps->call; @@ -2472,7 +2480,6 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, /* control rtcp-mux */ __rtcp_mux_logic(flags, media, other_media); - /* XXX update ICE if rtcp-mux changes */ /* SDES and DTLS */ __generate_crypto(flags, media, other_media); @@ -2548,6 +2555,7 @@ init: /* we are now ready to fire up ICE if so desired and requested */ ice_update(other_media->ice_agent, sp); + ice_update(media->ice_agent, NULL); /* this is in case rtcp-mux has changed */ } return 0; diff --git a/daemon/ice.c b/daemon/ice.c index 4b43ae6b9..9ab3404c6 100644 --- a/daemon/ice.c +++ b/daemon/ice.c @@ -317,8 +317,9 @@ void ice_update(struct ice_agent *ag, struct stream_params *sp) { int recalc = 0; unsigned int comps; struct packet_stream *components[MAX_COMPONENTS], *ps; + GQueue *candidates; - if (!ag || !sp) + if (!ag) return; media = ag->media; @@ -326,19 +327,25 @@ void ice_update(struct ice_agent *ag, struct stream_params *sp) { __role_change(ag, MEDIA_ISSET(media, ICE_CONTROLLING)); - /* check for ICE restarts */ - if (ag->ufrag[0].s && sp->ice_ufrag.s && str_cmp_str(&ag->ufrag[0], &sp->ice_ufrag)) - __ice_restart(ag); - else if (ag->pwd[0].s && sp->ice_pwd.s && str_cmp_str(&ag->pwd[0], &sp->ice_pwd)) - __ice_restart(ag); - else if (ag->local_interface != media->interface) - __ice_restart(ag); - - /* update remote info */ - if (sp->ice_ufrag.s) - call_str_cpy(call, &ag->ufrag[0], &sp->ice_ufrag); - if (sp->ice_pwd.s) - call_str_cpy(call, &ag->pwd[0], &sp->ice_pwd); + if (sp) { + /* check for ICE restarts */ + if (ag->ufrag[0].s && sp->ice_ufrag.s && str_cmp_str(&ag->ufrag[0], &sp->ice_ufrag)) + __ice_restart(ag); + else if (ag->pwd[0].s && sp->ice_pwd.s && str_cmp_str(&ag->pwd[0], &sp->ice_pwd)) + __ice_restart(ag); + else if (ag->local_interface != media->interface) + __ice_restart(ag); + + /* update remote info */ + if (sp->ice_ufrag.s) + call_str_cpy(call, &ag->ufrag[0], &sp->ice_ufrag); + if (sp->ice_pwd.s) + call_str_cpy(call, &ag->pwd[0], &sp->ice_pwd); + + candidates = &sp->ice_candidates; + } + else /* this is a dummy update in case rtcp-mux has changed */ + candidates = &ag->remote_candidates; /* get our component streams */ ZERO(components); @@ -349,7 +356,7 @@ void ice_update(struct ice_agent *ag, struct stream_params *sp) { components[1] = NULL; comps = 0; - for (l = sp->ice_candidates.head; l; l = l->next) { + for (l = candidates->head; l; l = l->next) { if (ag->remote_candidates.length >= MAX_ICE_CANDIDATES) { ilog(LOG_WARNING, "Maxmimum number of ICE candidates exceeded"); break; @@ -360,15 +367,16 @@ void ice_update(struct ice_agent *ag, struct stream_params *sp) { /* skip invalid */ if (!cand->component_id || cand->component_id > G_N_ELEMENTS(components)) continue; - /* skip if we don't have a candidate of our own */ ps = components[cand->component_id - 1]; - if (!ps) - continue; - comps = MAX(comps, cand->component_id); + if (ps) /* only count active components */ + comps = MAX(comps, cand->component_id); - /* check for duplicates */ dup = g_hash_table_lookup(ag->candidate_hash, cand); + if (!sp && dup) /* this isn't a real update, so only check pairings */ + goto pair; + + /* check for duplicates */ if (dup) { /* if this is peer reflexive, we've learned it through STUN. * otherwise it's simply one we've seen before. */ @@ -410,6 +418,9 @@ void ice_update(struct ice_agent *ag, struct stream_params *sp) { g_hash_table_insert(ag->foundation_hash, dup, dup); +pair: + if (!ps) + continue; for (k = ag->local_interface->list.head; k; k = k->next) { /* skip duplicates here also */ if (__pair_lookup(ag, dup, k->data)) @@ -1088,8 +1099,10 @@ int ice_request(struct packet_stream *ps, struct sockaddr_in6 *src, struct in6_a g_tree_insert(ag->nominated_pairs, pair, pair); - if (PAIR_ISSET(pair, SUCCEEDED)) + if (PAIR_ISSET(pair, SUCCEEDED)) { + PAIR_SET(pair, VALID); g_tree_insert(ag->valid_pairs, pair, pair); + } if (!AGENT_ISSET(ag, CONTROLLING)) ret = __check_valid(ag); @@ -1129,7 +1142,7 @@ static int __check_succeeded_complete(struct ice_agent *ag) { int ice_response(struct packet_stream *ps, struct sockaddr_in6 *src, struct in6_addr *dst, struct stun_attrs *attrs, u_int32_t transaction[3]) { - struct ice_candidate_pair *pair; + struct ice_candidate_pair *pair, *opair; struct ice_agent *ag; struct call_media *media = ps->media; const char *err; @@ -1222,20 +1235,21 @@ int ice_response(struct packet_stream *ps, struct sockaddr_in6 *src, struct in6_ cand = __foundation_lookup(ag, &pair->remote_candidate->foundation, component); if (!cand) continue; - pair = __pair_lookup(ag, cand, ifa); - if (!pair) + opair = __pair_lookup(ag, cand, ifa); + if (!opair) continue; - if (PAIR_ISSET(pair, FAILED)) + if (PAIR_ISSET(opair, FAILED)) continue; - if (!PAIR_CLEAR(pair, FROZEN)) + if (!PAIR_CLEAR(opair, FROZEN)) continue; - ilog(LOG_DEBUG, "Unfreezing related ICE pair "PAIR_FORMAT, PAIR_FMT(pair)); + ilog(LOG_DEBUG, "Unfreezing related ICE pair "PAIR_FORMAT, PAIR_FMT(opair)); } /* if this was previously nominated by the peer, it's now valid */ if (PAIR_ISSET(pair, NOMINATED)) { + PAIR_SET(pair, VALID); g_tree_insert(ag->valid_pairs, pair, pair); if (!AGENT_ISSET(ag, CONTROLLING)) From 3a45a169ef261f9ad8c7516b365983ac79feca2c Mon Sep 17 00:00:00 2001 From: Alexander Lutay Date: Fri, 27 Mar 2015 10:21:23 +0100 Subject: [PATCH 71/82] MT#7505 Add .gitreview file for rtpengine --- .gitreview | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .gitreview diff --git a/.gitreview b/.gitreview new file mode 100644 index 000000000..90ab7e1d1 --- /dev/null +++ b/.gitreview @@ -0,0 +1,4 @@ +[gerrit] +host=gerrit.mgm.sipwise.com +port=29418 +project=rtpengine From cca0f2bf7115af5e8211e49fc83cee10d9296726 Mon Sep 17 00:00:00 2001 From: Frederic-Philippe Metz Date: Mon, 30 Mar 2015 11:24:15 +0200 Subject: [PATCH 72/82] Replaced rtpengine-ctl shell script with perl script. --- utils/rtpengine-ctl | 128 +++++++++++++++++++++++--------------------- 1 file changed, 67 insertions(+), 61 deletions(-) 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 }; From 2cfc12bcc64d47ab99e4ccad8c04d8722ada1995 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 3 Apr 2015 18:22:53 -0400 Subject: [PATCH 73/82] fix incorrect free order of ICE components fixes #95 --- daemon/ice.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daemon/ice.c b/daemon/ice.c index 9ab3404c6..dcd7c0fd9 100644 --- a/daemon/ice.c +++ b/daemon/ice.c @@ -470,8 +470,6 @@ void ice_shutdown(struct ice_agent **agp) { obj_put(ag); } static void __ice_agent_free_components(struct ice_agent *ag) { - ice_candidates_free(&ag->remote_candidates); - ice_candidate_pairs_free(&ag->candidate_pairs); g_queue_clear(&ag->triggered); g_hash_table_destroy(ag->candidate_hash); g_hash_table_destroy(ag->pair_hash); @@ -482,6 +480,8 @@ static void __ice_agent_free_components(struct ice_agent *ag) { g_tree_destroy(ag->nominated_pairs); g_tree_destroy(ag->succeeded_pairs); g_tree_destroy(ag->valid_pairs); + ice_candidates_free(&ag->remote_candidates); + ice_candidate_pairs_free(&ag->candidate_pairs); } static void __ice_agent_free(void *p) { struct ice_agent *ag = p; From b85baee230a086143d866dba4f2961b85b4421e0 Mon Sep 17 00:00:00 2001 From: Alexander Lutay Date: Wed, 8 Apr 2015 17:27:38 +0200 Subject: [PATCH 74/82] MT#11493 Release new version 4.0.0.0+0~mr4.0.0.0 Change-Id: Ibf6dd7188650438dfd6950646aacf8412fae3cb1 --- debian/changelog | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/debian/changelog b/debian/changelog index ffa836f4b..d1ff5a63f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,35 @@ +ngcp-rtpengine (4.0.0.0+0~mr4.0.0.0) unstable; urgency=medium + + [ Richard Fuchs ] + * [2db33ef] reset ICE/STUN retransmit data on triggered checks + * [20ac7cd] process nominating ICE request only once per pair + * [93294f8] don't relearn addresses if the endpoint hasn't changed + * [0b202d8] Fall back to a different address family if the requested one is unavailable + * [fd99ecf] don't close call on UDP send error + * [c0fab9b] various fixes related to ICE negotation and rtcp-mux + * [2cfc12b] fix incorrect free order of ICE components + + [ Peter Lemenkov ] + * [eba4414] Remove BuildRoot + * [597c35a] Remove duplicated text in descriptions + * [49ed2a8] Remove autoadded deps + * [b0240ee] Use generic dependency name for nc + * [cd70a8f] Use proper versionin scheme + * [8d56ed5] Typo fix in Source0 url + * [9890371] Remove no longer required section + * [234b454] Use macro instead of /etc/rc.d/init.d + * [882731b] Remove double slash + * [d25b697] Check for username,group before creating + * [e71a7e1] Consistently use rtpengine name everywhere + * [a6ab5b5] Restrict access to a working directory + * [9a12b49] Don't specify access rights for _usrsrc dir + * [3b27031] Explicitly set rights on dkms.conf as 644 + + [ Alexander Lutay ] + * [3a45a16] MT#7505 Add .gitreview file for rtpengine + + -- Alexander Lutay Wed, 08 Apr 2015 17:26:40 +0200 + ngcp-rtpengine (3.3.0.0+0~mr4.0.0.0) unstable; urgency=low [ Sergey Lavrov ] From 3acc850083240edbc14ddbc7fe6a556fbce5e295 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Thu, 9 Apr 2015 09:49:50 -0400 Subject: [PATCH 75/82] zero-fill padding bytes --- daemon/stun.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/daemon/stun.c b/daemon/stun.c index 85ac584ea..3a5de6273 100644 --- a/daemon/stun.c +++ b/daemon/stun.c @@ -271,6 +271,11 @@ INLINE void __output_add(struct msghdr *mh, struct tlv *tlv, unsigned int len, u iov = &mh->msg_iov[mh->msg_iovlen++]; iov->iov_base = append; /* must have space for padding */ iov->iov_len = (append_len + 3) & 0xfffc; + + if ((append_len & 0x3)) { + if (memcmp(append + append_len, "\0\0\0", 4 - (append_len & 0x3))) + memset(append + append_len, 0, 4 - (append_len & 0x3)); + } } } From d50208488d0b49dd3149b9bfdd1a60e1e275b430 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Thu, 9 Apr 2015 09:50:15 -0400 Subject: [PATCH 76/82] fix refcount bug fixes double-free and mem corruption bugs (segfault fix) closes #96 --- daemon/ice.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/daemon/ice.c b/daemon/ice.c index dcd7c0fd9..25ee60e2b 100644 --- a/daemon/ice.c +++ b/daemon/ice.c @@ -283,11 +283,11 @@ static int __copy_cand(struct call *call, struct ice_candidate *dst, const struc } static void __ice_reset(struct ice_agent *ag) { + __agent_deschedule(ag); AGENT_CLEAR2(ag, COMPLETED, NOMINATING); __ice_agent_free_components(ag); ZERO(ag->active_components); ZERO(ag->start_nominating); - ZERO(ag->next_check); ZERO(ag->last_run); __ice_agent_initialize(ag); } @@ -518,7 +518,7 @@ static void __agent_schedule_abs(struct ice_agent *ag, const struct timeval *tv) if (ag->next_check.tv_sec && timeval_cmp(&ag->next_check, &nxt) <= 0) goto nope; /* already scheduled sooner */ if (!g_tree_remove(ice_agents_timers, ag)) - obj_hold(ag); /* if it wasn't removed (should never happen), we make a new reference */ + obj_hold(ag); /* if it wasn't removed, we make a new reference */ ag->next_check = nxt; g_tree_insert(ice_agents_timers, ag, ag); cond_broadcast(&ice_agents_timers_cond); @@ -532,7 +532,7 @@ static void __agent_deschedule(struct ice_agent *ag) { goto nope; /* already descheduled */ ret = g_tree_remove(ice_agents_timers, ag); ZERO(ag->next_check); - if (ret) /* should always be true */ + if (ret) obj_put(ag); nope: mutex_unlock(&ice_agents_timers_lock); From 46c60e5284c543be5230a976329fd7e1c08ca98c Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Mon, 13 Apr 2015 22:21:09 +0200 Subject: [PATCH 77/82] MT#12519 debian/control: switch from Pre-Depends to Suggests for ngcp-system-tools package On upgrades from 2.8 LTS we have to install ngcp-rtpengine-kernl-dkms package before the upgrade in order to get dkms to work. No need to upgrade the new version of ngcp-system-tools too Change-Id: I36fbfdffc5ca1e283ba0e8d5a42a96a72fbf324e --- debian/control | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/control b/debian/control index 146e2d6ed..09b49a5a8 100644 --- a/debian/control +++ b/debian/control @@ -17,7 +17,7 @@ Homepage: http://sipwise.com/ Package: ngcp-rtpengine-daemon Architecture: any -Pre-Depends: ngcp-system-tools +Suggests: ngcp-system-tools Depends: ${misc:Depends}, ${shlibs:Depends} Conflicts: ngcp-mediaproxy-ng-daemon @@ -63,7 +63,7 @@ Description: IPtables kernel module for the NGCP media proxy - source. Package: ngcp-rtpengine-kernel-dkms Architecture: all -Pre-Depends: ngcp-system-tools +Suggests: ngcp-system-tools Depends: dkms (>= 1.95), ${misc:Depends} Conflicts: ngcp-mediaproxy-ng-kernel-dkms From 4ea8d9603358bb336abb19fbdf947dd2a936f875 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Tue, 14 Apr 2015 11:35:30 -0400 Subject: [PATCH 78/82] remove stray compiler warning --- daemon/cli.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/daemon/cli.c b/daemon/cli.c index 711812531..88cddafe1 100644 --- a/daemon/cli.c +++ b/daemon/cli.c @@ -17,11 +17,13 @@ static const char* TRUNCATED = " ... Output truncated. Increase Output Buffer ...\n"; -#define truncate_output(x) do { x -= strlen(TRUNCATED)+1; x += sprintf(x,"%s",TRUNCATED); } while (0); +#define truncate_output(x) strcpy(x - strlen(TRUNCATED) - 1, TRUNCATED) -#define ADJUSTLEN(printlen,outbuflen,replybuffer) do { if (printlen>=(outbufend-replybuffer)) \ - truncate_output(replybuffer); \ - replybuffer += (printlen>=outbufend-replybuffer)?outbufend-replybuffer:printlen; } while (0); +#define ADJUSTLEN(printlen,outbuflen,replybuffer) do { \ + replybuffer += (printlen>=outbufend-replybuffer)?outbufend-replybuffer:printlen; \ + if (replybuffer == outbufend) \ + truncate_output(replybuffer); \ + } while (0); static void cli_incoming_list_totals(char* buffer, int len, struct callmaster* m, char* replybuffer, const char* outbufend) { int printlen=0; From 8a0fbf201f3ff79c5bf048a00bcd127c5446157e Mon Sep 17 00:00:00 2001 From: Frederic-Philippe Metz Date: Tue, 14 Apr 2015 18:59:06 +0200 Subject: [PATCH 79/82] Fixed RTCP parsing of bitfield structs. --- daemon/rtcp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/daemon/rtcp.c b/daemon/rtcp.c index 434a065e3..f3e8973dc 100644 --- a/daemon/rtcp.c +++ b/daemon/rtcp.c @@ -483,11 +483,11 @@ int rtcp_demux_is_rtcp(const str *s) { 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, ", - ntohl(common->version), - ntohl(common->p), - ntohl(common->count), - ntohl(common->pt), - ntohl(common->length), + common->version, + common->p, + common->count, + common->pt, + common->length, ntohl(common->ssrc)); } From 1d1b8f21ce840a2643d2d64382c63a68c42041b5 Mon Sep 17 00:00:00 2001 From: Frederic-Philippe Metz Date: Wed, 15 Apr 2015 06:54:52 +0200 Subject: [PATCH 80/82] Fixed RTCP Parsing also for XR reports. --- daemon/rtcp_xr.c | 58 ++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/daemon/rtcp_xr.c b/daemon/rtcp_xr.c index 9b4bc016b..7aa61da8c 100644 --- a/daemon/rtcp_xr.c +++ b/daemon/rtcp_xr.c @@ -23,19 +23,19 @@ void print_rtcp_xr_common(char* cdrbufcur,pjmedia_rtcp_xr_pkt *rtcp_xr) { cdrbufcur += sprintf(cdrbufcur,"version=%u, padding=%u, count=%u, payloadtype=%u, length=%u, ssrc=%u, ", - ntohl(rtcp_xr->common.version), - ntohl(rtcp_xr->common.p), - ntohl(rtcp_xr->common.count), - ntohl(rtcp_xr->common.pt), - ntohl(rtcp_xr->common.length), + 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,pjmedia_rtcp_xr_rb_header *rb_header) { cdrbufcur += sprintf(cdrbufcur,"rb_header_blocktype=%u, rb_header_blockspecdata=%u, rb_header_blocklength=%u, ", - ntohl(rb_header->bt), - ntohl(rb_header->specific), - ntohl(rb_header->length)); + rb_header->bt, + rb_header->specific, + ntohs(rb_header->length)); } void print_rtcp_xr_rb_rr_time(char* cdrbufcur,pjmedia_rtcp_xr_rb_rr_time *rb_rr_time) { @@ -59,7 +59,7 @@ void print_rtcp_xr_rb_stats(char* cdrbufcur,pjmedia_rtcp_xr_rb_stats *rb_stats) "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), - ntohl(rb_stats->begin_seq), + ntohs(rb_stats->begin_seq), ntohl(rb_stats->end_seq), ntohl(rb_stats->lost), ntohl(rb_stats->dup), @@ -82,26 +82,26 @@ void print_rtcp_xr_rb_voip_mtc(char* cdrbufcur,pjmedia_rtcp_xr_rb_voip_mtc *rb_v "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), - ntohl(rb_voip_mtc->loss_rate), - ntohl(rb_voip_mtc->discard_rate), - ntohl(rb_voip_mtc->burst_den), - ntohl(rb_voip_mtc->gap_den), - ntohl(rb_voip_mtc->burst_dur), - ntohl(rb_voip_mtc->gap_dur), - ntohl(rb_voip_mtc->rnd_trip_delay), - ntohl(rb_voip_mtc->end_sys_delay), - ntohl(rb_voip_mtc->signal_lvl), - ntohl(rb_voip_mtc->noise_lvl), - ntohl(rb_voip_mtc->rerl), - ntohl(rb_voip_mtc->gmin), - ntohl(rb_voip_mtc->r_factor), - ntohl(rb_voip_mtc->ext_r_factor), - ntohl(rb_voip_mtc->mos_lq), - ntohl(rb_voip_mtc->mos_cq), - ntohl(rb_voip_mtc->rx_config), - ntohl(rb_voip_mtc->jb_nom), - ntohl(rb_voip_mtc->jb_max), - ntohl(rb_voip_mtc->jb_abs_max)); + 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) { From ba48d7b518739cbf4dd97c1da8353329764e1eef Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Wed, 15 Apr 2015 11:06:31 -0400 Subject: [PATCH 81/82] fix call timeout when ICE fails --- daemon/call.c | 11 ++++++----- daemon/ice.c | 9 +++++++++ daemon/ice.h | 1 + 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index 34255e1d9..16a254aaa 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -1083,6 +1083,7 @@ static void call_timer_iterator(void *key, void *val, void *ptr) { struct call_monologue *ml; GSList *i; enum call_stream_state css; + atomic64 *timestamp; rwlock_lock_r(&c->master_lock); log_info_call(c); @@ -1104,6 +1105,8 @@ static void call_timer_iterator(void *key, void *val, void *ptr) { for (it = c->streams; it; it = it->next) { ps = it->data; + timestamp = &ps->last_packet; + if (!ps->media) goto next; sfd = ps->sfd; @@ -1114,10 +1117,8 @@ static void call_timer_iterator(void *key, void *val, void *ptr) { css = call_stream_state_machine(ps); - if (css == CSS_ICE) { - good = 1; - goto next; - } + if (css == CSS_ICE) + timestamp = &ps->media->ice_agent->last_activity; if (hlp->ports[sfd->fd.localport]) goto next; @@ -1135,7 +1136,7 @@ no_sfd: tmp_t_reason = 2; } - if (poller_now - atomic64_get(&ps->last_packet) < check) + if (poller_now - atomic64_get(timestamp) < check) good = 1; next: diff --git a/daemon/ice.c b/daemon/ice.c index 25ee60e2b..b7e9ea81b 100644 --- a/daemon/ice.c +++ b/daemon/ice.c @@ -249,6 +249,8 @@ static void __ice_agent_initialize(struct ice_agent *ag) { create_random_ice_string(call, &ag->ufrag[1], 8); create_random_ice_string(call, &ag->pwd[1], 26); + + atomic64_set(&ag->last_activity, poller_now); } static struct ice_agent *__ice_agent_new(struct call_media *media) { @@ -322,6 +324,7 @@ void ice_update(struct ice_agent *ag, struct stream_params *sp) { if (!ag) return; + atomic64_set(&ag->last_activity, poller_now); media = ag->media; call = media->call; @@ -690,6 +693,8 @@ static void __do_ice_checks(struct ice_agent *ag) { if (!ag->pwd[0].s) return; + atomic64_set(&ag->last_activity, poller_now); + __DBG("running checks, call "STR_FORMAT" tag "STR_FORMAT"", STR_FMT(&ag->call->callid), STR_FMT(&ag->media->monologue->tag)); @@ -1049,6 +1054,8 @@ int ice_request(struct packet_stream *ps, struct sockaddr_in6 *src, struct in6_a if (!ag) return -1; + atomic64_set(&ag->last_activity, poller_now); + ifa = get_interface_from_address(ag->local_interface, dst); err = "ICE/STUN binding request received on unknown local interface address"; if (!ifa) @@ -1157,6 +1164,8 @@ int ice_response(struct packet_stream *ps, struct sockaddr_in6 *src, struct in6_ if (!ag) return -1; + atomic64_set(&ag->last_activity, poller_now); + mutex_lock(&ag->lock); pair = g_hash_table_lookup(ag->transaction_hash, transaction); diff --git a/daemon/ice.h b/daemon/ice.h index 44fe15adc..cd7922ee4 100644 --- a/daemon/ice.h +++ b/daemon/ice.h @@ -111,6 +111,7 @@ struct ice_agent { struct call_media *media; struct local_interface *local_interface; int desired_family; + atomic64 last_activity; mutex_t lock; /* for elements below. and call must be locked in R */ /* lock order: in_lock first, then agent->lock */ From cb85cbf44f3d5f2d23420c2b3e204c4fe09500a5 Mon Sep 17 00:00:00 2001 From: Frederic-Philippe Metz Date: Tue, 21 Apr 2015 16:15:16 +0200 Subject: [PATCH 82/82] Fixed wrong Zeroing of stats member --- daemon/graphite.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daemon/graphite.c b/daemon/graphite.c index 4ead81427..14fcd1bb1 100644 --- a/daemon/graphite.c +++ b/daemon/graphite.c @@ -146,8 +146,8 @@ 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; }