Browse Source

Merge remote-tracking branch 'upstream/master'

Conflicts:
	daemon/graphite.c
	daemon/graphite.h
pull/101/head
Frederic-Philippe Metz 11 years ago
parent
commit
9f421a9d8f
42 changed files with 4589 additions and 1287 deletions
  1. +0
    -1
      README.md
  2. +1
    -1
      daemon/.ycm_extra_conf.py
  3. +5
    -3
      daemon/Makefile
  4. +25
    -32
      daemon/aux.c
  5. +409
    -94
      daemon/aux.h
  6. +603
    -336
      daemon/call.c
  7. +87
    -46
      daemon/call.h
  8. +31
    -32
      daemon/call_interfaces.c
  9. +0
    -2
      daemon/call_interfaces.h
  10. +39
    -35
      daemon/cli.c
  11. +30
    -11
      daemon/control_ng.c
  12. +6
    -0
      daemon/control_tcp.c
  13. +5
    -0
      daemon/control_udp.c
  14. +0
    -6
      daemon/cookie_cache.c
  15. +2
    -1
      daemon/crypto.h
  16. +6
    -4
      daemon/dtls.c
  17. +52
    -23
      daemon/graphite.c
  18. +1
    -0
      daemon/graphite.h
  19. +1357
    -0
      daemon/ice.c
  20. +208
    -0
      daemon/ice.h
  21. +53
    -16
      daemon/log.c
  22. +53
    -8
      daemon/log.h
  23. +14
    -37
      daemon/main.c
  24. +22
    -26
      daemon/poller.c
  25. +4
    -5
      daemon/poller.h
  26. +4
    -4
      daemon/rtcp.c
  27. +92
    -6
      daemon/rtp.c
  28. +11
    -0
      daemon/rtp.h
  29. +405
    -207
      daemon/sdp.c
  30. +6
    -1
      daemon/sdp.h
  31. +4
    -0
      daemon/str.c
  32. +23
    -0
      daemon/str.h
  33. +224
    -61
      daemon/stun.c
  34. +23
    -1
      daemon/stun.h
  35. +2
    -4
      debian/control
  36. +127
    -37
      kernel-module/xt_RTPENGINE.c
  37. +15
    -0
      kernel-module/xt_RTPENGINE.h
  38. +29
    -247
      tests/simulator-ng.pl
  39. +91
    -0
      tests/stun-client
  40. +204
    -0
      tests/stun-server
  41. +281
    -0
      utils/SRTP.pm
  42. +35
    -0
      utils/srtp-debug-helper

+ 0
- 1
README.md View File

@ -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


+ 1
- 1
daemon/.ycm_extra_conf.py View File

@ -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',


+ 5
- 3
daemon/Makefile View File

@ -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
@ -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)
@ -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

+ 25
- 32
daemon/aux.c View File

@ -34,6 +34,13 @@ 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
GList *g_list_link(GList *list, GList *el) {
@ -79,38 +86,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;
@ -219,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;
}

+ 409
- 94
daemon/aux.h View File

