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 + Bridging between ICE-enabled and ICE-unaware user agents
+ Optionally acting only as additional ICE relay/candidate + Optionally acting only as additional ICE relay/candidate
+ Optionally forcing relay of media streams by removing other ICE candidates + Optionally forcing relay of media streams by removing other ICE candidates
+ Supports ice-lite only
- SRTP (RFC 3711) support: - SRTP (RFC 3711) support:
+ Support for SDES (RFC 4568) and DTLS-SRTP (RFC 5764) + Support for SDES (RFC 4568) and DTLS-SRTP (RFC 5764)
+ AES-CM and AES-F8 ciphers, both in userspace and in kernel + 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 --dtls-passive Always prefer DTLS passive role
-g, --graphite=[IP46:]PORT TCP address of graphite statistics server -g, --graphite=[IP46:]PORT TCP address of graphite statistics server
-w, --graphite-interval=INT Graphite data statistics send interval -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 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. 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. 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: 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 \ /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_GNU_SOURCE',
'-D__DEBUG=1', '-D__DEBUG=1',
'-DRTPENGINE_VERSION="dummy"', '-DRTPENGINE_VERSION="dummy"',
'-DMP_PLUGIN_DIR="/usr/lib/rtpengine"',
'-DRE_PLUGIN_DIR="/usr/lib/rtpengine"',
'-O2', '-O2',
'-fstack-protector', '-fstack-protector',
'--param=ssp-buffer-size=4', '--param=ssp-buffer-size=4',


+ 6
- 3
daemon/Makefile View File

