Browse Source

Merge branch 'dev-fmetz' of https://github.com/1and1/rtpengine into dev-fmetz

pull/120/head^2
pkuzak 11 years ago
parent
commit
2eb65cea99
49 changed files with 5101 additions and 1478 deletions
  1. +4
    -0
      .gitreview
  2. +5
    -1
      README.md
  3. +1
    -1
      daemon/.ycm_extra_conf.py
  4. +6
    -3
      daemon/Makefile
  5. +25
    -32
      daemon/aux.c
  6. +409
    -94
      daemon/aux.h
  7. +700
    -392
      daemon/call.c
  8. +90
    -50
      daemon/call.h
  9. +31
    -32
      daemon/call_interfaces.c
  10. +0
    -2
      daemon/call_interfaces.h
  11. +71
    -65
      daemon/cli.c
  12. +30
    -11
      daemon/control_ng.c
  13. +6
    -0
      daemon/control_tcp.c
  14. +5
    -0
      daemon/control_udp.c
  15. +0
    -6
      daemon/cookie_cache.c
  16. +2
    -1
      daemon/crypto.h
  17. +6
    -4
      daemon/dtls.c
  18. +161
    -35
      daemon/graphite.c
  19. +2
    -0
      daemon/graphite.h
  20. +1381
    -0
      daemon/ice.c
  21. +209
    -0
      daemon/ice.h
  22. +54
    -16
      daemon/log.c
  23. +53
    -8
      daemon/log.h
  24. +19
    -37
      daemon/main.c
  25. +22
    -26
      daemon/poller.c
  26. +4
    -5
      daemon/poller.h
  27. +9
    -9
      daemon/rtcp.c
  28. +35
    -35
      daemon/rtcp_xr.c
  29. +92
    -6
      daemon/rtp.c
  30. +11
    -0
      daemon/rtp.h
  31. +405
    -207
      daemon/sdp.c
  32. +6
    -1
      daemon/sdp.h
  33. +4
    -0
      daemon/str.c
  34. +23
    -0
      daemon/str.h
  35. +229
    -61
      daemon/stun.c
  36. +23
    -1
      daemon/stun.h
  37. +122
    -0
      debian/changelog
  38. +4
    -4
      debian/control
  39. +2
    -1
      debian/ngcp-rtpengine-daemon.default
  40. +1
    -0
      debian/ngcp-rtpengine-daemon.init
  41. +6
    -1
      el/rtpengine.init
  42. +35
    -38
      el/rtpengine.spec
  43. +143
    -46
      kernel-module/xt_RTPENGINE.c
  44. +15
    -0
      kernel-module/xt_RTPENGINE.h
  45. +29
    -247
      tests/simulator-ng.pl
  46. +91
    -0
      tests/stun-client
  47. +204
    -0
      tests/stun-server
  48. +281
    -0
      utils/SRTP.pm
  49. +35
    -0
      utils/srtp-debug-helper

+ 4
- 0
.gitreview View File

@ -0,0 +1,4 @@
[gerrit]
host=gerrit.mgm.sipwise.com
port=29418
project=rtpengine

+ 5
- 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
@ -187,6 +186,7 @@ option and which are reproduced below:
--dtls-passive Always prefer DTLS passive role
-g, --graphite=[IP46:]PORT TCP address of graphite statistics server
-w, --graphite-interval=INT Graphite data statistics send interval
--graphite-prefix=STRING Graphite prefix for every line
Most of these options are indeed optional, with two exceptions. It's mandatory to specify at least one local
IP address through `--interface`, and at least one of the `--listen-...` options must be given.
@ -370,6 +370,10 @@ The options are described in more detail below.
Interval of the time when information is sent to the graphite server.
* --graphite-prefix
Add a prefix for every graphite line.
A typical command line (enabling both UDP and NG protocols) thus may look like:
/usr/sbin/rtpengine --table=0 --interface=10.64.73.31 --interface=2001:db8::4f3:3d \


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


+ 6
- 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,8 @@ endif
SRCS= main.c kernel.c poller.c aux.c control_tcp.c streambuf.c call.c control_udp.c redis.c \
bencode.c cookie_cache.c udp_listener.c control_ng.c sdp.c str.c stun.c rtcp.c \
crypto.c rtp.c call_interfaces.c dtls.c log.c cli.c graphite.c rtcp_xr.c
crypto.c rtp.c call_interfaces.c dtls.c log.c cli.c graphite.c ice.c rtcp_xr.c
OBJS= $(SRCS:.c=.o)
@ -83,8 +84,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

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