@ -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,20 +40,39 @@
/*** 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);
#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
/*** 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) {
@ -77,6 +80,10 @@ INLINE int g_hash_table_contains(GHashTable *h, const void *k) {
}
#endif
/* GQUEUE */
INLINE void g_queue_move(GQueue *dst, GQueue *src) {
GList *l;
while ((l = g_queue_pop_head_link(src)))
@ -86,8 +93,60 @@ 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 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);
@ -101,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);
@ -113,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;
@ -138,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;
@ -194,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) {
@ -211,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;
@ -303,21 +429,61 @@ 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;
@ -339,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)
@ -360,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)
@ -430,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)
@ -437,77 +613,216 @@ INLINE int __debug_rwlock_unlock_w(rwlock_t *m, const char *file, unsigned int l
/*** 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);
}
/*** ATOMIC BITFIELD OPERATIONS ***/
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;
/* checks if at least one of the flags is set */
INLINE int bf_isset(const volatile unsigned int *u, unsigned int f) {
if ((g_atomic_int_get(u) & f))
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;
}
/* checks if at least one of the flags is set */
INLINE int bf_isset(const unsigned int *u, unsigned int f) {
if ((*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;
}
INLINE void bf_set(unsigned int *u, unsigned int f) {
*u |= f;
/* 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(unsigned int *u, unsigned int f) {
*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_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)
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)]
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)));
}
unsigned int in6_addr_hash(const void *p);
int in6_addr_eq(const void *a, const void *b);
/*** ATOMIC64 ***/
#if GLIB_SIZEOF_VOID_P >= 8
typedef struct {
volatile void *p;
} atomic64;
INLINE u_int64_t atomic64_get(const atomic64 *u) {
return (u_int64_t) g_atomic_pointer_get(&u->p);
}
INLINE u_int64_t atomic64_get_na(const atomic64 *u) {
return (u_int64_t) u->p;
}
INLINE void atomic64_set(atomic64 *u, u_int64_t a) {
g_atomic_pointer_set(&u->p, (void *) a);
}
INLINE void atomic64_set_na(atomic64 *u, u_int64_t a) {
u->p = (void *) a;
}
INLINE void atomic64_add(atomic64 *u, u_int64_t a) {
g_atomic_pointer_add(&u->p, 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 atomic64_get_set(atomic64 *u, u_int64_t a) {
u_int64_t old;
do {
old = atomic64_get(u);
if (g_atomic_pointer_compare_and_exchange(&u->p, (void *) old, (void *) 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))
/*** 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

+ 603
- 336
daemon/call.c
File diff suppressed because it is too large
View File


+ 87
- 46
daemon/call.h View File

@ -3,6 +3,9 @@
/* XXX split everything into call_signalling.[ch] and call_packets.[ch] or w/e */
#include <sys/types.h>
#include <glib.h>
@ -12,6 +15,7 @@
#include <openssl/x509.h>
#include "compat.h"
#include "control_ng.h"
#include "aux.h"
enum termination_reason {
UNKNOWN=0,
@ -54,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,
};
@ -65,6 +76,7 @@ struct call_monologue;
#include "str.h"
#include "crypto.h"
#include "dtls.h"
#include "rtp.h"
@ -99,6 +111,8 @@ 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
#define SHARED_FLAG_ICE_LITE 0x00000800
/* struct stream_params */
#define SP_FLAG_NO_RTCP 0x00010000
@ -112,21 +126,24 @@ 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
#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
#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
#define PS_FLAG_ICE SHARED_FLAG_ICE
/* struct call_media */
#define MEDIA_FLAG_INITIALIZED 0x00010000
@ -141,15 +158,22 @@ 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
#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)
@ -165,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;
@ -178,6 +204,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;
};
@ -187,33 +214,31 @@ extern const struct transport_protocol transport_protocols[];
struct stats {
u_int64_t packets;
u_int64_t bytes;
u_int64_t errors;
atomic64 packets;
atomic64 bytes;
atomic64 errors;
};
struct totalstats {
time_t started;
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;
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 {
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;
@ -227,6 +252,10 @@ struct stream_params {
int desired_family;
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 {
@ -249,6 +278,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;
@ -258,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 */
@ -268,9 +306,10 @@ 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;
atomic64 last_packet;
GHashTable *rtp_stats; /* LOCK: call->master_lock */
#if RTP_LOOP_PROTECT
/* LOCK: in_lock: */
@ -282,8 +321,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 */
@ -302,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;
@ -314,8 +352,9 @@ struct call_media {
GQueue streams; /* normally RTP + RTCP */
GSList *endpoint_maps;
GHashTable *rtp_payload_types;
unsigned int media_flags;
volatile unsigned int media_flags;
};
/* half a dialogue */
@ -365,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;
@ -375,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 {
@ -390,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 {
@ -399,18 +443,15 @@ 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 */
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;
struct totalstats totalstats_interval;
/* control_ng_stats stuff */
@ -455,22 +496,23 @@ 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 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);
}
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);
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_add(struct timeval *result, const struct timeval *a, const struct timeval *b);
INLINE void *call_malloc(struct call *c, size_t l) {
@ -483,6 +525,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;
@ -521,10 +565,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;
@ -534,6 +575,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

+ 31
- 32
daemon/call_interfaces.c View File

@ -16,6 +16,8 @@
#include "str.h"
#include "control_tcp.h"
#include "control_udp.h"
#include "rtp.h"
#include "ice.h"
@ -221,7 +223,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;
}
@ -274,14 +276,21 @@ 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);
ice_candidates_free(&s->ice_candidates);
g_slice_free1(sizeof(*s), s);
}
static void streams_free(GQueue *q) {
g_queue_clear_full(q, sp_free);
}
@ -294,7 +303,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));
@ -386,8 +395,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);
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:
@ -431,20 +440,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);
atomic64_get(&m->stats.bytes), 0, 0);
while (q.head) {
c = g_queue_pop_head(&q);
@ -726,14 +729,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", 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;
totals->packets += s->packets;
totals->bytes += s->bytes;
totals->errors += 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) {
@ -772,7 +775,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", atomic64_get(&ps->last_packet));
flags = bencode_dictionary_add_list(dict, "flags");
@ -788,8 +791,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 < atomic64_get(&ps->last_packet))
totals->last_packet = atomic64_get(&ps->last_packet);
/* XXX distinguish between input and output */
s = &totals->totals[0];
@ -918,7 +921,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;
@ -932,7 +934,6 @@ static void ng_list_calls( struct callmaster *m, bencode_item_t *output, long lo
rwlock_unlock_r(&m->hashlock);
}
#endif
@ -957,7 +958,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;
@ -974,4 +974,3 @@ const char *call_list_ng(bencode_item_t *input, struct callmaster *m, bencode_it
return NULL;
}
#endif

+ 0
- 2
daemon/call_interfaces.h View File

@ -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);


+ 39
- 35
daemon/cli.c View File

@ -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 :%llu\n", (unsigned long long)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 :%llu\n",(unsigned long long)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 :%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",atomic64_get(&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",atomic64_get(&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",atomic64_get(&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",atomic64_get(&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",atomic64_get(&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", atomic64_get(&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",atomic64_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",
@ -97,9 +100,8 @@ 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));
struct timeval tim_result_duration;
struct timeval now;
if (len<=1) {
printlen = snprintf(replybuffer,(outbufend-replybuffer), "%s\n", "More parameters required.");
@ -131,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,
@ -148,15 +150,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, "
"%llu p, %llu b, %llu 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)" : "",
(unsigned long long) ps->stats.packets,
(unsigned long long) ps->stats.bytes,
(unsigned long long) ps->stats.errors,
(unsigned long long) 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);
}
}
@ -165,6 +167,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) {
@ -244,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;
}
@ -269,18 +271,19 @@ 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;
}
}
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) {
@ -289,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;
@ -303,10 +306,10 @@ 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\n", strerror(errno));
ilog(LOG_INFO, "Accept error:%s", strerror(errno));
goto next;
}
@ -316,23 +319,24 @@ 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);
} while (readbytes > 0 && inlen < sizeof(inbuf)-1);
ilog(LOG_INFO, "Got CLI command:%s\n",inbuf);
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);