@ -29,7 +29,7 @@ ifeq ($(RTPENGINE_VERSION),)
endif endif
CFLAGS+= -DRTPENGINE_VERSION="\"$(RTPENGINE_VERSION)\"" 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+= -DSRTCP_KEY_DERIVATION_RFC_COMPLIANCE
#CFLAGS+= -DTERMINATE_SDP_AT_BLANK_LINE #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 \ 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 \ 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) OBJS= $(SRCS:.c=.o)
@ -83,8 +84,10 @@ clean:
.depend: $(SRCS) Makefile .depend: $(SRCS) Makefile
$(CC) $(CFLAGS) -M $(SRCS) | sed -e 's/:/ .depend:/' > .depend $(CC) $(CFLAGS) -M $(SRCS) | sed -e 's/:/ .depend:/' > .depend
rtpengine: $(OBJS) .depend
rtpengine: $(OBJS) .depend Makefile
$(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS)
$(OBJS): Makefile
include .depend 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 struct thread_buf __thread t_bufs[NUM_THREAD_BUFS];
static int __thread t_buf_idx; 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) { 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() { static void thread_join_me() {
pthread_t *me; pthread_t *me;
@ -219,3 +194,21 @@ char *get_thread_buf(void) {
t_buf_idx = 0; t_buf_idx = 0;
return ret; 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 OFFSET_OF(t,e) ((unsigned int) (unsigned long) &(((t *) 0)->e))
#define ZERO(x) memset(&(x), 0, sizeof(x)) #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 UINT64F "%" G_GUINT64_FORMAT
#define THREAD_BUF_SIZE 64 #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 *); 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 *); int pcre_multi_match(pcre *, pcre_extra *, const char *, unsigned int, parse_func, void *, GQueue *);
INLINE void strmove(char **, char **); INLINE void strmove(char **, char **);
INLINE void strdupfree(char **, const char *); INLINE void strdupfree(char **, const char *);
char *get_thread_buf(void); 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) #if !GLIB_CHECK_VERSION(2,32,0)
INLINE int g_hash_table_contains(GHashTable *h, const void *k) { 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 #endif
/* GQUEUE */
INLINE void g_queue_move(GQueue *dst, GQueue *src) { INLINE void g_queue_move(GQueue *dst, GQueue *src) {
GList *l; GList *l;
while ((l = g_queue_pop_head_link(src))) 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) while (q->length > len)
g_queue_pop_tail(q); 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) { INLINE void strmove(char **d, char **s) {
if (*d) if (*d)
free(*d); free(*d);
@ -101,6 +160,36 @@ INLINE void strdupfree(char **d, const char *s) {
*d = strdup(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) { INLINE void nonblock(int fd) {
fcntl(fd, F_SETFL, O_NONBLOCK); 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)); 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) { INLINE char chrtoupper(char x) {
return x & 0xdf; return x & 0xdf;
@ -138,6 +220,34 @@ INLINE void swap_ptrs(void *a, void *b) {
*bb = t; *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) { INLINE void in4_to_6(struct in6_addr *o, u_int32_t ip) {
o->s6_addr32[0] = 0; o->s6_addr32[0] = 0;
o->s6_addr32[1] = 0; o->s6_addr32[1] = 0;
@ -194,15 +304,19 @@ INLINE char *smart_ntop_p_buf(const struct in6_addr *a) {
return buf; 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; char *e;
e = smart_ntop_p(o, &a->sin6_addr, len);
e = smart_ntop_p(o, a, len);
if (!e) if (!e)
return; return;
if (len - (e - o) < 7) if (len - (e - o) < 7)
return; 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) { 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; 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) { INLINE int smart_pton(int af, char *src, void *dst) {
char *p; char *p;
int ret; int ret;
@ -303,21 +429,61 @@ fail:
return -1; 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 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_mutex_t mutex_t;
typedef pthread_rwlock_t rwlock_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_init(c) __debug_cond_init(c, __FILE__, __LINE__)
#define cond_wait(c,m) __debug_cond_wait(c,m, __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_signal(c) __debug_cond_signal(c, __FILE__, __LINE__)
#define cond_broadcast(c) __debug_cond_broadcast(c, __FILE__, __LINE__) #define cond_broadcast(c) __debug_cond_broadcast(c, __FILE__, __LINE__)
#define COND_STATIC_INIT PTHREAD_COND_INITIALIZER #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 #ifndef __THREAD_DEBUG
#define __debug_mutex_init(m, F, L) pthread_mutex_init(m, NULL) #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_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_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_signal(c, F, L) pthread_cond_signal(c)
#define __debug_cond_broadcast(c, F, L) pthread_cond_broadcast(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_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_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_signal(c, F, L) pthread_cond_signal(c)
#define __debug_cond_broadcast(c, F, L) pthread_cond_broadcast(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 threads_join_all(int);
void thread_create_detach(void (*)(void *), void *); 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; 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 -1;
return 0; 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 */ /* 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 */ /* 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 #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 <sys/types.h>
#include <glib.h> #include <glib.h>
@ -12,6 +15,7 @@
#include <openssl/x509.h> #include <openssl/x509.h>
#include "compat.h" #include "compat.h"
#include "control_ng.h" #include "control_ng.h"
#include "aux.h"
enum termination_reason { enum termination_reason {
UNKNOWN=0, UNKNOWN=0,
@ -54,7 +58,14 @@ enum xmlrpc_format {
XF_CALLID, 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 "str.h"
#include "crypto.h" #include "crypto.h"
#include "dtls.h" #include "dtls.h"
#include "rtp.h"
@ -99,6 +111,8 @@ struct call_monologue;
#define SHARED_FLAG_ICE 0x00000080 #define SHARED_FLAG_ICE 0x00000080
#define SHARED_FLAG_STRICT_SOURCE 0x00000100 #define SHARED_FLAG_STRICT_SOURCE 0x00000100
#define SHARED_FLAG_MEDIA_HANDOVER 0x00000200 #define SHARED_FLAG_MEDIA_HANDOVER 0x00000200
#define SHARED_FLAG_TRICKLE_ICE 0x00000400
#define SHARED_FLAG_ICE_LITE 0x00000800
/* struct stream_params */ /* struct stream_params */
#define SP_FLAG_NO_RTCP 0x00010000 #define SP_FLAG_NO_RTCP 0x00010000
@ -112,21 +126,24 @@ struct call_monologue;
#define SP_FLAG_ICE SHARED_FLAG_ICE #define SP_FLAG_ICE SHARED_FLAG_ICE
#define SP_FLAG_STRICT_SOURCE SHARED_FLAG_STRICT_SOURCE #define SP_FLAG_STRICT_SOURCE SHARED_FLAG_STRICT_SOURCE
#define SP_FLAG_MEDIA_HANDOVER SHARED_FLAG_MEDIA_HANDOVER #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 */ /* struct packet_stream */
#define PS_FLAG_RTP 0x00010000 #define PS_FLAG_RTP 0x00010000
#define PS_FLAG_RTCP 0x00020000 #define PS_FLAG_RTCP 0x00020000
#define PS_FLAG_IMPLICIT_RTCP SHARED_FLAG_IMPLICIT_RTCP #define PS_FLAG_IMPLICIT_RTCP SHARED_FLAG_IMPLICIT_RTCP
#define PS_FLAG_FALLBACK_RTCP 0x00040000 #define PS_FLAG_FALLBACK_RTCP 0x00040000
#define PS_FLAG_STUN 0x00080000
#define PS_FLAG_UNUSED2 0x00080000
#define PS_FLAG_FILLED 0x00100000 #define PS_FLAG_FILLED 0x00100000
#define PS_FLAG_CONFIRMED 0x00200000 #define PS_FLAG_CONFIRMED 0x00200000
#define PS_FLAG_KERNELIZED 0x00400000 #define PS_FLAG_KERNELIZED 0x00400000
#define PS_FLAG_NO_KERNEL_SUPPORT 0x00800000 #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_FINGERPRINT_VERIFIED 0x02000000
#define PS_FLAG_STRICT_SOURCE SHARED_FLAG_STRICT_SOURCE #define PS_FLAG_STRICT_SOURCE SHARED_FLAG_STRICT_SOURCE
#define PS_FLAG_MEDIA_HANDOVER SHARED_FLAG_MEDIA_HANDOVER #define PS_FLAG_MEDIA_HANDOVER SHARED_FLAG_MEDIA_HANDOVER
#define PS_FLAG_ICE SHARED_FLAG_ICE
/* struct call_media */ /* struct call_media */
#define MEDIA_FLAG_INITIALIZED 0x00010000 #define MEDIA_FLAG_INITIALIZED 0x00010000
@ -141,15 +158,22 @@ struct call_monologue;
#define MEDIA_FLAG_SETUP_PASSIVE SHARED_FLAG_SETUP_PASSIVE #define MEDIA_FLAG_SETUP_PASSIVE SHARED_FLAG_SETUP_PASSIVE
#define MEDIA_FLAG_PASSTHRU 0x00100000 #define MEDIA_FLAG_PASSTHRU 0x00100000
#define MEDIA_FLAG_ICE SHARED_FLAG_ICE #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 */ /* access macros */
#define SP_ISSET(p, f) bf_isset(&(p)->sp_flags, SP_FLAG_ ## f) #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_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 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_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_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 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_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_SET(p, f) bf_set(&(p)->media_flags, MEDIA_FLAG_ ## f)
#define MEDIA_CLEAR(p, f) bf_clear(&(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 streamhandler;
struct sdp_ng_flags; struct sdp_ng_flags;
struct local_interface; struct local_interface;
struct call_monologue;
struct ice_agent;
typedef bencode_buffer_t call_buffer_t; typedef bencode_buffer_t call_buffer_t;
@ -178,46 +204,42 @@ typedef bencode_buffer_t call_buffer_t;
struct transport_protocol { struct transport_protocol {
enum transport_protocol_index index; enum transport_protocol_index index;
const char *name; const char *name;
int rtp:1; /* also set to 1 for SRTP */
int srtp:1; int srtp:1;
int avpf:1; int avpf:1;
}; };
extern const struct transport_protocol transport_protocols[]; extern const struct transport_protocol transport_protocols[];
struct stats { 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_min;
struct timespec delay_avg; struct timespec delay_avg;
struct timespec delay_max; struct timespec delay_max;
u_int8_t in_tos_tclass;
atomic64 in_tos_tclass;
}; };
struct totalstats { struct totalstats {
time_t started; 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_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 { struct udp_fd {
int fd; int fd;
u_int16_t localport; u_int16_t localport;
}; };
struct endpoint {
struct in6_addr ip46;
u_int16_t port;
};
struct stream_params { struct stream_params {
unsigned int index; /* starting with 1 */ unsigned int index; /* starting with 1 */
str type; str type;
@ -231,6 +253,10 @@ struct stream_params {
int desired_family; int desired_family;
struct dtls_fingerprint fingerprint; struct dtls_fingerprint fingerprint;
unsigned int sp_flags; 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 { struct stream_fd {
@ -253,6 +279,15 @@ struct loop_protector {
unsigned char buf[RTP_LOOP_PROTECT]; 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 { struct packet_stream {
mutex_t in_lock, mutex_t in_lock,
out_lock; out_lock;
@ -262,6 +297,7 @@ struct packet_stream {
struct call_media *media; /* RO */ struct call_media *media; /* RO */
struct call *call; /* RO */ struct call *call; /* RO */
unsigned int component; /* RO, starts with 1 */
struct stream_fd *sfd; /* LOCK: call->master_lock */ struct stream_fd *sfd; /* LOCK: call->master_lock */
struct packet_stream *rtp_sink; /* 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 endpoint advertised_endpoint; /* RO */
struct crypto_context crypto; /* OUT direction, LOCK: out_lock */ 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 #if RTP_LOOP_PROTECT
/* LOCK: in_lock: */ /* LOCK: in_lock: */
@ -286,8 +323,7 @@ struct packet_stream {
X509 *dtls_cert; /* LOCK: in_lock */ X509 *dtls_cert; /* LOCK: in_lock */
/* in_lock must be held for SETTING these: */ /* 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 */ /* 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. */ * atomic ops to access it when holding an R lock. */
volatile struct interface_address *local_address; volatile struct interface_address *local_address;
str ice_ufrag;
str ice_pwd;
struct ice_agent *ice_agent;
struct { struct {
struct crypto_params params; struct crypto_params params;
unsigned int tag; unsigned int tag;
@ -318,8 +354,9 @@ struct call_media {
GQueue streams; /* normally RTP + RTCP */ GQueue streams; /* normally RTP + RTCP */
GSList *endpoint_maps; GSList *endpoint_maps;
GHashTable *rtp_payload_types;
unsigned int media_flags;
volatile unsigned int media_flags;
}; };
/* half a dialogue */ /* half a dialogue */
@ -369,8 +406,9 @@ struct call {
struct local_interface { struct local_interface {
str name; 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 { struct interface_address {
str interface_name; str interface_name;
@ -379,6 +417,7 @@ struct interface_address {
struct in6_addr advertised; struct in6_addr advertised;
str ice_foundation; str ice_foundation;
char foundation_buf[16]; char foundation_buf[16];
unsigned int preference; /* starting with 0 */
}; };
struct callmaster_config { struct callmaster_config {
@ -394,6 +433,9 @@ struct callmaster_config {
char *b2b_url; char *b2b_url;
unsigned char default_tos; unsigned char default_tos;
enum xmlrpc_format fmt; enum xmlrpc_format fmt;
u_int32_t graphite_ip;
u_int16_t graphite_port;
int graphite_interval;
}; };
struct callmaster { struct callmaster {
@ -403,18 +445,16 @@ struct callmaster {
GHashTable *callhash; GHashTable *callhash;
GHashTable *interfaces; /* struct local_interface */ 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); BIT_ARRAY_DECLARE(ports_used, 0x10000);
/* XXX rework these */ /* XXX rework these */
mutex_t statspslock;
struct stats statsps; /* per second stats, running timer */ struct stats statsps; /* per second stats, running timer */
mutex_t statslock;
struct stats stats; /* copied from statsps once a second */ 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;
struct totalstats totalstats_interval; struct totalstats totalstats_interval;
/* control_ng_stats stuff */ /* 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, int call_delete_branch(struct callmaster *m, const str *callid, const str *branch,
const str *fromtag, const str *totag, bencode_item_t *output); const str *fromtag, const str *totag, bencode_item_t *output);
void call_destroy(struct call *); 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 *); void kernelize(struct packet_stream *);
int call_stream_address(char *, struct packet_stream *, enum stream_address_format, int *); 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 call_stream_address46(char *o, struct packet_stream *ps, enum stream_address_format format,
int *len, struct interface_address *ifa); 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_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); 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) { 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) { INLINE char *call_strdup_len(struct call *c, const char *s, unsigned int len) {
char *r; char *r;
if (!s)
return NULL;
r = call_malloc(c, len + 1); r = call_malloc(c, len + 1);
memcpy(r, s, len); memcpy(r, s, len);
r[len] = 0; r[len] = 0;
@ -525,10 +568,7 @@ INLINE str *call_str_init_dup(struct call *c, char *s) {
return call_str_dup(c, &t); return call_str_dup(c, &t);
} }
INLINE void callmaster_exclude_port(struct callmaster *m, u_int16_t p) { 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); bit_array_set(m->ports_used, p);
mutex_unlock(&m->portlock);
} }
INLINE struct packet_stream *packet_stream_sink(struct packet_stream *ps) { INLINE struct packet_stream *packet_stream_sink(struct packet_stream *ps) {
struct packet_stream *ret; struct packet_stream *ret;
@ -538,6 +578,6 @@ INLINE struct packet_stream *packet_stream_sink(struct packet_stream *ps) {
return ret; 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 #endif

+ 31
- 32
daemon/call_interfaces.c View File

@ -16,6 +16,8 @@
#include "str.h" #include "str.h"
#include "control_tcp.h" #include "control_tcp.h"
#include "control_udp.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) { static int info_parse_func(char **a, void **ret, void *p) {
GHashTable *ih = 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; 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); 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; GHashTable *infohash;
str_init(&callid, out[RE_TCP_RL_CALLID]); 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); c = call_get_opmode(&callid, m, opmode);
if (!c) { if (!c) {
ilog(LOG_WARNING, "["STR_FORMAT"] Got LOOKUP for unknown call-id", STR_FMT(&callid)); 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], 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), (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; goto out;
err: 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) { void calls_status_tcp(struct callmaster *m, struct control_stream *s) {
struct stats st;
GQueue q = G_QUEUE_INIT; GQueue q = G_QUEUE_INIT;
struct call *c; struct call *c;
mutex_lock(&m->statslock);
st = m->stats;
mutex_unlock(&m->statslock);
callmaster_get_all_calls(m, &q); 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), 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) { while (q.head) {
c = g_queue_pop_head(&q); 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) { 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) if (!totals)
return; 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) { 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) if (ps->crypto.params.crypto_suite)
bencode_dictionary_add_string(dict, "crypto suite", bencode_dictionary_add_string(dict, "crypto suite",
ps->crypto.params.crypto_suite->name); 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"); 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); BF_PS("media handover", MEDIA_HANDOVER);
stats: 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 */ /* XXX distinguish between input and output */
s = &totals->totals[0]; s = &totals->totals[0];
@ -918,7 +921,6 @@ stats:
ng_stats(bencode_dictionary_add_dictionary(dict, "RTCP"), &totals->totals[1], NULL); 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) { static void ng_list_calls( struct callmaster *m, bencode_item_t *output, long long int limit) {
GHashTableIter iter; GHashTableIter iter;
gpointer key, value; 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); 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) { const char *call_list_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output) {
bencode_item_t *calls = NULL; bencode_item_t *calls = NULL;
long long int limit; long long int limit;
@ -974,4 +974,3 @@ const char *call_list_ng(bencode_item_t *input, struct callmaster *m, bencode_it
return NULL; 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_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_delete_ng(bencode_item_t *, struct callmaster *, bencode_item_t *);
const char *call_query_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 *); 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, void ng_call_stats(struct call *call, const str *fromtag, const str *totag, bencode_item_t *output,
struct call_stats *totals); 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"; 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) { static void cli_incoming_list_totals(char* buffer, int len, struct callmaster* m, char* replybuffer, const char* outbufend) {
int printlen=0; 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"); printlen = snprintf(replybuffer,(outbufend-replybuffer), "\nTotal statistics (does not include current running sessions):\n\n");
ADJUSTLEN(printlen,outbufend,replybuffer); ADJUSTLEN(printlen,outbufend,replybuffer);
printlen = snprintf(replybuffer,(outbufend-replybuffer), " Uptime of rtpengine :%llu seconds\n", (unsigned long long)time(NULL)-m->totalstats.started); printlen = snprintf(replybuffer,(outbufend-replybuffer), " Uptime of rtpengine :%llu seconds\n", (unsigned long long)time(NULL)-m->totalstats.started);
ADJUSTLEN(printlen,outbufend,replybuffer); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); ADJUSTLEN(printlen,outbufend,replybuffer);
mutex_unlock(&m->totalstats_lock);
printlen = snprintf(replybuffer,(outbufend-replybuffer), "Control statistics:\n\n"); printlen = snprintf(replybuffer,(outbufend-replybuffer), "Control statistics:\n\n");
ADJUSTLEN(printlen,outbufend,replybuffer); ADJUSTLEN(printlen,outbufend,replybuffer);
printlen = snprintf(replybuffer,(outbufend-replybuffer), " %10s | %10s | %10s | %10s | %10s | %10s | %10s | %10s \n", 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; GSList *l;
GList *k, *o; GList *k, *o;
int printlen=0; 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) { if (len<=1) {
printlen = snprintf(replybuffer,(outbufend-replybuffer), "%s\n", "More parameters required."); 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); timeval_subtract(&tim_result_duration,&now,&ml->started);
printlen = snprintf(replybuffer,(outbufend-replybuffer), "--- Tag '"STR_FORMAT"' type: %s, callduration " printlen = snprintf(replybuffer,(outbufend-replybuffer), "--- Tag '"STR_FORMAT"' type: %s, callduration "
"%ld.%06ld , in dialogue with '"STR_FORMAT"'\n", "%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_sec,
tim_result_duration.tv_usec, tim_result_duration.tv_usec,
ml->active_dialogue ? ml->active_dialogue->tag.len : 6, 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 (RE_HAS_MEASUREDELAY)
if (!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) { 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 { } 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 #else
printlen = snprintf(replybuffer,(outbufend-replybuffer), "------ Media #%u, port %5u <> %15s:%-5hu%s, " 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, md->index,
(unsigned int) (ps->sfd ? ps->sfd->fd.localport : 0), (unsigned int) (ps->sfd ? ps->sfd->fd.localport : 0),
smart_ntop_p_buf(&ps->endpoint.ip46), ps->endpoint.port, smart_ntop_p_buf(&ps->endpoint.ip46), ps->endpoint.port,
(!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) ? " (RTCP)" : "", (!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 #endif
ADJUSTLEN(printlen,outbufend,replybuffer); 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); ADJUSTLEN(printlen,outbufend,replybuffer);
rwlock_unlock_w(&c->master_lock); // because of call_get(..) 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) { 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) { if (!c->ml_deleted) {
for (i = c->monologues; i; i = i->next) { for (i = c->monologues; i; i = i->next) {
ml = i->data; ml = i->data;
memset(&ml->terminated,0,sizeof(struct timeval));
gettimeofday(&(ml->terminated), NULL); gettimeofday(&(ml->terminated), NULL);
ml->term_reason = FORCED; ml->term_reason = FORCED;
} }
@ -302,18 +306,19 @@ static void cli_incoming_terminate(char* buffer, int len, struct callmaster* m,
if (!c->ml_deleted) { if (!c->ml_deleted) {
for (i = c->monologues; i; i = i->next) { for (i = c->monologues; i; i = i->next) {
ml = i->data; ml = i->data;
memset(&ml->terminated,0,sizeof(struct timeval));
gettimeofday(&(ml->terminated), NULL); gettimeofday(&(ml->terminated), NULL);
ml->term_reason = FORCED; ml->term_reason = FORCED;
} }
} }
call_destroy(c);
printlen = snprintf(replybuffer, outbufend-replybuffer, "\nCall Id (%s) successfully terminated by operator.\n\n",termparam.s); printlen = snprintf(replybuffer, outbufend-replybuffer, "\nCall Id (%s) successfully terminated by operator.\n\n",termparam.s);
ADJUSTLEN(printlen,outbufend,replybuffer); ADJUSTLEN(printlen,outbufend,replybuffer);
ilog(LOG_WARN, "Call Id (%s) successfully terminated by operator.",termparam.s); ilog(LOG_WARN, "Call Id (%s) successfully terminated by operator.",termparam.s);
rwlock_unlock_w(&c->master_lock); rwlock_unlock_w(&c->master_lock);
call_destroy(c);
obj_put(c);
} }
static void cli_incoming(int fd, void *p, uintptr_t u) { 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; struct cli *cli = (void *) p;
socklen_t sinl; socklen_t sinl;
static const int BUFLENGTH = 4096*1024; static const int BUFLENGTH = 4096*1024;
char replybuffer[BUFLENGTH]; memset(&replybuffer,0,BUFLENGTH);
char replybuffer[BUFLENGTH];
char* outbuf = replybuffer; char* outbuf = replybuffer;
const char* outbufend = replybuffer+BUFLENGTH; const char* outbufend = replybuffer+BUFLENGTH;
static const int MAXINPUT = 1024; static const int MAXINPUT = 1024;
char inbuf[MAXINPUT]; memset(&inbuf,0,MAXINPUT);
char inbuf[MAXINPUT];
int inlen = 0, readbytes = 0; int inlen = 0, readbytes = 0;
int rc=0; int rc=0;
@ -336,10 +341,10 @@ next:
nfd = accept(fd, (struct sockaddr *) &sin, &sinl); nfd = accept(fd, (struct sockaddr *) &sin, &sinl);
if (nfd == -1) { if (nfd == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) { 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; goto cleanup;
} }
ilog(LOG_INFO, "Accept error:%s\n", strerror(errno));
ilog(LOG_INFO, "Accept error:%s", strerror(errno));
goto next; goto next;
} }
@ -349,23 +354,24 @@ next:
readbytes = read(nfd, inbuf+inlen, MAXINPUT); readbytes = read(nfd, inbuf+inlen, MAXINPUT);
if (readbytes == -1) { if (readbytes == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) { 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; 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; 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* LIST = "list";
static const char* TERMINATE = "terminate"; 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); 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); cli_incoming_terminate(inbuf+strlen(TERMINATE), inlen-strlen(TERMINATE), cli->callmaster, outbuf, outbufend);
} else { } else {
sprintf(replybuffer, "%s:%s\n", "Unknown or incomplete command:", inbuf); 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; struct control_ng *c = (void *) obj;
bencode_buffer_t bencbuf; bencode_buffer_t bencbuf;
bencode_item_t *dict, *resp; bencode_item_t *dict, *resp;
str cmd, cookie, data, reply, *to_send;
str cmd, cookie, data, reply, *to_send, callid;
const char *errstr; const char *errstr;
struct msghdr mh; struct msghdr mh;
struct iovec iov[3]; struct iovec iov[3];
@ -119,11 +119,18 @@ static void control_ng_incoming(struct obj *obj, str *buf, struct sockaddr_in6 *
if (!cmd.s) if (!cmd.s)
goto err_send; 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; errstr = NULL;
if (!str_cmp(&cmd, "ping")) { 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); errstr = call_query_ng(dict, c->callmaster, resp);
g_atomic_int_inc(&cur->query); g_atomic_int_inc(&cur->query);
} }
#if GLIB_CHECK_VERSION(2,16,0)
else if (!str_cmp(&cmd, "list")) { else if (!str_cmp(&cmd, "list")) {
errstr = call_list_ng(dict, c->callmaster, resp); errstr = call_list_ng(dict, c->callmaster, resp);
g_atomic_int_inc(&cur->list); g_atomic_int_inc(&cur->list);
} }
#endif
else else
errstr = "Unrecognized command"; errstr = "Unrecognized command";
@ -165,15 +170,29 @@ err_send:
bencode_dictionary_add_string(resp, "result", "error"); bencode_dictionary_add_string(resp, "result", "error");
bencode_dictionary_add_string(resp, "error-reason", errstr); bencode_dictionary_add_string(resp, "error-reason", errstr);
g_atomic_int_inc(&cur->errors); g_atomic_int_inc(&cur->errors);
goto send_resp;
cmd = STR_NULL;
send_resp: send_resp:
bencode_collapse_str(resp, &reply); bencode_collapse_str(resp, &reply);
to_send = &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); ZERO(mh);
mh.msg_name = sin; mh.msg_name = sin;
mh.msg_namelen = sizeof(*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); 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")) if (!strcmp(out[RE_TCP_RL_CMD], "request"))
output = call_request_tcp(out, c->callmaster); output = call_request_tcp(out, c->callmaster);
else if (!strcmp(out[RE_TCP_RL_CMD], "lookup")) 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; 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') if (chrtoupper(out[RE_UDP_UL_CMD][0]) == 'U')
reply = call_update_udp(out, u->callmaster, addr, sin); reply = call_update_udp(out, u->callmaster, addr, sin);
else if (chrtoupper(out[RE_UDP_UL_CMD][0]) == 'L') 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) { static void __cookie_cache_check_swap(struct cookie_cache *c) {
if (poller_now - c->swap_time >= 30) { if (poller_now - c->swap_time >= 30) {
g_hash_table_remove_all(c->old.cookies); g_hash_table_remove_all(c->old.cookies);
#if GLIB_CHECK_VERSION(2,14,0)
g_string_chunk_clear(c->old.chunks); g_string_chunk_clear(c->old.chunks);
swap_ptrs(&c->old.chunks, &c->current.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); swap_ptrs(&c->old.cookies, &c->current.cookies);
c->swap_time = poller_now; 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; p->mki = NULL;
} }
INLINE void crypto_cleanup(struct crypto_context *c) { INLINE void crypto_cleanup(struct crypto_context *c) {
crypto_params_cleanup(&c->params);
if (!c->params.crypto_suite) if (!c->params.crypto_suite)
return; return;
if (c->params.crypto_suite->session_key_cleanup) if (c->params.crypto_suite->session_key_cleanup)
c->params.crypto_suite->session_key_cleanup(c); c->params.crypto_suite->session_key_cleanup(c);
c->have_session_key = 0; c->have_session_key = 0;
crypto_params_cleanup(&c->params);
c->params.crypto_suite = NULL;
} }
INLINE void crypto_reset(struct crypto_context *c) { INLINE void crypto_reset(struct crypto_context *c) {
crypto_cleanup(c); crypto_cleanup(c);


+ 6
- 4
daemon/dtls.c View File

@ -16,6 +16,7 @@
#include "log.h" #include "log.h"
#include "call.h" #include "call.h"
#include "poller.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->init) {
if ((d->active && active) || (!d->active && !active)) if ((d->active && active) || (!d->active && !active))
goto connect;
goto done;
dtls_connection_cleanup(d); 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->init = 1;
d->active = active ? -1 : 0; d->active = active ? -1 : 0;
connect:
dtls(ps, NULL, NULL);
done:
return 0; return 0;
error: error:
@ -628,6 +627,7 @@ error:
return -1; 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) { int dtls(struct packet_stream *ps, const str *s, struct sockaddr_in6 *fsin) {
struct dtls_connection *d; struct dtls_connection *d;
int ret; int ret;
@ -638,6 +638,8 @@ int dtls(struct packet_stream *ps, const str *s, struct sockaddr_in6 *fsin) {
if (!ps || !ps->sfd) if (!ps || !ps->sfd)
return 0; return 0;
if (!MEDIA_ISSET(ps->media, DTLS))
return 0;
d = &ps->sfd->dtls; d = &ps->sfd->dtls;


+ 161
- 35
daemon/graphite.c View File

@ -9,21 +9,58 @@
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <string.h>
#include "log.h" #include "log.h"
#include "call.h" #include "call.h"
#include "graphite.h"
static int graphite_sock=-1; static int graphite_sock=-1;
static int connectinprogress=0;
static u_int32_t graphite_ipaddress; static u_int32_t graphite_ipaddress;
static int graphite_port=0; static int graphite_port=0;
static struct callmaster* cm=0; static struct callmaster* cm=0;
//struct totalstats totalstats_prev; //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) { int connect_to_graphite_server(u_int32_t ipaddress, int port) {
if (graphite_sock>0)
close(graphite_sock);
graphite_sock=-1; graphite_sock=-1;
//int reconnect=0;
int rc=0; int rc=0;
struct sockaddr_in sin; struct sockaddr_in sin;
memset(&sin,0,sizeof(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_ipaddress = ipaddress;
graphite_port = port; 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; return -1;
} }
@ -48,18 +85,26 @@ int connect_to_graphite_server(u_int32_t ipaddress, int port) {
goto error; 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; struct in_addr ip;
ip.s_addr = graphite_ipaddress; 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); 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)); rc = connect(graphite_sock, (struct sockaddr *)&sin, sizeof(sin));
if (rc==-1) { 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; goto error;
} }
ilog(LOG_INFO, "Graphite server connected.");
return graphite_sock;
return 0;
error: error:
close(graphite_sock); close(graphite_sock);
@ -77,35 +122,58 @@ int send_graphite_data() {
} }
// format hostname "." totals.subkey SPACE value SPACE timestamp // format hostname "." totals.subkey SPACE value SPACE timestamp
char hostname[256]; memset(&hostname,0,256);
char hostname[256];
rc = gethostname(hostname,256); rc = gethostname(hostname,256);
if (rc<0) { if (rc<0) {
ilog(LOG_ERROR, "Could not retrieve host name information."); ilog(LOG_ERROR, "Could not retrieve host name information.");
goto error; goto error;
} }
char data_to_send[8192]; memset(&data_to_send,0,8192);
char data_to_send[8192];
char* ptr = data_to_send; 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) { if (rc<0) {
ilog(LOG_ERROR,"Could not write to graphite socket. Disconnecting graphite server."); ilog(LOG_ERROR,"Could not write to graphite socket. Disconnecting graphite server.");
goto error; goto error;
@ -120,27 +188,85 @@ error:
void graphite_loop_run(struct callmaster* callmaster, int seconds) { void graphite_loop_run(struct callmaster* callmaster, int seconds) {
int rc=0; 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) if (!cm)
cm = callmaster; cm = callmaster;
if (graphite_sock < 0) {
if (graphite_sock < 0 && !connectinprogress) {
rc = connect_to_graphite_server(graphite_ipaddress, graphite_port); 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(); rc = send_graphite_data();
if (rc<0) { if (rc<0) {
ilog(LOG_ERROR,"Sending graphite data failed."); 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 connect_to_graphite_server(u_int32_t ipaddress, int port);
int send_graphite_data(); int send_graphite_data();
void graphite_loop_run(struct callmaster* cm, int seconds); void graphite_loop_run(struct callmaster* cm, int seconds);
void set_prefix(char* prefix);
void graphite_loop(void *d);
#endif /* GRAPHITE_H_ */ #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 "str.h"
#include "call.h" #include "call.h"
#include "poller.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; 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); free(msg);
} }
void ilog(int prio, const char *fmt, ...) {
void __ilog(int prio, const char *fmt, ...) {
char prefix[256]; char prefix[256];
char *msg, *piece; char *msg, *piece;
const char *infix = ""; const char *infix = "";
@ -95,15 +105,6 @@ void ilog(int prio, const char *fmt, ...) {
xprio = LOG_LEVEL_MASK(prio); 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) { switch (log_info.e) {
case LOG_INFO_NONE: case LOG_INFO_NONE:
prefix[0] = 0; prefix[0] = 0;
@ -118,6 +119,19 @@ void ilog(int prio, const char *fmt, ...) {
STR_FMT(&log_info.u.stream_fd->call->callid), STR_FMT(&log_info.u.stream_fd->call->callid),
log_info.u.stream_fd->fd.localport); log_info.u.stream_fd->fd.localport);
break; 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); va_start(ap, fmt);
@ -129,8 +143,15 @@ void ilog(int prio, const char *fmt, ...) {
return; return;
} }
while (ret > 0 && msg[ret-1] == '\n')
ret--;
if ((prio & LOG_FLAG_LIMIT)) { if ((prio & LOG_FLAG_LIMIT)) {
time_t when; time_t when;
struct log_limiter_entry lle, *llep;
lle.prefix = prefix;
lle.msg = msg;
mutex_lock(&__log_limiter_lock); mutex_lock(&__log_limiter_lock);
@ -140,10 +161,13 @@ void ilog(int prio, const char *fmt, ...) {
__log_limiter_count = 0; __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) { 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++; __log_limiter_count++;
when = 0; when = 0;
} }
@ -163,7 +187,7 @@ void ilog(int prio, const char *fmt, ...) {
infix = "... "; infix = "... ";
} }
write_log(xprio, "%s%s%s", prefix, infix, piece);
write_log(xprio, "%s%s%.*s", prefix, infix, ret, piece);
out: out:
free(msg); free(msg);
@ -177,6 +201,20 @@ void cdrlog(const char* cdrbuffer) {
setlogmask(previous); 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) { void rtcplog(const char* cdrbuffer) {
int previous; int previous;
int mask = LOG_MASK (LOG_INFO); int mask = LOG_MASK (LOG_INFO);
@ -187,6 +225,6 @@ void rtcplog(const char* cdrbuffer) {
void log_init() { void log_init() {
mutex_init(&__log_limiter_lock); 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); __log_limiter_strings = g_string_chunk_new(1024);
} }

+ 53
- 8
daemon/log.h View File

@ -5,18 +5,26 @@
#include <syslog.h> #include <syslog.h>
#include <glib.h> #include <glib.h>
#include "compat.h" #include "compat.h"
#include "str.h"
struct log_info { struct log_info {
union { 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; } u;
enum { enum {
LOG_INFO_NONE = 0, LOG_INFO_NONE = 0,
LOG_INFO_CALL, LOG_INFO_CALL,
LOG_INFO_STREAM_FD, LOG_INFO_STREAM_FD,
LOG_INFO_STR,
LOG_INFO_C_STRING,
LOG_INFO_ICE_AGENT,
} e; } e;
}; };
@ -46,7 +54,20 @@ extern unsigned int max_log_line_length;
void log_init(void); 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 cdrlog(const char* cdrbuffer);
void rtcplog(const char* cdrbuffer); void rtcplog(const char* cdrbuffer);
@ -59,28 +80,52 @@ INLINE void log_info_clear() {
case LOG_INFO_NONE: case LOG_INFO_NONE:
return; return;
case LOG_INFO_CALL: case LOG_INFO_CALL:
__obj_put((void *) log_info.u.call);
break;
case LOG_INFO_STREAM_FD: 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; break;
} }
log_info.e = LOG_INFO_NONE; 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(); log_info_clear();
if (!c) if (!c)
return; return;
log_info.e = LOG_INFO_CALL; log_info.e = LOG_INFO_CALL;
log_info.u.call = __obj_get((void *) c); 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(); log_info_clear();
if (!sfd) if (!sfd)
return; return;
log_info.e = LOG_INFO_STREAM_FD; log_info.e = LOG_INFO_STREAM_FD;
log_info.u.stream_fd = __obj_get((void *) sfd); 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) { INLINE int get_log_level(void) {
return g_atomic_int_get(&log_level); return g_atomic_int_get(&log_level);
} }


+ 19
- 37
daemon/main.c View File

@ -27,10 +27,11 @@
#include "call_interfaces.h" #include "call_interfaces.h"
#include "cli.h" #include "cli.h"
#include "graphite.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 mutex_t *openssl_locks;
static char *pidfile; static char *pidfile;
@ -126,7 +126,7 @@ static void sighandler(gpointer x) {
ts.tv_sec = 0; ts.tv_sec = 0;
ts.tv_nsec = 100000000; /* 0.1 sec */ ts.tv_nsec = 100000000; /* 0.1 sec */
while (!global_shutdown) {
while (!g_shutdown) {
ret = sigtimedwait(&ss, NULL, &ts); ret = sigtimedwait(&ss, NULL, &ts);
if (ret == -1) { if (ret == -1) {
if (errno == EAGAIN || errno == EINTR) if (errno == EAGAIN || errno == EINTR)
@ -135,7 +135,7 @@ static void sighandler(gpointer x) {
} }
if (ret == SIGINT || ret == SIGTERM) if (ret == SIGINT || ret == SIGTERM)
global_shutdown = 1;
g_shutdown = 1;
else if (ret == SIGUSR1) { else if (ret == SIGUSR1) {
if (get_log_level() > 0) { if (get_log_level() > 0) {
g_atomic_int_add(&log_level, -1); g_atomic_int_add(&log_level, -1);
@ -237,7 +237,7 @@ static struct interface_address *if_addr_parse(char *s) {
return NULL; return NULL;
} }
ifa = g_slice_alloc(sizeof(*ifa));
ifa = g_slice_alloc0(sizeof(*ifa));
ifa->interface_name = name; ifa->interface_name = name;
ifa->addr = addr; ifa->addr = addr;
ifa->advertised = adv; ifa->advertised = adv;
@ -257,6 +257,7 @@ static void options(int *argc, char ***argv) {
char *listenngs = NULL; char *listenngs = NULL;
char *listencli = NULL; char *listencli = NULL;
char *graphitep = NULL; char *graphitep = NULL;
char *graphite_prefix_s = NULL;
char *redisps = NULL; char *redisps = NULL;
char *log_facility_s = NULL; char *log_facility_s = NULL;
char *log_facility_cdr_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" }, { "listen-cli", 'c', 0, G_OPTION_ARG_STRING, &listencli, "UDP port to listen on, CLI", "[IP46:]PORT" },
{ "graphite", 'g', 0, G_OPTION_ARG_STRING, &graphitep, "Address of the graphite server", "[IP46:]PORT" }, { "graphite", 'g', 0, G_OPTION_ARG_STRING, &graphitep, "Address of the graphite server", "[IP46:]PORT" },
{ "graphite-interval", 'w', 0, G_OPTION_ARG_INT, &graphite_interval, "Graphite send interval in seconds", "INT" }, { "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" }, { "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" }, { "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" }, { "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)"); die("Invalid IP or port (--graphite)");
} }
if (graphite_prefix_s)
set_prefix(graphite_prefix_s);
if (tos < 0 || tos > 255) if (tos < 0 || tos > 255)
die("Invalid TOS value"); die("Invalid TOS value");
@ -467,6 +472,7 @@ static void init_everything() {
resources(); resources();
sdp_init(); sdp_init();
dtls_init(); dtls_init();
ice_init();
} }
void redis_mod_verify(void *dlh) { void redis_mod_verify(void *dlh) {
@ -572,6 +578,9 @@ no_kernel:
mc.default_tos = tos; mc.default_tos = tos;
mc.b2b_url = b2b_url; mc.b2b_url = b2b_url;
mc.fmt = xmlrpc_fmt; mc.fmt = xmlrpc_fmt;
mc.graphite_port = graphite_port;
mc.graphite_ip = graphite_ip;
mc.graphite_interval = graphite_interval;
ct = NULL; ct = NULL;
if (listenport) { if (listenport) {
@ -605,8 +614,8 @@ no_kernel:
} }
if (redis_ip) { 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)) && g_file_test("../../rtpengine-redis/redis.so", G_FILE_TEST_IS_REGULAR))
dlh = dlopen("../../rtpengine-redis/redis.so", RTLD_NOW | RTLD_GLOBAL); dlh = dlopen("../../rtpengine-redis/redis.so", RTLD_NOW | RTLD_GLOBAL);
if (!dlh) if (!dlh)
@ -631,34 +640,6 @@ no_kernel:
die("Refusing to continue without working Redis database"); 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) { int main(int argc, char **argv) {
struct main_context ctx; struct main_context ctx;
int idx=0; int idx=0;
@ -670,10 +651,11 @@ int main(int argc, char **argv) {
ilog(LOG_INFO, "Startup complete, version %s", RTPENGINE_VERSION); ilog(LOG_INFO, "Startup complete, version %s", RTPENGINE_VERSION);
thread_create_detach(sighandler, NULL); thread_create_detach(sighandler, NULL);
thread_create_detach(timer_loop, ctx.p);
thread_create_detach(poller_timer_loop, ctx.p);
if (graphite_ip) if (graphite_ip)
thread_create_detach(graphite_loop, ctx.m); thread_create_detach(graphite_loop, ctx.m);
thread_create_detach(ice_thread_run, NULL);
if (num_threads < 1) { if (num_threads < 1) {
#ifdef _SC_NPROCESSORS_ONLN #ifdef _SC_NPROCESSORS_ONLN
@ -687,7 +669,7 @@ int main(int argc, char **argv) {
thread_create_detach(poller_loop, ctx.p); thread_create_detach(poller_loop, ctx.p);
} }
while (!global_shutdown) {
while (!g_shutdown) {
usleep(100000); usleep(100000);
threads_join_all(0); 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 *poller_new(void) {
struct poller *p; struct poller *p;
p = malloc(sizeof(*p)); p = malloc(sizeof(*p));
memset(p, 0, sizeof(*p)); memset(p, 0, sizeof(*p));
poller_now = time(NULL);
gettimeofday(&g_now, NULL);
p->fd = epoll_create1(0); p->fd = epoll_create1(0);
if (p->fd == -1) if (p->fd == -1)
abort(); abort();
@ -315,7 +309,7 @@ int poller_poll(struct poller *p, int timeout) {
if (ret <= 0) if (ret <= 0)
goto out; goto out;
poller_now = time(NULL);
gettimeofday(&g_now, NULL);
for (i = 0; i < ret; i++) { for (i = 0; i < ret; i++) {
ev = &evs[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() */ /* 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; struct timeval tv;
int wt; 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: 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; 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); struct poller *poller_new(void);
@ -45,7 +43,8 @@ int poller_isblocked(struct poller *, int);
void poller_error(struct poller *, int); void poller_error(struct poller *, int);
int poller_poll(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_add_timer(struct poller *, void (*)(void *), struct obj *);
int poller_del_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); 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) { 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) { 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) { 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, ", *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)); ntohl(common->ssrc));
} }


+ 35
- 35
daemon/rtcp_xr.c View File

@ -21,31 +21,31 @@
#define BT_VOIP_METRICS 7 #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, ", 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)); 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, ", 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); 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, ", 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_sec),
ntohl(rb_rr_time->ntp_frac)); 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); print_rtcp_xr_rb_header(cdrbufcur,&rb_dlrr->header);
cdrbufcur += sprintf(cdrbufcur,"rb_dlrr_ssrc=%u, rb_dlrr_lrr=%u, rb_dlrr_dlrr=%u, ", cdrbufcur += sprintf(cdrbufcur,"rb_dlrr_ssrc=%u, rb_dlrr_lrr=%u, rb_dlrr_dlrr=%u, ",
ntohl(rb_dlrr->item.ssrc), 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)); 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); 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," 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_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, ", "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->ssrc),
ntohl(rb_stats->begin_seq),
ntohs(rb_stats->begin_seq),
ntohl(rb_stats->end_seq), ntohl(rb_stats->end_seq),
ntohl(rb_stats->lost), ntohl(rb_stats->lost),
ntohl(rb_stats->dup), 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)); 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); 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, " cdrbufcur += sprintf(cdrbufcur,"rb_voip_mtc_ssrc=%u, rb_voip_mtc_loss_rate=%u, rb_voip_mtc_discard_rate=%u, rb_voip_mtc_burst_den=%u, "
"rb_voip_mtc_gap_den=%u, rb_voip_mtc_burst_dur=%u, rb_voip_mtc_gap_dur=%u, rb_voip_mtc_rnd_trip_delay=%u, " "rb_voip_mtc_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_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, ", "rb_voip_mtc_jb_abs_max=%u, ",
ntohl(rb_voip_mtc->ssrc), 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) { 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) { INLINE int check_session_keys(struct crypto_context *c) {
str s; str s;
const char *err; const char *err;
@ -51,7 +89,7 @@ error:
return -1; 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_header *rtp;
struct rtp_extension *ext; struct rtp_extension *ext;
const char *err; 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 */ if ((rtp->v_p_x_cc & 0xc0) != 0x80) /* version 2 */
goto error; goto error;
if (!p)
goto done;
*p = *s; *p = *s;
/* fixed header */ /* fixed header */
str_shift(p, sizeof(*rtp)); str_shift(p, sizeof(*rtp));
@ -84,6 +125,7 @@ static int rtp_payload(struct rtp_header **out, str *p, const str *s) {
goto error; goto error;
} }
done:
*out = rtp; *out = rtp;
return 0; return 0;
@ -184,13 +226,37 @@ int rtp_savp2avp(str *s, struct crypto_context *c) {
s, &payload)) s, &payload))
return -1; 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); 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)) if (crypto_decrypt_rtp(c, rtp, &to_decrypt, index))
return -1; return -1;
@ -242,3 +308,23 @@ error:
ilog(LOG_WARNING | LOG_FLAG_LIMIT, "Invalid SRTP/SRTCP packet received (short packet)"); ilog(LOG_WARNING | LOG_FLAG_LIMIT, "Invalid SRTP/SRTCP packet received (short packet)");
return -1; 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 "str.h"
#include <glib.h>
@ -18,10 +19,20 @@ struct rtp_header {
u_int32_t csrc[]; u_int32_t csrc[];
} __attribute__ ((packed)); } __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_avp2savp(str *, struct crypto_context *);
int rtp_savp2avp(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 "call.h"
#include "crypto.h" #include "crypto.h"
#include "dtls.h" #include "dtls.h"
#include "rtp.h"
#include "ice.h"
struct network_address { struct network_address {
str network_type; str network_type;
@ -58,7 +60,7 @@ struct sdp_media {
str media_type; str media_type;
str port; str port;
str transport; str transport;
/* ... format list */
str formats; /* space separated */
long int port_num; long int port_num;
int port_count; int port_count;
@ -66,6 +68,7 @@ struct sdp_media {
struct sdp_connection connection; struct sdp_connection connection;
int rr, rs; int rr, rs;
struct sdp_attributes attributes; struct sdp_attributes attributes;
GQueue format_list; /* list of slice-alloc'd str objects */
}; };
struct attribute_rtcp { struct attribute_rtcp {
@ -74,17 +77,19 @@ struct attribute_rtcp {
}; };
struct attribute_candidate { struct attribute_candidate {
str foundation;
str component_str; str component_str;
str transport;
str transport_str;
str priority_str; str priority_str;
str ip_str;
str address_str;
str port_str; str port_str;
str typ_str; str typ_str;
str type_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; int parsed:1;
}; };
@ -143,6 +148,14 @@ struct attribute_setup {
} value; } value;
}; };
struct attribute_rtpmap {
str payload_type_str;
str encoding_str;
str clock_rate_str;
struct rtp_payload_type rtp_pt;
};
struct sdp_attribute { struct sdp_attribute {
str full_line, /* including a= and \r\n */ str full_line, /* including a= and \r\n */
line_value, /* without a= and without \r\n */ line_value, /* without a= and without \r\n */
@ -156,7 +169,10 @@ struct sdp_attribute {
ATTR_RTCP, ATTR_RTCP,
ATTR_CANDIDATE, ATTR_CANDIDATE,
ATTR_ICE, ATTR_ICE,
ATTR_ICE_LITE,
ATTR_ICE_OPTIONS,
ATTR_ICE_UFRAG, ATTR_ICE_UFRAG,
ATTR_ICE_PWD,
ATTR_CRYPTO, ATTR_CRYPTO,
ATTR_SSRC, ATTR_SSRC,
ATTR_INACTIVE, ATTR_INACTIVE,
@ -169,6 +185,7 @@ struct sdp_attribute {
ATTR_MID, ATTR_MID,
ATTR_FINGERPRINT, ATTR_FINGERPRINT,
ATTR_SETUP, ATTR_SETUP,
ATTR_RTPMAP,
} attr; } attr;
union { union {
@ -179,24 +196,19 @@ struct sdp_attribute {
struct attribute_group group; struct attribute_group group;
struct attribute_fingerprint fingerprint; struct attribute_fingerprint fingerprint;
struct attribute_setup setup; struct attribute_setup setup;
struct attribute_rtpmap rtpmap;
} u; } 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) { INLINE struct sdp_attribute *attr_get_by_id(struct sdp_attributes *a, int id) {
return g_hash_table_lookup(a->id_hash, &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) { static struct sdp_attribute *attr_get_by_id_m_s(struct sdp_media *m, int id) {
struct sdp_attribute *a; struct sdp_attribute *a;
@ -309,6 +321,9 @@ INLINE int extract_token(char **sp, char *end, str *out) {
EXTRACT_NETWORK_ADDRESS_NP(field); \ EXTRACT_NETWORK_ADDRESS_NP(field); \
if (parse_address(&output->field)) output->field.parsed.s6_addr32[0] = 0xfe 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) { static int parse_origin(char *start, char *end, struct sdp_origin *output) {
if (output->parsed) if (output->parsed)
return -1; 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) { static int parse_media(char *start, char *end, struct sdp_media *output) {
char *ep; char *ep;
str s, *sp;
EXTRACT_TOKEN(media_type); EXTRACT_TOKEN(media_type);
EXTRACT_TOKEN(port); EXTRACT_TOKEN(port);
EXTRACT_TOKEN(transport); EXTRACT_TOKEN(transport);
str_init_len(&output->formats, start, end - start);
output->port_num = strtol(output->port.s, &ep, 10); output->port_num = strtol(output->port.s, &ep, 10);
if (ep == output->port.s) if (ep == output->port.s)
@ -355,6 +372,15 @@ static int parse_media(char *start, char *end, struct sdp_media *output) {
else else
output->port_count = 1; 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; return 0;
} }
@ -379,14 +405,12 @@ static int parse_attribute_group(struct sdp_attribute *output) {
} }
static int parse_attribute_ssrc(struct sdp_attribute *output) { static int parse_attribute_ssrc(struct sdp_attribute *output) {
char *start, *end;
PARSE_DECL;
struct attribute_ssrc *s; struct attribute_ssrc *s;
output->attr = ATTR_SSRC; 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.id_str);
EXTRACT_TOKEN(u.ssrc.attr_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) { static int parse_attribute_crypto(struct sdp_attribute *output) {
char *start, *end, *endp;
PARSE_DECL;
char *endp;
struct attribute_crypto *c; struct attribute_crypto *c;
int salt_key_len, enc_salt_key_len; int salt_key_len, enc_salt_key_len;
int b64_state = 0; int b64_state = 0;
@ -419,9 +444,7 @@ static int parse_attribute_crypto(struct sdp_attribute *output) {
output->attr = ATTR_CRYPTO; 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.tag_str);
EXTRACT_TOKEN(u.crypto.crypto_suite_str); EXTRACT_TOKEN(u.crypto.crypto_suite_str);
EXTRACT_TOKEN(u.crypto.key_params_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) { 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; 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.component_str);
EXTRACT_TOKEN(u.candidate.transport);
EXTRACT_TOKEN(u.candidate.transport_str);
EXTRACT_TOKEN(u.candidate.priority_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.port_str);
EXTRACT_TOKEN(u.candidate.typ_str); EXTRACT_TOKEN(u.candidate.typ_str);
EXTRACT_TOKEN(u.candidate.type_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; 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; 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; return 0;
} }
static int parse_attribute_fingerprint(struct sdp_attribute *output) { static int parse_attribute_fingerprint(struct sdp_attribute *output) {
char *end, *start;
PARSE_DECL;
unsigned char *c; unsigned char *c;
int i; int i;
start = output->value.s;
end = start + output->value.len;
output->attr = ATTR_FINGERPRINT; output->attr = ATTR_FINGERPRINT;
PARSE_INIT;
EXTRACT_TOKEN(u.fingerprint.hash_func_str); EXTRACT_TOKEN(u.fingerprint.hash_func_str);
EXTRACT_TOKEN(u.fingerprint.fingerprint_str); EXTRACT_TOKEN(u.fingerprint.fingerprint_str);
@ -647,6 +711,49 @@ static int parse_attribute_setup(struct sdp_attribute *output) {
return 0; 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) { static int parse_attribute(struct sdp_attribute *a) {
int ret; int ret;
@ -696,16 +803,18 @@ static int parse_attribute(struct sdp_attribute *a) {
ret = parse_attribute_crypto(a); ret = parse_attribute_crypto(a);
else if (!str_cmp(&a->name, "extmap")) else if (!str_cmp(&a->name, "extmap"))
a->attr = ATTR_EXTMAP; a->attr = ATTR_EXTMAP;
else if (!str_cmp(&a->name, "rtpmap"))
ret = parse_attribute_rtpmap(a);
break; break;
case 7: case 7:
if (!str_cmp(&a->name, "ice-pwd")) if (!str_cmp(&a->name, "ice-pwd"))
a->attr = ATTR_ICE;
a->attr = ATTR_ICE_PWD;
break; break;
case 8: case 8:
switch (a->name.s[0]) { switch (a->name.s[0]) {
case 'i': case 'i':
if (!str_cmp(&a->name, "ice-lite")) if (!str_cmp(&a->name, "ice-lite"))
a->attr = ATTR_ICE;
a->attr = ATTR_ICE_LITE;
else if (!str_cmp(&a->name, "inactive")) else if (!str_cmp(&a->name, "inactive"))
a->attr = ATTR_INACTIVE; a->attr = ATTR_INACTIVE;
break; break;
@ -731,7 +840,7 @@ static int parse_attribute(struct sdp_attribute *a) {
break; break;
case 11: case 11:
if (!str_cmp(&a->name, "ice-options")) if (!str_cmp(&a->name, "ice-options"))
a->attr = ATTR_ICE;
a->attr = ATTR_ICE_OPTIONS;
else if (!str_cmp(&a->name, "fingerprint")) else if (!str_cmp(&a->name, "fingerprint"))
ret = parse_attribute_fingerprint(a); ret = parse_attribute_fingerprint(a);
break; break;
@ -910,35 +1019,35 @@ int sdp_parse(str *body, GQueue *sessions) {
return 0; return 0;
error: 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); sdp_free(sessions);
return -1; return -1;
} }
static void attr_free(void *p) {
g_slice_free1(sizeof(struct sdp_attribute), p);
}
static void free_attributes(struct sdp_attributes *a) { static void free_attributes(struct sdp_attributes *a) {
struct sdp_attribute *attr;
/* g_hash_table_destroy(a->name_hash); */ /* g_hash_table_destroy(a->name_hash); */
g_hash_table_destroy(a->id_hash); g_hash_table_destroy(a->id_hash);
/* g_hash_table_destroy(a->name_lists_hash); */ /* g_hash_table_destroy(a->name_lists_hash); */
g_hash_table_destroy(a->id_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) { 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, 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) { int sdp_streams(const GQueue *sessions, GQueue *streams, struct sdp_ng_flags *flags) {
struct sdp_session *session; struct sdp_session *session;
struct sdp_media *media; 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; sp->type = media->media_type;
memcpy(sp->direction, flags->direction, sizeof(sp->direction)); memcpy(sp->direction, flags->direction, sizeof(sp->direction));
sp->desired_family = flags->address_family; 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 */ /* a=crypto */
attr = attr_get_by_id(&media->attributes, ATTR_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); 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 */ /* determine RTCP endpoint */
@ -1251,13 +1464,29 @@ static int insert_ice_address(struct sdp_chopper *chop, struct packet_stream *ps
return 0; 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, static int replace_network_address(struct sdp_chopper *chop, struct network_address *address,
struct packet_stream *ps, struct sdp_ng_flags *flags) struct packet_stream *ps, struct sdp_ng_flags *flags)
{ {
char buf[64]; char buf[64];
int len; 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; return 0;
if (copy_up_to(chop, &address->address_type)) 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); 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, static int process_session_attributes(struct sdp_chopper *chop, struct sdp_attributes *attrs,
struct sdp_ng_flags *flags) struct sdp_ng_flags *flags)
{ {
@ -1319,6 +1532,9 @@ static int process_session_attributes(struct sdp_chopper *chop, struct sdp_attri
switch (attr->attr) { switch (attr->attr) {
case ATTR_ICE: case ATTR_ICE:
case ATTR_ICE_UFRAG: case ATTR_ICE_UFRAG:
case ATTR_ICE_PWD:
case ATTR_ICE_OPTIONS:
case ATTR_ICE_LITE:
if (!flags->ice_remove && !flags->ice_force) if (!flags->ice_remove && !flags->ice_force)
break; break;
goto strip; goto strip;
@ -1378,6 +1594,9 @@ static int process_media_attributes(struct sdp_chopper *chop, struct sdp_media *
switch (attr->attr) { switch (attr->attr) {
case ATTR_ICE: case ATTR_ICE:
case ATTR_ICE_UFRAG: case ATTR_ICE_UFRAG:
case ATTR_ICE_PWD:
case ATTR_ICE_OPTIONS:
case ATTR_ICE_LITE:
if (MEDIA_ISSET(media, PASSTHRU)) if (MEDIA_ISSET(media, PASSTHRU))
break; break;
if (!flags->ice_remove && !flags->ice_force) if (!flags->ice_remove && !flags->ice_force)
@ -1440,124 +1659,135 @@ strip:
return 0; 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; GQueue *cands;
int pref;
unsigned long prio, tpref;
unsigned int lpref, tpref;
u_int32_t prio;
GList *l; GList *l;
struct sdp_attribute *a; struct sdp_attribute *a;
struct attribute_candidate *c; 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) if (!cands)
goto out; goto out;
for (l = cands->head; l; l = l->next) { for (l = cands->head; l; l = l->next) {
a = l->data; a = l->data;
c = &a->u.candidate; 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 */ * 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 { else {
/* we must deviate from the RFC recommended values */ /* we must deviate from the RFC recommended values */
if (tpref) if (tpref)
tpref--; tpref--;
pref = 65535;
lpref = 65535;
} }
prio = prio_calc(pref, tpref);
prio = ice_priority_pref(tpref, lpref, 1);
} }
} }
out: 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, 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; GList *l;
struct interface_address *ifa; 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) { 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'; *(--o) = '\0';
actpass = "holdconn"; 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)) else if (MEDIA_ISSET(media, SETUP_ACTIVE))
actpass = "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_session *session;
struct sdp_media *sdp_media; struct sdp_media *sdp_media;
GList *l, *k, *m, *j; 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 call_media *call_media;
struct packet_stream *ps, *ps_rtcp; struct packet_stream *ps, *ps_rtcp;
struct call *call;
m = monologue->medias.head; 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) { for (l = sessions->head; l; l = l->next) {
session = l->data; 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 (!MEDIA_ISSET(call_media, PASSTHRU)) {
if (process_session_attributes(chop, &session->attributes, flags)) if (process_session_attributes(chop, &session->attributes, flags))
goto error; 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; 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) if (!sdp_media->port_num || !ps->sfd)
goto next; 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"); 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"); 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"); chopper_append_c(chop, "a=recvonly\r\n");
else else
chopper_append_c(chop, "a=inactive\r\n"); 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_crypto(call_media, chop);
insert_dtls(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_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_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"); 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: next:
media_index++; media_index++;
@ -1813,8 +2016,3 @@ error:
void sdp_init() { 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_init(void);
void sdp_ice_foundation(struct interface_address *ifa);
int sdp_parse(str *body, GQueue *sessions); int sdp_parse(str *body, GQueue *sessions);
int sdp_streams(const GQueue *sessions, GQueue *streams, struct sdp_ng_flags *); 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); 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 #endif

+ 4
- 0
daemon/str.c View File

@ -29,3 +29,7 @@ str *__str_sprintf(const char *fmt, ...) {
va_end(ap); va_end(ap);
return ret; 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_FMT0(str) ((str) ? (str)->len : 6), ((str) ? (str)->s : "(NULL)")
#define STR_NULL ((str) { NULL, 0 }) #define STR_NULL ((str) { NULL, 0 })
#define STR_EMPTY ((str) { "", 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); INLINE int str_shift(str *s, int len);
/* binary compares str object with memory chunk of equal size */ /* binary compares str object with memory chunk of equal size */
INLINE int str_memcmp(const str *s, void *m); 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 */ /* asprintf() analogs */
#define str_sprintf(fmt, a...) __str_sprintf(STR_MALLOC_PADDING fmt, a) #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); guint str_hash(gconstpointer s);
gboolean str_equal(gconstpointer a, gconstpointer b); 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) { INLINE int str_memcmp(const str *s, void *m) {
return memcmp(s->s, m, s->len); 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 #endif

+ 229
- 61
daemon/stun.c View File

@ -6,11 +6,13 @@
#include <zlib.h> #include <zlib.h>
#include <openssl/hmac.h> #include <openssl/hmac.h>
#include <glib.h> #include <glib.h>
#include <endian.h>
#include "compat.h" #include "compat.h"
#include "str.h" #include "str.h"
#include "aux.h" #include "aux.h"
#include "log.h" #include "log.h"
#include "ice.h"
@ -21,7 +23,11 @@
#define STUN_ERROR_CODE 0x0009 #define STUN_ERROR_CODE 0x0009
#define STUN_UNKNOWN_ATTRIBUTES 0x000a #define STUN_UNKNOWN_ATTRIBUTES 0x000a
#define STUN_XOR_MAPPED_ADDRESS 0x0020 #define STUN_XOR_MAPPED_ADDRESS 0x0020
#define STUN_PRIORITY 0x0024
#define STUN_USE_CANDIDATE 0x0025
#define STUN_FINGERPRINT 0x8028 #define STUN_FINGERPRINT 0x8028
#define STUN_ICE_CONTROLLED 0x8029
#define STUN_ICE_CONTROLLING 0x802a
#define STUN_CLASS_REQUEST 0x00 #define STUN_CLASS_REQUEST 0x00
#define STUN_CLASS_INDICATION 0x01 #define STUN_CLASS_INDICATION 0x01
@ -35,6 +41,8 @@
| (((method) & 0x0f80) << 2) | (((class) & 0x1) << 4) \ | (((method) & 0x0f80) << 2) | (((class) & 0x1) << 4) \
| (((class) & 0x2) << 7)) | (((class) & 0x2) << 7))
#define STUN_BINDING_REQUEST \
STUN_MESSAGE_TYPE(STUN_METHOD_BINDING, STUN_CLASS_REQUEST)
#define STUN_BINDING_SUCCESS_RESPONSE \ #define STUN_BINDING_SUCCESS_RESPONSE \
STUN_MESSAGE_TYPE(STUN_METHOD_BINDING, STUN_CLASS_SUCCESS) STUN_MESSAGE_TYPE(STUN_METHOD_BINDING, STUN_CLASS_SUCCESS)
#define STUN_BINDING_ERROR_RESPONSE \ #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 { struct header {
u_int16_t msg_type; u_int16_t msg_type;
u_int16_t msg_len; u_int16_t msg_len;
@ -94,9 +90,23 @@ struct xor_mapped_address {
u_int32_t address[4]; u_int32_t address[4];
} __attribute__ ((packed)); } __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; struct tlv *tlv;
int len, type, uc; int len, type, uc;
str attr; str attr;
@ -129,12 +139,14 @@ static int stun_attributes(struct stun_attrs *out, str *s, u_int16_t *unknowns)
case STUN_USERNAME: case STUN_USERNAME:
out->username = attr; out->username = attr;
break; break;
case STUN_MESSAGE_INTEGRITY: case STUN_MESSAGE_INTEGRITY:
if (attr.len != 20) if (attr.len != 20)
return -1; return -1;
out->msg_integrity_attr = (void *) tlv; out->msg_integrity_attr = (void *) tlv;
out->msg_integrity = attr; out->msg_integrity = attr;
break; break;
case STUN_FINGERPRINT: case STUN_FINGERPRINT:
if (attr.len != 4) if (attr.len != 4)
return -1; 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); out->fingerprint = ntohl(*(u_int32_t *) attr.s);
goto out; goto out;
case 0x0025: /* use-candidate */
case STUN_USE_CANDIDATE:
out->use = 1; out->use = 1;
break; 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; out->controlled = 1;
break; 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; out->controlling = 1;
break; break;
case 0x0024: /* priority */
case STUN_PRIORITY:
if (attr.len != 4) if (attr.len != 4)
return -1; return -1;
out->priority = ntohl(*((u_int32_t *) attr.s)); 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 */ case 0x8022: /* software */
break; /* ignore but suppress warning message */ 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: default:
ilog(LOG_NOTICE, "Unknown STUN attribute: 0x%04x", type); ilog(LOG_NOTICE, "Unknown STUN attribute: 0x%04x", type);
if ((type & 0x8000)) 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 = &mh->msg_iov[mh->msg_iovlen++];
iov->iov_base = append; /* must have space for padding */ iov->iov_base = append; /* must have space for padding */
iov->iov_len = (append_len + 3) & 0xfffc; 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) __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; struct header *hdr;
hdr = mh->msg_iov->iov_base; hdr = mh->msg_iov->iov_base;
hdr->msg_len = htons(hdr->msg_len); 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) { 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 iovec *iov;
struct header *hdr; struct header *hdr;
if (!pwd || !pwd->s)
return;
output_add(mh, mi, STUN_MESSAGE_INTEGRITY); output_add(mh, mi, STUN_MESSAGE_INTEGRITY);
iov = mh->msg_iov; iov = mh->msg_iov;
hdr = iov->iov_base; 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); 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 code, char *reason, int len, u_int16_t add_attr, void *attr_cont,
int attr_len) int attr_len)
{ {
struct header hdr; struct header hdr;
struct error_code ec; struct error_code ec;
struct msg_integrity mi;
struct fingerprint fp; struct fingerprint fp;
struct generic aa; struct generic aa;
struct msghdr mh; 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]; unsigned char buf[256];
output_init(&mh, iov, sin, &hdr, STUN_BINDING_ERROR_RESPONSE, req->transaction, buf, sizeof(buf)); 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) if (attr_cont)
output_add_data(&mh, &aa, add_attr, attr_cont, attr_len); output_add_data(&mh, &aa, add_attr, attr_cont, attr_len);
integrity(&mh, &mi, &ps->media->ice_agent->pwd[0]);
fingerprint(&mh, &fp); fingerprint(&mh, &fp);
output_finish(&mh, ps);
output_finish_src(&mh, dst);
sendmsg(ps->sfd->fd.fd, &mh, 0); 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) 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) type, content, len)
@ -334,28 +402,35 @@ static int check_fingerprint(str *msg, struct stun_attrs *attrs) {
return 0; 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; u_int16_t lenX;
char digest[20]; char digest[20];
str ufrag[2]; str ufrag[2];
struct iovec iov[3]; 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; return -1;
if (!media->ice_pwd.s || !media->ice_pwd.len)
if (!ag->ufrag[dst].s || !ag->ufrag[dst].len)
return -1; 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; 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); lenX = htons((attrs->msg_integrity_attr - msg->s) - 20 + 24);
iov[0].iov_base = msg->s; 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_base = msg->s + OFFSET_OF(struct header, cookie);
iov[2].iov_len = ntohs(lenX) + - 24 + 20 - 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; 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, 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 header hdr;
struct xor_mapped_address xma; 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); 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); fingerprint(&mh, &fp);
output_finish(&mh, ps);
output_finish_src(&mh, dst);
sendmsg(ps->sfd->fd.fd, &mh, 0); sendmsg(ps->sfd->fd.fd, &mh, 0);
return 0; return 0;
@ -415,20 +491,56 @@ INLINE int u_int16_t_arr_len(u_int16_t *arr) {
} }
#define SLF " from %s" #define SLF " from %s"
#define SLP smart_ntop_port_buf(sin) #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: /* return values:
* 0 = stun packet processed successfully * 0 = stun packet processed successfully
* -1 = stun packet not processed, processing should continue as non-stun packet * -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; struct header *req = (void *) b->s;
int msglen, method, class; int msglen, method, class;
str attr_str; str attr_str;
struct stun_attrs attrs; struct stun_attrs attrs;
u_int16_t unknowns[UNKNOWNS_COUNT]; u_int16_t unknowns[UNKNOWNS_COUNT];
const char *err; const char *err;
int dst_idx, src_idx;
msglen = ntohs(req->msg_len); msglen = ntohs(req->msg_len);
err = "message-length mismatch"; 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.s = &b->s[20];
attr_str.len = b->len - 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"; err = "failed to parse attributes";
if (unknowns[0] == 0xffff) if (unknowns[0] == 0xffff)
goto ignore; goto ignore;
ilog(LOG_WARNING, "STUN packet contained unknown " ilog(LOG_WARNING, "STUN packet contained unknown "
"\"comprehension required\" attribute(s)" SLF, SLP); "\"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, STUN_UNKNOWN_ATTRIBUTES, unknowns,
u_int16_t_arr_len(unknowns) * 2); u_int16_t_arr_len(unknowns) * 2);
return 0; return 0;
} }
if (class != STUN_CLASS_REQUEST)
return -1;
err = "FINGERPRINT attribute missing"; err = "FINGERPRINT attribute missing";
if (!attrs.fingerprint_attr) if (!attrs.fingerprint_attr)
goto ignore; goto ignore;
err = "USERNAME attribute missing";
if (!attrs.username.s)
goto bad_req;
err = "MESSAGE_INTEGRITY attribute missing"; err = "MESSAGE_INTEGRITY attribute missing";
if (!attrs.msg_integrity.s) if (!attrs.msg_integrity.s)
goto bad_req; 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"; err = "FINGERPRINT mismatch";
if (check_fingerprint(b, &attrs)) if (check_fingerprint(b, &attrs))
goto ignore; goto ignore;
if (check_auth(b, &attrs, ps->media))
if (check_auth(b, &attrs, ps->media, dst_idx, src_idx))
goto unauth; 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: bad_req:
ilog(LOG_NOTICE, "Received invalid STUN packet" SLF ": %s", SLP, err); 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; return 0;
unauth: unauth:
ilog(LOG_NOTICE, "STUN authentication mismatch" SLF, SLP); 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; return 0;
ignore: ignore:
ilog(LOG_NOTICE, "Not handling potential STUN packet" SLF ": %s", SLP, err); ilog(LOG_NOTICE, "Not handling potential STUN packet" SLF ": %s", SLP, err);
return -1; 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 <string.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <sys/types.h>
#include "compat.h" #include "compat.h"
#include "call.h" #include "call.h"
#include "str.h" #include "str.h"
@ -12,6 +13,24 @@
#define STUN_COOKIE 0x2112A442UL #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) { INLINE int is_stun(const str *s) {
const unsigned char *b = (const void *) s->s; const unsigned char *b = (const void *) s->s;
const u_int32_t *u; 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 #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 ngcp-rtpengine (3.3.0.0+0~mr3.8.0.0) unstable; urgency=low
[ Richard Fuchs ] [ Richard Fuchs ]


+ 4
- 4
debian/control View File

@ -6,18 +6,18 @@ Build-Depends: debhelper (>= 5),
iptables-dev (>= 1.4), iptables-dev (>= 1.4),
libcurl4-openssl-dev | libcurl4-gnutls-dev | libcurl4-openssl-dev | libcurl4-gnutls-dev |
libcurl3-openssl-dev | libcurl3-gnutls-dev, libcurl3-openssl-dev | libcurl3-gnutls-dev,
libglib2.0-dev,
libglib2.0-dev (>= 2.30),
libpcre3-dev, libpcre3-dev,
libssl-dev (>= 1.0.1), libssl-dev (>= 1.0.1),
libxmlrpc-c3-dev (>= 1.16.07) | libxmlrpc-core-c3-dev (>= 1.16.07), libxmlrpc-c3-dev (>= 1.16.07) | libxmlrpc-core-c3-dev (>= 1.16.07),
markdown, markdown,
zlib1g-dev zlib1g-dev
Standards-Version: 3.9.5
Standards-Version: 3.9.6
Homepage: http://sipwise.com/ Homepage: http://sipwise.com/
Package: ngcp-rtpengine-daemon Package: ngcp-rtpengine-daemon
Architecture: any Architecture: any
Pre-Depends: ngcp-system-tools
Suggests: ngcp-system-tools
Depends: ${misc:Depends}, Depends: ${misc:Depends},
${shlibs:Depends} ${shlibs:Depends}
Conflicts: ngcp-mediaproxy-ng-daemon Conflicts: ngcp-mediaproxy-ng-daemon
@ -63,7 +63,7 @@ Description: IPtables kernel module for the NGCP media proxy - source.
Package: ngcp-rtpengine-kernel-dkms Package: ngcp-rtpengine-kernel-dkms
Architecture: all Architecture: all
Pre-Depends: ngcp-system-tools
Suggests: ngcp-system-tools
Depends: dkms (>= 1.95), Depends: dkms (>= 1.95),
${misc:Depends} ${misc:Depends}
Conflicts: ngcp-mediaproxy-ng-kernel-dkms Conflicts: ngcp-mediaproxy-ng-kernel-dkms


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

@ -25,4 +25,5 @@ TABLE=0
# NUM_THREADS=5 # NUM_THREADS=5
# DELETE_DELAY=30 # DELETE_DELAY=30
# GRAPHITE=9006 # 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 "$DELETE_DELAY" ] || OPTIONS="$OPTIONS --delete-delay=$DELETE_DELAY"
[ -z "$GRAPHITE" ] || OPTIONS="$OPTIONS --graphite=$GRAPHITE" [ -z "$GRAPHITE" ] || OPTIONS="$OPTIONS --graphite=$GRAPHITE"
[ -z "$GRAPHITE_INTERVAL" ] || OPTIONS="$OPTIONS --graphite-interval=$GRAPHITE_INTERVAL" [ -z "$GRAPHITE_INTERVAL" ] || OPTIONS="$OPTIONS --graphite-interval=$GRAPHITE_INTERVAL"
[ -z "$GRAPHITE_PREFIX" ] || OPTIONS="$OPTIONS --graphite-prefix=$GRAPHITE_PREFIX"
if test "$FORK" = "no" ; then if test "$FORK" = "no" ; then
OPTIONS="$OPTIONS --foreground" OPTIONS="$OPTIONS --foreground"
fi fi


+ 6
- 1
el/rtpengine.init View File

@ -39,7 +39,7 @@ MODULE=0
build_opts() { build_opts() {
shopt -s nocasematch shopt -s nocasematch
RPMS=`rpm -qa | grep ngcp-rtpengine-kernel`
RPMS=`rpm -qa | grep rtpengine-kernel`
if [[ "$KERNEL" == "yes" && -n "$TABLE" && -n "$RPMS" ]] if [[ "$KERNEL" == "yes" && -n "$TABLE" && -n "$RPMS" ]]
then then
MODULE=1 MODULE=1
@ -153,6 +153,11 @@ build_opts() {
OPTS+=" --graphite-interval=$GRAPHITE_INTERVAL" OPTS+=" --graphite-interval=$GRAPHITE_INTERVAL"
fi fi
if [[ -n "$GRAPHITE_PREFIX" ]]
then
OPTS+=" --graphite-prefix=$GRAPHITE_PREFIX"
fi
if [[ -n "$LOG_FACILITY_CDR" ]] if [[ -n "$LOG_FACILITY_CDR" ]]
then then
OPTS+=" --log-facility-cdr=$LOG_FACILITY_CDR" 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 Version: 2.3.6
Release: 0%{?dist} Release: 0%{?dist}
Summary: The Sipwise NGCP rtpengine Summary: The Sipwise NGCP rtpengine
@ -6,14 +6,15 @@ Summary: The Sipwise NGCP rtpengine
Group: System Environment/Daemons Group: System Environment/Daemons
License: GPLv3 License: GPLv3
URL: https://github.com/sipwise/rtpengine 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: gcc make pkgconfig redhat-rpm-config
BuildRequires: glib2-devel libcurl-devel openssl-devel pcre-devel BuildRequires: glib2-devel libcurl-devel openssl-devel pcre-devel
BuildRequires: xmlrpc-c-devel zlib-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 %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 Summary: NGCP rtpengine in-kernel packet forwarding
Group: System Environment/Daemons Group: System Environment/Daemons
BuildRequires: gcc make redhat-rpm-config iptables-devel 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 %description kernel
NGCP rtpengine in-kernel packet forwarding
%{summary}.
%package dkms %package dkms
@ -43,7 +45,7 @@ Requires(post): epel-release dkms
Requires(preun): epel-release dkms Requires(preun): epel-release dkms
%description dkms %description dkms
Kernel module for rtpengine in-kernel packet forwarding
%{summary}.
%prep %prep
@ -60,16 +62,16 @@ cd ..
%install %install
# Install the userspace daemon # 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 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 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 the iptables plugin
install -D -p -m755 iptables-extension/libxt_RTPENGINE.so \ 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 ## DKMS module source install
install -D -p -m644 kernel-module/Makefile \ 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 \ 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 \ 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 %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 %post
if [ $1 -eq 1 ]; then if [ $1 -eq 1 ]; then
/sbin/chkconfig --add rtpengine || :
/sbin/chkconfig --add %{name} || :
fi fi
@ -113,8 +110,8 @@ true
%preun %preun
if [ $1 = 0 ] ; then 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 fi
@ -126,14 +123,14 @@ true
%files %files
# Userspace daemon # Userspace daemon
%{_sbindir}/rtpengine
%{_sbindir}/%{name}
# CLI (command line interface) # CLI (command line interface)
%{_sbindir}/rtpengine-ctl
%{_sbindir}/%{name}-ctl
# init.d script and configuration file # 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 # Documentation
%doc LICENSE README.md el/README.el.md debian/changelog debian/copyright %doc LICENSE README.md el/README.el.md debian/changelog debian/copyright
@ -144,7 +141,7 @@ true
%files dkms %files dkms
%attr(0755,root,root) %{_usrsrc}/%{name}-%{version}-%{release}/
%{_usrsrc}/%{name}-%{version}-%{release}/
%changelog %changelog


+ 143
- 46
kernel-module/xt_RTPENGINE.c View File

@ -17,6 +17,8 @@
#include <net/dst.h> #include <net/dst.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/bsearch.h>
#include <linux/atomic.h>
#include <linux/netfilter_ipv4/ip_tables.h> #include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h> #include <linux/netfilter_ipv6.h>
@ -143,13 +145,26 @@ struct re_crypto_context {
const struct re_hmac *hmac; 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 { struct rtpengine_target {
atomic_t refcnt; atomic_t refcnt;
u_int32_t table; u_int32_t table;
struct rtpengine_target_info target; 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 decrypt;
struct re_crypto_context encrypt; 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; u_int32_t id;
struct rtpengine_table *t; struct rtpengine_table *t;
struct rtpengine_list_entry op; struct rtpengine_list_entry op;
int err;
int err, port, i;
struct rtpengine_target *g; struct rtpengine_target *g;
unsigned long flags; unsigned long flags;
int port;
if (l != sizeof(op)) if (l != sizeof(op))
return -EINVAL; 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)); memset(&op, 0, sizeof(op));
memcpy(&op.target, &g->target, sizeof(op.target)); 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); spin_lock_irqsave(&g->decrypt.lock, flags);
op.target.decrypt.last_index = g->target.decrypt.last_index; op.target.decrypt.last_index = g->target.decrypt.last_index;
@ -869,10 +892,6 @@ err:
return err; return err;
} }
static int proc_list_open(struct inode *i, struct file *f) { static int proc_list_open(struct inode *i, struct file *f) {
int err; int err;
struct seq_file *p; 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) { static int proc_list_show(struct seq_file *f, void *v) {
struct rtpengine_target *g = v; struct rtpengine_target *g = v;
unsigned long flags;
int i;
seq_printf(f, "port %5u:\n", g->target.target_port); seq_printf(f, "port %5u:\n", g->target.target_port);
proc_list_addr_print(f, "src", &g->target.src_addr); 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); 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)) 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]); 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", 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->decrypt, &g->target.decrypt, "decryption (incoming)");
proc_list_crypto_print(f, &g->encrypt, &g->target.encrypt, "encryption (outgoing)"); proc_list_crypto_print(f, &g->encrypt, &g->target.encrypt, "encryption (outgoing)");
if (g->target.rtcp_mux) 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 rtpengine_target *g;
struct re_bucket *b, *ba = NULL; struct re_bucket *b, *ba = NULL;
struct rtpengine_target *og = NULL; struct rtpengine_target *og = NULL;
int err;
int err, j;
unsigned long flags; unsigned long flags;
if (!i->target_port) 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)); memset(g, 0, sizeof(*g));
g->table = t->id; g->table = t->id;
atomic_set(&g->refcnt, 1); atomic_set(&g->refcnt, 1);
spin_lock_init(&g->stats_lock);
spin_lock_init(&g->decrypt.lock); spin_lock_init(&g->decrypt.lock);
spin_lock_init(&g->encrypt.lock); spin_lock_init(&g->encrypt.lock);
memcpy(&g->target, i, sizeof(*i)); 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) if (!og)
goto fail4; 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 { else {
err = -EEXIST; err = -EEXIST;
@ -1902,6 +1933,17 @@ static u_int64_t packet_index(struct re_crypto_context *c,
return index; 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, static int srtp_hash(unsigned char *hmac,
struct re_crypto_context *c, struct re_crypto_context *c,
struct rtpengine_srtp *s, struct rtp_parsed *r, 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, static int srtp_auth_validate(struct re_crypto_context *c,
struct rtpengine_srtp *s, struct rtp_parsed *r, 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 *auth_tag;
unsigned char hmac[20]; unsigned char hmac[20];
u_int64_t pkt_idx = *pkt_idx_p;
if (s->hmac == REH_NULL) if (s->hmac == REH_NULL)
return 0; 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)) if (srtp_hash(hmac, c, s, r, pkt_idx))
return -1; 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; 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; 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) { static void re_timespec_subtract (struct timespec *result, const struct timespec *a, const struct timespec *b) {
long long nanoseconds=0; 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); 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 udphdr *uh;
struct rtpengine_target *g; struct rtpengine_target *g;
struct sk_buff *skb2; struct sk_buff *skb2;
int err;
int err, rtp_pt_idx = -2;
unsigned int datalen; unsigned int datalen;
unsigned long flags;
u_int32_t *u32; u_int32_t *u32;
struct rtp_parsed rtp; struct rtp_parsed rtp;
u_int64_t pkt_idx = 0;
u_int64_t pkt_idx = 0, pkt_idx_u;
#if (RE_HAS_MEASUREDELAY) #if (RE_HAS_MEASUREDELAY)
struct timespec endtime, delay; struct timespec endtime, delay;
@ -2217,17 +2302,28 @@ not_stun:
src_check_ok: src_check_ok:
if (g->target.dtls && is_dtls(skb)) if (g->target.dtls && is_dtls(skb))
goto skip1; goto skip1;
rtp.ok = 0;
if (!g->target.rtp)
goto not_rtp;
parse_rtp(&rtp, skb); parse_rtp(&rtp, skb);
if (!rtp.ok) { if (!rtp.ok) {
if (g->target.rtp_only) if (g->target.rtp_only)
goto skip1; goto skip1;
goto not_rtp; goto not_rtp;
} }
if (g->target.rtcp_mux && is_muxed_rtcp(&rtp)) if (g->target.rtcp_mux && is_muxed_rtcp(&rtp))
goto skip1; 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; 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)) if (srtp_decrypt(&g->decrypt, &g->target.decrypt, &rtp, pkt_idx))
goto skip_error; goto skip_error;
@ -2245,11 +2341,8 @@ not_rtp:
DBG("sending mirror packet to dst "MIPF"\n", MIPP(g->target.mirror_addr)); DBG("sending mirror packet to dst "MIPF"\n", MIPP(g->target.mirror_addr));
skb2 = skb_copy(skb, GFP_ATOMIC); skb2 = skb_copy(skb, GFP_ATOMIC);
err = send_proxy_packet(skb2, &g->target.src_addr, &g->target.mirror_addr, g->target.tos); 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) { 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); err = send_proxy_packet(skb, &g->target.src_addr, &g->target.dst_addr, g->target.tos);
out: 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) if (err)
g->stats.errors++;
atomic64_inc(&g->stats.errors);
else { 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) #if (RE_HAS_MEASUREDELAY)
getnstimeofday(&endtime); getnstimeofday(&endtime);
re_timespec_subtract(&delay,&endtime, starttime); 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_min=delay;
g->stats.delay_avg=delay; g->stats.delay_avg=delay;
g->stats.delay_max=delay; g->stats.delay_max=delay;
@ -2289,13 +2385,16 @@ out:
g->stats.delay_max = delay; 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_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 #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); target_push(g);
table_push(t); table_push(t);
@ -2303,9 +2402,7 @@ out:
return NF_DROP; return NF_DROP;
skip_error: 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: skip1:
target_push(g); target_push(g);
skip2: skip2:


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

@ -1,6 +1,12 @@
#ifndef XT_RTPPROXY_H #ifndef XT_RTPPROXY_H
#define XT_RTPPROXY_H #define XT_RTPPROXY_H
#define NUM_PAYLOAD_TYPES 16
struct xt_rtpengine_info { struct xt_rtpengine_info {
u_int32_t id; u_int32_t id;
}; };
@ -14,6 +20,10 @@ struct rtpengine_stats {
struct timespec delay_max; struct timespec delay_max;
u_int8_t in_tos; u_int8_t in_tos;
}; };
struct rtpengine_rtp_stats {
u_int64_t packets;
u_int64_t bytes;
};
struct re_address { struct re_address {
int family; int family;
@ -77,10 +87,14 @@ struct rtpengine_target_info {
struct rtpengine_srtp decrypt; struct rtpengine_srtp decrypt;
struct rtpengine_srtp encrypt; struct rtpengine_srtp encrypt;
unsigned char payload_types[NUM_PAYLOAD_TYPES]; /* must be sorted */
unsigned int num_payload_types;
unsigned char tos; unsigned char tos;
int rtcp_mux:1, int rtcp_mux:1,
dtls:1, dtls:1,
stun:1, stun:1,
rtp:1,
rtp_only:1; rtp_only:1;
}; };
@ -98,6 +112,7 @@ struct rtpengine_message {
struct rtpengine_list_entry { struct rtpengine_list_entry {
struct rtpengine_target_info target; struct rtpengine_target_info target;
struct rtpengine_stats stats; 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 Digest::SHA qw(hmac_sha1);
use MIME::Base64; use MIME::Base64;
use Data::Dumper; use Data::Dumper;
use SRTP;
my ($NUM, $RUNTIME, $STREAMS, $PAYLOAD, $INTERVAL, $RTCP_INTERVAL, $STATS_INTERVAL) my ($NUM, $RUNTIME, $STREAMS, $PAYLOAD, $INTERVAL, $RTCP_INTERVAL, $STATS_INTERVAL)
= (1000, 30, 1, 160, 20, 5, 5); = (1000, 30, 1, 160, 20, 5, 5);
@ -105,216 +106,33 @@ sub send_receive {
return $x; 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 { sub rtcp_encrypt {
my ($r, $ctx, $dir) = @_; 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}; ($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 ($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; 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 .= pack("N", 1); # mki
$pkt .= substr($hmac, 0, 10); $pkt .= substr($hmac, 0, 10);
$$ctx{$dir}{rtcp_index}++;
$$dctx{rtcp_index}++;
$NOENC{rtcp_packet} = $pkt; $NOENC{rtcp_packet} = $pkt;
@ -324,68 +142,32 @@ sub rtcp_encrypt {
sub rtp_encrypt { sub rtp_encrypt {
my ($r, $ctx, $dir) = @_; 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}; ($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; $NOENC{rtp_packet} = $pkt;
return $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 { sub savp_sdp {
my ($ctx, $ctx_o) = @_; my ($ctx, $ctx_o) = @_;
if (!$$ctx{out}{crypto_suite}) { if (!$$ctx{out}{crypto_suite}) {
$$ctx{out}{crypto_suite} = $$ctx_o{in}{crypto_suite} ? $$ctx_o{in}{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; $$ctx{out}{rtp_mki_len} = 0;
if (rand() > .5) { if (rand() > .5) {
@ -479,7 +261,7 @@ sub rtcp_savpf {
sub rtp { sub rtp {
my ($ctx) = @_; my ($ctx) = @_;
my $seq = $$ctx{rtp_seqnum}; 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 $hdr = pack("CCnNN", 0x80, 0x00, $seq, rand(2**32), rand(2**32));
my $pack = $hdr . rand_str($PAYLOAD); my $pack = $hdr . rand_str($PAYLOAD);
$$ctx{rtp_seqnum} = (++$seq & 0xffff); $$ctx{rtp_seqnum} = (++$seq & 0xffff);
@ -512,10 +294,9 @@ sub savp_crypto {
@a or die; @a or die;
my $i = 0; my $i = 0;
while (@a >= 6) { 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} = $a[4];
$$ctx[$i]{in}{rtp_mki_len} = $a[5]; $$ctx[$i]{in}{rtp_mki_len} = $a[5];
undef($$ctx[$i]{in}{rtp_session_key}); undef($$ctx[$i]{in}{rtp_session_key});
@ -777,8 +558,9 @@ t=0 0
and $cp = $p; and $cp = $p;
$sdp .= <<"!"; $sdp .= <<"!";
m=audio $p $$tr{name} 8
m=audio $p $$tr{name} 0 8 111
a=rtpmap:8 PCMA/8000 a=rtpmap:8 PCMA/8000
a=rtpmap:111 opus/48000/2
! !
if ($$A{want_rtcpmux} && $op eq 'offer') { if ($$A{want_rtcpmux} && $op eq 'offer') {
$sdp .= "a=rtcp-mux\n"; $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