+ 90
- 50
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,46 +204,42 @@ 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;
};
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 timespec delay_min;
struct timespec delay_avg;
struct timespec delay_max;
u_int8_t in_tos_tclass;
atomic64 in_tos_tclass;
};
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;
@ -231,6 +253,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 {
@ -253,6 +279,15 @@ 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;
atomic64 in_tos_tclass;
};
struct packet_stream {
mutex_t in_lock,
out_lock;
@ -262,6 +297,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 */
@ -272,9 +308,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: */
@ -286,8 +323,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 */
@ -306,8 +342,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;
@ -318,8 +354,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 */
@ -369,8 +406,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;
@ -379,6 +417,7 @@ struct interface_address {
struct in6_addr advertised;
str ice_foundation;
char foundation_buf[16];
unsigned int preference; /* starting with 0 */
};
struct callmaster_config {
@ -394,6 +433,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 {
@ -403,18 +445,16 @@ 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 */
mutex_t statspslock;
struct totalstats totalstats;
struct totalstats totalstats_interval;
/* control_ng_stats stuff */
@ -459,22 +499,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) {
@ -487,6 +528,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;
@ -525,10 +568,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;
@ -538,6 +578,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);


+ 71
- 65
daemon/cli.c View File

@ -18,44 +18,49 @@
static const char* TRUNCATED = " ... Output truncated. Increase Output Buffer ...\n";
#define truncate_output(x) do { x -= strlen(TRUNCATED)+1; x += sprintf(x,"%s",TRUNCATED); } while (0);
#define truncate_output(x) strcpy(x - strlen(TRUNCATED) - 1, TRUNCATED)
#define ADJUSTLEN(printlen,outbuflen,replybuffer) do { if (printlen>=(outbufend-replybuffer)) \
truncate_output(replybuffer); \
replybuffer += (printlen>=outbufend-replybuffer)?outbufend-replybuffer:printlen; } while (0);
#define ADJUSTLEN(printlen,outbuflen,replybuffer) do { \
replybuffer += (printlen>=outbufend-replybuffer)?outbufend-replybuffer:printlen; \
if (replybuffer == outbufend) \
truncate_output(replybuffer); \
} while (0);
static void cli_incoming_list_totals(char* buffer, int len, struct callmaster* m, char* replybuffer, const char* outbufend) {
int printlen=0;
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",
@ -98,9 +103,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.");
@ -132,7 +136,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,
@ -150,45 +154,45 @@ static void cli_incoming_list_callid(char* buffer, int len, struct callmaster* m
#if (RE_HAS_MEASUREDELAY)
if (!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) {
printlen = snprintf(replybuffer,(outbufend-replybuffer), "------ Media #%u, port %5u <> %15s:%-5hu%s, "
"%llu p, %llu b, %llu e, %llu last_packet\n",
md->index,
(unsigned int) (ps->sfd ? ps->sfd->fd.localport : 0),
smart_ntop_p_buf(&ps->endpoint.ip46), ps->endpoint.port,
(!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) ? " (RTCP)" : "",
(unsigned long long) ps->stats.packets,
(unsigned long long) ps->stats.bytes,
(unsigned long long) ps->stats.errors,
(unsigned long long) ps->last_packet);
printlen = snprintf(replybuffer,(outbufend-replybuffer), "------ Media #%u, port %5u <> %15s:%-5hu%s, "
""UINT64F" p, "UINT64F" b, "UINT64F" e, "UINT64F" last_packet\n",
md->index,
(unsigned int) (ps->sfd ? ps->sfd->fd.localport : 0),
smart_ntop_p_buf(&ps->endpoint.ip46), ps->endpoint.port,
(!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) ? " (RTCP)" : "",
atomic64_get(&ps->stats.packets),
atomic64_get(&ps->stats.bytes),
atomic64_get(&ps->stats.errors),
atomic64_get(&ps->last_packet));
} else {
printlen = snprintf(replybuffer,(outbufend-replybuffer), "------ Media #%u, port %5u <> %15s:%-5hu%s, "
"%llu p, %llu b, %llu e, %llu last_packet, %llu.%09llu delay_min, %llu.%09llu delay_avg, %llu.%09llu delay_max\n",
md->index,
(unsigned int) (ps->sfd ? ps->sfd->fd.localport : 0),
smart_ntop_p_buf(&ps->endpoint.ip46), ps->endpoint.port,
(!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) ? " (RTCP)" : "",
(unsigned long long) ps->stats.packets,
(unsigned long long) ps->stats.bytes,
(unsigned long long) ps->stats.errors,
(unsigned long long) ps->last_packet,
(unsigned long long) ps->stats.delay_min.tv_sec,
(unsigned long long) ps->stats.delay_min.tv_nsec,
(unsigned long long) ps->stats.delay_avg.tv_sec,
(unsigned long long) ps->stats.delay_avg.tv_nsec,
(unsigned long long) ps->stats.delay_max.tv_sec,
(unsigned long long) ps->stats.delay_max.tv_nsec);
printlen = snprintf(replybuffer,(outbufend-replybuffer), "------ Media #%u, port %5u <> %15s:%-5hu%s, "
""UINT64F" p, "UINT64F" b, "UINT64F" e, "UINT64F" last_packet, %llu.%09llu delay_min, %llu.%09llu delay_avg, %llu.%09llu delay_max\n",
md->index,
(unsigned int) (ps->sfd ? ps->sfd->fd.localport : 0),
smart_ntop_p_buf(&ps->endpoint.ip46), ps->endpoint.port,
(!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) ? " (RTCP)" : "",
atomic64_get(&ps->stats.packets),
atomic64_get(&ps->stats.bytes),
atomic64_get(&ps->stats.errors),
atomic64_get(&ps->last_packet),
(unsigned long long) ps->stats.delay_min.tv_sec,
(unsigned long long) ps->stats.delay_min.tv_nsec,
(unsigned long long) ps->stats.delay_avg.tv_sec,
(unsigned long long) ps->stats.delay_avg.tv_nsec,
(unsigned long long) ps->stats.delay_max.tv_sec,
(unsigned long long) ps->stats.delay_max.tv_nsec);
}
#else
printlen = snprintf(replybuffer,(outbufend-replybuffer), "------ Media #%u, port %5u <> %15s:%-5hu%s, "
"%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));
#endif
ADJUSTLEN(printlen,outbufend,replybuffer);
}
@ -198,6 +202,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) {
@ -277,7 +282,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;
}
@ -302,18 +306,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) {
@ -322,11 +327,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;
@ -336,10 +341,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;
}
@ -349,23 +354,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;


+ 161
- 35
daemon/graphite.c View File