+ 30
- 11
daemon/control_ng.c View File

@ -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")) {
@ -146,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";
@ -165,15 +170,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);


+ 6
- 0
daemon/control_tcp.c View File

@ -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"))


+ 5
- 0
daemon/control_udp.c View File

@ -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')


+ 0
- 6
daemon/cookie_cache.c View File

@ -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;
}


+ 2
- 1
daemon/crypto.h View File

@ -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);


+ 6
- 4
daemon/dtls.c View File

@ -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;


+ 52
- 23
daemon/graphite.c View File

@ -25,7 +25,8 @@ 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;
// HEAD: static time_t g_now, next_run;
static char* graphite_prefix = NULL;
void set_prefix(char* prefix) {
@ -121,44 +122,58 @@ 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;
mutex_lock(&cm->totalstats_lock);
struct totalstats ts;
/* atomically copy values to stack and reset to zero */
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;
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);
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); 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;
rc = sprintf(ptr,"%s.totals.average_call_dur.tv_sec %llu %llu\n",hostname, (unsigned long long) ts.total_average_call_dur.tv_sec,(unsigned long long)g_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"%s.totals.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.average_call_dur.tv_usec %lu %llu\n",hostname, ts.total_average_call_dur.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"%s.totals.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.forced_term_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_forced_term_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"%s.totals.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.managed_sess "UINT64F" %llu\n",hostname, ts.total_managed_sess,(unsigned long long)g_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"%s.totals.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.nopacket_relayed_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_nopacket_relayed_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"%s.totals.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.oneway_stream_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_oneway_stream_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"%s.totals.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.regular_term_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_regular_term_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"%s.totals.relayed_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.relayed_errors "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_relayed_errors),(unsigned long long)g_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"%s.totals.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.relayed_packets "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_relayed_packets),(unsigned long long)g_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"%s.totals.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);
mutex_unlock(&cm->totalstats_lock);
rc = sprintf(ptr,"%s.totals.silent_timeout_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_silent_timeout_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"%s.totals.timeout_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_timeout_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
rc = write(graphite_sock, data_to_send, 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;
@ -214,13 +229,13 @@ void graphite_loop_run(struct callmaster* callmaster, int seconds) {
}
}
g_now = time(NULL);
if (g_now < next_run) {
gettimeofday(&g_now, NULL);
if (g_now.tv_sec < next_run) {
usleep(100000);
return;
}
next_run = g_now + seconds;
next_run = g_now.tv_sec + seconds;
if (!cm)
cm = callmaster;
@ -241,3 +256,17 @@ void graphite_loop_run(struct callmaster* callmaster, int seconds) {
}
}
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,cm->conf.graphite_port);
while (!g_shutdown)
graphite_loop_run(cm,cm->conf.graphite_interval); // time in seconds
}

