Browse Source

implement full ICE support

Squashed commit of the following:

commit 00213e66c7
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Fri Feb 27 09:40:04 2015 -0500

    perform ICE restart if we change ports

commit 27fbcbd6cb
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Thu Feb 26 15:58:12 2015 -0500

    locking fixes

commit 60c1c5ae13
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Thu Feb 26 15:58:06 2015 -0500

    unkernelize when ICE completes

commit 1d816f9864
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Thu Feb 26 15:54:44 2015 -0500

    relaxed locking where not needed

commit 75b58a9093
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Thu Feb 26 11:46:08 2015 -0500

    use atomic bitfield instead of bitfield+mutex

commit 03552eeed9
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Thu Feb 26 11:24:59 2015 -0500

    shuffle around aux.h a bit more

commit b9b8a3aa5e
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Thu Feb 26 11:16:12 2015 -0500

    remove some code redundancy through a "state machine"

commit 0b4bfef1b1
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Thu Feb 26 10:10:41 2015 -0500

    reorder to match struct

commit a2a51d81a8
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Thu Feb 26 10:10:03 2015 -0500

    clear more states for ICE restart

commit d554a2b858
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Thu Feb 26 09:58:29 2015 -0500

    dont duplicate candidates on re-invite

commit 4c804652b7
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Thu Feb 26 09:58:20 2015 -0500

    retain ICE role across re-invites

commit 4a586dd72d
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Thu Feb 26 09:42:09 2015 -0500

    eliminate duplicate log messages

commit ef0be2e308
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Thu Feb 26 09:32:36 2015 -0500

    fix incorrect log message

commit 2544b60f00
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Wed Feb 25 15:26:48 2015 -0500

    better logging for ICE agents

commit c42807384e
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Wed Feb 25 15:18:27 2015 -0500

    dont run ICE checks if we dont have a password

commit 1b56cb75b5
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Wed Feb 25 15:16:19 2015 -0500

    ICE pairs should go in triggered queue only once

commit d10c56f3ae
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Wed Feb 25 14:04:00 2015 -0500

    obsolete the ICE agent running flag

commit 52237e3399
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Wed Feb 25 14:02:06 2015 -0500

    cease checks once ICE is completed

commit 5332d18612
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Wed Feb 25 13:53:57 2015 -0500

    fix ICE completion logging

commit 85f5fd63aa
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Wed Feb 25 13:53:33 2015 -0500

    make better use of bit flags

commit 796b48bb78
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Wed Feb 25 12:09:46 2015 -0500

    improved learned candidate pairing and completion logic

commit d15561072e
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Wed Feb 25 11:21:45 2015 -0500

    support upper case transport strings

commit 557da7b1c3
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Wed Feb 25 10:43:57 2015 -0500

    use struct endpoint in ice candidates

commit 951040bfd6
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Wed Feb 25 10:31:13 2015 -0500

    more meaningful ICE log messages

commit 8ec2426bd3
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Wed Feb 25 09:54:49 2015 -0500

    shut down agent if no components - limit number of candidates

commit 149260f3a6
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Wed Feb 25 09:44:13 2015 -0500

    handle ICE restarts

commit 6a18c31f81
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Tue Feb 24 16:29:01 2015 -0500

    dont clear succeeded flag when nominating

commit 93e0861d02
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Tue Feb 24 16:21:40 2015 -0500

    use correct pwd in stun binding response

commit 32ba3ea406
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Tue Feb 24 16:03:09 2015 -0500

    use deterministic foundation for prflx cands

commit 2f2dc91515
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Tue Feb 24 15:21:13 2015 -0500

    handle initial ICE role

commit a6b8ad25e6
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Tue Feb 24 14:43:47 2015 -0500

    another ICE scheduling fix

commit c572b04e55
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Tue Feb 24 14:14:29 2015 -0500

    make ICE aware of rtcp-mux

commit 93cd2d2560
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Tue Feb 24 14:13:27 2015 -0500

    print timestamp when logging to stderr

commit 22a52ffda2
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Tue Feb 24 14:13:13 2015 -0500

    ICE scheduling fixes

commit 5d2d1a7739
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Tue Feb 24 13:27:59 2015 -0500

    increase ICE pwd length to make chrome happy

commit ceff6698db
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Tue Feb 24 13:09:54 2015 -0500

    dont discard RTP if ICE hasnt finished yet

commit e809877d0e
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Tue Feb 24 13:06:31 2015 -0500

    implement remote-candidates (untested)

commit 41670eadbb
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Tue Feb 24 11:58:13 2015 -0500

    shut down ICE agent when everything fails

commit 1ca26c4a81
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Tue Feb 24 11:10:46 2015 -0500

    fix up SDP output for ICE candidates

commit 0287d68f33
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Tue Feb 24 10:09:49 2015 -0500

    process ICE completion and fire up DTLS if desired

commit 5b6386036b
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Tue Feb 24 09:37:52 2015 -0500

    use a btree to schedule ice checks

commit 2bc25f1e0f
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Tue Feb 24 09:37:02 2015 -0500

    convert shutdown condition into global var

commit ecf0c5587c
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Mon Feb 23 14:50:46 2015 -0500

    replace poller_now with timeval g_now

commit 164ecdd7ac
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Mon Feb 23 13:00:15 2015 -0500

    handle nominations if we're controlling

commit d013659365
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Mon Feb 23 12:11:40 2015 -0500

    copy controlling/ed role into agent

commit 09f1cae14a
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Mon Feb 23 12:02:54 2015 -0500

    separate nominated pairs from valid pairs

commit f75f338cde
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Mon Feb 23 11:55:30 2015 -0500

    organize aux.h into sections

commit d6acee1392
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Mon Feb 23 11:55:17 2015 -0500

    use b-tree for various ICE lists

commit af9804d139
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Fri Feb 20 16:21:09 2015 -0500

    prepare to finalize ice processing

commit 18df118375
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Fri Feb 20 16:19:50 2015 -0500

    prettier logging

commit 280755c61a
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Fri Feb 20 15:02:14 2015 -0500

    implement unfreezing of other components on success

commit 5d13657d5b
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Fri Feb 20 15:00:24 2015 -0500

    bitfield access macros

commit 71746ad6a1
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Fri Feb 20 14:15:05 2015 -0500

    handle ice updates and duplicate candidates

commit 02309d1b5b
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Fri Feb 20 12:22:29 2015 -0500

    handle role conflicts

commit 52acf54ba5
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Fri Feb 20 10:24:01 2015 -0500

    proper pair priorities calculation

commit 307af79e8d
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Fri Feb 20 09:54:18 2015 -0500

    fix address family mixups

commit 7cbfd4d36a
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Fri Feb 20 09:14:49 2015 -0500

    delay dtls startup and timeout checks while ice is running

commit 2a8ab75228
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Thu Feb 19 16:47:56 2015 -0500

    process ice/stun responses

commit 92da323dcf
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Thu Feb 19 15:14:30 2015 -0500

    adding stun server script for testing

commit c5cfeb122c
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Thu Feb 19 13:42:40 2015 -0500

    act on stun requests, learn prflx candidates

commit 1cafd35e7a
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Thu Feb 19 13:30:14 2015 -0500

    fix pktinfo for ipv4 packets

commit 8e338b8426
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Thu Feb 19 11:48:55 2015 -0500

    rework interface handling to prepare to learn ICE candidate

commit 09e365c142
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Wed Feb 18 16:24:42 2015 -0500

    add some locking

commit 8fc7b75095
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Wed Feb 18 16:05:20 2015 -0500

    extend logic in response to stun req and implement triggered checks

commit 35eeb04376
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Wed Feb 18 12:46:42 2015 -0500

    handle ice/stun retransmits and timeouts

commit b5637565b6
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Wed Feb 18 11:19:19 2015 -0500

    first implementation of sending ICE checks

commit f0c1928c05
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Tue Feb 17 14:39:11 2015 -0500

    preliminary list and loop for ICE checks

commit c38d6e22c1
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Tue Feb 17 12:00:24 2015 -0500

    pair up candidates and prepare to run checks

commit d9559b4c59
Author: Richard Fuchs <rfuchs@sipwise.com>
Date:   Fri Feb 13 15:36:29 2015 -0500

    parse and remeber basic ICE attributes
pull/81/head
Richard Fuchs 11 years ago
parent
commit
f99d6d4f42
21 changed files with 2960 additions and 557 deletions
  1. +1
    -1
      daemon/Makefile
  2. +21
    -0
      daemon/aux.c
  3. +248
    -91
      daemon/aux.h
  4. +274
    -170
      daemon/call.c
  5. +50
    -20
      daemon/call.h
  6. +2
    -0
      daemon/call_interfaces.c
  7. +6
    -4
      daemon/dtls.c
  8. +15
    -15
      daemon/graphite.c
  9. +1357
    -0
      daemon/ice.c
  10. +208
    -0
      daemon/ice.h
  11. +9
    -1
      daemon/log.c
  12. +11
    -3
      daemon/log.h
  13. +11
    -8
      daemon/main.c
  14. +3
    -9
      daemon/poller.c
  15. +2
    -4
      daemon/poller.h
  16. +200
    -168
      daemon/sdp.c
  17. +0
    -1
      daemon/sdp.h
  18. +224
    -61
      daemon/stun.c
  19. +23
    -1
      daemon/stun.h
  20. +91
    -0
      tests/stun-client
  21. +204
    -0
      tests/stun-server

+ 1
- 1
daemon/Makefile View File

@ -63,7 +63,7 @@ endif
SRCS= main.c kernel.c poller.c aux.c control_tcp.c streambuf.c call.c control_udp.c redis.c \
bencode.c cookie_cache.c udp_listener.c control_ng.c sdp.c str.c stun.c rtcp.c \
crypto.c rtp.c call_interfaces.c dtls.c log.c cli.c graphite.c
crypto.c rtp.c call_interfaces.c dtls.c log.c cli.c graphite.c ice.c
OBJS= $(SRCS:.c=.o)


+ 21
- 0
daemon/aux.c View File

@ -34,6 +34,9 @@ static cond_t threads_cond = COND_STATIC_INIT;
static struct thread_buf __thread t_bufs[NUM_THREAD_BUFS];
static int __thread t_buf_idx;
__thread struct timeval g_now;
volatile int g_shutdown;
#ifdef NEED_ATOMIC64_MUTEX
mutex_t __atomic64_mutex = MUTEX_STATIC_INIT;
#endif
@ -191,3 +194,21 @@ char *get_thread_buf(void) {
t_buf_idx = 0;
return ret;
}
int g_tree_find_first_cmp(void *k, void *v, void *d) {
void **p = d;
GEqualFunc f = p[1];
if (!f || f(v, p[0])) {
p[2] = v;
return TRUE;
}
return FALSE;
}
int g_tree_find_all_cmp(void *k, void *v, void *d) {
void **p = d;
GEqualFunc f = p[1];
GQueue *q = p[2];
if (!f || f(v, p[0]))
g_queue_push_tail(q, v);
return FALSE;
}

+ 248
- 91
daemon/aux.h View File