@ -9,21 +9,58 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <string.h>
#include "log.h"
#include "call.h"
#include "graphite.h"
static int graphite_sock=-1;
static int connectinprogress=0;
static u_int32_t graphite_ipaddress;
static int graphite_port=0;
static struct callmaster* cm=0;
//struct totalstats totalstats_prev;
static time_t 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) {
graphite_prefix = prefix;
}
/**
* Set a file descriptor to blocking or non-blocking mode.
*
* @param fd The file descriptor
* @param blocking 0:non-blocking mode, 1:blocking mode
*
* @return 1:success, 0:failure.
**/
int fd_set_blocking(int fd, int blocking) {
/* Save the current flags */
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1)
return 0;
if (blocking)
flags &= ~O_NONBLOCK;
else
flags |= O_NONBLOCK;
return fcntl(fd, F_SETFL, flags) != -1;
}
int connect_to_graphite_server(u_int32_t ipaddress, int port) {
if (graphite_sock>0)
close(graphite_sock);
graphite_sock=-1;
//int reconnect=0;
int rc=0;
struct sockaddr_in sin;
memset(&sin,0,sizeof(sin));
@ -32,9 +69,9 @@ int connect_to_graphite_server(u_int32_t ipaddress, int port) {
graphite_ipaddress = ipaddress;
graphite_port = port;
rc = graphite_sock = socket(AF_INET, SOCK_STREAM,0);
if(rc<0) {
ilog(LOG_ERROR,"Couldn't make socket for connecting to graphite.");
graphite_sock = socket(AF_INET, SOCK_STREAM,0);
if(graphite_sock<0) {
ilog(LOG_ERROR,"Couldn't make socket for connecting to graphite.Reason:%s\n",strerror(errno));
return -1;
}
@ -48,18 +85,26 @@ int connect_to_graphite_server(u_int32_t ipaddress, int port) {
goto error;
}
rc = fd_set_blocking(graphite_sock,0);
if (!rc) {
ilog(LOG_ERROR,"Could not set the socket to nonblocking.");
goto error;
}
struct in_addr ip;
ip.s_addr = graphite_ipaddress;
ilog(LOG_INFO, "Connecting to graphite server %s at port:%i with fd:%i",inet_ntoa(ip),graphite_port,graphite_sock);
rc = connect(graphite_sock, (struct sockaddr *)&sin, sizeof(sin));
if (rc==-1) {
ilog(LOG_ERROR, "Connection could not be established. Trying again next time of graphite-interval.");
ilog(LOG_WARN, "Connection information:%s\n",strerror(errno));
if (errno==EINPROGRESS) {
connectinprogress=1;
return 0;
}
goto error;
}
ilog(LOG_INFO, "Graphite server connected.");
return graphite_sock;
return 0;
error:
close(graphite_sock);
@ -77,35 +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;
struct totalstats ts;
mutex_lock(&cm->totalstats_lock);
rc = sprintf(ptr,"%s.totals.average_call_dur.tv_sec %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_average_call_dur.tv_sec,(unsigned long long)g_now); ptr += rc;
rc = sprintf(ptr,"%s.totals.average_call_dur.tv_usec %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_average_call_dur.tv_usec,(unsigned long long)g_now); ptr += rc;
rc = sprintf(ptr,"%s.totals.forced_term_sess %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_forced_term_sess,(unsigned long long)g_now); ptr += rc;
rc = sprintf(ptr,"%s.totals.managed_sess %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_managed_sess,(unsigned long long)g_now); ptr += rc;
rc = sprintf(ptr,"%s.totals.nopacket_relayed_sess %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_nopacket_relayed_sess,(unsigned long long)g_now); ptr += rc;
rc = sprintf(ptr,"%s.totals.oneway_stream_sess %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_oneway_stream_sess,(unsigned long long)g_now); ptr += rc;
rc = sprintf(ptr,"%s.totals.regular_term_sess %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_regular_term_sess,(unsigned long long)g_now); ptr += rc;
rc = sprintf(ptr,"%s.totals.relayed_errors %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_relayed_errors,(unsigned long long)g_now); ptr += rc;
rc = sprintf(ptr,"%s.totals.relayed_packets %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_relayed_packets,(unsigned long long)g_now); ptr += rc;
rc = sprintf(ptr,"%s.totals.silent_timeout_sess %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_silent_timeout_sess,(unsigned long long)g_now); ptr += rc;
rc = sprintf(ptr,"%s.totals.timeout_sess %llu %llu\n",hostname, (unsigned long long) cm->totalstats_interval.total_timeout_sess,(unsigned long long)g_now); ptr += rc;
/* 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);
ZERO(cm->totalstats_interval);
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(cm->totalstats_interval.total_average_call_dur);
ZERO(cm->totalstats_interval.total_managed_sess);
mutex_unlock(&cm->totalstats_interval.total_average_lock);
mutex_unlock(&cm->totalstats_lock);
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"%s.totals.average_call_dur.tv_sec %llu %llu\n",hostname, (unsigned long long) ts.total_average_call_dur.tv_sec,(unsigned long long)g_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"%s.totals.average_call_dur.tv_usec %lu %llu\n",hostname, ts.total_average_call_dur.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"%s.totals.forced_term_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_forced_term_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"%s.totals.managed_sess "UINT64F" %llu\n",hostname, ts.total_managed_sess,(unsigned long long)g_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"%s.totals.nopacket_relayed_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_nopacket_relayed_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"%s.totals.oneway_stream_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_oneway_stream_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"%s.totals.regular_term_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_regular_term_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"%s.totals.relayed_errors "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_relayed_errors),(unsigned long long)g_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"%s.totals.relayed_packets "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_relayed_packets),(unsigned long long)g_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"%s.totals.silent_timeout_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_silent_timeout_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s.",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"%s.totals.timeout_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_timeout_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
rc = write(graphite_sock, data_to_send, 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;
@ -120,27 +188,85 @@ error:
void graphite_loop_run(struct callmaster* callmaster, int seconds) {
int rc=0;
fd_set wfds;
FD_ZERO(&wfds);
struct timeval tv;
int optval=0;
socklen_t optlen=sizeof(optval);
if (connectinprogress && graphite_sock>0) {
FD_SET(graphite_sock,&wfds);
tv.tv_sec = 0;
tv.tv_usec = 1000000;
rc = select (graphite_sock+1, NULL, &wfds, NULL, &tv);
if ((rc == -1) && (errno == EINTR)) {
ilog(LOG_ERROR,"Error on the socket.");
close(graphite_sock);
graphite_sock=-1;connectinprogress=0;
return;
} else if (rc==0) {
// timeout
return;
} else {
if (!FD_ISSET(graphite_sock,&wfds)) {
ilog(LOG_WARN,"fd active but not the graphite fd.");
close(graphite_sock);
graphite_sock=-1;connectinprogress=0;
return;
}
rc = getsockopt(graphite_sock, SOL_SOCKET, SO_ERROR, &optval, &optlen);
if (rc) ilog(LOG_ERROR,"getsockopt failure.");
if (optval != 0) {
ilog(LOG_ERROR,"Socket connect failed. fd: %i, Reason: %s\n",graphite_sock, strerror(optval));
close(graphite_sock);
graphite_sock=-1;connectinprogress=0;
return;
}
ilog(LOG_INFO, "Graphite server connected.");
connectinprogress=0;
next_run=0; // fake next run to skip sleep after reconnect
}
}
g_now = time(NULL);
if (g_now < next_run)
goto sleep;
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;
if (graphite_sock < 0) {
if (graphite_sock < 0 && !connectinprogress) {
rc = connect_to_graphite_server(graphite_ipaddress, graphite_port);
if (rc) {
close(graphite_sock);
graphite_sock=-1;
}
}
if (rc>=0) {
if (graphite_sock>0 && !connectinprogress) {
rc = send_graphite_data();
if (rc<0) {
ilog(LOG_ERROR,"Sending graphite data failed.");
}
}
sleep:
usleep(100000);
}
void graphite_loop(void *d) {
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
}

+ 2
- 0
daemon/graphite.h View File

@ -13,5 +13,7 @@
int connect_to_graphite_server(u_int32_t ipaddress, int port);
int send_graphite_data();
void graphite_loop_run(struct callmaster* cm, int seconds);
void set_prefix(char* prefix);
void graphite_loop(void *d);
#endif /* GRAPHITE_H_ */

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


+ 209
- 0
daemon/ice.h View File

@ -0,0 +1,209 @@
#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;
atomic64 last_activity;
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

+ 54
- 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;
};
@ -81,12 +90,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 = "";
@ -95,15 +105,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;
@ -118,6 +119,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);
@ -129,8 +143,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);
@ -140,10 +161,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;
}
@ -163,7 +187,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);
@ -177,6 +201,20 @@ 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 rtcplog(const char* cdrbuffer) {
int previous;
int mask = LOG_MASK (LOG_INFO);
@ -187,6 +225,6 @@ void rtcplog(const char* cdrbuffer) {
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;
};
@ -46,7 +54,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);
void rtcplog(const char* cdrbuffer);
@ -59,28 +80,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);
}


+ 19
- 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;
@ -257,6 +257,7 @@ static void options(int *argc, char ***argv) {
char *listenngs = NULL;
char *listencli = NULL;
char *graphitep = NULL;
char *graphite_prefix_s = NULL;
char *redisps = NULL;
char *log_facility_s = NULL;
char *log_facility_cdr_s = NULL;
@ -275,6 +276,7 @@ static void options(int *argc, char ***argv) {
{ "listen-cli", 'c', 0, G_OPTION_ARG_STRING, &listencli, "UDP port to listen on, CLI", "[IP46:]PORT" },
{ "graphite", 'g', 0, G_OPTION_ARG_STRING, &graphitep, "Address of the graphite server", "[IP46:]PORT" },
{ "graphite-interval", 'w', 0, G_OPTION_ARG_INT, &graphite_interval, "Graphite send interval in seconds", "INT" },
{ "graphite-prefix",0, 0, G_OPTION_ARG_STRING, &graphite_prefix_s, "Prefix for graphite line", "STRING"},
{ "tos", 'T', 0, G_OPTION_ARG_INT, &tos, "Default TOS value to set on streams", "INT" },
{ "timeout", 'o', 0, G_OPTION_ARG_INT, &timeout, "RTP timeout", "SECS" },
{ "silent-timeout",'s',0,G_OPTION_ARG_INT, &silent_timeout,"RTP timeout for muted", "SECS" },
@ -342,6 +344,9 @@ static void options(int *argc, char ***argv) {
die("Invalid IP or port (--graphite)");
}
if (graphite_prefix_s)
set_prefix(graphite_prefix_s);
if (tos < 0 || tos > 255)
die("Invalid TOS value");
@ -467,6 +472,7 @@ static void init_everything() {
resources();
sdp_init();
dtls_init();
ice_init();
}
void redis_mod_verify(void *dlh) {
@ -572,6 +578,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) {
@ -605,8 +614,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)
@ -631,34 +640,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;
@ -670,10 +651,11 @@ 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
@ -687,7 +669,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 *);


+ 9
- 9
daemon/rtcp.c View File

@ -239,11 +239,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) {
@ -483,11 +483,11 @@ int rtcp_demux_is_rtcp(const str *s) {
void print_rtcp_common(char** cdrbufcur, const pjmedia_rtcp_common *common) {
*cdrbufcur += sprintf(*cdrbufcur,"version=%u, padding=%u, count=%u, payloadtype=%u, length=%u, ssrc=%u, ",
ntohl(common->version),
ntohl(common->p),
ntohl(common->count),
ntohl(common->pt),
ntohl(common->length),
common->version,
common->p,
common->count,
common->pt,
common->length,
ntohl(common->ssrc));
}


+ 35
- 35
daemon/rtcp_xr.c View File

@ -21,31 +21,31 @@
#define BT_VOIP_METRICS 7
void print_rtcp_xr_common(char* cdrbufcur,pjmedia_rtcp_xr_pkt *rtcp_xr) {
void print_rtcp_xr_common(char* cdrbufcur,const pjmedia_rtcp_xr_pkt *rtcp_xr) {
cdrbufcur += sprintf(cdrbufcur,"version=%u, padding=%u, count=%u, payloadtype=%u, length=%u, ssrc=%u, ",
ntohl(rtcp_xr->common.version),
ntohl(rtcp_xr->common.p),
ntohl(rtcp_xr->common.count),
ntohl(rtcp_xr->common.pt),
ntohl(rtcp_xr->common.length),
rtcp_xr->common.version,
rtcp_xr->common.p,
rtcp_xr->common.count,
rtcp_xr->common.pt,
rtcp_xr->common.length,
ntohl(rtcp_xr->common.ssrc));
}
void print_rtcp_xr_rb_header(char* cdrbufcur,pjmedia_rtcp_xr_rb_header *rb_header) {
void print_rtcp_xr_rb_header(char* cdrbufcur,const pjmedia_rtcp_xr_rb_header *rb_header) {
cdrbufcur += sprintf(cdrbufcur,"rb_header_blocktype=%u, rb_header_blockspecdata=%u, rb_header_blocklength=%u, ",
ntohl(rb_header->bt),
ntohl(rb_header->specific),
ntohl(rb_header->length));
rb_header->bt,
rb_header->specific,
ntohs(rb_header->length));
}
void print_rtcp_xr_rb_rr_time(char* cdrbufcur,pjmedia_rtcp_xr_rb_rr_time *rb_rr_time) {
void print_rtcp_xr_rb_rr_time(char* cdrbufcur,const pjmedia_rtcp_xr_rb_rr_time *rb_rr_time) {
print_rtcp_xr_rb_header(cdrbufcur,&rb_rr_time->header);
cdrbufcur += sprintf(cdrbufcur,"rb_rr_time_ntp_sec=%u, rb_rr_time_ntp_frac=%u, ",
ntohl(rb_rr_time->ntp_sec),
ntohl(rb_rr_time->ntp_frac));
}
void print_rtcp_xr_rb_dlrr(char* cdrbufcur,pjmedia_rtcp_xr_rb_dlrr *rb_dlrr) {
void print_rtcp_xr_rb_dlrr(char* cdrbufcur,const pjmedia_rtcp_xr_rb_dlrr *rb_dlrr) {
print_rtcp_xr_rb_header(cdrbufcur,&rb_dlrr->header);
cdrbufcur += sprintf(cdrbufcur,"rb_dlrr_ssrc=%u, rb_dlrr_lrr=%u, rb_dlrr_dlrr=%u, ",
ntohl(rb_dlrr->item.ssrc),
@ -53,13 +53,13 @@ void print_rtcp_xr_rb_dlrr(char* cdrbufcur,pjmedia_rtcp_xr_rb_dlrr *rb_dlrr) {
ntohl(rb_dlrr->item.dlrr));
}
void print_rtcp_xr_rb_stats(char* cdrbufcur,pjmedia_rtcp_xr_rb_stats *rb_stats) {
void print_rtcp_xr_rb_stats(char* cdrbufcur,const pjmedia_rtcp_xr_rb_stats *rb_stats) {
print_rtcp_xr_rb_header(cdrbufcur,&rb_stats->header);
cdrbufcur += sprintf(cdrbufcur,"rb_stats_ssrc=%u, rb_stats_begin_seq=%u, rb_stats_end_seq=%u, rb_stats_lost_packets=%u, rb_stats_duplicate_packets=%u,"
"rb_stats_jitter_min=%u, rb_stats_jitter_max=%u, rb_stats_jitter_mean=%u, rb_stats_jitter_deviation=%u,"
"rb_stats_toh_min=%u, rb_stats_toh_max=%u, rb_stats_toh_mean=%u, rb_stats_toh_deviation=%u, ",
ntohl(rb_stats->ssrc),
ntohl(rb_stats->begin_seq),
ntohs(rb_stats->begin_seq),
ntohl(rb_stats->end_seq),
ntohl(rb_stats->lost),
ntohl(rb_stats->dup),
@ -73,7 +73,7 @@ void print_rtcp_xr_rb_stats(char* cdrbufcur,pjmedia_rtcp_xr_rb_stats *rb_stats)
ntohl(rb_stats->toh_dev));
}
void print_rtcp_xr_rb_voip_mtc(char* cdrbufcur,pjmedia_rtcp_xr_rb_voip_mtc *rb_voip_mtc) {
void print_rtcp_xr_rb_voip_mtc(char* cdrbufcur,const pjmedia_rtcp_xr_rb_voip_mtc *rb_voip_mtc) {
print_rtcp_xr_rb_header(cdrbufcur,&rb_voip_mtc->header);
cdrbufcur += sprintf(cdrbufcur,"rb_voip_mtc_ssrc=%u, rb_voip_mtc_loss_rate=%u, rb_voip_mtc_discard_rate=%u, rb_voip_mtc_burst_den=%u, "
"rb_voip_mtc_gap_den=%u, rb_voip_mtc_burst_dur=%u, rb_voip_mtc_gap_dur=%u, rb_voip_mtc_rnd_trip_delay=%u, "
@ -82,26 +82,26 @@ void print_rtcp_xr_rb_voip_mtc(char* cdrbufcur,pjmedia_rtcp_xr_rb_voip_mtc *rb_v
"rb_voip_mtc_mos_cq=%u, rb_voip_mtc_rx_config=%u, rb_voip_mtc_jb_nom=%u, rb_voip_mtc_jb_max=%u, "
"rb_voip_mtc_jb_abs_max=%u, ",
ntohl(rb_voip_mtc->ssrc),
ntohl(rb_voip_mtc->loss_rate),
ntohl(rb_voip_mtc->discard_rate),
ntohl(rb_voip_mtc->burst_den),
ntohl(rb_voip_mtc->gap_den),
ntohl(rb_voip_mtc->burst_dur),
ntohl(rb_voip_mtc->gap_dur),
ntohl(rb_voip_mtc->rnd_trip_delay),
ntohl(rb_voip_mtc->end_sys_delay),
ntohl(rb_voip_mtc->signal_lvl),
ntohl(rb_voip_mtc->noise_lvl),
ntohl(rb_voip_mtc->rerl),
ntohl(rb_voip_mtc->gmin),
ntohl(rb_voip_mtc->r_factor),
ntohl(rb_voip_mtc->ext_r_factor),
ntohl(rb_voip_mtc->mos_lq),
ntohl(rb_voip_mtc->mos_cq),
ntohl(rb_voip_mtc->rx_config),
ntohl(rb_voip_mtc->jb_nom),
ntohl(rb_voip_mtc->jb_max),
ntohl(rb_voip_mtc->jb_abs_max));
rb_voip_mtc->loss_rate,
rb_voip_mtc->discard_rate,
rb_voip_mtc->burst_den,
rb_voip_mtc->gap_den,
ntohs(rb_voip_mtc->burst_dur),
ntohs(rb_voip_mtc->gap_dur),
ntohs(rb_voip_mtc->rnd_trip_delay),
ntohs(rb_voip_mtc->end_sys_delay),
rb_voip_mtc->signal_lvl,
rb_voip_mtc->noise_lvl,
rb_voip_mtc->rerl,
rb_voip_mtc->gmin,
rb_voip_mtc->r_factor,
rb_voip_mtc->ext_r_factor,
rb_voip_mtc->mos_lq,
rb_voip_mtc->mos_cq,
rb_voip_mtc->rx_config,
ntohs(rb_voip_mtc->jb_nom),
ntohs(rb_voip_mtc->jb_max),
ntohs(rb_voip_mtc->jb_abs_max));
}
void pjmedia_rtcp_xr_rx_rtcp_xr(char* cdrbufcur, const void *pkt, size_t size) {


+ 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

+ 229
- 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))
@ -220,6 +271,11 @@ INLINE void __output_add(struct msghdr *mh, struct tlv *tlv, unsigned int len, u
iov = &mh->msg_iov[mh->msg_iovlen++];
iov->iov_base = append; /* must have space for padding */
iov->iov_len = (append_len + 3) & 0xfffc;
if ((append_len & 0x3)) {
if (memcmp(append + append_len, "\0\0\0", 4 - (append_len & 0x3)))
memset(append + append_len, 0, 4 - (append_len & 0x3));
}
}
}
@ -231,13 +287,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 +339,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 +352,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 +373,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 +402,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 +440,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 +474,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 +491,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 +558,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

+ 122
- 0
debian/changelog View File

@ -1,3 +1,125 @@
ngcp-rtpengine (4.0.0.0+0~mr4.0.0.0) unstable; urgency=medium
[ Richard Fuchs ]
* [2db33ef] reset ICE/STUN retransmit data on triggered checks
* [20ac7cd] process nominating ICE request only once per pair
* [93294f8] don't relearn addresses if the endpoint hasn't changed
* [0b202d8] Fall back to a different address family if the requested one is unavailable
* [fd99ecf] don't close call on UDP send error
* [c0fab9b] various fixes related to ICE negotation and rtcp-mux
* [2cfc12b] fix incorrect free order of ICE components
[ Peter Lemenkov ]
* [eba4414] Remove BuildRoot
* [597c35a] Remove duplicated text in descriptions
* [49ed2a8] Remove autoadded deps
* [b0240ee] Use generic dependency name for nc
* [cd70a8f] Use proper versionin scheme
* [8d56ed5] Typo fix in Source0 url
* [9890371] Remove no longer required section
* [234b454] Use macro instead of /etc/rc.d/init.d
* [882731b] Remove double slash
* [d25b697] Check for username,group before creating
* [e71a7e1] Consistently use rtpengine name everywhere
* [a6ab5b5] Restrict access to a working directory
* [9a12b49] Don't specify access rights for _usrsrc dir
* [3b27031] Explicitly set rights on dkms.conf as 644
[ Alexander Lutay ]
* [3a45a16] MT#7505 Add .gitreview file for rtpengine
-- Alexander Lutay <alutay@sipwise.com> Wed, 08 Apr 2015 17:26:40 +0200
ngcp-rtpengine (3.3.0.0+0~mr4.0.0.0) unstable; urgency=low
[ Sergey Lavrov ]
* [bb14eba] Fix %post/%preun directives.
* [fe5e129] Added rtpengine-ctl and dependency to spec file.
[ Richard Fuchs ]
* [a0068f4] relax sdes key lifetime validation check
* [caba5be] MT#10583 remember the "created from" address in raw format
* [6e32379] MT#10583 support ipv6 control connection in simulator
* [02d27d5] MT#10583 support b2bua callback to sip proxy address
* [43cd3f5] reset other side's crypto params only in passthru mode
* [2af682b] use SSL_CTX_set_read_ahead to fix for openssl 1.0.1k
* [e24253a] move parse_ip(6)_port into aux.h
* [a81588e] provide convenience function get_log_level()
* [37d98ad] dump DTLS cert and keys
* [57c0a84] add locking to totalstats
* [f544471] fix compiler warnings
* [36c7141] change control_ng_stats into a hash and use locking and atomic ops
* [c0b2f3d] fix graphite code not to leak fds in error cases
* [965d989] static'ize graphite global vars
* [7175a26] decrease sleep time of graphite loop
* [3882285] decrease calls to time() in graphite code
* [de37ac0] use TLS buffer array for temporary strings
* [ee4f2d2] convert ilog() into macro
* [411a888] use atomic ops for stats
* [28c6db9] fix references and locking in CLI
* [20fd255] use UINT64F instead of %llu where appropriate
* [3bc1672] more atomic stats, and fix average length calc
* [fedaadd] remove superfluous \n from log messages
* [1e3f06a] split some perl SRTP stuff into module and add debug script
* [deba231] rename an old MP_ variable to RE_
* [fb9ed6e] use trial & error approach to guess ROC in case of SRTP auth failures
* [f6dee07] fix initial seqnum in simulator
* [787d90f] rename atomic_uint64 to atomic64 for brevity
* [49328cd] implement atomic64 for non-64-bit platforms
* [da6b6a1] fix compiler warning on 32bit
* [ca149e6] "long" can be 32 bits only and may truncate math
* [b023d6c] update redis mod version to 7
* [adeb0b3] the log rate limiter must take the prefix into account
* [fb667a8] turn tag and termination texts into array lookups
* [d79c68f] remove some unnecessary memsets
* [a5f7315] string handling fixes and improvements
* [037c6aa] shorter locking in stats loop
* [f7a1594] more descriptive warning for failed kernelizing
* [5fa9902] atomic vars should be volatile
* [c21193a] fix byte stats in kernel module
* [c2ff5c3] MT#9935 understand, learn, remember and report RTP payload types (codecs)
* [b586fa7] fix memory leak
* [65b3f66] use atomic64_t for kernel stats
* [4a97bb1] attempt to work around trickle ice
* [b44bb28] segfault fix
* [015b2a9] legacy tcp protocol fix
* [016f8b3] Rework logging a bit (primarily in NG)
* [91a85e6] fix for skewed stats
* [6173a7a] implement atomic bitfield ops
* [1f7c8a2] remove obsolete HAS_HANDLER flag
* [f224bab] segfault fix if no rtpmap attribute is present
* [26ca844] drop support for glib versions older than 2.30
* [b83e80c] add makefile dependency to itself
* [956d07d] fix erroneously reported 0.0.0.0 address when bridging from ipv6
* [f99d6d4] implement full ICE support
* [98e0b6d] update for redis plugin
* [0e4e6f2] revamp and move timeval functions
* [26137d2] move thread loop functions out of main.c
* [5fca71d] fix segfault when parsing server reflexive ICE candidates
* [3520617] remove ice-lite mention from readme
* [9593519] Leave unsupported (non-RTP) protocols alone when translation is requested
[ Eric Tamme ]
* [b8b17bf] clear crypto for BOTH legs when in passthrough mode
* [506076b] add raddr and rport on all non host type candidates per rfc5245 section-15.1
[ Carsten Bock ]
* [9a2da87] Fix randomness source for key generation.
[ Frederic-Philippe Metz ]
* [2ae0e35] Added functionality to report statistics to graphite
* [7446822] Forgot the files ..
* [61a72b1] Added some more statistics.
* [0ccb11a] Added statistics on control ng interface.
* [794709f] Changed retry behaviour for connecting to graphite server.
[ Michael Prokop ]
* [fca4500] MT#9127 Bump Standards-Version to 3.9.6
[ Sipwise Jenkins Builder ]
-- Sipwise Jenkins Builder <jenkins@sipwise.com> Wed, 18 Mar 2015 14:15:06 +0100
ngcp-rtpengine (3.3.0.0+0~mr3.8.0.0) unstable; urgency=low
[ Richard Fuchs ]


+ 4
- 4
debian/control View File

@ -6,18 +6,18 @@ 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
Suggests: ngcp-system-tools
Depends: ${misc:Depends},
${shlibs:Depends}
Conflicts: ngcp-mediaproxy-ng-daemon
@ -63,7 +63,7 @@ Description: IPtables kernel module for the NGCP media proxy - source.
Package: ngcp-rtpengine-kernel-dkms
Architecture: all
Pre-Depends: ngcp-system-tools
Suggests: ngcp-system-tools
Depends: dkms (>= 1.95),
${misc:Depends}
Conflicts: ngcp-mediaproxy-ng-kernel-dkms


+ 2
- 1
debian/ngcp-rtpengine-daemon.default View File

@ -25,4 +25,5 @@ TABLE=0
# NUM_THREADS=5
# DELETE_DELAY=30
# GRAPHITE=9006
# GRAPHITE_INTERVAL=60
# GRAPHITE_INTERVAL=60
# GRAPHITE_PREFIX=myownprefix

+ 1
- 0
debian/ngcp-rtpengine-daemon.init View File

@ -75,6 +75,7 @@ OPTIONS="$OPTIONS --table=$TABLE"
[ -z "$DELETE_DELAY" ] || OPTIONS="$OPTIONS --delete-delay=$DELETE_DELAY"
[ -z "$GRAPHITE" ] || OPTIONS="$OPTIONS --graphite=$GRAPHITE"
[ -z "$GRAPHITE_INTERVAL" ] || OPTIONS="$OPTIONS --graphite-interval=$GRAPHITE_INTERVAL"
[ -z "$GRAPHITE_PREFIX" ] || OPTIONS="$OPTIONS --graphite-prefix=$GRAPHITE_PREFIX"
if test "$FORK" = "no" ; then
OPTIONS="$OPTIONS --foreground"
fi


+ 6
- 1
el/rtpengine.init View File

@ -39,7 +39,7 @@ MODULE=0
build_opts() {
shopt -s nocasematch
RPMS=`rpm -qa | grep ngcp-rtpengine-kernel`
RPMS=`rpm -qa | grep rtpengine-kernel`
if [[ "$KERNEL" == "yes" && -n "$TABLE" && -n "$RPMS" ]]
then
MODULE=1
@ -153,6 +153,11 @@ build_opts() {
OPTS+=" --graphite-interval=$GRAPHITE_INTERVAL"
fi
if [[ -n "$GRAPHITE_PREFIX" ]]
then
OPTS+=" --graphite-prefix=$GRAPHITE_PREFIX"
fi
if [[ -n "$LOG_FACILITY_CDR" ]]
then
OPTS+=" --log-facility-cdr=$LOG_FACILITY_CDR"


+ 35
- 38
el/rtpengine.spec View File

@ -1,4 +1,4 @@
Name: ngcp-rtpengine
Name: rtpengine
Version: 2.3.6
Release: 0%{?dist}
Summary: The Sipwise NGCP rtpengine
@ -6,14 +6,15 @@ Summary: The Sipwise NGCP rtpengine
Group: System Environment/Daemons
License: GPLv3
URL: https://github.com/sipwise/rtpengine
Source0: https://github.com/sipwise/rtpengine/archive/%{version}/%{name}-%{version}.tar.gz
Conflicts: %{name}-kernel < %{version}
BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
Source0: https://github.com/sipwise/rtpengine/archive/mr%{version}/%{name}-%{version}.tar.gz
Conflicts: %{name}-kernel < %{version}-%{release}
BuildRequires: gcc make pkgconfig redhat-rpm-config
BuildRequires: glib2-devel libcurl-devel openssl-devel pcre-devel
BuildRequires: xmlrpc-c-devel zlib-devel
Requires: glibc libcurl openssl pcre xmlrpc-c nmap-ncat
Requires: nc
# Remain compat with other installations
Provides: ngcp-rtpengine = %{version}-%{release}
%description
@ -26,11 +27,12 @@ drop-in replacement for any of the other available RTP and media proxies.
Summary: NGCP rtpengine in-kernel packet forwarding
Group: System Environment/Daemons
BuildRequires: gcc make redhat-rpm-config iptables-devel
Requires: iptables iptables-ipv6 ngcp-rtpengine = %{version}
Requires: ngcp-rtpengine-dkms = %{version}
Requires: iptables iptables-ipv6
Requires: %{name}%{?_isa} = %{version}-%{release}
Requires: %{name}-dkms%{?_isa} = %{version}-%{release}
%description kernel
NGCP rtpengine in-kernel packet forwarding
%{summary}.
%package dkms
@ -43,7 +45,7 @@ Requires(post): epel-release dkms
Requires(preun): epel-release dkms
%description dkms
Kernel module for rtpengine in-kernel packet forwarding
%{summary}.
%prep
@ -60,16 +62,16 @@ cd ..
%install
# Install the userspace daemon
install -D -p -m755 daemon/rtpengine %{buildroot}/%{_sbindir}/rtpengine
install -D -p -m755 daemon/%{name} %{buildroot}%{_sbindir}/%{name}
# Install CLI (command line interface)
install -D -p -m755 utils/rtpengine-ctl %{buildroot}/%{_sbindir}/rtpengine-ctl
install -D -p -m755 utils/%{name}-ctl %{buildroot}%{_sbindir}/%{name}-ctl
## Install the init.d script and configuration file
install -D -p -m755 el/rtpengine.init \
%{buildroot}/%{_sysconfdir}/rc.d/init.d/rtpengine
install -D -p -m644 el/rtpengine.sysconfig \
%{buildroot}/%{_sysconfdir}/sysconfig/rtpengine
mkdir -p %{buildroot}/%{_sharedstatedir}/rtpengine
install -D -p -m755 el/%{name}.init \
%{buildroot}%{_initrddir}/%{name}
install -D -p -m644 el/%{name}.sysconfig \
%{buildroot}%{_sysconfdir}/sysconfig/%{name}
mkdir -p %{buildroot}%{_sharedstatedir}/%{name}
# Install the iptables plugin
install -D -p -m755 iptables-extension/libxt_RTPENGINE.so \
@ -77,29 +79,24 @@ install -D -p -m755 iptables-extension/libxt_RTPENGINE.so \
## DKMS module source install
install -D -p -m644 kernel-module/Makefile \
%{buildroot}/%{_usrsrc}/%{name}-%{version}-%{release}/Makefile
%{buildroot}%{_usrsrc}/%{name}-%{version}-%{release}/Makefile
install -D -p -m644 kernel-module/xt_RTPENGINE.c \
%{buildroot}/%{_usrsrc}/%{name}-%{version}-%{release}/xt_RTPENGINE.c
%{buildroot}%{_usrsrc}/%{name}-%{version}-%{release}/xt_RTPENGINE.c
install -D -p -m644 kernel-module/xt_RTPENGINE.h \
%{buildroot}/%{_usrsrc}/%{name}-%{version}-%{release}/xt_RTPENGINE.h
sed "s/__VERSION__/%{version}-%{release}/g" debian/dkms.conf.in > \
%{buildroot}/%{_usrsrc}/%{name}-%{version}-%{release}/dkms.conf
%clean
rm -rf %{buildroot}
%{buildroot}%{_usrsrc}/%{name}-%{version}-%{release}/xt_RTPENGINE.h
sed -i -e "s/__VERSION__/%{version}-%{release}/g;s/ngcp-rtpengine/rtpengine/g" debian/dkms.conf.in
install -D -p -m644 debian/dkms.conf.in %{buildroot}%{_usrsrc}/%{name}-%{version}-%{release}/dkms.conf
%pre
/usr/sbin/groupadd -r rtpengine 2> /dev/null || :
/usr/sbin/useradd -r -g rtpengine -s /sbin/nologin -c "rtpengine daemon" \
-d %{_sharedstatedir}/rtpengine rtpengine \
2> /dev/null || :
getent group %{name} >/dev/null || /usr/sbin/groupadd -r %{name}
getent passwd %{name} >/dev/null || /usr/sbin/useradd -r -g %{name} \
-s /sbin/nologin -c "%{name} daemon" -d %{_sharedstatedir}/%{name} %{name}
%post
if [ $1 -eq 1 ]; then
/sbin/chkconfig --add rtpengine || :
/sbin/chkconfig --add %{name} || :
fi
@ -113,8 +110,8 @@ true
%preun
if [ $1 = 0 ] ; then
/sbin/service rtpengine stop >/dev/null 2>&1
/sbin/chkconfig --del rtpengine
/sbin/service %{name} stop >/dev/null 2>&1
/sbin/chkconfig --del %{name}
fi
@ -126,14 +123,14 @@ true
%files
# Userspace daemon
%{_sbindir}/rtpengine
%{_sbindir}/%{name}
# CLI (command line interface)
%{_sbindir}/rtpengine-ctl
%{_sbindir}/%{name}-ctl
# init.d script and configuration file
%{_sysconfdir}/rc.d/init.d/rtpengine
%config(noreplace) %{_sysconfdir}/sysconfig/rtpengine
%dir %{_sharedstatedir}/rtpengine
%{_initrddir}/%{name}
%config(noreplace) %{_sysconfdir}/sysconfig/%{name}
%attr(0750,%{name},%{name}) %dir %{_sharedstatedir}/%{name}
# Documentation
%doc LICENSE README.md el/README.el.md debian/changelog debian/copyright
@ -144,7 +141,7 @@ true
%files dkms
%attr(0755,root,root) %{_usrsrc}/%{name}-%{version}-%{release}/
%{_usrsrc}/%{name}-%{version}-%{release}/
%changelog


+ 143
- 46
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>
@ -143,13 +145,26 @@ struct re_crypto_context {
const struct re_hmac *hmac;
};
struct rtpengine_stats_a {
atomic64_t packets;
atomic64_t bytes;
atomic64_t errors;
struct timespec delay_min;
struct timespec delay_avg;
struct timespec delay_max;
atomic_t in_tos;
};
struct rtpengine_rtp_stats_a {
atomic64_t packets;
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;
@ -817,10 +832,9 @@ static ssize_t proc_blist_read(struct file *f, char __user *b, size_t l, loff_t
u_int32_t id;
struct rtpengine_table *t;
struct rtpengine_list_entry op;
int err;
int err, port, i;
struct rtpengine_target *g;
unsigned long flags;
int port;
if (l != sizeof(op))
return -EINVAL;
@ -843,9 +857,18 @@ 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);
op.stats.delay_min = g->stats.delay_min;
op.stats.delay_max = g->stats.delay_max;
op.stats.delay_avg = g->stats.delay_avg;
op.stats.in_tos = atomic64_read(&g->stats.in_tos);
for (i = 0; i < g->target.num_payload_types; i++) {
op.rtp_stats[i].packets = atomic64_read(&g->rtp_stats[i].packets);
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;
@ -869,10 +892,6 @@ err:
return err;
}
static int proc_list_open(struct inode *i, struct file *f) {
int err;
struct seq_file *p;
@ -972,7 +991,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);
@ -981,10 +1000,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)
@ -1375,7 +1399,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)
@ -1406,7 +1430,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));
@ -1454,9 +1477,17 @@ 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));
g->stats.delay_min = og->stats.delay_min;
g->stats.delay_max = og->stats.delay_max;
g->stats.delay_avg = og->stats.delay_avg;
for (j = 0; j < NUM_PAYLOAD_TYPES; j++) {
atomic64_set(&g->rtp_stats[j].packets, atomic64_read(&og->rtp_stats[j].packets));
atomic64_set(&g->rtp_stats[j].bytes, atomic64_read(&og->rtp_stats[j].bytes));
}
}
else {
err = -EEXIST;
@ -1902,6 +1933,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,
@ -1986,10 +2028,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;
@ -2018,12 +2061,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;
}
@ -2108,6 +2175,25 @@ 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 void re_timespec_subtract (struct timespec *result, const struct timespec *a, const struct timespec *b) {
long long nanoseconds=0;
nanoseconds = ((long)a->tv_sec - (long long)b->tv_sec) * (long long)1000000000 + ((long long)a->tv_nsec - (long long)b->tv_nsec);
@ -2153,12 +2239,11 @@ static int re_timespec_cmp (struct timespec *a, struct timespec *b)
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;
#if (RE_HAS_MEASUREDELAY)
struct timespec endtime, delay;
@ -2217,17 +2302,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;
@ -2245,11 +2341,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) {
@ -2261,23 +2354,26 @@ 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 (g->stats.packets==0)
g->stats.in_tos = in_tos;
if (atomic64_read(&g->stats.packets)==0)
atomic_set(&g->stats.in_tos,in_tos);
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);
#if (RE_HAS_MEASUREDELAY)
getnstimeofday(&endtime);
re_timespec_subtract(&delay,&endtime, starttime);
if (g->stats.packets==1) {
if (atomic64_read(&g->stats.packets)==1) {
g->stats.delay_min=delay;
g->stats.delay_avg=delay;
g->stats.delay_max=delay;
@ -2289,13 +2385,16 @@ out:
g->stats.delay_max = delay;
}
re_timespec_multiply(&g->stats.delay_avg,&g->stats.delay_avg,g->stats.packets-1);
re_timespec_multiply(&g->stats.delay_avg,&g->stats.delay_avg,atomic64_read(&g->stats.packets)-1);
re_timespec_add(&g->stats.delay_avg,&g->stats.delay_avg,&delay);
re_timespec_devide(&g->stats.delay_avg,&g->stats.delay_avg,g->stats.packets);
re_timespec_devide(&g->stats.delay_avg,&g->stats.delay_avg,atomic64_read(&g->stats.packets));
}
#endif
}
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);
@ -2303,9 +2402,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;
};
@ -14,6 +20,10 @@ struct rtpengine_stats {
struct timespec delay_max;
u_int8_t in_tos;
};
struct rtpengine_rtp_stats {
u_int64_t packets;
u_int64_t bytes;
};
struct re_address {
int family;
@ -77,10 +87,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;
};
@ -98,6 +112,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