+ 1
- 0
daemon/graphite.h View File

@ -14,5 +14,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);
void graphite_loop(void *d);
#endif /* GRAPHITE_H_ */

+ 1357
- 0
daemon/ice.c
File diff suppressed because it is too large
View File


+ 208
- 0
daemon/ice.h View File

@ -0,0 +1,208 @@
#ifndef __ICE_H__
#define __ICE_H__
#include <arpa/inet.h>
#include <glib.h>
#include <sys/time.h>
#include <sys/types.h>
#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

+ 53
- 16
daemon/log.c View File

@ -6,6 +6,15 @@
#include "str.h"
#include "call.h"
#include "poller.h"
#include "ice.h"
struct log_limiter_entry {
char *prefix;
char *msg;
};
@ -80,12 +89,13 @@ 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);
}
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 +104,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;
@ -117,6 +118,19 @@ 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;
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);
@ -128,8 +142,15 @@ 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;
lle.prefix = prefix;
lle.msg = msg;
mutex_lock(&__log_limiter_lock);
@ -139,10 +160,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;
}
@ -162,7 +186,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);
@ -176,8 +200,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);
}

+ 53
- 8
daemon/log.h View File

@ -5,18 +5,26 @@
#include <syslog.h>
#include <glib.h>
#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 struct ice_agent *ice_agent;
const void *ptr;
} u;
enum {
LOG_INFO_NONE = 0,
LOG_INFO_CALL,
LOG_INFO_STREAM_FD,
LOG_INFO_STR,
LOG_INFO_C_STRING,
LOG_INFO_ICE_AGENT,
} e;
};
@ -45,7 +53,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);
@ -57,28 +78,52 @@ 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:
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 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);
}


+ 14
- 37
daemon/main.c View File

@ -27,10 +27,11 @@
#include "call_interfaces.h"
#include "cli.h"
#include "graphite.h"
#include "ice.h"
#define REDIS_MODULE_VERSION "redis/6"
#define REDIS_MODULE_VERSION "redis/8"
@ -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;
@ -463,6 +463,7 @@ static void init_everything() {
resources();
sdp_init();
dtls_init();
ice_init();
}
void redis_mod_verify(void *dlh) {
@ -568,6 +569,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) {
@ -601,8 +605,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)
@ -627,34 +631,6 @@ no_kernel:
die("Refusing to continue without working Redis database");
}
static void timer_loop(void *d) {
struct poller *p = d;
while (!global_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 (!global_shutdown)
graphite_loop_run(cm,graphite_interval); // time in seconds
}
static void poller_loop(void *d) {
struct poller *p = d;
while (!global_shutdown)
poller_poll(p, 100);
}
int main(int argc, char **argv) {
struct main_context ctx;
int idx=0;
@ -666,9 +642,10 @@ 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);
if (num_threads < 1) {
#ifdef _SC_NPROCESSORS_ONLN
@ -682,7 +659,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);
}