@ -27,27 +27,11 @@
/*** HELPER MACROS ***/
#define OFFSET_OF(t,e) ((unsigned int) (unsigned long) &(((t *) 0)->e))
#define ZERO(x) memset(&(x), 0, sizeof(x))
#define IPF "%u.%u.%u.%u"
#define IPP(x) ((unsigned char *) (&(x)))[0], ((unsigned char *) (&(x)))[1], ((unsigned char *) (&(x)))[2], ((unsigned char *) (&(x)))[3]
#define IP6F "%x:%x:%x:%x:%x:%x:%x:%x"
#define IP6P(x) ntohs(((u_int16_t *) (x))[0]), \
ntohs(((u_int16_t *) (x))[1]), \
ntohs(((u_int16_t *) (x))[2]), \
ntohs(((u_int16_t *) (x))[3]), \
ntohs(((u_int16_t *) (x))[4]), \
ntohs(((u_int16_t *) (x))[5]), \
ntohs(((u_int16_t *) (x))[6]), \
ntohs(((u_int16_t *) (x))[7])
#define D6F "["IP6F"]:%u"
#define D6P(x) IP6P((x).sin6_addr.s6_addr), ntohs((x).sin6_port)
#define DF IPF ":%u"
#define DP(x) IPP((x).sin_addr.s_addr), ntohs((x).sin_port)
#define BIT_ARRAY_DECLARE(name, size) unsigned long name[((size) + sizeof(long) * 8 - 1) / (sizeof(long) * 8)]
#define UINT64F "%" G_GUINT64_FORMAT
#define THREAD_BUF_SIZE 64
@ -56,21 +40,50 @@
/*** TYPES ***/
struct endpoint {
struct in6_addr ip46;
u_int16_t port;
};
/*** GLOBALS ***/
extern __thread struct timeval g_now;
extern volatile int g_shutdown;
/*** PROTOTYPES ***/
typedef int (*parse_func)(char **, void **, void *);
GList *g_list_link(GList *, GList *);
int pcre_multi_match(pcre *, pcre_extra *, const char *, unsigned int, parse_func, void *, GQueue *);
INLINE void strmove(char **, char **);
INLINE void strdupfree(char **, const char *);
char *get_thread_buf(void);
unsigned int in6_addr_hash(const void *p);
int in6_addr_eq(const void *a, const void *b);
/*** GLIB HELPERS ***/
GList *g_list_link(GList *, GList *);
#if !GLIB_CHECK_VERSION(2,32,0)
INLINE int g_hash_table_contains(GHashTable *h, const void *k) {
return g_hash_table_lookup(h, k) ? 1 : 0;
}
#endif
/* GQUEUE */
INLINE void g_queue_move(GQueue *dst, GQueue *src) {
GList *l;
while ((l = g_queue_pop_head_link(src)))
@ -85,8 +98,55 @@ INLINE void g_queue_clear_full(GQueue *q, GDestroyNotify free_func) {
while ((p = g_queue_pop_head(q)))
free_func(p);
}
INLINE void g_queue_append(GQueue *dst, const GQueue *src) {
GList *l;
if (!src || !dst)
return;
for (l = src->head; l; l = l->next)
g_queue_push_tail(dst, l->data);
}
/* GTREE */
int g_tree_find_first_cmp(void *, void *, void *);
int g_tree_find_all_cmp(void *, void *, void *);
INLINE void *g_tree_find_first(GTree *t, GEqualFunc f, void *data) {
void *p[3];
p[0] = data;
p[1] = f;
p[2] = NULL;
g_tree_foreach(t, g_tree_find_first_cmp, p);
return p[2];
}
INLINE void g_tree_find_all(GQueue *out, GTree *t, GEqualFunc f, void *data) {
void *p[3];
p[0] = data;
p[1] = f;
p[2] = out;
g_tree_foreach(t, g_tree_find_all_cmp, p);
}
INLINE void g_tree_get_values(GQueue *out, GTree *t) {
g_tree_find_all(out, t, NULL, NULL);
}
INLINE void g_tree_remove_all(GQueue *out, GTree *t) {
GList *l;
g_queue_init(out);
g_tree_find_all(out, t, NULL, NULL);
for (l = out->head; l; l = l->next)
g_tree_remove(t, l->data);
}
INLINE void g_tree_add_all(GTree *t, GQueue *q) {
GList *l;
for (l = q->head; l; l = l->next)
g_tree_insert(t, l->data, l->data);
g_queue_clear(q);
}
/*** STRING HELPERS ***/
INLINE void strmove(char **d, char **s) {
if (*d)
free(*d);
@ -100,6 +160,36 @@ INLINE void strdupfree(char **d, const char *s) {
*d = strdup(s);
}
INLINE int strmemcmp(const void *mem, int len, const char *str) {
int l = strlen(str);
if (l < len)
return -1;
if (l > len)
return 1;
return memcmp(mem, str, len);
}
INLINE void random_string(unsigned char *buf, int len) {
RAND_bytes(buf, len);
}
INLINE const char *__get_enum_array_text(const char * const *array, unsigned int idx,
unsigned int len, const char *deflt)
{
const char *ret;
if (idx >= len)
return deflt;
ret = array[idx];
return ret ? : deflt;
}
#define get_enum_array_text(array, idx, deflt) \
__get_enum_array_text(array, idx, G_N_ELEMENTS(array), deflt)
/*** SOCKET/FD HELPERS ***/
INLINE void nonblock(int fd) {
fcntl(fd, F_SETFL, O_NONBLOCK);
@ -112,17 +202,10 @@ INLINE void ipv6only(int fd, int yn) {
setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yn, sizeof(yn));
}
INLINE unsigned long bit_array_isset(unsigned long *name, unsigned int bit) {
return name[(bit) / (sizeof(long) * 8)] & (1UL << ((bit) % (sizeof(long) * 8)));
}
INLINE void bit_array_set(unsigned long *name, unsigned int bit) {
name[(bit) / (sizeof(long) * 8)] |= 1UL << ((bit) % (sizeof(long) * 8));
}
INLINE void bit_array_clear(unsigned long *name, unsigned int bit) {
name[(bit) / (sizeof(long) * 8)] &= ~(1UL << ((bit) % (sizeof(long) * 8)));
}
/*** GENERIC HELPERS ***/
INLINE char chrtoupper(char x) {
return x & 0xdf;
@ -137,6 +220,34 @@ INLINE void swap_ptrs(void *a, void *b) {
*bb = t;
}
INLINE int rlim(int res, rlim_t val) {
struct rlimit rlim;
ZERO(rlim);
rlim.rlim_cur = rlim.rlim_max = val;
return setrlimit(res, &rlim);
}
/*** INET ADDRESS HELPERS ***/
#define IPF "%u.%u.%u.%u"
#define IPP(x) ((unsigned char *) (&(x)))[0], ((unsigned char *) (&(x)))[1], ((unsigned char *) (&(x)))[2], ((unsigned char *) (&(x)))[3]
#define IP6F "%x:%x:%x:%x:%x:%x:%x:%x"
#define IP6P(x) ntohs(((u_int16_t *) (x))[0]), \
ntohs(((u_int16_t *) (x))[1]), \
ntohs(((u_int16_t *) (x))[2]), \
ntohs(((u_int16_t *) (x))[3]), \
ntohs(((u_int16_t *) (x))[4]), \
ntohs(((u_int16_t *) (x))[5]), \
ntohs(((u_int16_t *) (x))[6]), \
ntohs(((u_int16_t *) (x))[7])
#define D6F "["IP6F"]:%u"
#define D6P(x) IP6P((x).sin6_addr.s6_addr), ntohs((x).sin6_port)
#define DF IPF ":%u"
#define DP(x) IPP((x).sin_addr.s_addr), ntohs((x).sin_port)
INLINE void in4_to_6(struct in6_addr *o, u_int32_t ip) {
o->s6_addr32[0] = 0;
o->s6_addr32[1] = 0;
@ -193,15 +304,19 @@ INLINE char *smart_ntop_p_buf(const struct in6_addr *a) {
return buf;
}
INLINE void smart_ntop_port(char *o, const struct sockaddr_in6 *a, size_t len) {
INLINE void smart_ntop_ap(char *o, const struct in6_addr *a, unsigned int port, size_t len) {
char *e;
e = smart_ntop_p(o, &a->sin6_addr, len);
e = smart_ntop_p(o, a, len);
if (!e)
return;
if (len - (e - o) < 7)
return;
sprintf(e, ":%hu", ntohs(a->sin6_port));
sprintf(e, ":%u", port);
}
INLINE void smart_ntop_port(char *o, const struct sockaddr_in6 *a, size_t len) {
return smart_ntop_ap(o, &a->sin6_addr, ntohs(a->sin6_port), len);
}
INLINE char *smart_ntop_port_buf(const struct sockaddr_in6 *a) {
@ -210,6 +325,18 @@ INLINE char *smart_ntop_port_buf(const struct sockaddr_in6 *a) {
return buf;
}
INLINE char *smart_ntop_ap_buf(const struct in6_addr *a, unsigned int port) {
char *buf = get_thread_buf();
smart_ntop_ap(buf, a, port, THREAD_BUF_SIZE);
return buf;
}
INLINE char *smart_ntop_ep_buf(const struct endpoint *ep) {
char *buf = get_thread_buf();
smart_ntop_ap(buf, &ep->ip46, ep->port, THREAD_BUF_SIZE);
return buf;
}
INLINE int smart_pton(int af, char *src, void *dst) {
char *p;
int ret;
@ -302,22 +429,62 @@ fail:
return -1;
}
INLINE int strmemcmp(const void *mem, int len, const char *str) {
int l = strlen(str);
if (l < len)
return -1;
if (l > len)
INLINE int is_addr_unspecified(const struct in6_addr *a) {
if (a->s6_addr32[0])
return 0;
if (a->s6_addr32[1])
return 0;
if (a->s6_addr32[3])
return 0;
if (a->s6_addr32[2] == 0 || a->s6_addr32[2] == htonl(0xffff))
return 1;
return memcmp(mem, str, len);
return 0;
}
INLINE void random_string(unsigned char *buf, int len) {
RAND_bytes(buf, len);
INLINE int family_from_address(const struct in6_addr *a) {
if (IN6_IS_ADDR_V4MAPPED(a))
return AF_INET;
return AF_INET6;
}
INLINE void msg_mh_src(const struct in6_addr *src, struct msghdr *mh) {
struct cmsghdr *ch;
struct in_pktinfo *pi;
struct in6_pktinfo *pi6;
struct sockaddr_in6 *sin6;
sin6 = mh->msg_name;
ch = CMSG_FIRSTHDR(mh);
ZERO(*ch);
if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
ch->cmsg_len = CMSG_LEN(sizeof(*pi));
ch->cmsg_level = IPPROTO_IP;
ch->cmsg_type = IP_PKTINFO;
pi = (void *) CMSG_DATA(ch);
ZERO(*pi);
pi->ipi_spec_dst.s_addr = in6_to_4(src);
mh->msg_controllen = CMSG_SPACE(sizeof(*pi));
}
else {
ch->cmsg_len = CMSG_LEN(sizeof(*pi6));
ch->cmsg_level = IPPROTO_IPV6;
ch->cmsg_type = IPV6_PKTINFO;
pi6 = (void *) CMSG_DATA(ch);
ZERO(*pi6);
pi6->ipi6_addr = *src;
mh->msg_controllen = CMSG_SPACE(sizeof(*pi6));
}
}
/*** MUTEX ABSTRACTION ***/
typedef pthread_mutex_t mutex_t;
typedef pthread_rwlock_t rwlock_t;
typedef pthread_cond_t cond_t;
@ -338,10 +505,18 @@ typedef pthread_cond_t cond_t;
#define cond_init(c) __debug_cond_init(c, __FILE__, __LINE__)
#define cond_wait(c,m) __debug_cond_wait(c,m, __FILE__, __LINE__)
#define cond_timedwait(c,m,t) __debug_cond_timedwait(c,m,t, __FILE__, __LINE__)
#define cond_signal(c) __debug_cond_signal(c, __FILE__, __LINE__)
#define cond_broadcast(c) __debug_cond_broadcast(c, __FILE__, __LINE__)
#define COND_STATIC_INIT PTHREAD_COND_INITIALIZER
INLINE int __cond_timedwait_tv(cond_t *c, mutex_t *m, const struct timeval *tv) {
struct timespec ts;
ts.tv_sec = tv->tv_sec;
ts.tv_nsec = tv->tv_usec * 1000;
return pthread_cond_timedwait(c, m, &ts);
}
#ifndef __THREAD_DEBUG
#define __debug_mutex_init(m, F, L) pthread_mutex_init(m, NULL)
@ -359,6 +534,7 @@ typedef pthread_cond_t cond_t;
#define __debug_cond_init(c, F, L) pthread_cond_init(c, NULL)
#define __debug_cond_wait(c, m, F, L) pthread_cond_wait(c,m)
#define __debug_cond_timedwait(c, m, t, F, L) __cond_timedwait_tv(c,m,t)
#define __debug_cond_signal(c, F, L) pthread_cond_signal(c)
#define __debug_cond_broadcast(c, F, L) pthread_cond_broadcast(c)
@ -429,6 +605,7 @@ INLINE int __debug_rwlock_unlock_w(rwlock_t *m, const char *file, unsigned int l
#define __debug_cond_init(c, F, L) pthread_cond_init(c, NULL)
#define __debug_cond_wait(c, m, F, L) pthread_cond_wait(c,m)
#define __debug_cond_timedwait(c, m, t, F, L) __cond_timedwait_tv(c,m,t)
#define __debug_cond_signal(c, F, L) pthread_cond_signal(c)
#define __debug_cond_broadcast(c, F, L) pthread_cond_broadcast(c)
@ -436,36 +613,16 @@ INLINE int __debug_rwlock_unlock_w(rwlock_t *m, const char *file, unsigned int l
void threads_join_all(int);
void thread_create_detach(void (*)(void *), void *);
/*** THREAD HELPERS ***/
void threads_join_all(int);
void thread_create_detach(void (*)(void *), void *);
INLINE int rlim(int res, rlim_t val) {
struct rlimit rlim;
ZERO(rlim);
rlim.rlim_cur = rlim.rlim_max = val;
return setrlimit(res, &rlim);
}
INLINE int is_addr_unspecified(const struct in6_addr *a) {
if (a->s6_addr32[0])
return 0;
if (a->s6_addr32[1])
return 0;
if (a->s6_addr32[3])
return 0;
if (a->s6_addr32[2] == 0 || a->s6_addr32[2] == htonl(0xffff))
return 1;
return 0;
}
INLINE int family_from_address(const struct in6_addr *a) {
if (IN6_IS_ADDR_V4MAPPED(a))
return AF_INET;
return AF_INET6;
}
/*** ATOMIC BITFIELD OPERATIONS ***/
/* checks if at least one of the flags is set */
INLINE int bf_isset(const volatile unsigned int *u, unsigned int f) {
@ -473,11 +630,19 @@ INLINE int bf_isset(const volatile unsigned int *u, unsigned int f) {
return -1;
return 0;
}
INLINE void bf_set(volatile unsigned int *u, unsigned int f) {
g_atomic_int_or(u, f);
/* checks if all of the flags are set */
INLINE int bf_areset(const volatile unsigned int *u, unsigned int f) {
if ((g_atomic_int_get(u) & f) == f)
return -1;
return 0;
}
/* returns true if at least one of the flags was set already */
INLINE int bf_set(volatile unsigned int *u, unsigned int f) {
return (g_atomic_int_or(u, f) & f) ? -1 : 0;
}
INLINE void bf_clear(volatile unsigned int *u, unsigned int f) {
g_atomic_int_and(u, ~f);
/* returns true if at least one of the flags was set */
INLINE int bf_clear(volatile unsigned int *u, unsigned int f) {
return (g_atomic_int_and(u, ~f) & f) ? -1 : 0;
}
INLINE void bf_set_clear(volatile unsigned int *u, unsigned int f, int cond) {
if (cond)
@ -502,21 +667,27 @@ INLINE void bf_copy_same(volatile unsigned int *u, const volatile unsigned int *
}
INLINE void g_queue_append(GQueue *dst, const GQueue *src) {
GList *l;
if (!src || !dst)
return;
for (l = src->head; l; l = l->next)
g_queue_push_tail(dst, l->data);
}
/*** BIT ARRAY FUNCTIONS ***/
#define BIT_ARRAY_DECLARE(name, size) \
volatile unsigned int name[((size) + sizeof(int) * 8 - 1) / (sizeof(int) * 8)]
unsigned int in6_addr_hash(const void *p);
int in6_addr_eq(const void *a, const void *b);
INLINE int bit_array_isset(const volatile unsigned int *name, unsigned int bit) {
return bf_isset(&name[bit / (sizeof(int) * 8)], 1U << (bit % (sizeof(int) * 8)));
}
INLINE int bit_array_set(volatile unsigned int *name, unsigned int bit) {
return bf_set(&name[bit / (sizeof(int) * 8)], 1U << (bit % (sizeof(int) * 8)));
}
INLINE int bit_array_clear(volatile unsigned int *name, unsigned int bit) {
return bf_clear(&name[bit / (sizeof(int) * 8)], 1U << (bit % (sizeof(int) * 8)));
}
/*** ATOMIC64 ***/
#if GLIB_SIZEOF_VOID_P >= 8
typedef struct {
@ -611,18 +782,4 @@ INLINE void atomic64_local_copy_zero(atomic64 *dst, atomic64 *src) {
INLINE const char *__get_enum_array_text(const char * const *array, unsigned int idx,
unsigned int len, const char *deflt)
{
const char *ret;
if (idx >= len)
return deflt;
ret = array[idx];
return ret ? : deflt;
}
#define get_enum_array_text(array, idx, deflt) \
__get_enum_array_text(array, idx, G_N_ELEMENTS(array), deflt)
#endif

+ 274
- 170
daemon/call.c View File

@ -32,6 +32,7 @@
#include "rtcp.h"
#include "rtp.h"
#include "call_interfaces.h"
#include "ice.h"
@ -320,12 +321,12 @@ static const struct rtpengine_srtp __res_null = {
static void unkernelize(struct packet_stream *);
static void __stream_unkernelize(struct packet_stream *ps);
static void stream_unkernelize(struct packet_stream *ps);
static void __unkernelize(struct packet_stream *);
static void __stream_unconfirm(struct packet_stream *ps);
static void stream_unconfirm(struct packet_stream *ps);
static void __monologue_destroy(struct call_monologue *monologue);
static struct interface_address *get_interface_address(struct local_interface *lif, int family);
static const GQueue *get_interface_addresses(struct local_interface *lif, int family);
static struct interface_address *get_any_interface_address(struct local_interface *lif, int family);
@ -412,7 +413,7 @@ void kernelize(struct packet_stream *stream) {
ZERO(reti);
if (PS_ISSET(stream, STRICT_SOURCE) || PS_ISSET(stream, MEDIA_HANDOVER)) {
if (PS_ISSET2(stream, STRICT_SOURCE, MEDIA_HANDOVER)) {
mutex_lock(&stream->out_lock);
__re_address_translate(&reti.expected_src, &stream->endpoint);
mutex_unlock(&stream->out_lock);
@ -428,7 +429,7 @@ void kernelize(struct packet_stream *stream) {
reti.tos = call->tos;
reti.rtcp_mux = MEDIA_ISSET(stream->media, RTCP_MUX);
reti.dtls = MEDIA_ISSET(stream->media, DTLS);
reti.stun = PS_ISSET(stream, STUN);
reti.stun = stream->media->ice_agent ? 1 : 0;
__re_address_translate(&reti.dst_addr, &sink->endpoint);
@ -583,41 +584,10 @@ noop:
}
void stream_msg_mh_src(struct packet_stream *ps, struct msghdr *mh) {
struct cmsghdr *ch;
struct in_pktinfo *pi;
struct in6_pktinfo *pi6;
struct sockaddr_in6 *sin6;
struct interface_address *ifa;
sin6 = mh->msg_name;
ifa = g_atomic_pointer_get(&ps->media->local_address);
ch = CMSG_FIRSTHDR(mh);
ZERO(*ch);
if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
ch->cmsg_len = CMSG_LEN(sizeof(*pi));
ch->cmsg_level = IPPROTO_IP;
ch->cmsg_type = IP_PKTINFO;
pi = (void *) CMSG_DATA(ch);
ZERO(*pi);
pi->ipi_spec_dst.s_addr = in6_to_4(&ifa->addr);
mh->msg_controllen = CMSG_SPACE(sizeof(*pi));
}
else {
ch->cmsg_len = CMSG_LEN(sizeof(*pi6));
ch->cmsg_level = IPPROTO_IPV6;
ch->cmsg_type = IPV6_PKTINFO;
pi6 = (void *) CMSG_DATA(ch);
ZERO(*pi6);
pi6->ipi6_addr = ifa->addr;
mh->msg_controllen = CMSG_SPACE(sizeof(*pi6));
}
msg_mh_src(&ifa->addr, mh);
}
/* XXX split this function into pieces */
@ -654,35 +624,39 @@ static int stream_packet(struct stream_fd *sfd, str *s, struct sockaddr_in6 *fsi
if (!stream)
goto unlock_out;
mutex_lock(&stream->in_lock);
media = stream->media;
if (!stream->sfd)
goto done;
goto unlock_out;
/* demux other protocols running on this port */
if (MEDIA_ISSET(media, DTLS) && is_dtls(s)) {
mutex_lock(&stream->in_lock);
ret = dtls(stream, s, fsin);
mutex_unlock(&stream->in_lock);
if (!ret)
goto done;
goto unlock_out;
}
if (PS_ISSET(stream, STUN) && is_stun(s)) {
stun_ret = stun(s, stream, fsin);
if (media->ice_agent && is_stun(s)) {
stun_ret = stun(s, stream, fsin, dst);
if (!stun_ret)
goto done;
goto unlock_out;
if (stun_ret == 1) {
ilog(LOG_INFO, "STUN: using this candidate");
goto use_cand;
call_stream_state_machine(stream);
mutex_lock(&stream->in_lock); /* for the jump */
goto kernel_check;
}
else /* not an stun packet */
stun_ret = 0;
}
#if RTP_LOOP_PROTECT
mutex_lock(&stream->in_lock);
for (i = 0; i < RTP_LOOP_PACKETS; i++) {
if (stream->lp_buf[i].len != s->len)
continue;
@ -706,9 +680,8 @@ static int stream_packet(struct stream_fd *sfd, str *s, struct sockaddr_in6 *fsi
memcpy(stream->lp_buf[stream->lp_idx].buf, s->s, MIN(s->len, RTP_LOOP_PROTECT));
stream->lp_idx = (stream->lp_idx + 1) % RTP_LOOP_PACKETS;
loop_ok:
#endif
mutex_unlock(&stream->in_lock);
#endif
/* demux RTCP */
@ -792,9 +765,11 @@ loop_ok:
mutex_unlock(&out_srtp->out_lock);
mutex_unlock(&in_srtp->in_lock);
/* endpoint address handling */
mutex_lock(&stream->in_lock);
use_cand:
/* we're OK to (potentially) use the source address of this packet as destination
* in the other direction. */
/* if the other side hasn't been signalled yet, just forward the packet */
@ -808,7 +783,7 @@ use_cand:
/* if we have already updated the endpoint in the past ... */
if (PS_ISSET(stream, CONFIRMED)) {
/* see if we need to compare the source address with the known endpoint */
if (PS_ISSET(stream, STRICT_SOURCE) || PS_ISSET(stream, MEDIA_HANDOVER)) {
if (PS_ISSET2(stream, STRICT_SOURCE, MEDIA_HANDOVER)) {
endpoint.ip46 = fsin->sin6_addr;
endpoint.port = ntohs(fsin->sin6_port);
mutex_lock(&stream->out_lock);
@ -874,7 +849,7 @@ kernel_check:
if (PS_ISSET(stream, NO_KERNEL_SUPPORT))
goto forward;
if (PS_ISSET(stream, CONFIRMED) && sink && PS_ISSET(sink, CONFIRMED) && PS_ISSET(sink, FILLED))
if (PS_ISSET(stream, CONFIRMED) && sink && PS_ARESET2(sink, CONFIRMED, FILLED))
kernelize(stream);
forward:
@ -936,11 +911,11 @@ out:
done:
if (unk)
__stream_unkernelize(stream);
__stream_unconfirm(stream);
mutex_unlock(&stream->in_lock);
if (unk) {
stream_unkernelize(stream->rtp_sink);
stream_unkernelize(stream->rtcp_sink);
stream_unconfirm(stream->rtp_sink);
stream_unconfirm(stream->rtcp_sink);
}
unlock_out:
rwlock_unlock_r(&call->master_lock);
@ -964,7 +939,8 @@ static void stream_fd_readable(int fd, void *p, uintptr_t u) {
char control[128];
struct cmsghdr *cmh;
struct in6_pktinfo *pi6;
struct in6_addr *dst;
struct in6_addr dst_buf, *dst;
struct in_pktinfo *pi;
if (sfd->fd.fd != fd)
goto out;
@ -1003,20 +979,30 @@ static void stream_fd_readable(int fd, void *p, uintptr_t u) {
if (ret >= MAX_RTP_PACKET_SIZE)
ilog(LOG_WARNING, "UDP packet possibly truncated");
dst = NULL;
for (cmh = CMSG_FIRSTHDR(&mh); cmh; cmh = CMSG_NXTHDR(&mh, cmh)) {
if (cmh->cmsg_level == IPPROTO_IPV6 && cmh->cmsg_type == IPV6_PKTINFO) {
pi6 = (void *) CMSG_DATA(cmh);
dst = &pi6->ipi6_addr;
goto got_dst;
}
if (cmh->cmsg_level == IPPROTO_IP && cmh->cmsg_type == IP_PKTINFO) {
pi = (void *) CMSG_DATA(cmh);
in4_to_6(&dst_buf, pi->ipi_addr.s_addr);
dst = &dst_buf;
goto got_dst;
}
}
ilog(LOG_WARNING, "No pkt_info present in received UDP packet, cannot handle packet");
goto done;
got_dst:
str_init_len(&s, buf + RTP_BUFFER_HEAD_ROOM, ret);
ret = stream_packet(sfd, &s, &sin6_src, dst);
if (ret < 0) {
ilog(LOG_WARNING, "Write error on RTP socket: %s", strerror(-ret));
call_destroy(sfd->call);
return;
goto done;
}
if (ret == 1)
update = 1;
@ -1096,6 +1082,7 @@ static void call_timer_iterator(void *key, void *val, void *ptr) {
int tmp_t_reason=0;
struct call_monologue *ml;
GSList *i;
enum call_stream_state css;
rwlock_lock_r(&c->master_lock);
log_info_call(c);
@ -1116,7 +1103,6 @@ static void call_timer_iterator(void *key, void *val, void *ptr) {
for (it = c->streams; it; it = it->next) {
ps = it->data;
mutex_lock(&ps->in_lock);
if (!ps->media)
goto next;
@ -1124,8 +1110,14 @@ static void call_timer_iterator(void *key, void *val, void *ptr) {
if (!sfd)
goto no_sfd;
if (MEDIA_ISSET(ps->media, DTLS) && sfd->dtls.init && !sfd->dtls.connected)
dtls(ps, NULL, NULL);
/* valid stream */
css = call_stream_state_machine(ps);
if (css == CSS_ICE) {
good = 1;
goto next;
}
if (hlp->ports[sfd->fd.localport])
goto next;
@ -1147,7 +1139,7 @@ no_sfd:
good = 1;
next:
mutex_unlock(&ps->in_lock);
;
}
if (good)
@ -1532,6 +1524,7 @@ static void __get_pktinfo(int fd) {
int x;
x = 1;
setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &x, sizeof(x));
setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &x, sizeof(x));
}
static int get_port6(struct udp_fd *r, u_int16_t p, const struct call *c) {
@ -1571,23 +1564,17 @@ static int get_port(struct udp_fd *r, u_int16_t p, const struct call *c) {
__C_DBG("attempting to open port %u", p);
mutex_lock(&m->portlock);
if (bit_array_isset(m->ports_used, p)) {
mutex_unlock(&m->portlock);
if (bit_array_set(m->ports_used, p)) {
__C_DBG("port in use");
return -1;
}
bit_array_set(m->ports_used, p);
mutex_unlock(&m->portlock);
__C_DBG("port locked");
ret = get_port6(r, p, c);
if (ret) {
__C_DBG("couldn't open port");
mutex_lock(&m->portlock);
bit_array_clear(m->ports_used, p);
mutex_unlock(&m->portlock);
return ret;
}
@ -1600,9 +1587,7 @@ static void release_port(struct udp_fd *r, struct callmaster *m) {
if (r->fd == -1 || !r->localport)
return;
__C_DBG("releasing port %u", r->localport);
mutex_lock(&m->portlock);
bit_array_clear(m->ports_used, r->localport);
mutex_unlock(&m->portlock);
close(r->fd);
r->fd = -1;
r->localport = 0;
@ -1619,9 +1604,7 @@ int __get_consecutive_ports(struct udp_fd *array, int array_len, int wanted_star
if (wanted_start_port > 0)
port = wanted_start_port;
else {
mutex_lock(&m->portlock);
port = m->lastport;
mutex_unlock(&m->portlock);
port = g_atomic_int_get(&m->lastport);
#if PORT_RANDOM_MIN && PORT_RANDOM_MAX
port += PORT_RANDOM_MIN + (random() % (PORT_RANDOM_MAX - PORT_RANDOM_MIN));
#endif
@ -1658,9 +1641,7 @@ release_restart:
}
/* success */
mutex_lock(&m->portlock);
m->lastport = port;
mutex_unlock(&m->portlock);
g_atomic_int_set(&m->lastport, port);
ilog(LOG_DEBUG, "Opened ports %u..%u for media relay",
array[0].localport, array[array_len - 1].localport);
@ -1810,22 +1791,27 @@ static void __assign_stream_fds(struct call_media *media, GList *sfds) {
GList *l;
struct packet_stream *ps;
struct stream_fd *sfd;
int reset = 0;
for (l = media->streams.head; l; l = l->next) {
assert(sfds != NULL);
ps = l->data;
sfd = sfds->data;
/* if we switch local ports, we reset crypto params */
/* if we switch local ports, we reset crypto params and ICE */
if (ps->sfd && ps->sfd != sfd) {
dtls_shutdown(ps);
crypto_reset(&ps->sfd->crypto);
reset = 1;
}
ps->sfd = sfd;
sfd->stream = ps;
sfds = sfds->next;
}
if (reset && media->ice_agent)
ice_restart(media->ice_agent);
}
static int __wildcard_endpoint_map(struct call_media *media, unsigned int num_ports) {
@ -1869,6 +1855,7 @@ static int __num_media_streams(struct call_media *media, unsigned int num_ports)
stream = __packet_stream_new(call);
stream->media = media;
g_queue_push_tail(&media->streams, stream);
stream->component = media->streams.length;
ret++;
}
@ -1889,6 +1876,29 @@ static void __fill_stream(struct packet_stream *ps, const struct endpoint *ep, u
PS_SET(ps, FILLED);
}
/* called with call locked in R or W, but ps not locked */
enum call_stream_state call_stream_state_machine(struct packet_stream *ps) {
struct call_media *media = ps->media;
if (!ps->sfd)
return CSS_SHUTDOWN;
if (MEDIA_ISSET(media, ICE) && !ice_has_finished(media))
return CSS_ICE; /* handled by ICE timer */
if (MEDIA_ISSET(media, DTLS)) {
mutex_lock(&ps->in_lock);
if (ps->sfd->dtls.init && !ps->sfd->dtls.connected) {
dtls(ps, NULL, NULL);
mutex_unlock(&ps->in_lock);
return CSS_DTLS;
}
mutex_unlock(&ps->in_lock);
}
return CSS_RUNNING;
}
static int __init_stream(struct packet_stream *ps) {
struct call_media *media = ps->media;
struct call *call = ps->call;
@ -1908,6 +1918,8 @@ static int __init_stream(struct packet_stream *ps) {
if (dtls_verify_cert(ps))
return -1;
}
call_stream_state_machine(ps);
}
}
@ -1967,6 +1979,8 @@ static int __init_streams(struct call_media *A, struct call_media *B, const stru
bf_copy_same(&a->ps_flags, &sp->sp_flags,
SHARED_FLAG_STRICT_SOURCE | SHARED_FLAG_MEDIA_HANDOVER);
}
bf_copy_same(&a->ps_flags, &A->media_flags, SHARED_FLAG_ICE);
if (__init_stream(a))
return -1;
@ -2016,6 +2030,8 @@ static int __init_streams(struct call_media *A, struct call_media *B, const stru
bf_copy_same(&a->ps_flags, &sp->sp_flags,
SHARED_FLAG_STRICT_SOURCE | SHARED_FLAG_MEDIA_HANDOVER);
}
bf_copy_same(&a->ps_flags, &A->media_flags, SHARED_FLAG_ICE);
if (__init_stream(a))
return -1;
@ -2046,6 +2062,29 @@ static void __ice_offer(const struct sdp_ng_flags *flags, struct call_media *thi
ilog(LOG_DEBUG, "enabling passthrough mode");
MEDIA_SET(this, PASSTHRU);
MEDIA_SET(other, PASSTHRU);
return;
}
/* determine roles (even if we don't actually do ICE) */
/* this = receiver, other = sender */
/* ICE_CONTROLLING is from our POV, the other ICE flags are from peer's POV */
if (MEDIA_ISSET(this, ICE_LITE))
MEDIA_SET(this, ICE_CONTROLLING);
else if (!MEDIA_ISSET(this, INITIALIZED)) {
if (flags->opmode == OP_OFFER)
MEDIA_SET(this, ICE_CONTROLLING);
else
MEDIA_CLEAR(this, ICE_CONTROLLING);
}
/* roles are reversed for the other side */
if (MEDIA_ISSET(other, ICE_LITE))
MEDIA_SET(other, ICE_CONTROLLING);
else if (!MEDIA_ISSET(other, INITIALIZED)) {
if (flags->opmode == OP_OFFER)
MEDIA_CLEAR(other, ICE_CONTROLLING);
else
MEDIA_SET(other, ICE_CONTROLLING);
}
}
@ -2265,9 +2304,8 @@ static void __init_interface(struct call_media *media, const str *ifname) {
if (!str_cmp_str(&media->interface->name, ifname))
return;
get:
media->interface = get_local_interface(media->call->callmaster, ifname);
media->interface = get_local_interface(media->call->callmaster, ifname, media->desired_family);
if (!media->interface) {
media->interface = get_local_interface(media->call->callmaster, NULL);
/* legacy support */
if (!str_cmp(ifname, "internal"))
media->desired_family = AF_INET;
@ -2275,6 +2313,7 @@ get:
media->desired_family = AF_INET6;
else
ilog(LOG_WARNING, "Interface '"STR_FORMAT"' not found, using default", STR_FMT(ifname));
media->interface = get_local_interface(media->call->callmaster, NULL, media->desired_family);
}
media->local_address = ifa = get_interface_address(media->interface, media->desired_family);
if (!ifa) {
@ -2317,7 +2356,7 @@ static void __dtls_logic(const struct sdp_ng_flags *flags, struct call_media *me
other_media->fingerprint = sp->fingerprint;
}
MEDIA_CLEAR(other_media, DTLS);
if ((MEDIA_ISSET(other_media, SETUP_PASSIVE) || MEDIA_ISSET(other_media, SETUP_ACTIVE))
if (MEDIA_ISSET2(other_media, SETUP_PASSIVE, SETUP_ACTIVE)
&& other_media->fingerprint.hash_func)
MEDIA_SET(other_media, DTLS);
}
@ -2335,6 +2374,15 @@ static void __rtp_payload_types(struct call_media *media, GQueue *types) {
}
}
static void __ice_start(struct call_media *media) {
if (!MEDIA_ISSET(media, ICE) || MEDIA_ISSET(media, PASSTHRU)) {
ice_shutdown(&media->ice_agent);
return;
}
ice_agent_init(&media->ice_agent, media);
}
/* called with call->master_lock held in W */
int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams,
const struct sdp_ng_flags *flags)
@ -2396,7 +2444,7 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams,
/* copy parameters advertised by the sender of this message */
bf_copy_same(&other_media->media_flags, &sp->sp_flags,
SHARED_FLAG_RTCP_MUX | SHARED_FLAG_ASYMMETRIC | SHARED_FLAG_ICE
| SHARED_FLAG_TRICKLE_ICE);
| SHARED_FLAG_TRICKLE_ICE | SHARED_FLAG_ICE_LITE);
crypto_params_copy(&other_media->sdes_in.params, &sp->crypto);
other_media->sdes_in.tag = sp->sdes_tag;
@ -2414,11 +2462,9 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams,
/* DTLS stuff */
__dtls_logic(flags, media, other_media, sp);
/* ICE negotiation */
__ice_offer(flags, media, other_media);
/* control rtcp-mux */
__rtcp_mux_logic(flags, media, other_media);
/* XXX update ICE if rtcp-mux changes */
/* SDES and DTLS */
__generate_crypto(flags, media, other_media);
@ -2431,11 +2477,16 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams,
if (sp->desired_family)
media->desired_family = sp->desired_family;
/* local interface selection */
__init_interface(media, &sp->direction[1]);
__init_interface(other_media, &sp->direction[0]);
/* ICE stuff - must come after interface and address family selection */
__ice_offer(flags, media, other_media);
__ice_start(other_media);
__ice_start(media);
/* we now know what's being advertised by the other side */
MEDIA_SET(other_media, INITIALIZED);
@ -2486,6 +2537,9 @@ init:
return -1;
if (__init_streams(other_media, media, sp))
return -1;
/* we are now ready to fire up ICE if so desired and requested */
ice_update(other_media->ice_agent, sp);
}
return 0;
@ -2496,7 +2550,7 @@ error:
}
/* must be called with in_lock held or call->master_lock held in W */
static void unkernelize(struct packet_stream *p) {
static void __unkernelize(struct packet_stream *p) {
if (!PS_ISSET(p, KERNELIZED))
return;
if (PS_ISSET(p, NO_KERNEL_SUPPORT))
@ -2508,33 +2562,57 @@ static void unkernelize(struct packet_stream *p) {
PS_CLEAR(p, KERNELIZED);
}
void timeval_subtract (struct timeval *result, const struct timeval *a, const struct timeval *b) {
u_int64_t microseconds=0;
/* XXX move these somewhere else */
u_int64_t timeval_diff(const struct timeval *a, const struct timeval *b) {
u_int64_t microseconds;
microseconds = ((u_int64_t)a->tv_sec - (u_int64_t)b->tv_sec) * 1000000LLU + (a->tv_usec - b->tv_usec);
return microseconds;
}
void timeval_subtract (struct timeval *result, const struct timeval *a, const struct timeval *b) {
u_int64_t microseconds;
microseconds = timeval_diff(a, b);
result->tv_sec = microseconds/1000000LLU;
result->tv_usec = microseconds%1000000LLU;
}
void timeval_multiply(struct timeval *result, const struct timeval *a, const long multiplier) {
u_int64_t microseconds=0;
u_int64_t microseconds;
microseconds = (((u_int64_t)a->tv_sec * 1000000LLU) + a->tv_usec) * multiplier;
result->tv_sec = microseconds/1000000LLU;
result->tv_usec = microseconds%1000000LLU;
}
void timeval_divide(struct timeval *result, const struct timeval *a, const long divisor) {
u_int64_t microseconds=0;
u_int64_t microseconds;
microseconds = (((u_int64_t)a->tv_sec * 1000000LLU) + a->tv_usec) / divisor;
result->tv_sec = microseconds/1000000LLU;
result->tv_usec = microseconds%1000000LLU;
}
void timeval_add(struct timeval *result, const struct timeval *a, const struct timeval *b) {
u_int64_t microseconds=0;
u_int64_t microseconds;
microseconds = ((u_int64_t)a->tv_sec + (u_int64_t)b->tv_sec) * 1000000LLU + (a->tv_usec + b->tv_usec);
result->tv_sec = microseconds/1000000LLU;
result->tv_usec = microseconds%1000000LLU;
}
void timeval_add_usec(struct timeval *tv, long usec) {
struct timeval a;
a.tv_sec = usec / 1000000LLU;
a.tv_usec = usec % 1000000LLU;
timeval_add(tv, tv, &a);
}
int timeval_cmp(const struct timeval *a, const struct timeval *b) {
if (a->tv_sec < b->tv_sec)
return -1;
if (a->tv_sec > b->tv_sec)
return 1;
if (a->tv_usec < b->tv_usec)
return -1;
if (a->tv_usec > b->tv_usec)
return 1;
return 0;
}
static void timeval_totalstats_average_add(struct totalstats *s, const struct timeval *add) {
struct timeval dp, oa;
@ -2746,6 +2824,8 @@ void call_destroy(struct call *c) {
atomic64_add(&m->totalstats_interval.total_relayed_errors,
atomic64_get(&ps->stats.errors));
}
ice_shutdown(&md->ice_agent);
}
if (_log_facility_cdr)
++cdrlinecnt;
@ -2833,7 +2913,7 @@ void call_destroy(struct call *c) {
for (l = c->streams; l; l = l->next) {
ps = l->data;
unkernelize(ps);
__unkernelize(ps);
dtls_shutdown(ps);
ps->sfd = NULL;
crypto_cleanup(&ps->crypto);
@ -3084,16 +3164,23 @@ void __monologue_tag(struct call_monologue *ml, const str *tag) {
g_hash_table_insert(call->tags, &ml->tag, ml);
}
static void __stream_unkernelize(struct packet_stream *ps) {
unkernelize(ps);
static void __stream_unconfirm(struct packet_stream *ps) {
__unkernelize(ps);
PS_CLEAR(ps, CONFIRMED);
ps->handler = NULL;
}
static void stream_unkernelize(struct packet_stream *ps) {
static void stream_unconfirm(struct packet_stream *ps) {
if (!ps)
return;
mutex_lock(&ps->in_lock);
__stream_unkernelize(ps);
__stream_unconfirm(ps);
mutex_unlock(&ps->in_lock);
}
static void unkernelize(struct packet_stream *ps) {
if (!ps)
return;
mutex_lock(&ps->in_lock);
__unkernelize(ps);
mutex_unlock(&ps->in_lock);
}
@ -3114,15 +3201,28 @@ static void __monologue_unkernelize(struct call_monologue *monologue) {
for (m = media->streams.head; m; m = m->next) {
stream = m->data;
__stream_unkernelize(stream);
__stream_unconfirm(stream);
if (stream->rtp_sink)
__stream_unkernelize(stream->rtp_sink);
__stream_unconfirm(stream->rtp_sink);
if (stream->rtcp_sink)
__stream_unkernelize(stream->rtcp_sink);
__stream_unconfirm(stream->rtcp_sink);
}
}
}
/* call locked in R */
void call_media_unkernelize(struct call_media *media) {
GList *m;
struct packet_stream *stream;
for (m = media->streams.head; m; m = m->next) {
stream = m->data;
unkernelize(stream);
unkernelize(stream->rtp_sink);
unkernelize(stream->rtcp_sink);
}
}
/* must be called with call->master_lock held in W */
static void __monologue_destroy(struct call_monologue *monologue) {
struct call *call;
@ -3357,102 +3457,106 @@ out:
return NULL;
}
static unsigned int __local_interface_hash(const void *p) {
const struct local_interface *lif = p;
return str_hash(&lif->name) ^ lif->preferred_family;
}
static int __local_interface_eq(const void *a, const void *b) {
const struct local_interface *A = a, *B = b;
return str_equal(&A->name, &B->name) && A->preferred_family == B->preferred_family;
}
static GQueue *__interface_list_for_family(struct callmaster *m, int family) {
return (family == AF_INET6) ? &m->interface_list_v6 : &m->interface_list_v4;
}
static void __interface_append(struct callmaster *m, struct interface_address *ifa, int family) {
struct local_interface *lif;
GQueue *q;
struct interface_address *ifc;
lif = get_local_interface(m, &ifa->interface_name, family);
if (!lif) {
lif = g_slice_alloc0(sizeof(*lif));
lif->name = ifa->interface_name;
lif->preferred_family = family;
lif->addr_hash = g_hash_table_new(in6_addr_hash, in6_addr_eq);
g_hash_table_insert(m->interfaces, lif, lif);
if (ifa->family == family) {
q = __interface_list_for_family(m, family);
g_queue_push_tail(q, lif);
}
}
if (!ifa->ice_foundation.s)
ice_foundation(ifa);
ifc = g_slice_alloc(sizeof(*ifc));
*ifc = *ifa;
ifc->preference = lif->list.length;
g_queue_push_tail(&lif->list, ifc);
g_hash_table_insert(lif->addr_hash, &ifc->addr, ifc);
}
/* XXX interface handling should go somewhere else */
void callmaster_config_init(struct callmaster *m) {
GList *l;
struct interface_address *ifa;
struct local_interface *lif;
m->interfaces = g_hash_table_new(str_hash, str_equal);
m->interfaces = g_hash_table_new(__local_interface_hash, __local_interface_eq);
/* build primary lists first */
for (l = m->conf.interfaces->head; l; l = l->next) {
ifa = l->data;
__interface_append(m, ifa, ifa->family);
}
lif = g_hash_table_lookup(m->interfaces, &ifa->interface_name);
if (!lif) {
lif = g_slice_alloc0(sizeof(*lif));
lif->name = ifa->interface_name;
g_hash_table_insert(m->interfaces, &lif->name, lif);
g_queue_push_tail(&m->interface_list, lif);
}
if (IN6_IS_ADDR_V4MAPPED(&ifa->addr))
g_queue_push_tail(&lif->ipv4, ifa);
/* then append to each other as lower-preference alternatives */
for (l = m->conf.interfaces->head; l; l = l->next) {
ifa = l->data;
if (ifa->family == AF_INET)
__interface_append(m, ifa, AF_INET6);
else if (ifa->family == AF_INET6)
__interface_append(m, ifa, AF_INET);
else
g_queue_push_tail(&lif->ipv6, ifa);
sdp_ice_foundation(ifa);
abort();
}
}
struct local_interface *get_local_interface(struct callmaster *m, const str *name) {
struct local_interface *lif;
if (!name || !name->s)
return m->interface_list.head->data;
struct local_interface *get_local_interface(struct callmaster *m, const str *name, int family) {
struct local_interface d, *lif;
lif = g_hash_table_lookup(m->interfaces, name);
return lif;
}
if (!name || !name->s) {
GQueue *q;
q = __interface_list_for_family(m, family);
return q->head ? q->head->data : NULL;
}
static const GQueue *get_interface_addresses(struct local_interface *lif, int family) {
if (!lif)
return NULL;
d.name = *name;
d.preferred_family = family;
switch (family) {
case AF_INET:
return &lif->ipv4;
break;
case AF_INET6:
return &lif->ipv6;
break;
default:
return NULL;
}
lif = g_hash_table_lookup(m->interfaces, &d);
return lif;
}
static struct interface_address *get_interface_address(struct local_interface *lif, int family) {
const GQueue *q;
q = get_interface_addresses(lif, family);
if (!q || !q->head)
q = &lif->list;
if (!q->head)
return NULL;
return q->head->data;
}
/* safety fallback */
struct interface_address *get_any_interface_address(struct local_interface *lif, int family) {
static struct interface_address *get_any_interface_address(struct local_interface *lif, int family) {
struct interface_address *ifa;
GQueue q = G_QUEUE_INIT;
get_all_interface_addresses(&q, lif, family);
ifa = q.head->data;
g_queue_clear(&q);
return ifa;
}
void get_all_interface_addresses(GQueue *q, struct local_interface *lif, int family) {
g_queue_append(q, get_interface_addresses(lif, family));
if (family == AF_INET)
g_queue_append(q, get_interface_addresses(lif, AF_INET6));
else
g_queue_append(q, get_interface_addresses(lif, AF_INET));
}
struct interface_address *get_interface_from_address(struct local_interface *lif, const struct in6_addr *addr) {
GQueue *q;
GList *l;
struct interface_address *ifa;
if (IN6_IS_ADDR_V4MAPPED(addr))
q = &lif->ipv4;
else
q = &lif->ipv6;
for (l = q->head; l; l = l->next) {
ifa = l->data;
if (!memcmp(&ifa->addr, addr, sizeof(*addr)))
return ifa;
}
return NULL;
ifa = get_interface_address(lif, family);
if (ifa)
return ifa;
ifa = get_interface_address(lif, AF_INET);
if (ifa)
return ifa;
return get_interface_address(lif, AF_INET6);
}

+ 50
- 20
daemon/call.h View File

@ -58,7 +58,14 @@ enum xmlrpc_format {
XF_CALLID,
};
struct call_monologue;
enum call_stream_state {
CSS_UNKNOWN = 0,
CSS_SHUTDOWN,
CSS_ICE,
CSS_DTLS,
CSS_RUNNING,
};
@ -105,6 +112,7 @@ struct call_monologue;
#define SHARED_FLAG_STRICT_SOURCE 0x00000100
#define SHARED_FLAG_MEDIA_HANDOVER 0x00000200
#define SHARED_FLAG_TRICKLE_ICE 0x00000400
#define SHARED_FLAG_ICE_LITE 0x00000800
/* struct stream_params */
#define SP_FLAG_NO_RTCP 0x00010000
@ -119,13 +127,14 @@ struct call_monologue;
#define SP_FLAG_STRICT_SOURCE SHARED_FLAG_STRICT_SOURCE
#define SP_FLAG_MEDIA_HANDOVER SHARED_FLAG_MEDIA_HANDOVER
#define SP_FLAG_TRICKLE_ICE SHARED_FLAG_TRICKLE_ICE
#define SP_FLAG_ICE_LITE SHARED_FLAG_ICE_LITE
/* struct packet_stream */
#define PS_FLAG_RTP 0x00010000
#define PS_FLAG_RTCP 0x00020000
#define PS_FLAG_IMPLICIT_RTCP SHARED_FLAG_IMPLICIT_RTCP
#define PS_FLAG_FALLBACK_RTCP 0x00040000
#define PS_FLAG_STUN 0x00080000
#define PS_FLAG_UNUSED2 0x00080000
#define PS_FLAG_FILLED 0x00100000
#define PS_FLAG_CONFIRMED 0x00200000
#define PS_FLAG_KERNELIZED 0x00400000
@ -134,6 +143,7 @@ struct call_monologue;
#define PS_FLAG_FINGERPRINT_VERIFIED 0x02000000
#define PS_FLAG_STRICT_SOURCE SHARED_FLAG_STRICT_SOURCE
#define PS_FLAG_MEDIA_HANDOVER SHARED_FLAG_MEDIA_HANDOVER
#define PS_FLAG_ICE SHARED_FLAG_ICE
/* struct call_media */
#define MEDIA_FLAG_INITIALIZED 0x00010000
@ -149,15 +159,21 @@ struct call_monologue;
#define MEDIA_FLAG_PASSTHRU 0x00100000
#define MEDIA_FLAG_ICE SHARED_FLAG_ICE
#define MEDIA_FLAG_TRICKLE_ICE SHARED_FLAG_TRICKLE_ICE
#define MEDIA_FLAG_ICE_LITE SHARED_FLAG_ICE_LITE
#define MEDIA_FLAG_ICE_CONTROLLING 0x00200000
/* access macros */
#define SP_ISSET(p, f) bf_isset(&(p)->sp_flags, SP_FLAG_ ## f)
#define SP_SET(p, f) bf_set(&(p)->sp_flags, SP_FLAG_ ## f)
#define SP_CLEAR(p, f) bf_clear(&(p)->sp_flags, SP_FLAG_ ## f)
#define PS_ISSET(p, f) bf_isset(&(p)->ps_flags, PS_FLAG_ ## f)
#define PS_ISSET2(p, f, g) bf_isset(&(p)->ps_flags, PS_FLAG_ ## f | PS_FLAG_ ## g)
#define PS_ARESET2(p, f, g) bf_areset(&(p)->ps_flags, PS_FLAG_ ## f | PS_FLAG_ ## g)
#define PS_SET(p, f) bf_set(&(p)->ps_flags, PS_FLAG_ ## f)
#define PS_CLEAR(p, f) bf_clear(&(p)->ps_flags, PS_FLAG_ ## f)
#define MEDIA_ISSET(p, f) bf_isset(&(p)->media_flags, MEDIA_FLAG_ ## f)
#define MEDIA_ISSET2(p, f, g) bf_isset(&(p)->media_flags, MEDIA_FLAG_ ## f | MEDIA_FLAG_ ## g)
#define MEDIA_ARESET2(p, f, g) bf_areset(&(p)->media_flags, MEDIA_FLAG_ ## f | MEDIA_FLAG_ ## g)
#define MEDIA_SET(p, f) bf_set(&(p)->media_flags, MEDIA_FLAG_ ## f)
#define MEDIA_CLEAR(p, f) bf_clear(&(p)->media_flags, MEDIA_FLAG_ ## f)
@ -173,6 +189,8 @@ struct rtpengine_srtp;
struct streamhandler;
struct sdp_ng_flags;
struct local_interface;
struct call_monologue;
struct ice_agent;
typedef bencode_buffer_t call_buffer_t;
@ -221,10 +239,6 @@ struct udp_fd {
int fd;
u_int16_t localport;
};
struct endpoint {
struct in6_addr ip46;
u_int16_t port;
};
struct stream_params {
unsigned int index; /* starting with 1 */
str type;
@ -239,6 +253,9 @@ struct stream_params {
struct dtls_fingerprint fingerprint;
unsigned int sp_flags;
GQueue rtp_payload_types; /* slice-alloc'd */
GQueue ice_candidates; /* slice-alloc'd */
str ice_ufrag;
str ice_pwd;
};
struct stream_fd {
@ -278,6 +295,7 @@ struct packet_stream {
struct call_media *media; /* RO */
struct call *call; /* RO */
unsigned int component; /* RO, starts with 1 */
struct stream_fd *sfd; /* LOCK: call->master_lock */
struct packet_stream *rtp_sink; /* LOCK: call->master_lock */
@ -322,8 +340,8 @@ struct call_media {
* atomic ops to access it when holding an R lock. */
volatile struct interface_address *local_address;
str ice_ufrag;
str ice_pwd;
struct ice_agent *ice_agent;
struct {
struct crypto_params params;
unsigned int tag;
@ -386,8 +404,9 @@ struct call {
struct local_interface {
str name;
GQueue ipv4; /* struct interface_address */
GQueue ipv6; /* struct interface_address */
int preferred_family;
GQueue list; /* struct interface_address */
GHashTable *addr_hash;
};
struct interface_address {
str interface_name;
@ -396,6 +415,7 @@ struct interface_address {
struct in6_addr advertised;
str ice_foundation;
char foundation_buf[16];
unsigned int preference; /* starting with 0 */
};
struct callmaster_config {
@ -420,10 +440,10 @@ struct callmaster {
GHashTable *callhash;
GHashTable *interfaces; /* struct local_interface */
GQueue interface_list; /* ditto */
GQueue interface_list_v4; /* ditto */
GQueue interface_list_v6; /* ditto */
mutex_t portlock;
u_int16_t lastport;
volatile unsigned int lastport;
BIT_ARRAY_DECLARE(ports_used, 0x10000);
/* XXX rework these */
@ -473,15 +493,19 @@ int monologue_offer_answer(struct call_monologue *monologue, GQueue *streams, co
int call_delete_branch(struct callmaster *m, const str *callid, const str *branch,
const str *fromtag, const str *totag, bencode_item_t *output);
void call_destroy(struct call *);
enum call_stream_state call_stream_state_machine(struct packet_stream *);
void call_media_unkernelize(struct call_media *media);
void kernelize(struct packet_stream *);
int call_stream_address(char *, struct packet_stream *, enum stream_address_format, int *);
int call_stream_address46(char *o, struct packet_stream *ps, enum stream_address_format format,
int *len, struct interface_address *ifa);
void get_all_interface_addresses(GQueue *q, struct local_interface *lif, int family);
struct local_interface *get_local_interface(struct callmaster *m, const str *name);
struct interface_address *get_any_interface_address(struct local_interface *lif, int family);
struct interface_address *get_interface_from_address(struct local_interface *lif, const struct in6_addr *addr);
struct local_interface *get_local_interface(struct callmaster *m, const str *name, int familiy);
INLINE struct interface_address *get_interface_from_address(struct local_interface *lif,
const struct in6_addr *addr)
{
return g_hash_table_lookup(lif->addr_hash, addr);
}
const struct transport_protocol *transport_protocol(const str *s);
@ -489,6 +513,15 @@ void timeval_subtract (struct timeval *result, const struct timeval *a, const st
void timeval_multiply(struct timeval *result, const struct timeval *a, const long multiplier);
void timeval_divide(struct timeval *result, const struct timeval *a, const long divisor);
void timeval_add(struct timeval *result, const struct timeval *a, const struct timeval *b);
int timeval_cmp(const struct timeval *a, const struct timeval *b);
void timeval_add_usec(struct timeval *tv, long usec);
u_int64_t timeval_diff(const struct timeval *a, const struct timeval *b);
INLINE void timeval_lowest(struct timeval *l, const struct timeval *n) {
if (!n->tv_sec)
return;
if (!l->tv_sec || timeval_cmp(l, n) == 1)
*l = *n;
}
INLINE void *call_malloc(struct call *c, size_t l) {
@ -541,10 +574,7 @@ INLINE str *call_str_init_dup(struct call *c, char *s) {
return call_str_dup(c, &t);
}
INLINE void callmaster_exclude_port(struct callmaster *m, u_int16_t p) {
/* XXX atomic bit field? */
mutex_lock(&m->portlock);
bit_array_set(m->ports_used, p);
mutex_unlock(&m->portlock);
}
INLINE struct packet_stream *packet_stream_sink(struct packet_stream *ps) {
struct packet_stream *ret;


+ 2
- 0
daemon/call_interfaces.c View File

@ -17,6 +17,7 @@
#include "control_tcp.h"
#include "control_udp.h"
#include "rtp.h"
#include "ice.h"
@ -285,6 +286,7 @@ static void sp_free(void *p) {
if (s->crypto.mki)
free(s->crypto.mki);
g_queue_clear_full(&s->rtp_payload_types, rtp_pt_free);
ice_candidates_free(&s->ice_candidates);
g_slice_free1(sizeof(*s), s);
}
static void streams_free(GQueue *q) {


+ 6
- 4
daemon/dtls.c View File

@ -16,6 +16,7 @@
#include "log.h"
#include "call.h"
#include "poller.h"
#include "ice.h"
@ -483,7 +484,7 @@ int dtls_connection_init(struct packet_stream *ps, int active, struct dtls_cert
if (d->init) {
if ((d->active && active) || (!d->active && !active))
goto connect;
goto done;
dtls_connection_cleanup(d);
}
@ -522,9 +523,7 @@ int dtls_connection_init(struct packet_stream *ps, int active, struct dtls_cert
d->init = 1;
d->active = active ? -1 : 0;
connect:
dtls(ps, NULL, NULL);
done:
return 0;
error:
@ -628,6 +627,7 @@ error:
return -1;
}
/* called with call locked in W or R with ps->in_lock held */
int dtls(struct packet_stream *ps, const str *s, struct sockaddr_in6 *fsin) {
struct dtls_connection *d;
int ret;
@ -638,6 +638,8 @@ int dtls(struct packet_stream *ps, const str *s, struct sockaddr_in6 *fsin) {
if (!ps || !ps->sfd)
return 0;
if (!MEDIA_ISSET(ps->media, DTLS))
return 0;
d = &ps->sfd->dtls;


+ 15
- 15
daemon/graphite.c View File

@ -18,7 +18,7 @@ static u_int32_t graphite_ipaddress;
static int graphite_port=0;
static struct callmaster* cm=0;
//struct totalstats totalstats_prev;
static time_t g_now, next_run;
static time_t next_run;
int connect_to_graphite_server(u_int32_t ipaddress, int port) {
@ -106,17 +106,17 @@ int send_graphite_data() {
ZERO(ts.total_managed_sess);
mutex_unlock(&cm->totalstats_interval.total_average_lock);
rc = sprintf(ptr,"%s.totals.average_call_dur.tv_sec %llu %llu\n",hostname, (unsigned long long) ts.total_average_call_dur.tv_sec,(unsigned long long)g_now); ptr += rc;
rc = sprintf(ptr,"%s.totals.average_call_dur.tv_usec %lu %llu\n",hostname, ts.total_average_call_dur.tv_usec,(unsigned long long)g_now); ptr += rc;
rc = sprintf(ptr,"%s.totals.forced_term_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_forced_term_sess),(unsigned long long)g_now); ptr += rc;
rc = sprintf(ptr,"%s.totals.managed_sess "UINT64F" %llu\n",hostname, ts.total_managed_sess,(unsigned long long)g_now); ptr += rc;
rc = sprintf(ptr,"%s.totals.nopacket_relayed_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_nopacket_relayed_sess),(unsigned long long)g_now); ptr += rc;
rc = sprintf(ptr,"%s.totals.oneway_stream_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_oneway_stream_sess),(unsigned long long)g_now); ptr += rc;
rc = sprintf(ptr,"%s.totals.regular_term_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_regular_term_sess),(unsigned long long)g_now); ptr += rc;
rc = sprintf(ptr,"%s.totals.relayed_errors "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_relayed_errors),(unsigned long long)g_now); ptr += rc;
rc = sprintf(ptr,"%s.totals.relayed_packets "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_relayed_packets),(unsigned long long)g_now); ptr += rc;
rc = sprintf(ptr,"%s.totals.silent_timeout_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_silent_timeout_sess),(unsigned long long)g_now); ptr += rc;
rc = sprintf(ptr,"%s.totals.timeout_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_timeout_sess),(unsigned long long)g_now); ptr += rc;
rc = sprintf(ptr,"%s.totals.average_call_dur.tv_sec %llu %llu\n",hostname, (unsigned long long) ts.total_average_call_dur.tv_sec,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"%s.totals.average_call_dur.tv_usec %lu %llu\n",hostname, ts.total_average_call_dur.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"%s.totals.forced_term_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_forced_term_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"%s.totals.managed_sess "UINT64F" %llu\n",hostname, ts.total_managed_sess,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"%s.totals.nopacket_relayed_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_nopacket_relayed_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"%s.totals.oneway_stream_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_oneway_stream_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"%s.totals.regular_term_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_regular_term_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"%s.totals.relayed_errors "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_relayed_errors),(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"%s.totals.relayed_packets "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_relayed_packets),(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"%s.totals.silent_timeout_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_silent_timeout_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"%s.totals.timeout_sess "UINT64F" %llu\n",hostname, atomic64_get_na(&ts.total_timeout_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
rc = write(graphite_sock, data_to_send, ptr - data_to_send);
if (rc<0) {
@ -134,11 +134,11 @@ void graphite_loop_run(struct callmaster* callmaster, int seconds) {
int rc=0;
g_now = time(NULL);
if (g_now < next_run)
gettimeofday(&g_now, NULL);
if (g_now.tv_sec < next_run)
goto sleep;
next_run = g_now + seconds;
next_run = g_now.tv_sec + seconds;
if (!cm)
cm = callmaster;


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


+ 208
- 0
daemon/ice.h View File

@ -0,0 +1,208 @@
#ifndef __ICE_H__
#define __ICE_H__
#include <arpa/inet.h>
#include <glib.h>
#include <sys/time.h>
#include <sys/types.h>
#include "str.h"
#include "obj.h"
#include "aux.h"
#include "call.h"
#define MAX_COMPONENTS 2
#define TIMER_RUN_INTERVAL 20 /* ms */
#define STUN_RETRANSMIT_INTERVAL 100 /* ms, with exponential backoff */
#define STUN_MAX_RETRANSMITS 7
#define MAX_ICE_CANDIDATES 100
#define ICE_AGENT_COMPLETED 0x0002
#define ICE_AGENT_CONTROLLING 0x0004
#define ICE_AGENT_NOMINATING 0x0008
#define ICE_PAIR_FROZEN 0x0001
#define ICE_PAIR_IN_PROGRESS 0x0002
#define ICE_PAIR_FAILED 0x0004
#define ICE_PAIR_SUCCEEDED 0x0008
#define ICE_PAIR_NOMINATED 0x0010
#define ICE_PAIR_LEARNED 0x0020
#define ICE_PAIR_VALID 0x0040
#define ICE_PAIR_TO_USE 0x0080
#define ICE_PAIR_TRIGGERED 0x0100
#define PAIR_ISSET(p, f) bf_isset(&(p)->pair_flags, ICE_PAIR_ ## f)
#define PAIR_SET(p, f) bf_set(&(p)->pair_flags, ICE_PAIR_ ## f)
#define PAIR_SET2(p, f, g) bf_set(&(p)->pair_flags, ICE_PAIR_ ## f | ICE_PAIR_ ## g)
#define PAIR_CLEAR(p, f) bf_clear(&(p)->pair_flags, ICE_PAIR_ ## f)
#define PAIR_CLEAR2(p, f, g) bf_clear(&(p)->pair_flags, ICE_PAIR_ ## f | ICE_PAIR_ ## g)
#define AGENT_ISSET(p, f) bf_isset(&(p)->agent_flags, ICE_AGENT_ ## f)
#define AGENT_ISSET2(p, f, g) bf_isset(&(p)->agent_flags, ICE_AGENT_ ## f | ICE_AGENT_ ## g)
#define AGENT_SET(p, f) bf_set(&(p)->agent_flags, ICE_AGENT_ ## f)
#define AGENT_SET2(p, f, g) bf_set(&(p)->agent_flags, ICE_AGENT_ ## f | ICE_AGENT_ ## g)
#define AGENT_CLEAR(p, f) bf_clear(&(p)->agent_flags, ICE_AGENT_ ## f)
#define AGENT_CLEAR2(p, f, g) bf_clear(&(p)->agent_flags, ICE_AGENT_ ## f | ICE_AGENT_ ## g)
struct local_interface;
struct interface_address;
struct packet_stream;
struct call_media;
struct call;
struct stream_params;
struct stun_attrs;
enum ice_candidate_type {
ICT_UNKNOWN = 0,
ICT_HOST,
ICT_SRFLX,
ICT_PRFLX,
ICT_RELAY,
__ICT_LAST,
};
enum ice_transport {
ITP_UNKNOWN = 0,
ITP_UDP,
// ITP_TCP,
};
struct ice_candidate {
str foundation;
unsigned long component_id;
enum ice_transport transport;
unsigned long priority;
struct endpoint endpoint;
enum ice_candidate_type type;
struct in6_addr related_address;
unsigned int related_port;
};
struct ice_candidate_pair {
struct ice_candidate *remote_candidate;
struct interface_address *local_address;
struct packet_stream *packet_stream;
volatile unsigned int pair_flags;
u_int32_t stun_transaction[3]; /* belongs to transaction_hash, thus agent->lock */
unsigned int retransmit_ms;
struct timeval retransmit;
unsigned int retransmits;
struct ice_agent *agent;
u_int64_t pair_priority;
int was_controlling:1,
was_nominated:1;
};
/* these are protected by the call's master_lock */
struct ice_agent {
struct obj obj;
struct call *call; /* main reference */
struct call_media *media;
struct local_interface *local_interface;
int desired_family;
mutex_t lock; /* for elements below. and call must be locked in R */
/* lock order: in_lock first, then agent->lock */
GQueue remote_candidates;
GQueue candidate_pairs; /* for storage */
GQueue triggered;
GHashTable *candidate_hash;
GHashTable *pair_hash;
GHashTable *transaction_hash;
GHashTable *foundation_hash;
GTree *all_pairs;
GQueue all_pairs_list; /* sorted through gtree */
GTree *nominated_pairs; /* nominated by peer */
GTree *succeeded_pairs; /* checked by us */
GTree *valid_pairs; /* succeeded and nominated */
unsigned int active_components;
struct timeval start_nominating;
str ufrag[2]; /* 0 = remote, 1 = local */
str pwd[2]; /* ditto */
volatile unsigned int agent_flags;
struct timeval next_check; /* protected by ice_agents_timers_lock */
struct timeval last_run; /* ditto */
};
extern const unsigned int ice_type_preferences[];
extern const char * const ice_type_strings[];
void ice_init(void);
enum ice_candidate_type ice_candidate_type(const str *s);
enum ice_transport ice_transport(const str *s);
int ice_has_related(enum ice_candidate_type);
void ice_foundation(struct interface_address *ifa);
void ice_agent_init(struct ice_agent **agp, struct call_media *media);
void ice_update(struct ice_agent *, struct stream_params *);
void ice_shutdown(struct ice_agent **);
void ice_restart(struct ice_agent *);
void ice_candidates_free(GQueue *);
void ice_remote_candidates(GQueue *, struct ice_agent *);
void ice_thread_run(void *);
int ice_request(struct packet_stream *, struct sockaddr_in6 *, struct in6_addr *, struct stun_attrs *);
int ice_response(struct packet_stream *ps, struct sockaddr_in6 *src, struct in6_addr *dst,
struct stun_attrs *attrs, u_int32_t transaction[3]);
/* returns 0 if ICE still has work to do, 1 otherwise */
INLINE int ice_has_finished(struct call_media *media) {
if (!media)
return 1;
if (!MEDIA_ISSET(media, ICE))
return 1;
if (!media->ice_agent)
return 1;
if (AGENT_ISSET(media->ice_agent, COMPLETED))
return 1;
return 0;
}
INLINE unsigned int ice_type_preference(enum ice_candidate_type type) {
if (type >= __ICT_LAST)
return 0;
return ice_type_preferences[type];
}
/* local_pref starts with 0 */
INLINE u_int32_t ice_priority_pref(unsigned int type_pref, unsigned int local_pref, unsigned int component) {
return type_pref << 24 | (65535 - local_pref) << 8 | (256 - component);
}
INLINE u_int32_t ice_priority(enum ice_candidate_type type, unsigned int local_pref, unsigned int component) {
return ice_priority_pref(ice_type_preference(type), local_pref, component);
}
INLINE unsigned int ice_type_pref_from_prio(u_int32_t prio) {
return (prio & 0xff000000) >> 24;
}
INLINE unsigned int ice_local_pref_from_prio(u_int32_t prio) {
return 65535 - ((prio & 0xffff00) >> 8);
}
INLINE const char *ice_candidate_type_str(enum ice_candidate_type type) {
if (type >= __ICT_LAST)
return 0;
return ice_type_strings[type];
}
#endif

+ 9
- 1
daemon/log.c View File

@ -6,6 +6,7 @@
#include "str.h"
#include "call.h"
#include "poller.h"
#include "ice.h"
@ -88,7 +89,8 @@ void log_to_stderr(int facility_priority, char *format, ...) {
return;
}
fprintf(stderr, "%s: %s\n", prio_str[facility_priority & LOG_PRIMASK], msg);
fprintf(stderr, "[%lu.%06lu] %s: %s\n", (unsigned long) g_now.tv_sec, (unsigned long) g_now.tv_usec,
prio_str[facility_priority & LOG_PRIMASK], msg);
free(msg);
}
@ -123,6 +125,12 @@ void __ilog(int prio, const char *fmt, ...) {
case LOG_INFO_C_STRING:
snprintf(prefix, sizeof(prefix), "[%s] ", log_info.u.cstr);
break;
case LOG_INFO_ICE_AGENT:
snprintf(prefix, sizeof(prefix), "["STR_FORMAT"/"STR_FORMAT"/%u] ",
STR_FMT(&log_info.u.ice_agent->call->callid),
STR_FMT(&log_info.u.ice_agent->media->monologue->tag),
log_info.u.ice_agent->media->index);
break;
}
va_start(ap, fmt);


+ 11
- 3
daemon/log.h View File

@ -15,6 +15,7 @@ struct log_info {
const struct stream_fd *stream_fd;
const str *str;
const char *cstr;
const struct ice_agent *ice_agent;
const void *ptr;
} u;
enum {
@ -23,6 +24,7 @@ struct log_info {
LOG_INFO_STREAM_FD,
LOG_INFO_STR,
LOG_INFO_C_STRING,
LOG_INFO_ICE_AGENT,
} e;
};
@ -76,10 +78,9 @@ INLINE void log_info_clear() {
case LOG_INFO_NONE:
return;
case LOG_INFO_CALL:
__obj_put((void *) log_info.u.call);
break;
case LOG_INFO_STREAM_FD:
__obj_put((void *) log_info.u.stream_fd);
case LOG_INFO_ICE_AGENT:
__obj_put((void *) log_info.u.ptr);
break;
case LOG_INFO_STR:
case LOG_INFO_C_STRING:
@ -116,6 +117,13 @@ INLINE void log_info_c_string(const char *s) {
log_info.e = LOG_INFO_C_STRING;
log_info.u.cstr = s;
}
INLINE void log_info_ice_agent(const struct ice_agent *ag) {
log_info_clear();
if (!ag)
return;
log_info.e = LOG_INFO_ICE_AGENT;
log_info.u.ice_agent = __obj_get((void *) ag);
}
INLINE int get_log_level(void) {
return g_atomic_int_get(&log_level);
}


+ 11
- 8
daemon/main.c View File

@ -27,6 +27,7 @@
#include "call_interfaces.h"
#include "cli.h"
#include "graphite.h"
#include "ice.h"
@ -80,7 +81,6 @@ struct main_context {
static int global_shutdown;
static mutex_t *openssl_locks;
static char *pidfile;
@ -126,7 +126,7 @@ static void sighandler(gpointer x) {
ts.tv_sec = 0;
ts.tv_nsec = 100000000; /* 0.1 sec */
while (!global_shutdown) {
while (!g_shutdown) {
ret = sigtimedwait(&ss, NULL, &ts);
if (ret == -1) {
if (errno == EAGAIN || errno == EINTR)
@ -135,7 +135,7 @@ static void sighandler(gpointer x) {
}
if (ret == SIGINT || ret == SIGTERM)
global_shutdown = 1;
g_shutdown = 1;
else if (ret == SIGUSR1) {
if (get_log_level() > 0) {
g_atomic_int_add(&log_level, -1);
@ -237,7 +237,7 @@ static struct interface_address *if_addr_parse(char *s) {
return NULL;
}
ifa = g_slice_alloc(sizeof(*ifa));
ifa = g_slice_alloc0(sizeof(*ifa));
ifa->interface_name = name;
ifa->addr = addr;
ifa->advertised = adv;
@ -458,6 +458,7 @@ static void init_everything() {
resources();
sdp_init();
dtls_init();
ice_init();
}
void redis_mod_verify(void *dlh) {
@ -622,10 +623,11 @@ no_kernel:
die("Refusing to continue without working Redis database");
}
/* XXX move loop functions */
static void timer_loop(void *d) {
struct poller *p = d;
while (!global_shutdown)
while (!g_shutdown)
poller_timers_wait_run(p, 100);
}
@ -639,14 +641,14 @@ static void graphite_loop(void *d) {
connect_to_graphite_server(graphite_ip,graphite_port);
while (!global_shutdown)
while (!g_shutdown)
graphite_loop_run(cm,graphite_interval); // time in seconds
}
static void poller_loop(void *d) {
struct poller *p = d;
while (!global_shutdown)
while (!g_shutdown)
poller_poll(p, 100);
}
@ -664,6 +666,7 @@ int main(int argc, char **argv) {
thread_create_detach(timer_loop, ctx.p);
if (graphite_ip)
thread_create_detach(graphite_loop, ctx.m);
thread_create_detach(ice_thread_run, NULL);
if (num_threads < 1) {
#ifdef _SC_NPROCESSORS_ONLN
@ -677,7 +680,7 @@ int main(int argc, char **argv) {
thread_create_detach(poller_loop, ctx.p);
}
while (!global_shutdown) {
while (!g_shutdown) {
usleep(100000);
threads_join_all(0);
}


+ 3
- 9
daemon/poller.c View File

@ -50,18 +50,12 @@ struct poller {
__thread time_t poller_now;
struct poller *poller_new(void) {
struct poller *p;
p = malloc(sizeof(*p));
memset(p, 0, sizeof(*p));
poller_now = time(NULL);
gettimeofday(&g_now, NULL);
p->fd = epoll_create1(0);
if (p->fd == -1)
abort();
@ -315,7 +309,7 @@ int poller_poll(struct poller *p, int timeout) {
if (ret <= 0)
goto out;
poller_now = time(NULL);
gettimeofday(&g_now, NULL);
for (i = 0; i < ret; i++) {
ev = &evs[i];
@ -502,6 +496,6 @@ retry:
goto retry;
now:
poller_now = tv.tv_sec;
gettimeofday(&g_now, NULL);
poller_timers_run(p);
}

+ 2
- 4
daemon/poller.h View File

@ -30,10 +30,8 @@ struct poller_item {
struct poller;
extern __thread time_t poller_now;
/* XXX replace all occurrences with g_now */
#define poller_now g_now.tv_sec
struct poller *poller_new(void);


+ 200
- 168
daemon/sdp.c View File

@ -14,6 +14,7 @@
#include "crypto.h"
#include "dtls.h"
#include "rtp.h"
#include "ice.h"
struct network_address {
str network_type;
@ -76,17 +77,17 @@ struct attribute_rtcp {
};
struct attribute_candidate {
str foundation;
str component_str;
str transport;
str transport_str;
str priority_str;
str ip_str;
str address_str;
str port_str;
str typ_str;
str type_str;
str related_address_str;
str related_port_str;
unsigned long component;
unsigned long priority;
struct ice_candidate cand_parsed;
int parsed:1;
};
@ -166,8 +167,10 @@ struct sdp_attribute {
ATTR_RTCP,
ATTR_CANDIDATE,
ATTR_ICE,
ATTR_ICE_LITE,
ATTR_ICE_OPTIONS,
ATTR_ICE_UFRAG,
ATTR_ICE_PWD,
ATTR_CRYPTO,
ATTR_SSRC,
ATTR_INACTIVE,
@ -198,11 +201,6 @@ struct sdp_attribute {
static const char ice_chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
INLINE struct sdp_attribute *attr_get_by_id(struct sdp_attributes *a, int id) {
return g_hash_table_lookup(a->id_hash, &id);
}
@ -576,27 +574,59 @@ static int parse_attribute_rtcp(struct sdp_attribute *output) {
static int parse_attribute_candidate(struct sdp_attribute *output) {
PARSE_DECL;
char *ep;
struct attribute_candidate *c;
output->attr = ATTR_CANDIDATE;
c = &output->u.candidate;
PARSE_INIT;
EXTRACT_TOKEN(u.candidate.foundation);
EXTRACT_TOKEN(u.candidate.cand_parsed.foundation);
EXTRACT_TOKEN(u.candidate.component_str);
EXTRACT_TOKEN(u.candidate.transport);
EXTRACT_TOKEN(u.candidate.transport_str);
EXTRACT_TOKEN(u.candidate.priority_str);
EXTRACT_TOKEN(u.candidate.ip_str);
EXTRACT_TOKEN(u.candidate.address_str);
EXTRACT_TOKEN(u.candidate.port_str);
EXTRACT_TOKEN(u.candidate.typ_str);
EXTRACT_TOKEN(u.candidate.type_str);
output->u.candidate.component = strtoul(output->u.candidate.component_str.s, &ep, 10);
if (ep == output->u.candidate.component_str.s)
c->cand_parsed.component_id = strtoul(c->component_str.s, &ep, 10);
if (ep == c->component_str.s)
return -1;
c->cand_parsed.transport = ice_transport(&c->transport_str);
if (!c->cand_parsed.transport)
return 0;
c->cand_parsed.priority = strtoul(c->priority_str.s, &ep, 10);
if (ep == c->priority_str.s)
return -1;
if (__parse_address(&c->cand_parsed.endpoint.ip46, NULL, NULL, &c->address_str))
return 0;
c->cand_parsed.endpoint.port = strtoul(c->port_str.s, &ep, 10);
if (ep == c->port_str.s)
return -1;
output->u.candidate.priority = strtoul(output->u.candidate.priority_str.s, &ep, 10);
if (ep == output->u.candidate.priority_str.s)
if (str_cmp(&c->typ_str, "typ"))
return -1;
c->cand_parsed.type = ice_candidate_type(&c->type_str);
if (!c->cand_parsed.type)
return 0;
if (!ice_has_related(c->cand_parsed.type))
goto done;
if (__parse_address(&c->cand_parsed.related_address, NULL, NULL, &c->related_address_str))
return 0;
c->cand_parsed.related_port = strtoul(c->related_port_str.s, &ep, 10);
if (ep == c->related_port_str.s)
return -1;
output->u.candidate.parsed = 1;
done:
c->parsed = 1;
return 0;
}
@ -766,13 +796,13 @@ static int parse_attribute(struct sdp_attribute *a) {
break;
case 7:
if (!str_cmp(&a->name, "ice-pwd"))
a->attr = ATTR_ICE;
a->attr = ATTR_ICE_PWD;
break;
case 8:
switch (a->name.s[0]) {
case 'i':
if (!str_cmp(&a->name, "ice-lite"))
a->attr = ATTR_ICE;
a->attr = ATTR_ICE_LITE;
else if (!str_cmp(&a->name, "inactive"))
a->attr = ATTR_INACTIVE;
break;
@ -1093,6 +1123,48 @@ out:
return ret;
}
static void __sdp_ice(struct stream_params *sp, struct sdp_media *media) {
struct sdp_attribute *attr;
struct attribute_candidate *ac;
struct ice_candidate *cand;
GQueue *q;
GList *ql;
q = attr_list_get_by_id(&media->attributes, ATTR_CANDIDATE);
if (!q)
return;
SP_SET(sp, ICE);
for (ql = q->head; ql; ql = ql->next) {
attr = ql->data;
ac = &attr->u.candidate;
if (!ac->parsed)
continue;
cand = g_slice_alloc(sizeof(*cand));
*cand = ac->cand_parsed;
g_queue_push_tail(&sp->ice_candidates, cand);
}
if ((attr = attr_get_by_id(&media->attributes, ATTR_ICE_OPTIONS))) {
if (str_str(&attr->value, "trickle") >= 0)
SP_SET(sp, TRICKLE_ICE);
}
else if (is_trickle_ice_address(&sp->rtp_endpoint))
SP_SET(sp, TRICKLE_ICE);
if (attr_get_by_id(&media->attributes, ATTR_ICE_LITE))
SP_SET(sp, ICE_LITE);
attr = attr_get_by_id_m_s(media, ATTR_ICE_UFRAG);
if (attr)
sp->ice_ufrag = attr->value;
attr = attr_get_by_id_m_s(media, ATTR_ICE_PWD);
if (attr)
sp->ice_pwd = attr->value;
}
/* XXX split this function up */
int sdp_streams(const GQueue *sessions, GQueue *streams, struct sdp_ng_flags *flags) {
@ -1181,15 +1253,7 @@ int sdp_streams(const GQueue *sessions, GQueue *streams, struct sdp_ng_flags *fl
sp->fingerprint.hash_func->num_bytes);
}
/* ICE stuff */
if (attr_get_by_id(&media->attributes, ATTR_CANDIDATE))
SP_SET(sp, ICE);
if ((attr = attr_get_by_id(&media->attributes, ATTR_ICE_OPTIONS))) {
if (str_str(&attr->value, "trickle") >= 0)
SP_SET(sp, TRICKLE_ICE);
}
else if (is_trickle_ice_address(&sp->rtp_endpoint))
SP_SET(sp, TRICKLE_ICE);
__sdp_ice(sp, media);
/* determine RTCP endpoint */
@ -1430,22 +1494,6 @@ void sdp_chopper_destroy(struct sdp_chopper *chop) {
g_slice_free1(sizeof(*chop), chop);
}
static void random_ice_string(char *buf, int len) {
while (len--)
*buf++ = ice_chars[random() % strlen(ice_chars)];
}
static void create_random_ice_string(struct call *call, str *s, int len) {
char buf[30];
assert(len < sizeof(buf));
if (s->s)
return;
random_ice_string(buf, len);
call_str_cpy_len(call, s, buf, len);
}
static int process_session_attributes(struct sdp_chopper *chop, struct sdp_attributes *attrs,
struct sdp_ng_flags *flags)
{
@ -1458,6 +1506,9 @@ static int process_session_attributes(struct sdp_chopper *chop, struct sdp_attri
switch (attr->attr) {
case ATTR_ICE:
case ATTR_ICE_UFRAG:
case ATTR_ICE_PWD:
case ATTR_ICE_OPTIONS:
case ATTR_ICE_LITE:
if (!flags->ice_remove && !flags->ice_force)
break;
goto strip;
@ -1517,6 +1568,9 @@ static int process_media_attributes(struct sdp_chopper *chop, struct sdp_media *
switch (attr->attr) {
case ATTR_ICE:
case ATTR_ICE_UFRAG:
case ATTR_ICE_PWD:
case ATTR_ICE_OPTIONS:
case ATTR_ICE_LITE:
if (MEDIA_ISSET(media, PASSTHRU))
break;
if (!flags->ice_remove && !flags->ice_force)
@ -1579,32 +1633,19 @@ strip:
return 0;
}
INLINE unsigned long prio_calc(unsigned int pref, unsigned int tpref) {
return (1 << 24) * tpref + (1 << 8) * pref + (256 - 1);
}
INLINE unsigned long pref_from_prio(unsigned int prio) {
return (prio & 0xffff00) >> 8;
}
INLINE unsigned long type_from_prio(unsigned int prio) {
return (prio & 0xff000000) >> 24;
}
static unsigned long new_priority(struct sdp_media *media, int relay) {
static void new_priority(struct sdp_media *media, enum ice_candidate_type type, unsigned int *tprefp,
unsigned int *lprefp)
{
GQueue *cands;
int pref;
unsigned long prio, tpref;
unsigned int lpref, tpref;
u_int32_t prio;
GList *l;
struct sdp_attribute *a;
struct attribute_candidate *c;
tpref = 126;
if (relay)
tpref = 0;
pref = 65535;
prio = prio_calc(pref, tpref);
if (!media)
goto out;
lpref = 0;
tpref = ice_type_preference(type);
prio = ice_priority_pref(tpref, lpref, 1);
cands = attr_list_get_by_id(&media->attributes, ATTR_CANDIDATE);
if (!cands)
@ -1613,88 +1654,111 @@ static unsigned long new_priority(struct sdp_media *media, int relay) {
for (l = cands->head; l; l = l->next) {
a = l->data;
c = &a->u.candidate;
if (c->priority <= prio && !str_cmp(&c->type_str, "host")) {
/* tpref should come out as 126 here, unless the client isn't following
if (c->cand_parsed.priority <= prio && c->cand_parsed.type == type
&& c->cand_parsed.component_id == 1)
{
/* tpref should come out as 126 (if host) here, unless the client isn't following
* the RFC, in which case we must adapt */
tpref = type_from_prio(c->priority);
tpref = ice_type_pref_from_prio(c->cand_parsed.priority);
pref = pref_from_prio(c->priority);
if (pref)
pref--;
lpref = ice_local_pref_from_prio(c->cand_parsed.priority);
if (lpref)
lpref--;
else {
/* we must deviate from the RFC recommended values */
if (tpref)
tpref--;
pref = 65535;
lpref = 65535;
}
prio = prio_calc(pref, tpref);
prio = ice_priority_pref(tpref, lpref, 1);
}
}
out:
return prio;
*tprefp = tpref;
*lprefp = lpref;
}
static void insert_candidate(struct sdp_chopper *chop, struct packet_stream *ps, unsigned int component,
unsigned int type_pref, unsigned int local_pref, enum ice_candidate_type type,
struct interface_address *ifa)
{
unsigned long priority;
priority = ice_priority_pref(type_pref, local_pref, component);
chopper_append_c(chop, "a=candidate:");
chopper_append_str(chop, &ifa->ice_foundation);
chopper_append_printf(chop, " %u UDP %lu ", component, priority);
insert_ice_address(chop, ps, ifa);
chopper_append_c(chop, " typ ");
chopper_append_c(chop, ice_candidate_type_str(type));
chopper_append_c(chop, "\r\n");
}
static void insert_candidates(struct sdp_chopper *chop, struct packet_stream *rtp, struct packet_stream *rtcp,
unsigned long priority, struct sdp_media *media,
unsigned int relay)
struct sdp_ng_flags *flags, struct sdp_media *sdp_media)
{
GQueue addrs = G_QUEUE_INIT;
GList *l;
struct interface_address *ifa;
unsigned int pref;
struct call_media *media;
struct local_interface *lif;
struct ice_agent *ag;
unsigned int type_pref, local_pref;
enum ice_candidate_type cand_type;
struct ice_candidate *cand;
media = rtp->media;
cand_type = ICT_HOST;
if (flags->ice_force_relay)
cand_type = ICT_RELAY;
if (MEDIA_ISSET(media, PASSTHRU))
new_priority(sdp_media, cand_type, &type_pref, &local_pref);
else {
type_pref = ice_type_preference(cand_type);
local_pref = -1;
}
get_all_interface_addresses(&addrs, rtp->media->interface, rtp->media->desired_family);
for (l = addrs.head; l; l = l->next) {
ifa = l->data;
chopper_append_c(chop, "a=candidate:");
chopper_append_str(chop, &ifa->ice_foundation);
chopper_append_printf(chop, " 1 UDP %lu ", priority);
insert_ice_address(chop, rtp, ifa);
if (relay)
chopper_append_c(chop, " typ relay\r\n");
else
chopper_append_c(chop, " typ host\r\n");
if (rtcp) {
/* rtcp-mux only possible in answer */
chopper_append_c(chop, "a=candidate:");
chopper_append_str(chop, &ifa->ice_foundation);
chopper_append_printf(chop, " 2 UDP %lu ", priority - 1);
insert_ice_address(chop, rtcp, ifa);
if (relay)
chopper_append_c(chop, " typ relay\r\n");
else
chopper_append_c(chop, " typ host\r\n");
ag = media->ice_agent;
lif = ag ? ag->local_interface : media->interface;
if (ag && AGENT_ISSET(ag, COMPLETED)) {
ifa = g_atomic_pointer_get(&media->local_address);
insert_candidate(chop, rtp, 1, type_pref, ifa->preference, cand_type, ifa);
if (rtcp) /* rtcp-mux only possible in answer */
insert_candidate(chop, rtcp, 2, type_pref, ifa->preference, cand_type, ifa);
if (flags->opmode == OP_OFFER && AGENT_ISSET(ag, CONTROLLING)) {
GQueue rc;
GList *l;
chopper_append_c(chop, "a=remote-candidates:");
ice_remote_candidates(&rc, ag);
for (l = rc.head; l; l = l->next) {
if (l != rc.head)
chopper_append_c(chop, " ");
cand = l->data;
chopper_append_printf(chop, "%lu %s %u", cand->component_id,
smart_ntop_buf(&cand->endpoint.ip46), cand->endpoint.port);
}
chopper_append_c(chop, "\r\n");
g_queue_clear(&rc);
}
priority -= 256;
return;
}
g_queue_clear(&addrs);
}
static int has_ice(GQueue *sessions) {
GList *l, *m;
struct sdp_session *session;
struct sdp_media *media;
for (l = sessions->head; l; l = l->next) {
session = l->data;
for (l = lif->list.head; l; l = l->next) {
ifa = l->data;
pref = (local_pref == -1) ? ifa->preference : local_pref;
if (attr_get_by_id(&session->attributes, ATTR_ICE_UFRAG))
return 1;
insert_candidate(chop, rtp, 1, type_pref, pref, cand_type, ifa);
for (m = session->media_streams.head; m; m = m->next) {
media = m->data;
if (rtcp) /* rtcp-mux only possible in answer */
insert_candidate(chop, rtcp, 2, type_pref, pref, cand_type, ifa);
if (attr_get_by_id(&media->attributes, ATTR_ICE_UFRAG))
return 1;
}
if (local_pref != -1)
local_pref++;
}
return 0;
}
static void insert_dtls(struct call_media *media, struct sdp_chopper *chop) {
@ -1720,12 +1784,10 @@ static void insert_dtls(struct call_media *media, struct sdp_chopper *chop) {
*(--o) = '\0';
actpass = "holdconn";
if (MEDIA_ISSET(media, SETUP_PASSIVE)) {
if (MEDIA_ISSET(media, SETUP_ACTIVE))
actpass = "actpass";
else
actpass = "passive";
}
if (MEDIA_ARESET2(media, SETUP_PASSIVE, SETUP_ACTIVE))
actpass = "actpass";
else if (MEDIA_ISSET(media, SETUP_PASSIVE))
actpass = "passive";
else if (MEDIA_ISSET(media, SETUP_ACTIVE))
actpass = "active";
@ -1779,16 +1841,11 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call_monologu
struct sdp_session *session;
struct sdp_media *sdp_media;
GList *l, *k, *m, *j;
int do_ice, media_index, sess_conn;
unsigned long priority;
int media_index, sess_conn;
struct call_media *call_media;
struct packet_stream *ps, *ps_rtcp;
struct call *call;
m = monologue->medias.head;
do_ice = (flags->ice_force || flags->ice_force_relay ||
(!has_ice(sessions) && !flags->ice_remove)) ? 1 : 0;
call = monologue->call;
for (l = sessions->head; l; l = l->next) {
session = l->data;
@ -1829,11 +1886,6 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call_monologu
if (!MEDIA_ISSET(call_media, PASSTHRU)) {
if (process_session_attributes(chop, &session->attributes, flags))
goto error;
if (do_ice) {
copy_up_to_end_of(chop, &session->s);
chopper_append_c(chop, "a=ice-lite\r\n");
}
}
media_index = 1;
@ -1881,11 +1933,11 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call_monologu
if (!sdp_media->port_num || !ps->sfd)
goto next;
if (MEDIA_ISSET(call_media, SEND) && MEDIA_ISSET(call_media, RECV))
if (MEDIA_ARESET2(call_media, SEND, RECV))
chopper_append_c(chop, "a=sendrecv\r\n");
else if (MEDIA_ISSET(call_media, SEND) && !MEDIA_ISSET(call_media, RECV))
else if (MEDIA_ISSET(call_media, SEND))
chopper_append_c(chop, "a=sendonly\r\n");
else if (!MEDIA_ISSET(call_media, SEND) && MEDIA_ISSET(call_media, RECV))
else if (MEDIA_ISSET(call_media, RECV))
chopper_append_c(chop, "a=recvonly\r\n");
else
chopper_append_c(chop, "a=inactive\r\n");
@ -1908,31 +1960,16 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call_monologu
insert_crypto(call_media, chop);
insert_dtls(call_media, chop);
if (do_ice && !MEDIA_ISSET(call_media, PASSTHRU)) {
if (!call_media->ice_ufrag.s) {
create_random_ice_string(call, &call_media->ice_ufrag, 8);
create_random_ice_string(call, &call_media->ice_pwd, 28);
}
PS_SET(ps, STUN);
if (ps_rtcp)
PS_SET(ps_rtcp, STUN);
if (call_media->ice_agent) {
chopper_append_c(chop, "a=ice-ufrag:");
chopper_append_str(chop, &call_media->ice_ufrag);
chopper_append_str(chop, &call_media->ice_agent->ufrag[1]);
chopper_append_c(chop, "\r\na=ice-pwd:");
chopper_append_str(chop, &call_media->ice_pwd);
chopper_append_str(chop, &call_media->ice_agent->pwd[1]);
chopper_append_c(chop, "\r\n");
}
if (!flags->ice_remove) {
priority = new_priority(
(flags->ice_force || flags->ice_force_relay) ? NULL : sdp_media,
flags->ice_force_relay
);
insert_candidates(chop, ps, ps_rtcp,
priority, sdp_media, flags->ice_force_relay);
}
if (!flags->ice_remove)
insert_candidates(chop, ps, ps_rtcp, flags, sdp_media);
next:
media_index++;
@ -1950,8 +1987,3 @@ error:
void sdp_init() {
}
void sdp_ice_foundation(struct interface_address *ifa) {
random_ice_string(ifa->foundation_buf, sizeof(ifa->foundation_buf));
str_init_len(&ifa->ice_foundation, ifa->foundation_buf, sizeof(ifa->foundation_buf));
}

+ 0
- 1
daemon/sdp.h View File

@ -45,7 +45,6 @@ struct sdp_chopper {
};
void sdp_init(void);
void sdp_ice_foundation(struct interface_address *ifa);
int sdp_parse(str *body, GQueue *sessions);
int sdp_streams(const GQueue *sessions, GQueue *streams, struct sdp_ng_flags *);


+ 224
- 61
daemon/stun.c View File

@ -6,11 +6,13 @@
#include <zlib.h>
#include <openssl/hmac.h>
#include <glib.h>
#include <endian.h>
#include "compat.h"
#include "str.h"
#include "aux.h"
#include "log.h"
#include "ice.h"
@ -21,7 +23,11 @@
#define STUN_ERROR_CODE 0x0009
#define STUN_UNKNOWN_ATTRIBUTES 0x000a
#define STUN_XOR_MAPPED_ADDRESS 0x0020
#define STUN_PRIORITY 0x0024
#define STUN_USE_CANDIDATE 0x0025
#define STUN_FINGERPRINT 0x8028
#define STUN_ICE_CONTROLLED 0x8029
#define STUN_ICE_CONTROLLING 0x802a
#define STUN_CLASS_REQUEST 0x00
#define STUN_CLASS_INDICATION 0x01
@ -35,6 +41,8 @@
| (((method) & 0x0f80) << 2) | (((class) & 0x1) << 4) \
| (((class) & 0x2) << 7))
#define STUN_BINDING_REQUEST \
STUN_MESSAGE_TYPE(STUN_METHOD_BINDING, STUN_CLASS_REQUEST)
#define STUN_BINDING_SUCCESS_RESPONSE \
STUN_MESSAGE_TYPE(STUN_METHOD_BINDING, STUN_CLASS_SUCCESS)
#define STUN_BINDING_ERROR_RESPONSE \
@ -44,18 +52,6 @@
struct stun_attrs {
str username;
char *msg_integrity_attr;
str msg_integrity;
u_int32_t priority;
char *fingerprint_attr;
u_int32_t fingerprint;
int use:1,
controlled:1,
controlling:1;
};
struct header {
u_int16_t msg_type;
u_int16_t msg_len;
@ -94,9 +90,23 @@ struct xor_mapped_address {
u_int32_t address[4];
} __attribute__ ((packed));
struct controlled_ing {
struct tlv tlv;
u_int64_t tiebreaker;
} __attribute__ ((packed));
struct priority {
struct tlv tlv;
u_int32_t priority;
} __attribute__ ((packed));
/* XXX add const in functions */
static int stun_attributes(struct stun_attrs *out, str *s, u_int16_t *unknowns) {
static int stun_attributes(struct stun_attrs *out, str *s, u_int16_t *unknowns, struct header *req) {
struct tlv *tlv;
int len, type, uc;
str attr;
@ -129,12 +139,14 @@ static int stun_attributes(struct stun_attrs *out, str *s, u_int16_t *unknowns)
case STUN_USERNAME:
out->username = attr;
break;
case STUN_MESSAGE_INTEGRITY:
if (attr.len != 20)
return -1;
out->msg_integrity_attr = (void *) tlv;
out->msg_integrity = attr;
break;
case STUN_FINGERPRINT:
if (attr.len != 4)
return -1;
@ -142,17 +154,29 @@ static int stun_attributes(struct stun_attrs *out, str *s, u_int16_t *unknowns)
out->fingerprint = ntohl(*(u_int32_t *) attr.s);
goto out;
case 0x0025: /* use-candidate */
case STUN_USE_CANDIDATE:
out->use = 1;
break;
case 0x8029: /* ice-controlled */
case STUN_ICE_CONTROLLED:
if (out->controlling)
return -1;
if (attr.len != 8)
return -1;
out->tiebreaker = be64toh(*((u_int64_t *) attr.s));
out->controlled = 1;
break;
case 0x802a: /* ice-controlling */
case STUN_ICE_CONTROLLING:
if (out->controlled)
return -1;
if (attr.len != 8)
return -1;
out->tiebreaker = be64toh(*((u_int64_t *) attr.s));
out->controlling = 1;
break;
case 0x0024: /* priority */
case STUN_PRIORITY:
if (attr.len != 4)
return -1;
out->priority = ntohl(*((u_int32_t *) attr.s));
@ -161,6 +185,33 @@ static int stun_attributes(struct stun_attrs *out, str *s, u_int16_t *unknowns)
case 0x8022: /* software */
break; /* ignore but suppress warning message */
case STUN_XOR_MAPPED_ADDRESS:
if (attr.len < 8)
return -1;
out->mapped_port = ntohs(*((u_int16_t *) (&attr.s[2]))) ^ (STUN_COOKIE >> 16);
if (attr.len == 8 && ntohs(*((u_int16_t *) attr.s)) == 1)
in4_to_6(&out->mapped_address,
ntohl(*((u_int32_t *) (&attr.s[4]))) ^ STUN_COOKIE);
else if (attr.len == 20 && ntohs(*((u_int16_t *) attr.s)) == 1) {
out->mapped_address.s6_addr32[0]
= *((u_int32_t *) (&attr.s[4])) ^ htonl(STUN_COOKIE);
out->mapped_address.s6_addr32[1]
= *((u_int32_t *) (&attr.s[8])) ^ req->transaction[0];
out->mapped_address.s6_addr32[2]
= *((u_int32_t *) (&attr.s[12])) ^ req->transaction[1];
out->mapped_address.s6_addr32[3]
= *((u_int32_t *) (&attr.s[16])) ^ req->transaction[2];
}
break;
case STUN_ERROR_CODE:
if (attr.len < 4)
return -1;
out->error_code = ntohl(*((u_int32_t *) attr.s));
out->error_code = ((out->error_code & 0x700) >> 8) * 100
+ (out->error_code & 0x0ff);
break;
default:
ilog(LOG_NOTICE, "Unknown STUN attribute: 0x%04x", type);
if ((type & 0x8000))
@ -231,13 +282,19 @@ INLINE void __output_add(struct msghdr *mh, struct tlv *tlv, unsigned int len, u
__output_add(mh, &(attr)->tlv, sizeof(*(attr)), code, data, len)
static void output_finish(struct msghdr *mh, struct packet_stream *ps) {
static void __output_finish(struct msghdr *mh) {
struct header *hdr;
hdr = mh->msg_iov->iov_base;
hdr->msg_len = htons(hdr->msg_len);
stream_msg_mh_src(ps, mh);
}
//static void output_finish_ps(struct msghdr *mh, struct packet_stream *ps) {
// __output_finish(mh);
// stream_msg_mh_src(ps, mh);
//}
static void output_finish_src(struct msghdr *mh, const struct in6_addr *src) {
__output_finish(mh);
msg_mh_src(src, mh);
}
static void fingerprint(struct msghdr *mh, struct fingerprint *fp) {
@ -277,6 +334,9 @@ static void integrity(struct msghdr *mh, struct msg_integrity *mi, str *pwd) {
struct iovec *iov;
struct header *hdr;
if (!pwd || !pwd->s)
return;
output_add(mh, mi, STUN_MESSAGE_INTEGRITY);
iov = mh->msg_iov;
hdr = iov->iov_base;
@ -287,16 +347,18 @@ static void integrity(struct msghdr *mh, struct msg_integrity *mi, str *pwd) {
hdr->msg_len = ntohs(hdr->msg_len);
}
static void stun_error_len(struct packet_stream *ps, struct sockaddr_in6 *sin, struct header *req,
static void stun_error_len(struct packet_stream *ps, struct sockaddr_in6 *sin, struct in6_addr *dst,
struct header *req,
int code, char *reason, int len, u_int16_t add_attr, void *attr_cont,
int attr_len)
{
struct header hdr;
struct error_code ec;
struct msg_integrity mi;
struct fingerprint fp;
struct generic aa;
struct msghdr mh;
struct iovec iov[6]; /* hdr, ec, reason, aa, attr_cont, fp */
struct iovec iov[7]; /* hdr, ec, reason, aa, attr_cont, mi, fp */
unsigned char buf[256];
output_init(&mh, iov, sin, &hdr, STUN_BINDING_ERROR_RESPONSE, req->transaction, buf, sizeof(buf));
@ -306,17 +368,18 @@ static void stun_error_len(struct packet_stream *ps, struct sockaddr_in6 *sin, s
if (attr_cont)
output_add_data(&mh, &aa, add_attr, attr_cont, attr_len);
integrity(&mh, &mi, &ps->media->ice_agent->pwd[0]);
fingerprint(&mh, &fp);
output_finish(&mh, ps);
output_finish_src(&mh, dst);
sendmsg(ps->sfd->fd.fd, &mh, 0);
}
#define stun_error(ps, sin, str, code, reason) \
stun_error_len(ps, sin, str, code, reason "\0\0\0", strlen(reason), \
#define stun_error(ps, sin, dst, req, code, reason) \
stun_error_len(ps, sin, dst, req, code, reason "\0\0\0", strlen(reason), \
0, NULL, 0)
#define stun_error_attrs(ps, sin, str, code, reason, type, content, len) \
stun_error_len(ps, sin, str, code, reason "\0\0\0", strlen(reason), \
#define stun_error_attrs(ps, sin, dst, req, code, reason, type, content, len) \
stun_error_len(ps, sin, dst, req, code, reason "\0\0\0", strlen(reason), \
type, content, len)
@ -334,28 +397,35 @@ static int check_fingerprint(str *msg, struct stun_attrs *attrs) {
return 0;
}
static int check_auth(str *msg, struct stun_attrs *attrs, struct call_media *media) {
static int check_auth(str *msg, struct stun_attrs *attrs, struct call_media *media, int dst, int src) {
u_int16_t lenX;
char digest[20];
str ufrag[2];
struct iovec iov[3];
struct ice_agent *ag;
if (!media->ice_ufrag.s || !media->ice_ufrag.len)
ag = media->ice_agent;
if (!ag)
return -1;
if (!media->ice_pwd.s || !media->ice_pwd.len)
if (!ag->ufrag[dst].s || !ag->ufrag[dst].len)
return -1;
ufrag[0] = attrs->username;
str_chr_str(&ufrag[1], &ufrag[0], ':');
if (!ufrag[1].s)
if (!ag->pwd[dst].s || !ag->pwd[dst].len)
return -1;
ufrag[0].len -= ufrag[1].len;
str_shift(&ufrag[1], 1);
if (!ufrag[0].len || !ufrag[1].len)
return -1;
if (str_cmp_str(&ufrag[0], &media->ice_ufrag))
return -1;
if (attrs->username.s) {
/* request */
ufrag[dst] = attrs->username;
str_chr_str(&ufrag[src], &ufrag[dst], ':');
if (!ufrag[src].s)
return -1;
ufrag[dst].len -= ufrag[src].len;
str_shift(&ufrag[src], 1);
if (!ufrag[src].len || !ufrag[dst].len)
return -1;
if (str_cmp_str(&ufrag[dst], &ag->ufrag[dst]))
return -1;
}
lenX = htons((attrs->msg_integrity_attr - msg->s) - 20 + 24);
iov[0].iov_base = msg->s;
@ -365,13 +435,14 @@ static int check_auth(str *msg, struct stun_attrs *attrs, struct call_media *med
iov[2].iov_base = msg->s + OFFSET_OF(struct header, cookie);
iov[2].iov_len = ntohs(lenX) + - 24 + 20 - OFFSET_OF(struct header, cookie);
__integrity(iov, G_N_ELEMENTS(iov), &media->ice_pwd, digest);
__integrity(iov, G_N_ELEMENTS(iov), &ag->pwd[dst], digest);
return memcmp(digest, attrs->msg_integrity.s, 20) ? -1 : 0;
}
/* XXX way too many parameters being passed around here, unify into a struct */
static int stun_binding_success(struct packet_stream *ps, struct header *req, struct stun_attrs *attrs,
struct sockaddr_in6 *sin)
struct sockaddr_in6 *sin, struct in6_addr *dst)
{
struct header hdr;
struct xor_mapped_address xma;
@ -398,10 +469,10 @@ static int stun_binding_success(struct packet_stream *ps, struct header *req, st
output_add(&mh, &xma, STUN_XOR_MAPPED_ADDRESS);
}
integrity(&mh, &mi, &ps->media->ice_pwd);
integrity(&mh, &mi, &ps->media->ice_agent->pwd[1]);
fingerprint(&mh, &fp);
output_finish(&mh, ps);
output_finish_src(&mh, dst);
sendmsg(ps->sfd->fd.fd, &mh, 0);
return 0;
@ -415,20 +486,56 @@ INLINE int u_int16_t_arr_len(u_int16_t *arr) {
}
#define SLF " from %s"
#define SLP smart_ntop_port_buf(sin)
static int __stun_request(struct packet_stream *ps, struct sockaddr_in6 *sin,
struct in6_addr *dst, struct header *req, struct stun_attrs *attrs)
{
int ret;
ret = ice_request(ps, sin, dst, attrs);
if (ret == -2) {
ilog(LOG_DEBUG, "ICE role conflict detected");
stun_error(ps, sin, dst, req, 487, "Role conflict");
return 0;
}
if (ret < 0)
return -1;
ilog(LOG_DEBUG, "Successful STUN binding request" SLF, SLP);
stun_binding_success(ps, req, attrs, sin, dst);
return ret;
}
static int __stun_success(struct packet_stream *ps, struct sockaddr_in6 *sin,
struct in6_addr *dst, struct header *req, struct stun_attrs *attrs)
{
return ice_response(ps, sin, dst, attrs, req->transaction);
}
static int __stun_error(struct packet_stream *ps, struct sockaddr_in6 *sin,
struct in6_addr *dst, struct header *req, struct stun_attrs *attrs)
{
return ice_response(ps, sin, dst, attrs, req->transaction);
}
/* return values:
* 0 = stun packet processed successfully
* -1 = stun packet not processed, processing should continue as non-stun packet
* 1 = stun packet processed and "use candidate" was set
* 1 = stun packet processed and ICE has completed
*
* call is locked in R
*/
int stun(str *b, struct packet_stream *ps, struct sockaddr_in6 *sin) {
int stun(str *b, struct packet_stream *ps, struct sockaddr_in6 *sin, struct in6_addr *dst) {
struct header *req = (void *) b->s;
int msglen, method, class;
str attr_str;
struct stun_attrs attrs;
u_int16_t unknowns[UNKNOWNS_COUNT];
const char *err;
int dst_idx, src_idx;
msglen = ntohs(req->msg_len);
err = "message-length mismatch";
@ -446,51 +553,107 @@ int stun(str *b, struct packet_stream *ps, struct sockaddr_in6 *sin) {
attr_str.s = &b->s[20];
attr_str.len = b->len - 20;
if (stun_attributes(&attrs, &attr_str, unknowns)) {
if (stun_attributes(&attrs, &attr_str, unknowns, req)) {
err = "failed to parse attributes";
if (unknowns[0] == 0xffff)
goto ignore;
ilog(LOG_WARNING, "STUN packet contained unknown "
"\"comprehension required\" attribute(s)" SLF, SLP);
stun_error_attrs(ps, sin, req, 420, "Unknown attribute",
stun_error_attrs(ps, sin, dst, req, 420, "Unknown attribute",
STUN_UNKNOWN_ATTRIBUTES, unknowns,
u_int16_t_arr_len(unknowns) * 2);
return 0;
}
if (class != STUN_CLASS_REQUEST)
return -1;
err = "FINGERPRINT attribute missing";
if (!attrs.fingerprint_attr)
goto ignore;
err = "USERNAME attribute missing";
if (!attrs.username.s)
goto bad_req;
err = "MESSAGE_INTEGRITY attribute missing";
if (!attrs.msg_integrity.s)
goto bad_req;
if (class == STUN_CLASS_REQUEST) {
err = "USERNAME attribute missing";
if (!attrs.username.s)
goto bad_req;
dst_idx = 1;
src_idx = 0;
}
else {
dst_idx = 0;
src_idx = 1;
}
err = "FINGERPRINT mismatch";
if (check_fingerprint(b, &attrs))
goto ignore;
if (check_auth(b, &attrs, ps->media))
if (check_auth(b, &attrs, ps->media, dst_idx, src_idx))
goto unauth;
ilog(LOG_INFO, "Successful STUN binding request" SLF, SLP);
stun_binding_success(ps, req, &attrs, sin);
return attrs.use ? 1 : 0;
switch (class) {
case STUN_CLASS_REQUEST:
return __stun_request(ps, sin, dst, req, &attrs);
case STUN_CLASS_SUCCESS:
return __stun_success(ps, sin, dst, req, &attrs);
case STUN_CLASS_ERROR:
return __stun_error(ps, sin, dst, req, &attrs);
default:
return -1;
}
/* notreached */
bad_req:
ilog(LOG_NOTICE, "Received invalid STUN packet" SLF ": %s", SLP, err);
stun_error(ps, sin, req, 400, "Bad request");
stun_error(ps, sin, dst, req, 400, "Bad request");
return 0;
unauth:
ilog(LOG_NOTICE, "STUN authentication mismatch" SLF, SLP);
stun_error(ps, sin, req, 401, "Unauthorized");
stun_error(ps, sin, dst, req, 401, "Unauthorized");
return 0;
ignore:
ilog(LOG_NOTICE, "Not handling potential STUN packet" SLF ": %s", SLP, err);
return -1;
}
int stun_binding_request(struct sockaddr_in6 *dst, u_int32_t transaction[3], str *pwd,
str ufrags[2], int controlling, u_int64_t tiebreaker, u_int32_t priority,
struct in6_addr *src, int fd, int to_use)
{
struct header hdr;
struct msghdr mh;
struct iovec iov[8]; /* hdr, username x2, ice_controlled/ing, priority, uc, fp, mi */
unsigned char buf[256];
char username_buf[256];
int i;
struct generic un_attr;
struct controlled_ing cc;
struct priority prio;
struct generic uc;
struct fingerprint fp;
struct msg_integrity mi;
output_init(&mh, iov, dst, &hdr, STUN_BINDING_REQUEST, transaction, buf, sizeof(buf));
i = snprintf(username_buf, sizeof(username_buf), STR_FORMAT":"STR_FORMAT,
STR_FMT(&ufrags[0]), STR_FMT(&ufrags[1]));
if (i <= 0 || i >= sizeof(username_buf))
return -1;
output_add_data(&mh, &un_attr, STUN_USERNAME, username_buf, i);
cc.tiebreaker = htobe64(tiebreaker);
output_add(&mh, &cc, controlling ? STUN_ICE_CONTROLLING : STUN_ICE_CONTROLLED);
prio.priority = htonl(priority);
output_add(&mh, &prio, STUN_PRIORITY);
if (to_use)
output_add(&mh, &uc, STUN_USE_CANDIDATE);
integrity(&mh, &mi, pwd);
fingerprint(&mh, &fp);
output_finish_src(&mh, src);
sendmsg(fd, &mh, 0);
return 0;
}

+ 23
- 1
daemon/stun.h View File

@ -4,6 +4,7 @@
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include "compat.h"
#include "call.h"
#include "str.h"
@ -12,6 +13,24 @@
#define STUN_COOKIE 0x2112A442UL
struct stun_attrs {
str username;
char *msg_integrity_attr;
str msg_integrity;
u_int32_t priority;
char *fingerprint_attr;
u_int32_t fingerprint;
u_int64_t tiebreaker;
struct in6_addr mapped_address;
unsigned int mapped_port; /* XXX use struct endpoint */
unsigned int error_code;
int use:1,
controlled:1,
controlling:1;
};
INLINE int is_stun(const str *s) {
const unsigned char *b = (const void *) s->s;
const u_int32_t *u;
@ -30,7 +49,10 @@ INLINE int is_stun(const str *s) {
}
int stun(str *, struct packet_stream *, struct sockaddr_in6 *);
int stun(str *, struct packet_stream *, struct sockaddr_in6 *, struct in6_addr *);
int stun_binding_request(struct sockaddr_in6 *dst, u_int32_t transaction[3], str *pwd,
str ufrags[2], int controlling, u_int64_t tiebreaker, u_int32_t priority,
struct in6_addr *src, int fd, int);
#endif

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

Loading…
Cancel
Save