+ 22
- 26
daemon/poller.c View File

@ -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];
@ -480,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;
retry:
gettimeofday(&tv, NULL);
if (tv.tv_sec != poller_now)
goto now;
if (i)
return;
while (!g_shutdown) {
gettimeofday(&tv, NULL);
if (tv.tv_sec != poller_now)
goto now;
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:
poller_now = tv.tv_sec;
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);
}

+ 4
- 5
daemon/poller.h View File

@ -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);
@ -45,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 *);


+ 4
- 4
daemon/rtcp.c View File

@ -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) {


+ 92
- 6
daemon/rtp.c View File

@ -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;
@ -184,13 +226,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;
@ -242,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;
}

+ 11
- 0
daemon/rtp.h View File

@ -4,6 +4,7 @@
#include "str.h"
#include <glib.h>
@ -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 *);


+ 405
- 207
daemon/sdp.c View File

@ -13,6 +13,8 @@
#include "call.h"
#include "crypto.h"
#include "dtls.h"
#include "rtp.h"
#include "ice.h"
struct network_address {
str network_type;
@ -58,7 +60,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 +68,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 {
@ -74,17 +77,19 @@ 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 raddr_str;
str related_address_str;
str rport_str;
str related_port_str;
unsigned long component;
unsigned long priority;
struct ice_candidate cand_parsed;
int parsed:1;
};
@ -143,6 +148,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 */
@ -156,7 +169,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,
@ -169,6 +185,7 @@ struct sdp_attribute {
ATTR_MID,
ATTR_FINGERPRINT,
ATTR_SETUP,
ATTR_RTPMAP,
} attr;
union {
@ -179,24 +196,19 @@ struct sdp_attribute {
struct attribute_group group;
struct attribute_fingerprint fingerprint;
struct attribute_setup setup;
struct attribute_rtpmap rtpmap;
} u;
};
static const char ice_chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
//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 +321,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 +349,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 +372,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 +405,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 +431,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 +444,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,41 +574,82 @@ 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;
struct attribute_candidate *c;
start = output->value.s;
end = start + output->value.len;
output->attr = ATTR_CANDIDATE;
c = &output->u.candidate;
EXTRACT_TOKEN(u.candidate.foundation);
PARSE_INIT;
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;
output->u.candidate.priority = strtoul(output->u.candidate.priority_str.s, &ep, 10);
if (ep == output->u.candidate.priority_str.s)
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.parsed = 1;
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;
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;
c->cand_parsed.related_port = strtoul(c->related_port_str.s, &ep, 10);
if (ep == c->related_port_str.s)
return -1;
done:
c->parsed = 1;
return 0;
}
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 +711,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,16 +803,18 @@ 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"))
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;
@ -731,7 +840,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;
@ -910,35 +1019,35 @@ 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;
}
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 +1077,108 @@ 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 ? q->head : NULL; 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;
}
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) {
struct sdp_session *session;
struct sdp_media *media;
@ -996,9 +1207,13 @@ 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))
goto error;
/* a=crypto */
attr = attr_get_by_id(&media->attributes, ATTR_CRYPTO);
@ -1050,9 +1265,7 @@ int sdp_streams(const GQueue *sessions, GQueue *streams, struct sdp_ng_flags *fl
sp->fingerprint.hash_func->num_bytes);
}
/* a=candidate */
if (attr_get_by_id(&media->attributes, ATTR_CANDIDATE))
SP_SET(sp, ICE);
__sdp_ice(sp, media);
/* determine RTCP endpoint */
@ -1251,13 +1464,29 @@ 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)
{
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))
@ -1291,22 +1520,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)
{
@ -1319,6 +1532,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;
@ -1378,6 +1594,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)
@ -1440,124 +1659,135 @@ 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) {
int id;
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);
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;
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));
/* 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");
}
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) {
@ -1583,12 +1813,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";
@ -1642,16 +1870,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;
@ -1692,11 +1915,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;
@ -1744,11 +1962,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");
@ -1771,31 +1989,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++;
@ -1813,8 +2016,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));
}

+ 6
- 1
daemon/sdp.h View File

@ -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 *);
@ -57,4 +56,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

+ 4
- 0
daemon/str.c View File

@ -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);
}

+ 23
- 0
daemon/str.h View File

@ -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 }
@ -56,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)
@ -70,6 +73,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 *);
@ -208,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

+ 224
- 61
daemon/stun.c View File

@ -6,11 +6,13 @@
#include <zlib.h>
#include <openssl/hmac.h>
#include <glib.h>
#include <endian.h>
#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;
}

+ 23
- 1
daemon/stun.h View File

@ -4,6 +4,7 @@
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#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

+ 2
- 4
debian/control View File

@ -6,18 +6,17 @@ 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),
markdown,
zlib1g-dev
Standards-Version: 3.9.5
Standards-Version: 3.9.6
Homepage: http://sipwise.com/
Package: ngcp-rtpengine-daemon
Architecture: any
Pre-Depends: ngcp-system-tools
Depends: ${misc:Depends},
${shlibs:Depends}
Conflicts: ngcp-mediaproxy-ng-daemon
@ -63,7 +62,6 @@ Description: IPtables kernel module for the NGCP media proxy - source.
Package: ngcp-rtpengine-kernel-dkms
Architecture: all
Pre-Depends: ngcp-system-tools
Depends: dkms (>= 1.95),
${misc:Depends}
Conflicts: ngcp-mediaproxy-ng-kernel-dkms


+ 127
- 37
kernel-module/xt_RTPENGINE.c View File

@ -17,6 +17,8 @@
#include <net/dst.h>
#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#include <linux/bsearch.h>
#include <linux/atomic.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
@ -141,13 +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_stats_a stats;
struct rtpengine_rtp_stats_a rtp_stats[NUM_PAYLOAD_TYPES];
struct re_crypto_context decrypt;
struct re_crypto_context encrypt;
@ -815,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;
@ -841,9 +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));
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;
@ -970,7 +985,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);
@ -979,10 +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);
spin_unlock_irqrestore(&g->stats_lock, flags);
(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],
(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)
@ -1373,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)
@ -1404,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));
@ -1452,9 +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));
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;
@ -1899,6 +1923,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 +2018,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 +2051,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;
}
@ -2105,16 +2165,34 @@ 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;
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);
@ -2169,17 +2247,28 @@ 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;
pkt_idx = packet_index(&g->decrypt, &g->target.decrypt, rtp.header);
if (srtp_auth_validate(&g->decrypt, &g->target.decrypt, &rtp, pkt_idx))
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;
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;
@ -2197,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) {
@ -2213,14 +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 += skb->len;
atomic64_inc(&g->stats.packets);
atomic64_add(datalen, &g->stats.bytes);
}
if (rtp_pt_idx >= 0) {
atomic64_inc(&g->rtp_stats[rtp_pt_idx].packets);
atomic64_add(datalen, &g->rtp_stats[rtp_pt_idx].bytes);
}
spin_unlock_irqrestore(&g->stats_lock, flags);
else if (rtp_pt_idx == -2)
/* not RTP */ ;
else if (rtp_pt_idx == -1)
atomic64_inc(&g->stats.errors);
target_push(g);
table_push(t);
@ -2228,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:


+ 15
- 0
kernel-module/xt_RTPENGINE.h View File

@ -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];
};


+ 29
- 247
tests/simulator-ng.pl View File

@ -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) {
@ -479,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);
@ -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});
@ -777,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";


+ 91
- 0
tests/stun-client View File

@ -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)));
}

+ 204
- 0
tests/stun-server View File

@ -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)));
}

+ 281
- 0
utils/SRTP.pm View File

@ -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;

+ 35
- 0
utils/srtp-debug-helper View File

@ -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");

Loading…
Cancel
Save