From 6d3865b9710402509acb0e688095ba1e2e53ac1d Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Wed, 2 Dec 2015 10:04:56 -0500 Subject: [PATCH 1/6] Mass update - manual merge of rfuchs/socket-rework branch This brings master up to date with branch `rfuchs/socket-rework` at commit `b1bcc096b7`. The branches have diverged too much for a proper merge, so this is a manual (squashed) merge. The old master before this merge can be found in branch `old-master-before-socket-rework` (commit `82199216b2`). This is a complete rewrite of all socket handling routines. The most important functional change is that sockets aren't indiscriminately bound to INADDR_ANY (or rather in6addr_any), but instead are always bound to their respective local interface address and with the correct address family. Side effects of this are that in multi-homed environments, multiple sockets must be opened (one per interface address and family) which must be taken into account when considering RLIMIT_NOFILE values. As a benefit, this change allows rtpengine to utilize the full UDP port space per interface address, instead of just one port space per machine. The socket abstraction also makes it possible to support RTP over TCP in the future. Change-Id: If6cf4f42136229490186d2d2482fb4fc140c2b53 --- daemon/.ycm_extra_conf.py | 1 + daemon/Makefile | 4 +- daemon/aux.h | 307 +------ daemon/call.c | 1798 ++++++------------------------------- daemon/call.h | 130 +-- daemon/call_interfaces.c | 62 +- daemon/call_interfaces.h | 5 +- daemon/cli.c | 52 +- daemon/cli.h | 7 +- daemon/control_ng.c | 27 +- daemon/control_ng.h | 7 +- daemon/control_tcp.c | 27 +- daemon/control_tcp.h | 3 +- daemon/control_udp.c | 39 +- daemon/control_udp.h | 5 +- daemon/dtls.c | 64 +- daemon/dtls.h | 3 +- daemon/graphite.c | 105 +-- daemon/graphite.h | 2 +- daemon/ice.c | 195 ++-- daemon/ice.h | 35 +- daemon/kernel.c | 4 +- daemon/kernel.h | 3 +- daemon/log.c | 4 +- daemon/main.c | 133 ++- daemon/media_socket.c | 1443 +++++++++++++++++++++++++++++ daemon/media_socket.h | 112 +++ daemon/redis.c | 1199 ++++++++++++++----------- daemon/redis.h | 22 +- daemon/sdp.c | 175 ++-- daemon/sdp.h | 11 +- daemon/socket.c | 550 ++++++++++++ daemon/socket.h | 224 +++++ daemon/str.h | 13 + daemon/stun.c | 138 ++- daemon/stun.h | 10 +- daemon/udp_listener.c | 41 +- daemon/udp_listener.h | 9 +- 38 files changed, 3861 insertions(+), 3108 deletions(-) create mode 100644 daemon/media_socket.c create mode 100644 daemon/media_socket.h create mode 100644 daemon/socket.c create mode 100644 daemon/socket.h diff --git a/daemon/.ycm_extra_conf.py b/daemon/.ycm_extra_conf.py index 2a2b9d4fc..a58ab6aec 100644 --- a/daemon/.ycm_extra_conf.py +++ b/daemon/.ycm_extra_conf.py @@ -22,6 +22,7 @@ flags = [ '-I../kernel-module/', '-D_GNU_SOURCE', '-D__DEBUG=1', +'-D__YCM=1', '-DRTPENGINE_VERSION="dummy"', '-DRE_PLUGIN_DIR="/usr/lib/rtpengine"', '-O2', diff --git a/daemon/Makefile b/daemon/Makefile index d8e9e8a80..0b1f1b27b 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -64,8 +64,8 @@ endif SRCS= main.c kernel.c poller.c aux.c control_tcp.c streambuf.c call.c control_udp.c redis.c \ bencode.c cookie_cache.c udp_listener.c control_ng.c sdp.c str.c stun.c rtcp.c \ - crypto.c rtp.c call_interfaces.c dtls.c log.c cli.c graphite.c ice.c rtcp_xr.c - + crypto.c rtp.c call_interfaces.c dtls.c log.c cli.c graphite.c ice.c socket.c \ + media_socket.c rtcp_xr.c OBJS= $(SRCS:.c=.o) diff --git a/daemon/aux.h b/daemon/aux.h index c155ee8e9..afbae0deb 100644 --- a/daemon/aux.h +++ b/daemon/aux.h @@ -51,7 +51,6 @@ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \ /*** HELPER MACROS ***/ -#define OFFSET_OF(t,e) ((unsigned int) (unsigned long) &(((t *) 0)->e)) #define ZERO(x) memset(&(x), 0, sizeof(x)) #define UINT64F "%" G_GUINT64_FORMAT @@ -60,16 +59,8 @@ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \ #define NUM_THREAD_BUFS 8 - - -/*** TYPES ***/ - -struct endpoint { - struct in6_addr ip46; - u_int16_t port; -}; - - +#define ALGORITHM_DEFAULT "" +#define ALGORITHM_ROUND_ROBIN_CALLS "round-robin-calls" /*** GLOBALS ***/ @@ -213,22 +204,6 @@ INLINE const char *__get_enum_array_text(const char * const *array, unsigned int -/*** SOCKET/FD HELPERS ***/ - -INLINE void nonblock(int fd) { - fcntl(fd, F_SETFL, O_NONBLOCK); -} -INLINE void reuseaddr(int fd) { - int one = 1; - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); -} -INLINE void ipv6only(int fd, int yn) { - setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yn, sizeof(yn)); -} - - - - /*** GENERIC HELPERS ***/ INLINE char chrtoupper(char x) { @@ -272,239 +247,6 @@ INLINE int rlim(int res, rlim_t val) { #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; - o->s6_addr32[2] = htonl(0xffff); - o->s6_addr32[3] = ip; -} -INLINE u_int32_t in6_to_4(const struct in6_addr *a) { - return a->s6_addr32[3]; -} - -INLINE void smart_ntop(char *o, const struct in6_addr *a, size_t len) { - const char *r; - - if (IN6_IS_ADDR_V4MAPPED(a)) - r = inet_ntop(AF_INET, &(a->s6_addr32[3]), o, len); - else - r = inet_ntop(AF_INET6, a, o, len); - - if (!r) - *o = '\0'; -} - -INLINE char *smart_ntop_buf(const struct in6_addr *a) { - char *buf = get_thread_buf(); - smart_ntop(buf, a, THREAD_BUF_SIZE); - return buf; -} - -INLINE char *smart_ntop_p(char *o, const struct in6_addr *a, size_t len) { - int l; - - if (IN6_IS_ADDR_V4MAPPED(a)) { - if (inet_ntop(AF_INET, &(a->s6_addr32[3]), o, len)) - return o + strlen(o); - *o = '\0'; - return NULL; - } - else { - *o = '['; - if (!inet_ntop(AF_INET6, a, o+1, len-2)) { - *o = '\0'; - return NULL; - } - l = strlen(o); - o[l] = ']'; - o[l+1] = '\0'; - return o + (l + 1); - } -} - -INLINE char *smart_ntop_p_buf(const struct in6_addr *a) { - char *buf = get_thread_buf(); - smart_ntop_p(buf, a, THREAD_BUF_SIZE); - return buf; -} - -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, len); - if (!e) - return; - if (len - (e - o) < 7) - return; - 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) { - char *buf = get_thread_buf(); - smart_ntop_port(buf, a, THREAD_BUF_SIZE); - 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; - - if (af == AF_INET6) { - if (src[0] == '[' && (p = strchr(src, ']'))) { - *p = '\0'; - ret = inet_pton(af, src+1, dst); - *p = ']'; - return ret; - } - } - return inet_pton(af, src, dst); -} - -INLINE int pton_46(struct in6_addr *dst, const char *src, int *family) { - u_int32_t in4; - - if (inet_pton(AF_INET6, src, dst) == 1) { - if (family) - *family = AF_INET6; - return 0; - } - in4 = inet_addr(src); - if (in4 == INADDR_NONE) - return -1; - in4_to_6(dst, in4); - if (family) - *family = AF_INET; - return 0; -} - -INLINE int parse_ip_port(u_int32_t *ip, u_int16_t *port, char *s) { - char *p = NULL; - int ret = -1; - - p = strchr(s, ':'); - if (p) { - *p++ = 0; - *ip = inet_addr(s); - if (*ip == -1) - goto out; - *port = atoi(p); - } - else { - *ip = 0; - if (strchr(s, '.')) - goto out; - *port = atoi(s); - } - if (!*port) - goto out; - - ret = 0; - -out: - if (p) - *--p = ':'; - return ret; -} - -INLINE int parse_ip6_port(struct in6_addr *ip6, u_int16_t *port, char *s) { - u_int32_t ip; - char *p; - - if (!parse_ip_port(&ip, port, s)) { - if (ip) - in4_to_6(ip6, ip); - else - *ip6 = in6addr_any; - return 0; - } - if (*s != '[') - return -1; - p = strstr(s, "]:"); - if (!p) - return -1; - *p = '\0'; - if (inet_pton(AF_INET6, s+1, ip6) != 1) - goto fail; - *p = ']'; - *port = atoi(p+2); - if (!*port) - return -1; - - return 0; - -fail: - *p = ']'; - return -1; -} - -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; -} - -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 ***/ @@ -808,30 +550,30 @@ INLINE void atomic64_local_copy_zero(atomic64 *dst, atomic64 *src) { /*** TIMEVAL FUNCTIONS ***/ -INLINE long long timeval_ms(const struct timeval *t) { +INLINE long long timeval_us(const struct timeval *t) { return (long long) ((long long) t->tv_sec * 1000000LL) + t->tv_usec; } -INLINE void timeval_from_ms(struct timeval *t, long long ms) { +INLINE void timeval_from_us(struct timeval *t, long long ms) { t->tv_sec = ms/1000000LL; t->tv_usec = ms%1000000LL; } INLINE long long timeval_diff(const struct timeval *a, const struct timeval *b) { - return timeval_ms(a) - timeval_ms(b); + return timeval_us(a) - timeval_us(b); } INLINE void timeval_subtract(struct timeval *result, const struct timeval *a, const struct timeval *b) { - timeval_from_ms(result, timeval_diff(a, b)); + timeval_from_us(result, timeval_diff(a, b)); } INLINE void timeval_multiply(struct timeval *result, const struct timeval *a, const long multiplier) { - timeval_from_ms(result, timeval_ms(a) * multiplier); + timeval_from_us(result, timeval_us(a) * multiplier); } INLINE void timeval_divide(struct timeval *result, const struct timeval *a, const long divisor) { - timeval_from_ms(result, timeval_ms(a) / divisor); + timeval_from_us(result, timeval_us(a) / divisor); } INLINE void timeval_add(struct timeval *result, const struct timeval *a, const struct timeval *b) { - timeval_from_ms(result, timeval_ms(a) + timeval_ms(b)); + timeval_from_us(result, timeval_us(a) + timeval_us(b)); } INLINE void timeval_add_usec(struct timeval *tv, long usec) { - timeval_from_ms(tv, timeval_ms(tv) + usec); + timeval_from_us(tv, timeval_us(tv) + usec); } INLINE int timeval_cmp(const struct timeval *a, const struct timeval *b) { long long diff; @@ -849,4 +591,33 @@ INLINE void timeval_lowest(struct timeval *l, const struct timeval *n) { *l = *n; } + + + +/*** ALLOC WITH UNIQUE ID HELPERS ***/ + +#define uid_slice_alloc(ptr, q) __uid_slice_alloc(sizeof(*(ptr)), q, \ + G_STRUCT_OFFSET(__typeof__(*(ptr)), unique_id)) +#define uid_slice_alloc0(ptr, q) __uid_slice_alloc0(sizeof(*(ptr)), q, \ + G_STRUCT_OFFSET(__typeof__(*(ptr)), unique_id)) +INLINE void __uid_slice_alloc_fill(void *ptr, GQueue *q, unsigned int offset) { + unsigned int *id; + id = G_STRUCT_MEMBER_P(ptr, offset); + *id = g_queue_get_length(q); + g_queue_push_tail(q, ptr); +} +INLINE void *__uid_slice_alloc(unsigned int size, GQueue *q, unsigned int offset) { + void *ret; + ret = g_slice_alloc(size); + __uid_slice_alloc_fill(ret, q, offset); + return ret; +} +INLINE void *__uid_slice_alloc0(unsigned int size, GQueue *q, unsigned int offset) { + void *ret; + ret = g_slice_alloc0(size); + __uid_slice_alloc_fill(ret, q, offset); + return ret; +} + + #endif diff --git a/daemon/call.c b/daemon/call.c index b895d1c5c..4e35a0b69 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -38,26 +38,11 @@ -#ifndef PORT_RANDOM_MIN -#define PORT_RANDOM_MIN 6 -#define PORT_RANDOM_MAX 20 -#endif - -#ifndef MAX_RECV_ITERS -#define MAX_RECV_ITERS 50 -#endif - - - - - -typedef int (*rewrite_func)(str *, struct packet_stream *); - /* also serves as array index for callstream->peers[] */ struct iterator_helper { GSList *del_timeout; GSList *del_scheduled; - struct stream_fd *ports[0x10000]; + GHashTable *addr_sfd; }; struct xmlrpc_helper { enum xmlrpc_format fmt; @@ -65,16 +50,6 @@ struct xmlrpc_helper { GSList *tags_urls; }; -struct streamhandler_io { - rewrite_func rtp; - rewrite_func rtcp; - int (*kernel)(struct rtpengine_srtp *, struct packet_stream *); -}; -struct streamhandler { - const struct streamhandler_io *in; - const struct streamhandler_io *out; -}; - const struct transport_protocol transport_protocols[] = { [PROTO_RTP_AVP] = { .index = PROTO_RTP_AVP, @@ -146,937 +121,14 @@ const char * get_tag_type_text(enum tag_type t) { return get_enum_array_text(__tag_type_texts, t, "UNKNOWN"); } -static void determine_handler(struct packet_stream *in, const struct packet_stream *out); -static void __call_media_state_machine(struct call_media *m); - -static int __k_null(struct rtpengine_srtp *s, struct packet_stream *); -static int __k_srtp_encrypt(struct rtpengine_srtp *s, struct packet_stream *); -static int __k_srtp_decrypt(struct rtpengine_srtp *s, struct packet_stream *); - -static int call_avp2savp_rtp(str *s, struct packet_stream *); -static int call_savp2avp_rtp(str *s, struct packet_stream *); -static int call_avp2savp_rtcp(str *s, struct packet_stream *); -static int call_savp2avp_rtcp(str *s, struct packet_stream *); -static int call_avpf2avp_rtcp(str *s, struct packet_stream *); -//static int call_avpf2savp_rtcp(str *s, struct packet_stream *); -static int call_savpf2avp_rtcp(str *s, struct packet_stream *); -//static int call_savpf2savp_rtcp(str *s, struct packet_stream *); - -/* ********** */ - -static const struct streamhandler_io __shio_noop = { - .kernel = __k_null, -}; -static const struct streamhandler_io __shio_decrypt = { - .kernel = __k_srtp_decrypt, - .rtp = call_savp2avp_rtp, - .rtcp = call_savp2avp_rtcp, -}; -static const struct streamhandler_io __shio_encrypt = { - .kernel = __k_srtp_encrypt, - .rtp = call_avp2savp_rtp, - .rtcp = call_avp2savp_rtcp, -}; -static const struct streamhandler_io __shio_avpf_strip = { - .kernel = __k_null, - .rtcp = call_avpf2avp_rtcp, -}; -static const struct streamhandler_io __shio_decrypt_avpf_strip = { - .kernel = __k_srtp_decrypt, - .rtp = call_savp2avp_rtp, - .rtcp = call_savpf2avp_rtcp, -}; - -/* ********** */ - -static const struct streamhandler __sh_noop = { - .in = &__shio_noop, - .out = &__shio_noop, -}; -static const struct streamhandler __sh_savp2avp = { - .in = &__shio_decrypt, - .out = &__shio_noop, -}; -static const struct streamhandler __sh_avp2savp = { - .in = &__shio_noop, - .out = &__shio_encrypt, -}; -static const struct streamhandler __sh_avpf2avp = { - .in = &__shio_avpf_strip, - .out = &__shio_noop, -}; -static const struct streamhandler __sh_avpf2savp = { - .in = &__shio_avpf_strip, - .out = &__shio_encrypt, -}; -static const struct streamhandler __sh_savpf2avp = { - .in = &__shio_decrypt_avpf_strip, - .out = &__shio_noop, -}; -static const struct streamhandler __sh_savp2savp = { - .in = &__shio_decrypt, - .out = &__shio_encrypt, -}; -static const struct streamhandler __sh_savpf2savp = { - .in = &__shio_decrypt_avpf_strip, - .out = &__shio_encrypt, -}; - -/* ********** */ - -static const struct streamhandler *__sh_matrix_in_rtp_avp[] = { - [PROTO_RTP_AVP] = &__sh_noop, - [PROTO_RTP_AVPF] = &__sh_noop, - [PROTO_RTP_SAVP] = &__sh_avp2savp, - [PROTO_RTP_SAVPF] = &__sh_avp2savp, - [PROTO_UDP_TLS_RTP_SAVP] = &__sh_avp2savp, - [PROTO_UDP_TLS_RTP_SAVPF] = &__sh_avp2savp, - [PROTO_UDPTL] = &__sh_noop, -}; -static const struct streamhandler *__sh_matrix_in_rtp_avpf[] = { - [PROTO_RTP_AVP] = &__sh_avpf2avp, - [PROTO_RTP_AVPF] = &__sh_noop, - [PROTO_RTP_SAVP] = &__sh_avpf2savp, - [PROTO_RTP_SAVPF] = &__sh_avp2savp, - [PROTO_UDP_TLS_RTP_SAVP] = &__sh_avpf2savp, - [PROTO_UDP_TLS_RTP_SAVPF] = &__sh_avp2savp, - [PROTO_UDPTL] = &__sh_noop, -}; -static const struct streamhandler *__sh_matrix_in_rtp_savp[] = { - [PROTO_RTP_AVP] = &__sh_savp2avp, - [PROTO_RTP_AVPF] = &__sh_savp2avp, - [PROTO_RTP_SAVP] = &__sh_noop, - [PROTO_RTP_SAVPF] = &__sh_noop, - [PROTO_UDP_TLS_RTP_SAVP] = &__sh_noop, - [PROTO_UDP_TLS_RTP_SAVPF] = &__sh_noop, - [PROTO_UDPTL] = &__sh_noop, -}; -static const struct streamhandler *__sh_matrix_in_rtp_savpf[] = { - [PROTO_RTP_AVP] = &__sh_savpf2avp, - [PROTO_RTP_AVPF] = &__sh_savp2avp, - [PROTO_RTP_SAVP] = &__sh_savpf2savp, - [PROTO_RTP_SAVPF] = &__sh_noop, - [PROTO_UDP_TLS_RTP_SAVP] = &__sh_savpf2savp, - [PROTO_UDP_TLS_RTP_SAVPF] = &__sh_noop, - [PROTO_UDPTL] = &__sh_noop, -}; -static const struct streamhandler *__sh_matrix_in_rtp_savp_recrypt[] = { - [PROTO_RTP_AVP] = &__sh_savp2avp, - [PROTO_RTP_AVPF] = &__sh_savp2avp, - [PROTO_RTP_SAVP] = &__sh_savp2savp, - [PROTO_RTP_SAVPF] = &__sh_savp2savp, - [PROTO_UDP_TLS_RTP_SAVP] = &__sh_savp2savp, - [PROTO_UDP_TLS_RTP_SAVPF] = &__sh_savp2savp, - [PROTO_UDPTL] = &__sh_noop, -}; -static const struct streamhandler *__sh_matrix_in_rtp_savpf_recrypt[] = { - [PROTO_RTP_AVP] = &__sh_savpf2avp, - [PROTO_RTP_AVPF] = &__sh_savp2avp, - [PROTO_RTP_SAVP] = &__sh_savpf2savp, - [PROTO_RTP_SAVPF] = &__sh_savp2savp, - [PROTO_UDP_TLS_RTP_SAVP] = &__sh_savpf2savp, - [PROTO_UDP_TLS_RTP_SAVPF] = &__sh_savp2savp, - [PROTO_UDPTL] = &__sh_noop, -}; -static const struct streamhandler *__sh_matrix_noop[] = { - [PROTO_RTP_AVP] = &__sh_noop, - [PROTO_RTP_AVPF] = &__sh_noop, - [PROTO_RTP_SAVP] = &__sh_noop, - [PROTO_RTP_SAVPF] = &__sh_noop, - [PROTO_UDP_TLS_RTP_SAVP] = &__sh_noop, - [PROTO_UDP_TLS_RTP_SAVPF] = &__sh_noop, - [PROTO_UDPTL] = &__sh_noop, -}; - -/* ********** */ - -static const struct streamhandler **__sh_matrix[] = { - [PROTO_RTP_AVP] = __sh_matrix_in_rtp_avp, - [PROTO_RTP_AVPF] = __sh_matrix_in_rtp_avpf, - [PROTO_RTP_SAVP] = __sh_matrix_in_rtp_savp, - [PROTO_RTP_SAVPF] = __sh_matrix_in_rtp_savpf, - [PROTO_UDP_TLS_RTP_SAVP] = __sh_matrix_in_rtp_savp, - [PROTO_UDP_TLS_RTP_SAVPF] = __sh_matrix_in_rtp_savpf, - [PROTO_UDPTL] = __sh_matrix_noop, -}; -/* special case for DTLS as we can't pass through SRTP<>SRTP */ -static const struct streamhandler **__sh_matrix_recrypt[] = { - [PROTO_RTP_AVP] = __sh_matrix_in_rtp_avp, - [PROTO_RTP_AVPF] = __sh_matrix_in_rtp_avpf, - [PROTO_RTP_SAVP] = __sh_matrix_in_rtp_savp_recrypt, - [PROTO_RTP_SAVPF] = __sh_matrix_in_rtp_savpf_recrypt, - [PROTO_UDP_TLS_RTP_SAVP] = __sh_matrix_in_rtp_savp_recrypt, - [PROTO_UDP_TLS_RTP_SAVPF] = __sh_matrix_in_rtp_savpf_recrypt, - [PROTO_UDPTL] = __sh_matrix_noop, -}; /* ********** */ -static const struct rtpengine_srtp __res_null = { - .cipher = REC_NULL, - .hmac = REH_NULL, -}; - - - -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 int monologue_destroy(struct call_monologue *ml); -static struct interface_address *get_interface_address(struct local_interface *lif, int family); - - - - -/* called lock-free */ -static void stream_fd_closed(int fd, void *p, uintptr_t u) { - struct stream_fd *sfd = p; - struct call *c; - int i; - socklen_t j; - - assert(sfd->fd.fd == fd); - c = sfd->call; - if (!c) - return; - - j = sizeof(i); - getsockopt(fd, SOL_SOCKET, SO_ERROR, &i, &j); - ilog(LOG_WARNING, "Read error on media socket: %i (%s) -- closing call", i, strerror(i)); - - call_destroy(c); -} - - - -INLINE int _redis_id_generate(struct call *c, rc_type id_type) { - return c->rc.val[id_type]++; -} - -INLINE void redis_hkey_generate(char *out, struct call *c, rc_type rc_kind) { - snprintf(out, MAX_REDIS_HKEY_SIZE, "%.*s_%d", c->callid.len, c->callid.s, - _redis_id_generate(c, rc_kind)); -} - -INLINE void __re_address_translate(struct re_address *o, const struct endpoint *ep) { - o->family = family_from_address(&ep->ip46); - if (o->family == AF_INET) - o->u.ipv4 = in6_to_4(&ep->ip46); - else - memcpy(o->u.ipv6, &ep->ip46, sizeof(o->u.ipv6)); - o->port = ep->port; -} - -static int __rtp_stats_pt_sort(const void *ap, const void *bp) { - const struct rtp_stats *a = ap, *b = bp; - - if (a->payload_type < b->payload_type) - return -1; - if (a->payload_type > b->payload_type) - return 1; - return 0; -} - -/* called with in_lock held */ -void kernelize(struct packet_stream *stream) { - struct rtpengine_target_info reti; - struct call *call = stream->call; - struct callmaster *cm = call->callmaster; - struct packet_stream *sink = NULL; - struct interface_address *ifa; - const char *nk_warn_msg; - - if (PS_ISSET(stream, KERNELIZED)) - return; - if (cm->conf.kernelid < 0) - goto no_kernel; - nk_warn_msg = "interface to kernel module not open"; - if (cm->conf.kernelfd < 0) - goto no_kernel_warn; - if (!PS_ISSET(stream, RTP)) - goto no_kernel; - if (!stream->sfd) - goto no_kernel; - - ilog(LOG_INFO, "Kernelizing media stream: %s:%d", smart_ntop_p_buf(&stream->endpoint.ip46), stream->endpoint.port); - - sink = packet_stream_sink(stream); - if (!sink) { - ilog(LOG_WARNING, "Attempt to kernelize stream without sink"); - goto no_kernel; - } - - determine_handler(stream, sink); - - if (is_addr_unspecified(&sink->advertised_endpoint.ip46) - || !sink->advertised_endpoint.port) - goto no_kernel; - nk_warn_msg = "protocol not supported by kernel module"; - if (!stream->handler->in->kernel - || !stream->handler->out->kernel) - goto no_kernel_warn; - - ZERO(reti); - - 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); - if (PS_ISSET(stream, STRICT_SOURCE)) - reti.src_mismatch = MSM_DROP; - else if (PS_ISSET(stream, MEDIA_HANDOVER)) - reti.src_mismatch = MSM_PROPAGATE; - } - - mutex_lock(&sink->out_lock); - - reti.target_port = stream->sfd->fd.localport; - reti.tos = call->tos; - reti.rtcp_mux = MEDIA_ISSET(stream->media, RTCP_MUX); - reti.dtls = MEDIA_ISSET(stream->media, DTLS); - reti.stun = stream->media->ice_agent ? 1 : 0; - - __re_address_translate(&reti.dst_addr, &sink->endpoint); - - reti.src_addr.family = reti.dst_addr.family; - reti.src_addr.port = sink->sfd->fd.localport; - reti.ssrc = sink->crypto.ssrc; - - ifa = g_atomic_pointer_get(&sink->media->local_address); - if (reti.src_addr.family == AF_INET) - reti.src_addr.u.ipv4 = in6_to_4(&ifa->addr); - else - memcpy(reti.src_addr.u.ipv6, &ifa->addr, sizeof(reti.src_addr.u.ipv6)); - - stream->handler->in->kernel(&reti.decrypt, stream); - stream->handler->out->kernel(&reti.encrypt, sink); - - mutex_unlock(&sink->out_lock); - - nk_warn_msg = "encryption cipher or HMAC not supported by kernel module"; - if (!reti.encrypt.cipher || !reti.encrypt.hmac) - goto no_kernel_warn; - nk_warn_msg = "decryption cipher or HMAC not supported by kernel module"; - if (!reti.decrypt.cipher || !reti.decrypt.hmac) - goto no_kernel_warn; - - ZERO(stream->kernel_stats); - - if (stream->media->protocol && stream->media->protocol->rtp) { - GList *values, *l; - struct rtp_stats *rs; - - reti.rtp = 1; - values = g_hash_table_get_values(stream->rtp_stats); - values = g_list_sort(values, __rtp_stats_pt_sort); - for (l = values; l; l = l->next) { - if (reti.num_payload_types >= G_N_ELEMENTS(reti.payload_types)) { - ilog(LOG_WARNING, "Too many RTP payload types for kernel module"); - break; - } - rs = l->data; - reti.payload_types[reti.num_payload_types++] = rs->payload_type; - } - g_list_free(values); - } - - kernel_add_stream(cm->conf.kernelfd, &reti, 0); - PS_SET(stream, KERNELIZED); - - return; - -no_kernel_warn: - ilog(LOG_WARNING, "No support for kernel packet forwarding available (%s)", nk_warn_msg); -no_kernel: - PS_SET(stream, KERNELIZED); - PS_SET(stream, NO_KERNEL_SUPPORT); -} - - - - -/* returns: 0 = not a muxed stream, 1 = muxed, RTP, 2 = muxed, RTCP */ -static int rtcp_demux(str *s, struct call_media *media) { - if (!MEDIA_ISSET(media, RTCP_MUX)) - return 0; - return rtcp_demux_is_rtcp(s) ? 2 : 1; -} - -static int call_avpf2avp_rtcp(str *s, struct packet_stream *stream) { - return rtcp_avpf2avp(s); -} -static int call_avp2savp_rtp(str *s, struct packet_stream *stream) { - return rtp_avp2savp(s, &stream->crypto); -} -static int call_avp2savp_rtcp(str *s, struct packet_stream *stream) { - return rtcp_avp2savp(s, &stream->crypto); -} -static int call_savp2avp_rtp(str *s, struct packet_stream *stream) { - return rtp_savp2avp(s, &stream->sfd->crypto); -} -static int call_savp2avp_rtcp(str *s, struct packet_stream *stream) { - return rtcp_savp2avp(s, &stream->sfd->crypto); -} -static int call_savpf2avp_rtcp(str *s, struct packet_stream *stream) { - int ret; - ret = rtcp_savp2avp(s, &stream->sfd->crypto); - if (ret < 0) - return ret; - return rtcp_avpf2avp(s); -} - - -static int __k_null(struct rtpengine_srtp *s, struct packet_stream *stream) { - *s = __res_null; - return 0; -} -static int __k_srtp_crypt(struct rtpengine_srtp *s, struct crypto_context *c) { - if (!c->params.crypto_suite) - return -1; - - *s = (struct rtpengine_srtp) { - .cipher = c->params.crypto_suite->kernel_cipher, - .hmac = c->params.crypto_suite->kernel_hmac, - .mki_len = c->params.mki_len, - .last_index = c->last_index, - .auth_tag_len = c->params.crypto_suite->srtp_auth_tag, - }; - if (c->params.mki_len) - memcpy(s->mki, c->params.mki, c->params.mki_len); - memcpy(s->master_key, c->params.master_key, c->params.crypto_suite->master_key_len); - memcpy(s->master_salt, c->params.master_salt, c->params.crypto_suite->master_salt_len); - - if (c->params.session_params.unencrypted_srtp) - s->cipher = REC_NULL; - if (c->params.session_params.unauthenticated_srtp) - s->auth_tag_len = 0; - - return 0; -} -static int __k_srtp_encrypt(struct rtpengine_srtp *s, struct packet_stream *stream) { - return __k_srtp_crypt(s, &stream->crypto); -} -static int __k_srtp_decrypt(struct rtpengine_srtp *s, struct packet_stream *stream) { - return __k_srtp_crypt(s, &stream->sfd->crypto); -} - -/* must be called with call->master_lock held in R, and in->in_lock held */ -static void determine_handler(struct packet_stream *in, const struct packet_stream *out) { - const struct streamhandler **sh_pp, *sh; - const struct streamhandler ***matrix; - - if (in->handler) - return; - if (MEDIA_ISSET(in->media, PASSTHRU)) - goto noop; - - if (!in->media->protocol) - goto err; - if (!out->media->protocol) - goto err; - - matrix = __sh_matrix; - if (MEDIA_ISSET(in->media, DTLS) || MEDIA_ISSET(out->media, DTLS)) - matrix = __sh_matrix_recrypt; - else if (in->media->protocol->srtp && out->media->protocol->srtp - && in->sfd && out->sfd - && (crypto_params_cmp(&in->crypto.params, &out->sfd->crypto.params) - || crypto_params_cmp(&out->crypto.params, &in->sfd->crypto.params))) - matrix = __sh_matrix_recrypt; - - sh_pp = matrix[in->media->protocol->index]; - if (!sh_pp) - goto err; - sh = sh_pp[out->media->protocol->index]; - if (!sh) - goto err; - in->handler = sh; - - return; - -err: - ilog(LOG_WARNING, "Unknown transport protocol encountered"); -noop: - in->handler = &__sh_noop; - return; -} - -void stream_msg_mh_src(struct packet_stream *ps, struct msghdr *mh) { - struct interface_address *ifa; - - ifa = g_atomic_pointer_get(&ps->media->local_address); - msg_mh_src(&ifa->addr, mh); -} - -/* XXX split this function into pieces */ -/* called lock-free */ -static int stream_packet(struct stream_fd *sfd, str *s, struct sockaddr_in6 *fsin, struct in6_addr *dst) { - struct packet_stream *stream, - *sink = NULL, - *in_srtp, *out_srtp; - struct call_media *media; - int ret = 0, update = 0, stun_ret = 0, handler_ret = 0, muxed_rtcp = 0, rtcp = 0, - unk = 0; - int i; - struct sockaddr_in6 sin6; - struct msghdr mh; - struct iovec iov; - unsigned char buf[256]; - struct call *call; - struct callmaster *cm; - /*unsigned char cc;*/ - char *addr; - struct endpoint endpoint; - rewrite_func rwf_in, rwf_out; - struct interface_address *loc_addr; - struct rtp_header *rtp_h; - struct rtp_stats *rtp_s; - - call = sfd->call; - cm = call->callmaster; - addr = smart_ntop_port_buf(fsin); - - rwlock_lock_r(&call->master_lock); - - stream = sfd->stream; - if (!stream) - goto unlock_out; - - - media = stream->media; - - if (!stream->sfd) - 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 unlock_out; - } - - if (media->ice_agent && is_stun(s)) { - stun_ret = stun(s, stream, fsin, dst); - if (!stun_ret) - goto unlock_out; - if (stun_ret == 1) { - __call_media_state_machine(media); - 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; - if (memcmp(stream->lp_buf[i].buf, s->s, MIN(s->len, RTP_LOOP_PROTECT))) - continue; - - __C_DBG("packet dupe"); - if (stream->lp_count >= RTP_LOOP_MAX_COUNT) { - ilog(LOG_WARNING, "More than %d duplicate packets detected, dropping packet " - "to avoid potential loop", RTP_LOOP_MAX_COUNT); - goto done; - } - - stream->lp_count++; - goto loop_ok; - } - - /* not a dupe */ - stream->lp_count = 0; - stream->lp_buf[stream->lp_idx].len = s->len; - 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: - mutex_unlock(&stream->in_lock); -#endif - - - /* demux RTCP */ - - in_srtp = stream; - sink = stream->rtp_sink; - if (!sink && PS_ISSET(stream, RTCP)) { - sink = stream->rtcp_sink; - rtcp = 1; - } - else if (stream->rtcp_sink) { - muxed_rtcp = rtcp_demux(s, media); - if (muxed_rtcp == 2) { - sink = stream->rtcp_sink; - rtcp = 1; - in_srtp = stream->rtcp_sibling; - } - } - out_srtp = sink; - if (rtcp && sink && sink->rtcp_sibling) - out_srtp = sink->rtcp_sibling; - - - /* stats per RTP payload type */ - - if (media->protocol && media->protocol->rtp && !rtcp && !rtp_payload(&rtp_h, NULL, s)) { - i = (rtp_h->m_pt & 0x7f); - - rtp_s = g_hash_table_lookup(stream->rtp_stats, &i); - if (!rtp_s) { - ilog(LOG_WARNING | LOG_FLAG_LIMIT, - "RTP packet with unknown payload type %u received", i); - atomic64_inc(&stream->stats.errors); - atomic64_inc(&cm->statsps.errors); - } - - else { - atomic64_inc(&rtp_s->packets); - atomic64_add(&rtp_s->bytes, s->len); - } - } - - - /* do we have somewhere to forward it to? */ - - if (!sink || !sink->sfd || !out_srtp->sfd || !in_srtp->sfd) { - ilog(LOG_WARNING, "RTP packet from %s discarded", addr); - atomic64_inc(&stream->stats.errors); - atomic64_inc(&cm->statsps.errors); - goto unlock_out; - } - - - /* transcoding stuff, in and out */ - - mutex_lock(&in_srtp->in_lock); - - determine_handler(in_srtp, sink); - - if (!rtcp) { - rwf_in = in_srtp->handler->in->rtp; - rwf_out = in_srtp->handler->out->rtp; - } - else { - rwf_in = in_srtp->handler->in->rtcp; - rwf_out = in_srtp->handler->out->rtcp; - } - - mutex_lock(&out_srtp->out_lock); - - /* return values are: 0 = forward packet, -1 = error/dont forward, - * 1 = forward and push update to redis and kernel */ - if (rwf_in) - handler_ret = rwf_in(s, in_srtp); - if (handler_ret >= 0) { - if (rtcp && _log_facility_rtcp) - parse_and_log_rtcp_report(sfd, s->s, s->len); - if (rwf_out) - handler_ret += rwf_out(s, out_srtp); - } - - if (handler_ret > 0) { - __unkernelize(stream); - update = 1; - } - - mutex_unlock(&out_srtp->out_lock); - mutex_unlock(&in_srtp->in_lock); - - - /* endpoint address handling */ - - mutex_lock(&stream->in_lock); - - /* 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 */ - if (!PS_ISSET(stream, FILLED)) - goto forward; - - /* do not pay attention to source addresses of incoming packets for asymmetric streams */ - if (MEDIA_ISSET(media, ASYMMETRIC)) - PS_SET(stream, CONFIRMED); - - /* 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_ISSET2(stream, STRICT_SOURCE, MEDIA_HANDOVER)) { - memset(&endpoint, 0, sizeof(endpoint)); - endpoint.ip46 = fsin->sin6_addr; - endpoint.port = ntohs(fsin->sin6_port); - mutex_lock(&stream->out_lock); - - int tmp = memcmp(&endpoint, &stream->endpoint, sizeof(endpoint)); - if (tmp && PS_ISSET(stream, MEDIA_HANDOVER)) { - /* out_lock remains locked */ - ilog(LOG_INFO, "Peer address changed to %s", addr); - unk = 1; - goto update_addr; - } - - mutex_unlock(&stream->out_lock); - - if (tmp && PS_ISSET(stream, STRICT_SOURCE)) { - ilog(LOG_INFO, "Drop due to strict-source attribute; got endpoint: %s:%d, expected stream_endpoint: %s:%d", - smart_ntop_p_buf(&endpoint.ip46), endpoint.port, - smart_ntop_p_buf(&stream->endpoint.ip46), stream->endpoint.port); - atomic64_inc(&stream->stats.errors); - goto drop; - } - } - goto kernel_check; - } - - /* wait at least 3 seconds after last signal before committing to a particular - * endpoint address */ - if (!call->last_signal || poller_now <= call->last_signal + 3) - goto update_peerinfo; - - ilog(LOG_INFO, "Confirmed peer address as %s", addr); - - PS_SET(stream, CONFIRMED); - update = 1; - -update_peerinfo: - mutex_lock(&stream->out_lock); -update_addr: - endpoint = stream->endpoint; - stream->endpoint.ip46 = fsin->sin6_addr; - stream->endpoint.port = ntohs(fsin->sin6_port); - if (memcmp(&endpoint, &stream->endpoint, sizeof(endpoint))) - update = 1; - mutex_unlock(&stream->out_lock); - - /* check the destination address of the received packet against what we think our - * local interface to use is */ - loc_addr = g_atomic_pointer_get(&media->local_address); - if (dst && memcmp(dst, &loc_addr->addr, sizeof(*dst))) { - struct interface_address *ifa; - ifa = get_interface_from_address(media->interface, dst); - if (!ifa) { - ilog(LOG_ERROR, "No matching local interface for destination address %s found", - smart_ntop_buf(dst)); - goto drop; - } - if (g_atomic_pointer_compare_and_exchange(&media->local_address, loc_addr, ifa)) { - ilog(LOG_INFO, "Switching local interface to %s", - smart_ntop_buf(dst)); - update = 1; - } - } - - -kernel_check: - if (PS_ISSET(stream, NO_KERNEL_SUPPORT)) { - __C_DBG("No kernel support found for stream: %s:%d", smart_ntop_p_buf(&stream->endpoint.ip46), stream->endpoint.port); - goto forward; - } - - if (!PS_ISSET(stream, CONFIRMED)) { - __C_DBG("stream %s:%d not CONFIRMED", smart_ntop_p_buf(&stream->endpoint.ip46), stream->endpoint.port); - goto forward; - } - - if (!sink) { - __C_DBG("sink is NULL for stream %s:%d", smart_ntop_p_buf(&stream->endpoint.ip46), stream->endpoint.port); - goto forward; - } - - if (!PS_ISSET(sink, CONFIRMED)) { - __C_DBG("sink not CONFIRMED for stream %s:%d", smart_ntop_p_buf(&stream->endpoint.ip46), stream->endpoint.port); - goto forward; - } - - if (!PS_ISSET(sink, FILLED)) { - __C_DBG("sink not FILLED for stream %s:%d", smart_ntop_p_buf(&stream->endpoint.ip46), stream->endpoint.port); - goto forward; - } - - kernelize(stream); - -forward: - if (sink) - mutex_lock(&sink->out_lock); - - if (!sink - || !sink->advertised_endpoint.port - || (is_addr_unspecified(&sink->advertised_endpoint.ip46) - && !is_trickle_ice_address(&sink->advertised_endpoint)) - || stun_ret || handler_ret < 0) - goto drop; - - ZERO(mh); - mh.msg_control = buf; - mh.msg_controllen = sizeof(buf); - - ZERO(sin6); - sin6.sin6_family = AF_INET6; - sin6.sin6_addr = sink->endpoint.ip46; - sin6.sin6_port = htons(sink->endpoint.port); - mh.msg_name = &sin6; - mh.msg_namelen = sizeof(sin6); - - mutex_unlock(&sink->out_lock); - - stream_msg_mh_src(sink, &mh); - - ZERO(iov); - iov.iov_base = s->s; - iov.iov_len = s->len; - - mh.msg_iov = &iov; - mh.msg_iovlen = 1; - - ret = sendmsg(sink->sfd->fd.fd, &mh, 0); - __C_DBG("Forward to sink endpoint: %s:%d", smart_ntop_p_buf(&sink->endpoint.ip46), sink->endpoint.port); - - if (ret == -1) { - ret = 0; /* temp for address family mismatches */ - ilog(LOG_DEBUG,"Error when sending message. Error: %s",strerror(errno)); - atomic64_inc(&stream->stats.errors); - atomic64_inc(&cm->statsps.errors); - goto out; - } - - sink = NULL; - -drop: - if (sink) - mutex_unlock(&sink->out_lock); - ret = 0; - atomic64_inc(&stream->stats.packets); - atomic64_add(&stream->stats.bytes, s->len); - atomic64_set(&stream->last_packet, poller_now); - atomic64_inc(&cm->statsps.packets); - atomic64_add(&cm->statsps.bytes, s->len); - -out: - if (ret == 0 && update) - ret = 1; - -done: - if (unk) - __stream_unconfirm(stream); - mutex_unlock(&stream->in_lock); - if (unk) { - stream_unconfirm(stream->rtp_sink); - stream_unconfirm(stream->rtcp_sink); - } -unlock_out: - rwlock_unlock_r(&call->master_lock); - - return ret; -} - - - - -static void stream_fd_readable(int fd, void *p, uintptr_t u) { - struct stream_fd *sfd = p; - char buf[RTP_BUFFER_SIZE]; - int ret, iters; - struct sockaddr_in6 sin6_src; - int update = 0; - struct call *ca; - str s; - struct msghdr mh; - struct iovec iov; - char control[128]; - struct cmsghdr *cmh; - struct in6_pktinfo *pi6; - struct in6_addr dst_buf, *dst; - struct in_pktinfo *pi; - - if (sfd->fd.fd != fd) - goto out; - - log_info_stream_fd(sfd); - - for (iters = 0; ; iters++) { -#if MAX_RECV_ITERS - if (iters >= MAX_RECV_ITERS) { - ilog(LOG_ERROR, "Too many packets in UDP receive queue (more than %d), " - "aborting loop. Dropped packets possible", iters); - break; - } -#endif - - ZERO(mh); - mh.msg_name = &sin6_src; - mh.msg_namelen = sizeof(sin6_src); - mh.msg_iov = &iov; - mh.msg_iovlen = 1; - mh.msg_control = control; - mh.msg_controllen = sizeof(control); - iov.iov_base = buf + RTP_BUFFER_HEAD_ROOM; - iov.iov_len = MAX_RTP_PACKET_SIZE; - - ret = recvmsg(fd, &mh, 0); - - if (ret < 0) { - if (errno == EINTR) - continue; - if (errno == EAGAIN || errno == EWOULDBLOCK) - break; - stream_fd_closed(fd, sfd, 0); - goto done; - } - if (ret >= MAX_RTP_PACKET_SIZE) - ilog(LOG_WARNING, "UDP packet possibly truncated"); - - 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); - goto done; - } - if (ret == 1) - update = 1; - } - -out: - ca = sfd->call ? : NULL; - - if (ca && update) { - if (sfd->call->callmaster->conf.redis_write) { - redis_update(ca, sfd->call->callmaster->conf.redis_write, ANY_REDIS_ROLE); - } else if (sfd->call->callmaster->conf.redis) { - redis_update(ca, sfd->call->callmaster->conf.redis, MASTER_REDIS_ROLE); - } - } -done: - log_info_clear(); -} @@ -1084,7 +136,7 @@ done: /* called with call->master_lock held in R */ static int call_timer_delete_monologues(struct call *c) { - GSList *i; + GList *i; struct call_monologue *ml; int ret = 0; time_t min_deleted = 0; @@ -1093,7 +145,7 @@ static int call_timer_delete_monologues(struct call *c) { rwlock_unlock_r(&c->master_lock); rwlock_lock_w(&c->master_lock); - for (i = c->monologues; i; i = i->next) { + for (i = c->monologues.head; i; i = i->next) { ml = i->data; if (!ml->deleted) @@ -1125,7 +177,7 @@ out: static void call_timer_iterator(void *key, void *val, void *ptr) { struct call *c = val; struct iterator_helper *hlp = ptr; - GSList *it; + GList *it; struct callmaster *cm; unsigned int check; int good = 0; @@ -1133,7 +185,6 @@ static void call_timer_iterator(void *key, void *val, void *ptr) { struct stream_fd *sfd; int tmp_t_reason=0; struct call_monologue *ml; - GSList *i; enum call_stream_state css; atomic64 *timestamp; @@ -1151,17 +202,17 @@ static void call_timer_iterator(void *key, void *val, void *ptr) { goto delete; } - if (!c->streams) + if (!c->streams.head) goto drop; - for (it = c->streams; it; it = it->next) { + for (it = c->streams.head; it; it = it->next) { ps = it->data; timestamp = &ps->last_packet; if (!ps->media) goto next; - sfd = ps->sfd; + sfd = ps->selected_sfd; if (!sfd) goto no_sfd; @@ -1172,10 +223,9 @@ static void call_timer_iterator(void *key, void *val, void *ptr) { if (css == CSS_ICE) timestamp = &ps->media->ice_agent->last_activity; - if (hlp->ports[sfd->fd.localport]) + if (g_hash_table_contains(hlp->addr_sfd, &sfd->socket.local)) goto next; - hlp->ports[sfd->fd.localport] = sfd; - obj_hold(sfd); + g_hash_table_insert(hlp->addr_sfd, &sfd->socket.local, obj_get(sfd)); no_sfd: if (good) @@ -1201,8 +251,8 @@ next: if (c->ml_deleted) goto out; - for (i = c->monologues; i; i = i->next) { - ml = i->data; + for (it = c->monologues.head; it; it = it->next) { + ml = it->data; gettimeofday(&(ml->terminated),NULL); if (tmp_t_reason==1) { ml->term_reason = TIMEOUT; @@ -1324,7 +374,7 @@ fault: void kill_calls_timer(GSList *list, struct callmaster *m) { struct call *ca; - GSList *csl; + GList *csl; struct call_monologue *cm; const char *url, *url_prefix, *url_suffix; struct xmlrpc_helper *xh = NULL; @@ -1360,7 +410,7 @@ void kill_calls_timer(GSList *list, struct callmaster *m) { if (url_prefix) { snprintf(url_buf, sizeof(url_buf), "%s%s%s", - url_prefix, smart_ntop_p_buf(&ca->created_from_addr.sin6_addr), + url_prefix, sockaddr_print_buf(&ca->created_from_addr), url_suffix); } else @@ -1368,7 +418,7 @@ void kill_calls_timer(GSList *list, struct callmaster *m) { switch (m->conf.fmt) { case XF_SEMS: - for (csl = ca->monologues; csl; csl = csl->next) { + for (csl = ca->monologues.head; csl; csl = csl->next) { cm = csl->data; if (cm->tag.s && cm->tag.len) { xh->tags_urls = g_slist_prepend(xh->tags_urls, g_string_chunk_insert(xh->c, url_buf)); @@ -1409,7 +459,7 @@ destroy: static void callmaster_timer(void *ptr) { struct callmaster *m = ptr; struct iterator_helper hlp; - GList *i; + GList *i, *l; struct rtpengine_list_entry *ke; struct packet_stream *ps, *sink; struct stats tmpstats; @@ -1417,8 +467,10 @@ static void callmaster_timer(void *ptr) { struct stream_fd *sfd; struct rtp_stats *rs; unsigned int pt; + endpoint_t ep; ZERO(hlp); + hlp.addr_sfd = g_hash_table_new(g_endpoint_hash, g_endpoint_eq); rwlock_lock_r(&m->hashlock); g_hash_table_foreach(m->callhash, call_timer_iterator, &hlp); @@ -1436,14 +488,15 @@ static void callmaster_timer(void *ptr) { while (i) { ke = i->data; - sfd = hlp.ports[ke->target.target_port]; + kernel2endpoint(&ep, &ke->target.local); + sfd = g_hash_table_lookup(hlp.addr_sfd, &ep); if (!sfd) goto next; rwlock_lock_r(&sfd->call->master_lock); ps = sfd->stream; - if (!ps || ps->sfd != sfd) { + if (!ps || ps->selected_sfd != sfd) { rwlock_unlock_r(&sfd->call->master_lock); goto next; } @@ -1522,16 +575,18 @@ static void callmaster_timer(void *ptr) { } next: - hlp.ports[ke->target.target_port] = NULL; + g_hash_table_remove(hlp.addr_sfd, &ep); g_slice_free1(sizeof(*ke), ke); i = g_list_delete_link(i, i); if (sfd) obj_put(sfd); } - for (j = 0; j < (sizeof(hlp.ports) / sizeof(*hlp.ports)); j++) - if (hlp.ports[j]) - obj_put(hlp.ports[j]); + l = g_hash_table_get_values(hlp.addr_sfd); + for (i = l; i; i = i->next) + obj_put((struct stream_fd *) i->data); + g_list_free(l); + g_hash_table_destroy(hlp.addr_sfd); kill_calls_timer(hlp.del_scheduled, NULL); kill_calls_timer(hlp.del_timeout, m); @@ -1575,7 +630,7 @@ struct callmaster *callmaster_new(struct poller *p) { mutex_init(&c->totalstats_lastinterval_lock); mutex_init(&c->cngs_lock); - c->cngs_hash = g_hash_table_new(in6_addr_hash, in6_addr_eq); + c->cngs_hash = g_hash_table_new(g_sockaddr_hash, g_sockaddr_eq); return c; @@ -1586,157 +641,13 @@ fail: -static void __set_tos(int fd, const struct call *c) { - int tos; - - setsockopt(fd, IPPROTO_IP, IP_TOS, &c->tos, sizeof(c->tos)); -#ifdef IPV6_TCLASS - tos = c->tos; - setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos)); -#else -#warning "Will not set IPv6 traffic class" -#endif -} - -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) { - int fd; - struct sockaddr_in6 sin; - - fd = socket(AF_INET6, SOCK_DGRAM, 0); - if (fd < 0) - return -1; - - nonblock(fd); - reuseaddr(fd); - ipv6only(fd, 0); - __set_tos(fd, c); - __get_pktinfo(fd); - - ZERO(sin); - sin.sin6_family = AF_INET6; - sin.sin6_port = htons(p); - if (bind(fd, (struct sockaddr *) &sin, sizeof(sin))) - goto fail; - - r->fd = fd; - - return 0; - -fail: - close(fd); - return -1; -} - -static int get_port(struct udp_fd *r, u_int16_t p, const struct call *c) { - int ret; - struct callmaster *m = c->callmaster; - - assert(r->fd == -1); - - __C_DBG("attempting to open port %u", p); - - if (bit_array_set(m->ports_used, p)) { - __C_DBG("port %d in use", p); - return -1; - } - __C_DBG("port %d locked", p); - - ret = get_port6(r, p, c); - - if (ret) { - __C_DBG("couldn't open port %d", p); - bit_array_clear(m->ports_used, p); - return ret; - } - - r->localport = p; - - return 0; -} - -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); - bit_array_clear(m->ports_used, r->localport); - close(r->fd); - r->fd = -1; - r->localport = 0; -} - -int __get_consecutive_ports(struct udp_fd *array, int array_len, int wanted_start_port, const struct call *c) { - int i, j, cycle = 0; - struct udp_fd *it; - int port; - struct callmaster *m = c->callmaster; - - memset(array, -1, sizeof(*array) * array_len); - - if (wanted_start_port > 0) - port = wanted_start_port; - else { - 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 - } - - while (1) { - if (!wanted_start_port) { - if (port < m->conf.port_min) - port = m->conf.port_min; - if ((port & 1)) - port++; - } - - for (i = 0; i < array_len; i++) { - it = &array[i]; - - if (!wanted_start_port && port > m->conf.port_max) { - port = 0; - cycle++; - goto release_restart; - } - - if (get_port(it, port++, c)) - goto release_restart; - } - break; - -release_restart: - for (j = 0; j < i; j++) - release_port(&array[j], m); - - if (cycle >= 2 || wanted_start_port > 0) - goto fail; - } - - /* success */ - 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); - return 0; - -fail: - ilog(LOG_ERR, "Failed to get %u consecutive UDP ports for relay", - array_len); - return -1; -} - void __payload_type_free(void *p) { g_slice_free1(sizeof(struct rtp_payload_type), p); } static struct call_media *__get_media(struct call_monologue *ml, GList **it, const struct stream_params *sp) { struct call_media *med; + struct call *call; /* iterator points to last seen element, or NULL if uninitialized */ if (!*it) @@ -1755,65 +666,36 @@ static struct call_media *__get_media(struct call_monologue *ml, GList **it, con } __C_DBG("allocating new call_media for stream #%u", sp->index); - med = g_slice_alloc0(sizeof(*med)); + call = ml->call; + med = uid_slice_alloc0(med, &call->medias); med->monologue = ml; med->call = ml->call; med->index = sp->index; - redis_hkey_generate(med->redis_hkey, ml->call, RC_MEDIA); call_str_cpy(ml->call, &med->type, &sp->type); med->rtp_payload_types = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, __payload_type_free); g_queue_push_tail(&ml->medias, med); + *it = ml->medias.tail; return med; } -static void stream_fd_free(void *p) { - struct stream_fd *f = p; - struct callmaster *m = f->call->callmaster; - - release_port(&f->fd, m); - crypto_cleanup(&f->crypto); - dtls_connection_cleanup(&f->dtls); - - obj_put(f->call); -} - -struct stream_fd *__stream_fd_new(struct udp_fd *fd, struct call *call) { - struct stream_fd *sfd; - struct poller_item pi; - struct poller *po = call->callmaster->poller; - - sfd = obj_alloc0("stream_fd", sizeof(*sfd), stream_fd_free); - sfd->fd = *fd; - sfd->call = obj_get(call); - call->stream_fds = g_slist_prepend(call->stream_fds, sfd); /* hand over ref */ - - ZERO(pi); - pi.fd = sfd->fd.fd; - pi.obj = &sfd->obj; - pi.readable = stream_fd_readable; - pi.closed = stream_fd_closed; - - poller_add_item(po, &pi); - - return sfd; -} - static struct endpoint_map *__get_endpoint_map(struct call_media *media, unsigned int num_ports, - const struct endpoint *ep, unsigned int wanted_start_port) + const struct endpoint *ep) { - GSList *l; + GList *l; struct endpoint_map *em; - struct udp_fd fd_arr[16]; - unsigned int i; struct stream_fd *sfd; - struct call *call = media->call; + GQueue intf_sockets = G_QUEUE_INIT; + socket_t *sock; + struct intf_list *il, *em_il; - for (l = media->endpoint_maps; l; l = l->next) { + for (l = media->endpoint_maps.tail; l; l = l->prev) { em = l->data; - if (em->wildcard && em->sfds.length >= num_ports) { + if (em->logical_intf != media->logical_intf) + continue; + if (em->wildcard && em->num_ports >= num_ports) { __C_DBG("found a wildcard endpoint map%s", ep ? " and filling it in" : ""); if (ep) { em->endpoint = *ep; @@ -1824,85 +706,108 @@ static struct endpoint_map *__get_endpoint_map(struct call_media *media, unsigne if (!ep) /* creating wildcard map */ break; /* handle zero endpoint address */ - if (is_addr_unspecified(&ep->ip46) || is_addr_unspecified(&em->endpoint.ip46)) { + if (is_addr_unspecified(&ep->address) || is_addr_unspecified(&em->endpoint.address)) { if (ep->port != em->endpoint.port) continue; } else if (memcmp(&em->endpoint, ep, sizeof(*ep))) continue; - if (em->sfds.length >= num_ports) { - if (is_addr_unspecified(&em->endpoint.ip46)) - em->endpoint.ip46 = ep->ip46; + if (em->num_ports >= num_ports) { + if (is_addr_unspecified(&em->endpoint.address)) + em->endpoint.address = ep->address; return em; } /* endpoint matches, but not enough ports. flush existing ports * and allocate a new set. */ __C_DBG("endpoint matches, doesn't have enough ports"); - g_queue_clear(&em->sfds); + g_queue_clear_full(&em->intf_sfds, (void *) free_intf_list); goto alloc; } __C_DBG("allocating new %sendpoint map", ep ? "" : "wildcard "); - em = g_slice_alloc0(sizeof(*em)); + em = uid_slice_alloc0(em, &media->call->endpoint_maps); if (ep) em->endpoint = *ep; else em->wildcard = 1; - redis_hkey_generate(em->redis_hkey, call, RC_EM); - g_queue_init(&em->sfds); - media->endpoint_maps = g_slist_prepend(media->endpoint_maps, em); + em->logical_intf = media->logical_intf; + em->num_ports = num_ports; + g_queue_init(&em->intf_sfds); + g_queue_push_tail(&media->endpoint_maps, em); alloc: - if (num_ports > G_N_ELEMENTS(fd_arr)) + if (num_ports > 16) return NULL; - if (__get_consecutive_ports(fd_arr, num_ports, wanted_start_port, media->call)) + if (get_consecutive_ports(&intf_sockets, num_ports, media->logical_intf)) return NULL; __C_DBG("allocating stream_fds for %u ports", num_ports); - for (i = 0; i < num_ports; i++) { - sfd = __stream_fd_new(&fd_arr[i], call); - redis_hkey_generate(sfd->redis_hkey, call, RC_SFD); - g_queue_push_tail(&em->sfds, sfd); /* not referenced */ + + while ((il = g_queue_pop_head(&intf_sockets))) { + if (il->list.length != num_ports) + goto next_il; + + em_il = g_slice_alloc0(sizeof(*em_il)); + em_il->local_intf = il->local_intf; + g_queue_push_tail(&em->intf_sfds, em_il); + + while ((sock = g_queue_pop_head(&il->list))) { + set_tos(sock, media->call->tos); + sfd = stream_fd_new(sock, media->call, il->local_intf); + g_queue_push_tail(&em_il->list, sfd); /* not referenced */ + } + +next_il: + free_socket_intf_list(il); } return em; } -static void __assign_stream_fds(struct call_media *media, GList *sfds) { - GList *l; +static void __assign_stream_fds(struct call_media *media, GQueue *intf_sfds) { + GList *l, *k, *m; struct packet_stream *ps; struct stream_fd *sfd; - int reset = 0; + struct intf_list *il; + int first = 1; - for (l = media->streams.head; l; l = l->next) { - assert(sfds != NULL); - ps = l->data; - sfd = sfds->data; + for (l = intf_sfds->head; l; l = l->next) { + il = l->data; + + for (m = il->list.head, k = media->streams.head; m && k; m = m->next, k = k->next) { + sfd = m->data; + ps = k->data; + + if (first) + g_queue_clear(&ps->sfds); + + sfd->stream = ps; + g_queue_push_tail(&ps->sfds, sfd); - /* 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; + if (!ps->selected_sfd) + ps->selected_sfd = sfd; + + /* XXX: + * check whether previous/currect selected_sfd is actually part of + * current sfds list. + * if selected_sfd changes, take previously selected interface into account. + * handle crypto/dtls resets by moving contexts into sfd struct. + * handle ice resets too. + */ } - ps->sfd = sfd; - sfd->stream = ps; - sfds = sfds->next; + first = 0; } - - if (reset && media->ice_agent) - ice_restart(media->ice_agent); } -static int __wildcard_endpoint_map(struct call_media *media, unsigned int num_ports, unsigned int wanted_start_port) { +static int __wildcard_endpoint_map(struct call_media *media, unsigned int num_ports) { struct endpoint_map *em; - em = __get_endpoint_map(media, num_ports, NULL, wanted_start_port); + em = __get_endpoint_map(media, num_ports, NULL); if (!em) return -1; - __assign_stream_fds(media, em->sfds.head); + __assign_stream_fds(media, &em->intf_sfds); return 0; } @@ -1914,15 +819,13 @@ static void __rtp_stats_free(void *p) { struct packet_stream *__packet_stream_new(struct call *call) { struct packet_stream *stream; - stream = g_slice_alloc0(sizeof(*stream)); + stream = uid_slice_alloc0(stream, &call->streams); mutex_init(&stream->in_lock); mutex_init(&stream->out_lock); stream->call = call; atomic64_set_na(&stream->last_packet, poller_now); stream->rtp_stats = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, __rtp_stats_free); - call->streams = g_slist_prepend(call->streams, stream); - return stream; } @@ -1934,7 +837,6 @@ static int __num_media_streams(struct call_media *media, unsigned int num_ports) __C_DBG("allocating %i new packet_streams", num_ports - media->streams.length); while (media->streams.length < num_ports) { stream = __packet_stream_new(call); - redis_hkey_generate(stream->redis_hkey, call, RC_PS); stream->media = media; g_queue_push_tail(&media->streams, stream); stream->component = media->streams.length; @@ -1967,7 +869,6 @@ static void __fill_stream(struct packet_stream *ps, const struct endpoint *epp, return; ps->endpoint = ep; - __C_DBG("filling stream endpoint: %s:%d", smart_ntop_p_buf(&ps->endpoint.ip46), ps->endpoint.port); if (PS_ISSET(ps, FILLED)) { /* we reset crypto params whenever the endpoint changes */ @@ -1975,14 +876,16 @@ static void __fill_stream(struct packet_stream *ps, const struct endpoint *epp, dtls_shutdown(ps); } + ilog(LOG_DEBUG, "set FILLED flag for stream %s:%d", sockaddr_print_buf(&ps->endpoint.address), ps->endpoint.port); PS_SET(ps, FILLED); + /* XXX reset/repair ICE */ } /* 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) + if (!ps->selected_sfd || !ps->sfds.length) return CSS_SHUTDOWN; if (MEDIA_ISSET(media, ICE) && !ice_has_finished(media)) @@ -1990,7 +893,7 @@ enum call_stream_state call_stream_state_machine(struct packet_stream *ps) { if (MEDIA_ISSET(media, DTLS)) { mutex_lock(&ps->in_lock); - if (ps->sfd->dtls.init && !ps->sfd->dtls.connected) { + if (ps->selected_sfd->dtls.init && !ps->selected_sfd->dtls.connected) { dtls(ps, NULL, NULL); mutex_unlock(&ps->in_lock); return CSS_DTLS; @@ -2001,7 +904,7 @@ enum call_stream_state call_stream_state_machine(struct packet_stream *ps) { return CSS_RUNNING; } -static void __call_media_state_machine(struct call_media *m) { +void call_media_state_machine(struct call_media *m) { GList *l; for (l = m->streams.head; l; l = l->next) @@ -2013,9 +916,10 @@ static int __init_stream(struct packet_stream *ps) { struct call *call = ps->call; int active; - if (ps->sfd) { + if (ps->selected_sfd) { + // XXX apply SDES parms to all sfds? if (MEDIA_ISSET(media, SDES)) - crypto_init(&ps->sfd->crypto, &media->sdes_in.params); + crypto_init(&ps->selected_sfd->crypto, &media->sdes_in.params); if (MEDIA_ISSET(media, DTLS) && !PS_ISSET(ps, FALLBACK_RTCP)) { active = (PS_ISSET(ps, FILLED) && MEDIA_ISSET(media, SETUP_ACTIVE)); @@ -2317,7 +1221,8 @@ static void __disable_streams(struct call_media *media, unsigned int num_ports) for (l = media->streams.head; l; l = l->next) { ps = l->data; - ps->sfd = NULL; + g_queue_clear(&ps->sfds); + ps->selected_sfd = NULL; } } @@ -2401,12 +1306,12 @@ static void __fingerprint_changed(struct call_media *m) { } static void __set_all_tos(struct call *c) { - GSList *l; + GList *l; struct stream_fd *sfd; - for (l = c->stream_fds; l; l = l->next) { + for (l = c->stream_fds.head; l; l = l->next) { sfd = l->data; - __set_tos(sfd->fd.fd, c); + set_tos(&sfd->socket, c->tos); } } @@ -2431,36 +1336,41 @@ static void __tos_change(struct call *call, const struct sdp_ng_flags *flags) { __set_all_tos(call); } -static void __init_interface(struct call_media *media, const str *ifname) { +static void __init_interface(struct call_media *media, const str *ifname, int num_ports) { /* we're holding master_lock in W mode here, so we can safely ignore the * atomic ops */ - struct interface_address *ifa = (void *) media->local_address; + //struct local_intf *ifa = (void *) media->local_intf; - if (!media->interface || !ifa) + if (!media->logical_intf /* || !ifa */) goto get; if (!ifname || !ifname->s) return; - if (!str_cmp_str(&media->interface->name, ifname)) + if (!str_cmp_str(&media->logical_intf->name, ifname) || !str_cmp(ifname, ALGORITHM_ROUND_ROBIN_CALLS)) return; get: - media->interface = get_local_interface(media->call->callmaster, ifname, media->desired_family); - if (!media->interface) { + media->logical_intf = get_logical_interface(ifname, media->desired_family, num_ports); + if (G_UNLIKELY(!media->logical_intf)) { /* legacy support */ if (!str_cmp(ifname, "internal")) - media->desired_family = AF_INET; + media->desired_family = __get_socket_family_enum(SF_IP4); else if (!str_cmp(ifname, "external")) - media->desired_family = AF_INET6; + media->desired_family = __get_socket_family_enum(SF_IP6); 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) { - ilog(LOG_WARNING, "No usable address in interface '"STR_FORMAT"' found, using default", - STR_FMT(ifname)); - media->local_address = ifa = get_any_interface_address(media->interface, media->desired_family); - media->desired_family = family_from_address(&ifa->addr); + media->logical_intf = get_logical_interface(NULL, media->desired_family, num_ports); + if (!media->logical_intf) { + ilog(LOG_WARNING, "Requested address family (%s) not supported", + media->desired_family->name); + media->logical_intf = get_logical_interface(NULL, NULL, 0); + } } +// media->local_intf = ifa = get_interface_address(media->logical_intf, media->desired_family); +// if (!ifa) { +// ilog(LOG_WARNING, "No usable address in interface '"STR_FORMAT"' found, using default", +// STR_FMT(ifname)); +// media->local_intf = ifa = get_any_interface_address(media->logical_intf, media->desired_family); +// media->desired_family = ifa->spec->address.addr.family; +// } } @@ -2524,6 +1434,33 @@ static void __ice_start(struct call_media *media) { ice_agent_init(&media->ice_agent, media); } +static int get_algorithm_num_ports(GQueue *streams, char *algorithm) { + unsigned int algorithm_ports = 0; + struct stream_params *sp; + GList *media_iter; + + if (algorithm == NULL) { + return 0; + } + + for (media_iter = streams->head; media_iter; media_iter = media_iter->next) { + sp = media_iter->data; + + if (!str_cmp(&sp->direction[0], algorithm)) { + algorithm_ports += sp->consecutive_ports; + } + + if (!str_cmp(&sp->direction[1], algorithm)) { + algorithm_ports += sp->consecutive_ports; + } + } + + // XXX only do *=2 for RTP streams? + algorithm_ports *= 2; + + return algorithm_ports; +} + /* 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) @@ -2532,15 +1469,20 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, GList *media_iter, *ml_media, *other_ml_media; struct call_media *media, *other_media; unsigned int num_ports; + unsigned int rr_calls_ports; struct call_monologue *monologue = other_ml->active_dialogue; struct endpoint_map *em; struct call *call; + call = monologue->call; call->last_signal = poller_now; call->deleted = 0; + // get the total number of ports needed for ALGORITHM_ROUND_ROBIN_CALLS algorithm + rr_calls_ports = get_algorithm_num_ports(streams, ALGORITHM_ROUND_ROBIN_CALLS); + /* we must have a complete dialogue, even though the to-tag (monologue->tag) * may not be known yet */ if (!other_ml) { @@ -2556,6 +1498,7 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, for (media_iter = streams->head; media_iter; media_iter = media_iter->next) { sp = media_iter->data; __C_DBG("processing media stream #%u", sp->index); + __C_DBG("free ports needed for round-robin-calls, left for this call %u", rr_calls_ports); /* first, check for existance of call_media struct on both sides of * the dialogue */ @@ -2625,7 +1568,7 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, __generate_crypto(flags, media, other_media); /* deduct address family from stream parameters received */ - other_media->desired_family = family_from_address(&sp->rtp_endpoint.ip46); + other_media->desired_family = sp->rtp_endpoint.address.family; /* for outgoing SDP, use "direction"/DF or default to what was offered */ if (!media->desired_family) media->desired_family = other_media->desired_family; @@ -2634,8 +1577,12 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, } /* local interface selection */ - __init_interface(media, &sp->direction[1]); - __init_interface(other_media, &sp->direction[0]); + __init_interface(media, &sp->direction[1], rr_calls_ports); + __init_interface(other_media, &sp->direction[0], rr_calls_ports); + + if (media->logical_intf == NULL || other_media->logical_intf == NULL) { + goto error_intf; + } /* ICE stuff - must come after interface and address family selection */ __ice_offer(flags, media, other_media); @@ -2663,7 +1610,7 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, __disable_streams(other_media, num_ports); goto init; } - if (is_addr_unspecified(&sp->rtp_endpoint.ip46) && !is_trickle_ice_address(&sp->rtp_endpoint)) { + if (is_addr_unspecified(&sp->rtp_endpoint.address) && !is_trickle_ice_address(&sp->rtp_endpoint)) { /* Zero endpoint address, equivalent to setting the media stream * to sendonly or inactive */ MEDIA_CLEAR(media, RECV); @@ -2673,22 +1620,30 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, /* get that many ports for each side, and one packet stream for each port, then * assign the ports to the streams */ - em = __get_endpoint_map(media, num_ports, &sp->rtp_endpoint, - (unsigned int)GPOINTER_TO_UINT(g_queue_pop_head(&call->rtp_bridge_ports))); - - if (!em) - goto error; + em = __get_endpoint_map(media, num_ports, &sp->rtp_endpoint); + if (!em) { + goto error_ports; + } else { + // update the ports needed for ALGORITHM_ROUND_ROBIN_CALLS algorithm + if (str_cmp(&sp->direction[1], ALGORITHM_ROUND_ROBIN_CALLS) == 0) { + rr_calls_ports -= num_ports; + } + } __num_media_streams(media, num_ports); - __assign_stream_fds(media, em->sfds.head); + __assign_stream_fds(media, &em->intf_sfds); if (__num_media_streams(other_media, num_ports)) { /* new streams created on OTHER side. normally only happens in * initial offer. create a wildcard endpoint_map to be filled in * when the answer comes. */ - if (__wildcard_endpoint_map(other_media, num_ports, - (unsigned int)GPOINTER_TO_UINT(g_queue_pop_head(&call->rtp_bridge_ports)))) - goto error; + if (__wildcard_endpoint_map(other_media, num_ports)) + goto error_ports; + + // update the ports needed for ALGORITHM_ROUND_ROBIN_CALLS algorithm + if (str_cmp(&sp->direction[0], ALGORITHM_ROUND_ROBIN_CALLS) == 0) { + rr_calls_ports -= num_ports; + } } init: @@ -2704,22 +1659,13 @@ init: return 0; -error: +error_ports: ilog(LOG_ERR, "Error allocating media ports"); - return -1; -} - -/* must be called with in_lock held or call->master_lock held in W */ -static void __unkernelize(struct packet_stream *p) { - if (!PS_ISSET(p, KERNELIZED)) - return; - if (PS_ISSET(p, NO_KERNEL_SUPPORT)) - return; - - if (p->call->callmaster->conf.kernelfd >= 0) - kernel_del_stream(p->call->callmaster->conf.kernelfd, p->sfd->fd.localport); + return ERROR_NO_FREE_PORTS; - PS_CLEAR(p, KERNELIZED); +error_intf: + ilog(LOG_ERR, "Error finding logical interface with free ports"); + return ERROR_NO_FREE_LOGS; } static void timeval_totalstats_average_add(struct totalstats *s, const struct timeval *add) { @@ -2830,7 +1776,9 @@ struct timeval add_ongoing_calls_dur_in_interval(struct callmaster *m, while (g_hash_table_iter_next(&iter, &key, &value)) { call = (struct call*) value; - ml = call->monologues->data; + if (!call->monologues.head) + continue; + ml = call->monologues.head->data; if (timercmp(interval_start, &ml->started, >)) { timeval_add(&res, &res, interval_duration); } else { @@ -2851,7 +1799,7 @@ void call_destroy(struct call *c) { struct packet_stream *ps=0, *ps2=0; struct stream_fd *sfd; struct poller *p = m->poller; - GSList *l; + GList *l; int ret; struct call_monologue *ml; struct call_media *md; @@ -2904,7 +1852,7 @@ void call_destroy(struct call *c) { ADJUSTLEN(printlen,cdrbufend,cdrbufcur); } - for (l = c->monologues; l; l = l->next) { + for (l = c->monologues.head; l; l = l->next) { ml = l->data; if (!ml->terminated.tv_sec) { @@ -2976,7 +1924,7 @@ void call_destroy(struct call *c) { if (PS_ISSET(ps, FALLBACK_RTCP)) continue; - char *addr = smart_ntop_p_buf(&ps->endpoint.ip46); + char *addr = sockaddr_print_buf(&ps->endpoint.address); if (_log_facility_cdr) { const char* protocol = (!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) ? "rtcp" : "rtp"; @@ -2993,7 +1941,8 @@ void call_destroy(struct call *c) { "ml%i_midx%u_%s_in_tos_tclass=%" PRIu8 ", ", cdrlinecnt, md->index, protocol, addr, cdrlinecnt, md->index, protocol, ps->endpoint.port, - cdrlinecnt, md->index, protocol, (unsigned int) (ps->sfd ? ps->sfd->fd.localport : 0), + cdrlinecnt, md->index, protocol, + (ps->selected_sfd ? ps->selected_sfd->socket.local.port : 0), cdrlinecnt, md->index, protocol, atomic64_get(&ps->stats.packets), cdrlinecnt, md->index, protocol, @@ -3048,7 +1997,8 @@ void call_destroy(struct call *c) { "ml%i_midx%u_%s_in_tos_tclass=%" PRIu8 ", ", cdrlinecnt, md->index, protocol, addr, cdrlinecnt, md->index, protocol, ps->endpoint.port, - cdrlinecnt, md->index, protocol, (unsigned int) (ps->sfd ? ps->sfd->fd.localport : 0), + cdrlinecnt, md->index, protocol, + (ps->selected_sfd ? ps->selected_sfd->socket.local.port : 0), cdrlinecnt, md->index, protocol, atomic64_get(&ps->stats.packets), cdrlinecnt, md->index, protocol, @@ -3064,9 +2014,9 @@ void call_destroy(struct call *c) { } } - ilog(LOG_INFO, "--------- Port %5u <> %15s:%-5hu%s, " + ilog(LOG_INFO, "--------- Port %5u <> %15s:%-5u%s, " ""UINT64F" p, "UINT64F" b, "UINT64F" e, "UINT64F" last_packet", - (unsigned int) (ps->sfd ? ps->sfd->fd.localport : 0), + (unsigned int) (ps->selected_sfd ? ps->selected_sfd->socket.local.port : 0), addr, ps->endpoint.port, (!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) ? " (RTCP)" : "", atomic64_get(&ps->stats.packets), @@ -3093,7 +2043,7 @@ void call_destroy(struct call *c) { // --- for statistics getting one way stream or no relay at all int total_nopacket_relayed_sess = 0; - for (l = c->monologues; l; l = l->next) { + for (l = c->monologues.head; l; l = l->next) { ml = l->data; // --- go through partner ml and search the RTP @@ -3144,8 +2094,8 @@ void call_destroy(struct call *c) { atomic64_add(&m->totalstats.total_nopacket_relayed_sess, total_nopacket_relayed_sess / 2); atomic64_add(&m->totalstats_interval.total_nopacket_relayed_sess, total_nopacket_relayed_sess / 2); - if (c->monologues) { - ml = c->monologues->data; + if (c->monologues.head) { + ml = c->monologues.head->data; if (ml->term_reason==TIMEOUT) { atomic64_inc(&m->totalstats.total_timeout_sess); atomic64_inc(&m->totalstats_interval.total_timeout_sess); @@ -3171,22 +2121,22 @@ void call_destroy(struct call *c) { /* log it */ cdrlog(cdrbuffer); - for (l = c->streams; l; l = l->next) { + for (l = c->streams.head; l; l = l->next) { ps = l->data; __unkernelize(ps); dtls_shutdown(ps); - ps->sfd = NULL; + ps->selected_sfd = NULL; + g_queue_clear(&ps->sfds); crypto_cleanup(&ps->crypto); ps->rtp_sink = NULL; ps->rtcp_sink = NULL; } - while (c->stream_fds) { - sfd = c->stream_fds->data; - c->stream_fds = g_slist_delete_link(c->stream_fds, c->stream_fds); - poller_del_item(p, sfd->fd.fd); + while (c->stream_fds.head) { + sfd = g_queue_pop_head(&c->stream_fds); + poller_del_item(p, sfd->socket.fd); obj_put(sfd); } @@ -3195,78 +2145,35 @@ void call_destroy(struct call *c) { -static int call_stream_address4(char *o, struct packet_stream *ps, enum stream_address_format format, - int *len, struct interface_address *ifa) -{ - u_int32_t ip4; - int l = 0; - - if (format == SAF_NG) { - strcpy(o + l, "IP4 "); - l = 4; - } - - if (is_addr_unspecified(&ps->advertised_endpoint.ip46) - && !is_trickle_ice_address(&ps->advertised_endpoint)) { - strcpy(o + l, "0.0.0.0"); - l += 7; - } - else { - ip4 = in6_to_4(&ifa->advertised); - l += sprintf(o + l, IPF, IPP(ip4)); - } - - *len = l; - return AF_INET; -} - -static int call_stream_address6(char *o, struct packet_stream *ps, enum stream_address_format format, - int *len, struct interface_address *ifa) +/* XXX move these */ +int call_stream_address46(char *o, struct packet_stream *ps, enum stream_address_format format, + int *len, const struct local_intf *ifa) { + struct packet_stream *sink; int l = 0; + const struct intf_address *ifa_addr; - if (format == SAF_NG) { - strcpy(o + l, "IP6 "); - l += 4; - } - - if (is_addr_unspecified(&ps->advertised_endpoint.ip46) - && !is_trickle_ice_address(&ps->advertised_endpoint)) { - strcpy(o + l, "::"); - l += 2; - } - else { - inet_ntop(AF_INET6, &ifa->advertised, o + l, 45); /* lies ... */ - l += strlen(o + l); + if (!ifa) { + if (ps->selected_sfd) + ifa = ps->selected_sfd->local_intf; + else + ifa = get_any_interface_address(ps->media->logical_intf, ps->media->desired_family); } - - *len = l; - return AF_INET6; -} - - -int call_stream_address46(char *o, struct packet_stream *ps, enum stream_address_format format, - int *len, struct interface_address *ifa) -{ - struct packet_stream *sink; + ifa_addr = &ifa->spec->address; sink = packet_stream_sink(ps); - if (ifa->family == AF_INET) - return call_stream_address4(o, sink, format, len, ifa); - return call_stream_address6(o, sink, format, len, ifa); -} - -int call_stream_address(char *o, struct packet_stream *ps, enum stream_address_format format, int *len) { - struct interface_address *ifa; - struct call_media *media; - media = ps->media; + if (format == SAF_NG) + l += sprintf(o + l, "%s ", ifa_addr->addr.family->rfc_name); - ifa = g_atomic_pointer_get(&media->local_address); - if (!ifa) - return -1; + if (is_addr_unspecified(&sink->advertised_endpoint.address) + && !is_trickle_ice_address(&sink->advertised_endpoint)) + l += sprintf(o + l, "%s", ifa_addr->addr.family->unspec_string); + else + l += sprintf(o + l, "%s", sockaddr_print_buf(&ifa_addr->advertised)); - return call_stream_address46(o, ps, format, len, ifa); + *len = l; + return ifa_addr->addr.family->af; } @@ -3285,19 +2192,17 @@ static void __call_free(void *p) { rwlock_destroy(&c->master_lock); obj_put(c->dtls_cert); - while (c->monologues) { - m = c->monologues->data; - c->monologues = g_slist_delete_link(c->monologues, c->monologues); + while (c->monologues.head) { + m = g_queue_pop_head(&c->monologues); g_hash_table_destroy(m->other_tags); for (it = m->medias.head; it; it = it->next) { md = it->data; g_queue_clear(&md->streams); - while (md->endpoint_maps) { - em = md->endpoint_maps->data; - md->endpoint_maps = g_slist_delete_link(md->endpoint_maps, md->endpoint_maps); - g_queue_clear(&em->sfds); + while (md->endpoint_maps.head) { + em = g_queue_pop_head(&md->endpoint_maps); + g_queue_clear_full(&em->intf_sfds, (void *) free_intf_list); g_slice_free1(sizeof(*em), em); } g_hash_table_destroy(md->rtp_payload_types); @@ -3312,18 +2217,16 @@ static void __call_free(void *p) { g_hash_table_destroy(c->tags); g_hash_table_destroy(c->viabranches); + g_queue_clear(&c->medias); - while (c->streams) { - ps = c->streams->data; - c->streams = g_slist_delete_link(c->streams, c->streams); + while (c->streams.head) { + ps = g_queue_pop_head(&c->streams); g_hash_table_destroy(ps->rtp_stats); crypto_cleanup(&ps->crypto); g_slice_free1(sizeof(*ps), ps); } - g_queue_clear(&c->rtp_bridge_ports); - - assert(c->stream_fds == NULL); + assert(c->stream_fds.head == NULL); } static struct call *call_create(const str *callid, struct callmaster *m) { @@ -3341,7 +2244,6 @@ static struct call *call_create(const str *callid, struct callmaster *m) { c->created = poller_now; c->dtls_cert = dtls_cert(); c->tos = m->conf.default_tos; - g_queue_init(&c->rtp_bridge_ports); return c; } @@ -3413,7 +2315,7 @@ struct call_monologue *__monologue_create(struct call *call) { struct call_monologue *ret; __C_DBG("creating new monologue"); - ret = g_slice_alloc0(sizeof(*ret)); + ret = uid_slice_alloc0(ret, &call->monologues); ret->call = call; ret->created = poller_now; @@ -3421,8 +2323,6 @@ struct call_monologue *__monologue_create(struct call *call) { g_queue_init(&ret->medias); gettimeofday(&ret->started, NULL); - call->monologues = g_slist_prepend(call->monologues, ret); - return ret; } @@ -3447,26 +2347,6 @@ void __monologue_viabranch(struct call_monologue *ml, const str *viabranch) { g_hash_table_insert(call->viabranches, &ml->viabranch, ml); } -static void __stream_unconfirm(struct packet_stream *ps) { - __unkernelize(ps); - PS_CLEAR(ps, CONFIRMED); - ps->handler = NULL; -} -static void stream_unconfirm(struct packet_stream *ps) { - if (!ps) - return; - mutex_lock(&ps->in_lock); - __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); -} - /* must be called with call->master_lock held in W */ static void __monologue_unkernelize(struct call_monologue *monologue) { GList *l, *m; @@ -3555,46 +2435,44 @@ static struct call_monologue *call_get_monologue(struct call *call, const str *f __C_DBG("getting monologue for tag '"STR_FORMAT"' in call '"STR_FORMAT"'", STR_FMT(fromtag), STR_FMT(&call->callid)); ret = g_hash_table_lookup(call->tags, fromtag); - /* XXX reverse the conditional */ - if (ret) { - __C_DBG("found existing monologue"); - __monologue_unkernelize(ret); - __monologue_unkernelize(ret->active_dialogue); - - if (!viabranch) - goto ok_check_tag; - - /* check the viabranch. if it's not known, then this is a branched offer and we need - * to create a new "other side" for this branch. */ - if (!ret->active_dialogue->viabranch.s) { - /* previous "other side" hasn't been tagged with the via-branch, so we'll just - * use this one and tag it */ - __monologue_viabranch(ret->active_dialogue, viabranch); - goto ok_check_tag; - } - if (!str_cmp_str(&ret->active_dialogue->viabranch, viabranch)) - goto ok_check_tag; /* dialogue still intact */ - os = g_hash_table_lookup(call->viabranches, viabranch); - if (os) { - /* previously seen branch. use it */ - __monologue_unkernelize(os); - os->active_dialogue = ret; - ret->active_dialogue = os; - goto ok_check_tag; - } + if (!ret) { + __C_DBG("creating new monologue"); + ret = __monologue_create(call); + __monologue_tag(ret, fromtag); goto new_branch; } - __C_DBG("creating new monologue"); - ret = __monologue_create(call); - redis_hkey_generate(ret->redis_hkey, call, RC_MONO); - __monologue_tag(ret, fromtag); + __C_DBG("found existing monologue"); + __monologue_unkernelize(ret); + __monologue_unkernelize(ret->active_dialogue); + + if (!viabranch) + goto ok_check_tag; + + /* check the viabranch. if it's not known, then this is a branched offer and we need + * to create a new "other side" for this branch. */ + if (!ret->active_dialogue->viabranch.s) { + /* previous "other side" hasn't been tagged with the via-branch, so we'll just + * use this one and tag it */ + __monologue_viabranch(ret->active_dialogue, viabranch); + goto ok_check_tag; + } + if (!str_cmp_str(&ret->active_dialogue->viabranch, viabranch)) + goto ok_check_tag; /* dialogue still intact */ + os = g_hash_table_lookup(call->viabranches, viabranch); + if (os) { + /* previously seen branch. use it */ + __monologue_unkernelize(os); + os->active_dialogue = ret; + ret->active_dialogue = os; + goto ok_check_tag; + } + /* we need both sides of the dialogue even in the initial offer, so create * another monologue without to-tag (to be filled in later) */ new_branch: __C_DBG("create new \"other side\" monologue for viabranch "STR_FORMAT, STR_FMT0(viabranch)); os = __monologue_create(call); - redis_hkey_generate(os->redis_hkey, call, RC_MONO); ret->active_dialogue = os; os->active_dialogue = ret; __monologue_viabranch(os, viabranch); @@ -3639,10 +2517,8 @@ static struct call_monologue *call_get_dialogue(struct call *call, const str *fr /* if we don't have a fromtag monologue yet, we can use a half-complete dialogue * from the totag if there is one. otherwise we have to create a new one. */ ft = tt->active_dialogue; - if (ft->tag.s) { + if (ft->tag.s) ft = __monologue_create(call); - redis_hkey_generate(ft->redis_hkey, call, RC_MONO); - } } /* the fromtag monologue may be newly created, or half-complete from the totag, or @@ -3682,7 +2558,7 @@ int call_delete_branch(struct callmaster *m, const str *callid, const str *branc struct call_monologue *ml; int ret; const str *match_tag; - GSList *i; + GList *i; if (delete_delay < 0) delete_delay = m->conf.delete_delay; @@ -3693,7 +2569,7 @@ int call_delete_branch(struct callmaster *m, const str *callid, const str *branc goto err; } - for (i = c->monologues; i; i = i->next) { + for (i = c->monologues.head; i; i = i->next) { ml = i->data; gettimeofday(&(ml->terminated), NULL); ml->term_reason = REGULAR; @@ -3847,115 +2723,3 @@ const struct transport_protocol *transport_protocol(const str *s) { 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; - - 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); - } - - /* 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 - abort(); - } -} - -struct local_interface *get_local_interface(struct callmaster *m, const str *name, int family) { - struct local_interface d, *lif; - - if (!name || !name->s) { - GQueue *q; - q = __interface_list_for_family(m, family); - if (q->head) - return q->head->data; - q = __interface_list_for_family(m, AF_INET); - if (q->head) - return q->head->data; - q = __interface_list_for_family(m, AF_INET6); - if (q->head) - return q->head->data; - return NULL; - } - - d.name = *name; - d.preferred_family = family; - - 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 = &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) { - struct interface_address *ifa; - - ifa = get_interface_address(lif, family); - if (ifa) - return ifa; - ifa = get_interface_address(lif, AF_INET); - if (ifa) - return ifa; - return get_interface_address(lif, AF_INET6); -} diff --git a/daemon/call.h b/daemon/call.h index d4e0c4d60..f035a826c 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -17,6 +17,8 @@ #include "compat.h" #include "control_ng.h" #include "aux.h" +#include "socket.h" +#include "media_socket.h" #define TRUNCATED " ... Output truncated. Increase Output Buffer ... \n" @@ -86,14 +88,14 @@ enum call_stream_state { #include "rtp.h" +#define ERROR_NO_FREE_PORTS -100 +#define ERROR_NO_FREE_LOGS -101 #define MAX_RTP_PACKET_SIZE 8192 #define RTP_BUFFER_HEAD_ROOM 128 #define RTP_BUFFER_TAIL_ROOM 512 #define RTP_BUFFER_SIZE (MAX_RTP_PACKET_SIZE + RTP_BUFFER_HEAD_ROOM + RTP_BUFFER_TAIL_ROOM) -#define MAX_REDIS_HKEY_SIZE 150 - #ifndef RTP_LOOP_PROTECT #define RTP_LOOP_PROTECT 28 /* number of bytes */ #define RTP_LOOP_PACKETS 2 /* number of packets */ @@ -186,8 +188,8 @@ enum call_stream_state { #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) -typedef enum { RC_SFD, RC_EM, RC_PS, RC_MEDIA, RC_MONO, RC_LIMIT} rc_type; -typedef struct redis_hkey_counters { unsigned char val[RC_LIMIT]; } redis_hkey_counters; + + struct poller; struct control_stream; @@ -243,7 +245,7 @@ struct totalstats { mutex_t total_average_lock; /* for these two below */ u_int64_t total_managed_sess; - struct timeval total_average_call_dur; + struct timeval total_average_call_dur; mutex_t managed_sess_lock; /* for these below */ u_int64_t managed_sess_crt; @@ -254,10 +256,6 @@ struct totalstats { struct timeval total_calls_duration_interval; }; -struct udp_fd { - int fd; - u_int16_t localport; -}; struct stream_params { unsigned int index; /* starting with 1 */ str type; @@ -268,7 +266,7 @@ struct stream_params { struct crypto_params crypto; unsigned int sdes_tag; str direction[2]; - int desired_family; + sockfamily_t *desired_family; struct dtls_fingerprint fingerprint; unsigned int sp_flags; GQueue rtp_payload_types; /* slice-alloc'd */ @@ -277,21 +275,13 @@ struct stream_params { str ice_pwd; }; -struct stream_fd { - struct obj obj; - struct udp_fd fd; /* RO */ - struct call *call; /* RO */ - struct packet_stream *stream; /* LOCK: call->master_lock */ - struct crypto_context crypto; /* IN direction, LOCK: stream->in_lock */ - struct dtls_connection dtls; /* LOCK: stream->in_lock */ - char redis_hkey[MAX_REDIS_HKEY_SIZE]; -}; - struct endpoint_map { + unsigned int unique_id; struct endpoint endpoint; - GQueue sfds; + unsigned int num_ports; + const struct logical_intf *logical_intf; + GQueue intf_sfds; /* list of struct intf_list - contains stream_fd list */ int wildcard:1; - char redis_hkey[MAX_REDIS_HKEY_SIZE]; }; struct loop_protector { @@ -300,7 +290,7 @@ struct loop_protector { }; struct rtp_stats { - unsigned int payload_type; + unsigned int payload_type; atomic64 packets; atomic64 bytes; atomic64 kernel_packets; @@ -318,8 +308,10 @@ struct packet_stream { struct call_media *media; /* RO */ struct call *call; /* RO */ unsigned int component; /* RO, starts with 1 */ + unsigned int unique_id; /* RO */ - struct stream_fd *sfd; /* LOCK: call->master_lock */ + GQueue sfds; /* LOCK: call->master_lock */ + struct stream_fd * volatile selected_sfd; struct packet_stream *rtp_sink; /* LOCK: call->master_lock */ struct packet_stream *rtcp_sink; /* LOCK: call->master_lock */ struct packet_stream *rtcp_sibling; /* LOCK: call->master_lock */ @@ -344,7 +336,6 @@ struct packet_stream { /* in_lock must be held for SETTING these: */ volatile unsigned int ps_flags; - char redis_hkey[MAX_REDIS_HKEY_SIZE]; }; /* protected by call->master_lock, except the RO elements */ @@ -353,15 +344,11 @@ struct call_media { struct call *call; /* RO */ unsigned int index; /* RO */ + unsigned int unique_id; /* RO */ str type; /* RO */ const struct transport_protocol *protocol; - int desired_family; - struct local_interface *interface; - - /* local_address is protected by call->master_lock in W mode, but may - * still be modified if the lock is held in R mode, therefore we use - * atomic ops to access it when holding an R lock. */ - volatile struct interface_address *local_address; + sockfamily_t *desired_family; + const struct logical_intf *logical_intf; struct ice_agent *ice_agent; @@ -374,17 +361,17 @@ struct call_media { struct dtls_fingerprint fingerprint; /* as received */ GQueue streams; /* normally RTP + RTCP */ - GSList *endpoint_maps; + GQueue endpoint_maps; GHashTable *rtp_payload_types; volatile unsigned int media_flags; - char redis_hkey[MAX_REDIS_HKEY_SIZE]; }; /* half a dialogue */ /* protected by call->master_lock, except the RO elements */ struct call_monologue { struct call *call; /* RO */ + unsigned int unique_id; /* RO */ str tag; str viabranch; @@ -398,7 +385,6 @@ struct call_monologue { struct call_monologue *active_dialogue; GQueue medias; - char redis_hkey[MAX_REDIS_HKEY_SIZE]; }; struct call { @@ -407,52 +393,32 @@ struct call { struct callmaster *callmaster; /* RO */ mutex_t buffer_lock; - call_buffer_t buffer; - GQueue rtp_bridge_ports; + call_buffer_t buffer; /* everything below protected by master_lock */ rwlock_t master_lock; - GSList *monologues; - GHashTable *tags; + GQueue monologues; + GQueue medias; + GHashTable *tags; GHashTable *viabranches; - GSList *streams; - GSList *stream_fds; + GQueue streams; + GQueue stream_fds; + GQueue endpoint_maps; struct dtls_cert *dtls_cert; /* for outgoing */ - str callid; + str callid; time_t created; time_t last_signal; time_t deleted; time_t ml_deleted; - unsigned char tos; + unsigned char tos; char *created_from; - struct sockaddr_in6 created_from_addr; - - struct redis_hkey_counters rc; -}; - -struct local_interface { - str name; - int preferred_family; - GQueue list; /* struct interface_address */ - GHashTable *addr_hash; -}; -struct interface_address { - str interface_name; - int family; - struct in6_addr addr; - struct in6_addr advertised; - str ice_foundation; - char foundation_buf[16]; - unsigned int preference; /* starting with 0 */ + sockaddr_t created_from_addr; }; struct callmaster_config { int kernelfd; int kernelid; - GQueue *interfaces; /* struct interface_address */ - int port_min; - int port_max; int max_sessions; unsigned int timeout; unsigned int silent_timeout; @@ -463,8 +429,7 @@ struct callmaster_config { char *b2b_url; unsigned char default_tos; enum xmlrpc_format fmt; - u_int32_t graphite_ip; - u_int16_t graphite_port; + endpoint_t graphite_ep; int graphite_interval; }; @@ -474,13 +439,6 @@ struct callmaster { rwlock_t hashlock; GHashTable *callhash; - GHashTable *interfaces; /* struct local_interface */ - GQueue interface_list_v4; /* ditto */ - GQueue interface_list_v6; /* ditto */ - - volatile unsigned int lastport; - BIT_ARRAY_DECLARE(ports_used, 0x10000); - /* XXX rework these */ struct stats statsps; /* per second stats, running timer */ struct stats stats; /* copied from statsps once a second */ @@ -511,8 +469,6 @@ struct call_stats { struct callmaster *callmaster_new(struct poller *); -void callmaster_config_init(struct callmaster *); -void stream_msg_mh_src(struct packet_stream *, struct msghdr *); void callmaster_get_all_calls(struct callmaster *m, GQueue *q); struct timeval add_ongoing_calls_dur_in_interval(struct callmaster *m, struct timeval *iv_start, struct timeval *iv_duration); @@ -523,8 +479,6 @@ void calls_dump_redis_write(struct callmaster *); struct call_monologue *__monologue_create(struct call *call); void __monologue_tag(struct call_monologue *ml, const str *tag); void __monologue_viabranch(struct call_monologue *ml, const str *viabranch); -struct stream_fd *__stream_fd_new(struct udp_fd *fd, struct call *call); -int __get_consecutive_ports(struct udp_fd *array, int array_len, int wanted_start_port, const struct call *c); struct packet_stream *__packet_stream_new(struct call *call); @@ -538,19 +492,11 @@ int call_delete_branch(struct callmaster *m, const str *callid, const str *branc const str *fromtag, const str *totag, bencode_item_t *output, int delete_delay); void call_destroy(struct call *); enum call_stream_state call_stream_state_machine(struct packet_stream *); +void call_media_state_machine(struct call_media *m); 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); -struct local_interface *get_local_interface(struct callmaster *m, const str *name, int familiy); -INLINE struct interface_address *get_interface_from_address(struct local_interface *lif, - const struct in6_addr *addr) -{ - return g_hash_table_lookup(lif->addr_hash, addr); -} -struct interface_address *get_any_interface_address(struct local_interface *lif, int family); + int *len, const struct local_intf *ifa); const struct transport_protocol *transport_protocol(const str *s); void add_total_calls_duration_in_interval(struct callmaster *cm, struct timeval *interval_tv); @@ -608,9 +554,6 @@ INLINE str *call_str_init_dup(struct call *c, char *s) { str_init(&t, s); return call_str_dup(c, &t); } -INLINE void callmaster_exclude_port(struct callmaster *m, u_int16_t p) { - bit_array_set(m->ports_used, p); -} INLINE struct packet_stream *packet_stream_sink(struct packet_stream *ps) { struct packet_stream *ret; ret = ps->rtp_sink; @@ -619,11 +562,6 @@ INLINE struct packet_stream *packet_stream_sink(struct packet_stream *ps) { return ret; } -INLINE void redis_hkey_cpy(char *dst, char *src) { - strncpy(dst, src, MAX_REDIS_HKEY_SIZE); - dst[MAX_REDIS_HKEY_SIZE-1] = '\0'; -} - const char * get_tag_type_text(enum tag_type t); #endif diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index fa2783359..7824ae718 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -31,7 +31,7 @@ static int call_stream_address_gstring(GString *o, struct packet_stream *ps, enu int len, ret; char buf[64]; /* 64 bytes ought to be enough for anybody */ - ret = call_stream_address(buf, ps, format, &len); + ret = call_stream_address46(buf, ps, format, &len, NULL); g_string_append_len(o, buf, len); return ret; } @@ -66,7 +66,7 @@ found: if (format == SAF_TCP) call_stream_address_gstring(o, ps, format); - port = ps->sfd ? ps->sfd->fd.localport : 0; + port = ps->selected_sfd ? ps->selected_sfd->socket.local.port : 0; g_string_append_printf(o, (format == 1) ? "%i " : " %i", port); if (format == SAF_UDP) { @@ -83,7 +83,6 @@ out: } static int addr_parse_udp(struct stream_params *sp, char **out) { - u_int32_t ip4; const char *cp; char c; int i; @@ -95,13 +94,11 @@ static int addr_parse_udp(struct stream_params *sp, char **out) { sp->protocol = &transport_protocols[PROTO_RTP_AVP]; if (out[RE_UDP_UL_ADDR4] && *out[RE_UDP_UL_ADDR4]) { - ip4 = inet_addr(out[RE_UDP_UL_ADDR4]); - if (ip4 == -1) + if (sockaddr_parse_any(&sp->rtp_endpoint.address, out[RE_UDP_UL_ADDR4])) goto fail; - in4_to_6(&sp->rtp_endpoint.ip46, ip4); } else if (out[RE_UDP_UL_ADDR6] && *out[RE_UDP_UL_ADDR6]) { - if (inet_pton(AF_INET6, out[RE_UDP_UL_ADDR6], &sp->rtp_endpoint.ip46) != 1) + if (sockaddr_parse_any(&sp->rtp_endpoint.address, out[RE_UDP_UL_ADDR4])) goto fail; } else @@ -137,7 +134,7 @@ fail: } static str *call_update_lookup_udp(char **out, struct callmaster *m, enum call_opmode opmode, const char* addr, - const struct sockaddr_in6 *sin) + const endpoint_t *sin) { struct call *c; struct call_monologue *monologue; @@ -162,7 +159,7 @@ static str *call_update_lookup_udp(char **out, struct callmaster *m, enum call_o if (!c->created_from && addr) { c->created_from = call_strdup(c, addr); - c->created_from_addr = *sin; + c->created_from_addr = sin->address; } monologue = call_get_mono_dialogue(c, &fromtag, &totag, NULL); @@ -217,7 +214,7 @@ out: return ret; } -str *call_update_udp(char **out, struct callmaster *m, const char* addr, const struct sockaddr_in6 *sin) { +str *call_update_udp(char **out, struct callmaster *m, const char* addr, const endpoint_t *sin) { return call_update_lookup_udp(out, m, OP_OFFER, addr, sin); } str *call_lookup_udp(char **out, struct callmaster *m) { @@ -240,7 +237,6 @@ static void info_parse(const char *s, GHashTable *ih, struct callmaster *m) { static int streams_parse_func(char **a, void **ret, void *p) { struct stream_params *sp; - u_int32_t ip; int *i; i = p; @@ -250,12 +246,9 @@ static int streams_parse_func(char **a, void **ret, void *p) { SP_SET(sp, RECV); sp->protocol = &transport_protocols[PROTO_RTP_AVP]; - ip = inet_addr(a[0]); - if (ip == -1) + if (endpoint_parse_port_any(&sp->rtp_endpoint, a[0], atoi(a[1]))) goto fail; - in4_to_6(&sp->rtp_endpoint.ip46, ip); - sp->rtp_endpoint.port = atoi(a[1]); sp->index = ++(*i); sp->consecutive_ports = 1; @@ -648,13 +641,13 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *inpu out->transport_protocol = transport_protocol(&out->transport_protocol_str); bencode_get_alt(input, "media-address", "media address", &out->media_address); if (bencode_get_alt(input, "address-family", "address family", &out->address_family_str)) - out->address_family = address_family(&out->address_family_str); + out->address_family = get_socket_family_rfc(&out->address_family_str); out->tos = bencode_dictionary_get_integer(input, "TOS", 256); } static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output, enum call_opmode opmode, const char* addr, - const struct sockaddr_in6 *sin) + const endpoint_t *sin) { str sdp, fromtag, totag = STR_NULL, callid, viabranch; char *errstr; @@ -697,7 +690,7 @@ static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster if (!call->created_from && addr) { call->created_from = call_strdup(call, addr); - call->created_from_addr = *sin; + call->created_from_addr = sin->address; } /* At least the random ICE strings are contained within the call struct, so we * need to hold a ref until we're done sending the reply */ @@ -734,6 +727,12 @@ static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster gettimeofday(&(monologue->started), NULL); errstr = "Error rewriting SDP"; + + if (ret == ERROR_NO_FREE_PORTS || ret == ERROR_NO_FREE_LOGS) { + ilog(LOG_ERR, "Destroying call"); + call_destroy(call); + } + if (ret) goto out; @@ -750,7 +749,7 @@ out: } const char *call_offer_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output, const char* addr, - const struct sockaddr_in6 *sin) + const endpoint_t *sin) { if (m->conf.max_sessions>=0) { rwlock_lock_r(&m->hashlock); @@ -822,18 +821,11 @@ static void ng_stats(bencode_item_t *d, const struct stats *s, struct stats *tot atomic64_add_na(&totals->errors, atomic64_get(&s->errors)); } -static void ng_stats_endpoint(bencode_item_t *dict, const struct endpoint *ep) { - char buf[64]; - - if (IN6_IS_ADDR_V4MAPPED(&ep->ip46)) { - bencode_dictionary_add_string(dict, "family", "IPv4"); - inet_ntop(AF_INET, &(ep->ip46.s6_addr32[3]), buf, sizeof(buf)); - } - else { - bencode_dictionary_add_string(dict, "family", "IPv6"); - inet_ntop(AF_INET6, &ep->ip46, buf, sizeof(buf)); - } - bencode_dictionary_add_string_dup(dict, "address", buf); +static void ng_stats_endpoint(bencode_item_t *dict, const endpoint_t *ep) { + if (!ep->address.family) + return; + bencode_dictionary_add_string(dict, "family", ep->address.family->name); + bencode_dictionary_add_string_dup(dict, "address", sockaddr_print_buf(&ep->address)); bencode_dictionary_add_integer(dict, "port", ep->port); } @@ -850,8 +842,8 @@ static void ng_stats_stream(bencode_item_t *list, const struct packet_stream *ps dict = bencode_list_add_dictionary(list); - if (ps->sfd) - bencode_dictionary_add_integer(dict, "local port", ps->sfd->fd.localport); + if (ps->selected_sfd) + bencode_dictionary_add_integer(dict, "local port", ps->selected_sfd->socket.local.port); ng_stats_endpoint(bencode_dictionary_add_dictionary(dict, "endpoint"), &ps->endpoint); ng_stats_endpoint(bencode_dictionary_add_dictionary(dict, "advertised endpoint"), &ps->advertised_endpoint); @@ -963,7 +955,7 @@ void ng_call_stats(struct call *call, const str *fromtag, const str *totag, benc { bencode_item_t *tags = NULL, *dict; const str *match_tag; - GSList *l; + GList *l; struct call_monologue *ml; struct call_stats t_b; @@ -985,7 +977,7 @@ stats: match_tag = (totag && totag->s && totag->len) ? totag : fromtag; if (!match_tag || !match_tag->len) { - for (l = call->monologues; l; l = l->next) { + for (l = call->monologues.head; l; l = l->next) { ml = l->data; ng_stats_monologue(tags, ml, totals); } diff --git a/daemon/call_interfaces.h b/daemon/call_interfaces.h index 12db76fab..55f1d9a4a 100644 --- a/daemon/call_interfaces.h +++ b/daemon/call_interfaces.h @@ -6,6 +6,7 @@ #include #include "str.h" #include "bencode.h" +#include "socket.h" @@ -25,13 +26,13 @@ str *call_lookup_tcp(char **, struct callmaster *); void call_delete_tcp(char **, struct callmaster *); void calls_status_tcp(struct callmaster *, struct control_stream *); -str *call_update_udp(char **, struct callmaster *, const char*, const struct sockaddr_in6 *); +str *call_update_udp(char **, struct callmaster *, const char*, const endpoint_t *); str *call_lookup_udp(char **, struct callmaster *); str *call_delete_udp(char **, struct callmaster *); str *call_query_udp(char **, struct callmaster *); const char *call_offer_ng(bencode_item_t *, struct callmaster *, bencode_item_t *, const char*, - const struct sockaddr_in6 *); + const endpoint_t *); const char *call_answer_ng(bencode_item_t *, struct callmaster *, bencode_item_t *); const char *call_delete_ng(bencode_item_t *, struct callmaster *, bencode_item_t *); const char *call_query_ng(bencode_item_t *, struct callmaster *, bencode_item_t *); diff --git a/daemon/cli.c b/daemon/cli.c index 8cdec0906..ad831ffe8 100644 --- a/daemon/cli.c +++ b/daemon/cli.c @@ -13,6 +13,7 @@ #include "log.h" #include "call.h" #include "cli.h" +#include "socket.h" #include "rtpengine_config.h" @@ -86,9 +87,8 @@ static void cli_incoming_list_totals(char* buffer, int len, struct callmaster* m } for (GList *l = list; l; l = l->next) { struct control_ng_stats* cur = l->data; - printlen = snprintf(replybuffer,(outbufend-replybuffer), " %20s | %10u | %10u | %10u | %10u | %10u | %10u | %10u \n", - smart_ntop_p_buf(&cur->proxy), + sockaddr_print_buf(&cur->proxy), cur->offer, cur->answer, cur->delete, @@ -142,7 +142,7 @@ static void cli_incoming_list_callid(char* buffer, int len, struct callmaster* m struct call_monologue *ml; struct call_media *md; struct packet_stream *ps; - GSList *l; + GList *l; GList *k, *o; int printlen=0; struct timeval tim_result_duration; @@ -168,7 +168,7 @@ static void cli_incoming_list_callid(char* buffer, int len, struct callmaster* m c->callid.s , c->ml_deleted?"yes":"no", (int)c->created, c->created_from, (unsigned int)c->tos, (unsigned long long)c->last_signal); ADJUSTLEN(printlen,outbufend,replybuffer); - for (l = c->monologues; l; l = l->next) { + for (l = c->monologues.head; l; l = l->next) { ml = l->data; if (!ml->terminated.tv_sec) { gettimeofday(&now, NULL); @@ -200,7 +200,7 @@ static void cli_incoming_list_callid(char* buffer, int len, struct callmaster* m ""UINT64F" p, "UINT64F" b, "UINT64F" e, "UINT64F" last_packet\n", md->index, (unsigned int) (ps->sfd ? ps->sfd->fd.localport : 0), - smart_ntop_p_buf(&ps->endpoint.ip46), ps->endpoint.port, + sockaddr_print_buf(&ps->endpoint.ip46), ps->endpoint.port, (!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) ? " (RTCP)" : "", atomic64_get(&ps->stats.packets), atomic64_get(&ps->stats.bytes), @@ -211,7 +211,7 @@ static void cli_incoming_list_callid(char* buffer, int len, struct callmaster* m ""UINT64F" p, "UINT64F" b, "UINT64F" e, "UINT64F" last_packet, %.9f delay_min, %.9f delay_avg, %.9f delay_max\n", md->index, (unsigned int) (ps->sfd ? ps->sfd->fd.localport : 0), - smart_ntop_p_buf(&ps->endpoint.ip46), ps->endpoint.port, + sockaddr_print_buf(&ps->endpoint.ip46), ps->endpoint.port, (!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) ? " (RTCP)" : "", atomic64_get(&ps->stats.packets), atomic64_get(&ps->stats.bytes), @@ -222,11 +222,11 @@ static void cli_incoming_list_callid(char* buffer, int len, struct callmaster* m (double) ps->stats.delay_max / 1000000); } #else - printlen = snprintf(replybuffer,(outbufend-replybuffer), "------ Media #%u, port %5u <> %15s:%-5hu%s, " + printlen = snprintf(replybuffer,(outbufend-replybuffer), "------ Media #%u, port %5u <> %15s:%-5u%s, " ""UINT64F" p, "UINT64F" b, "UINT64F" e, "UINT64F" last_packet\n", md->index, - (unsigned int) (ps->sfd ? ps->sfd->fd.localport : 0), - smart_ntop_p_buf(&ps->endpoint.ip46), ps->endpoint.port, + (unsigned int) (ps->selected_sfd ? ps->selected_sfd->socket.local.port : 0), + sockaddr_print_buf(&ps->endpoint.address), ps->endpoint.port, (!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) ? " (RTCP)" : "", atomic64_get(&ps->stats.packets), atomic64_get(&ps->stats.bytes), @@ -408,7 +408,7 @@ static void cli_incoming_terminate(char* buffer, int len, struct callmaster* m, GHashTableIter iter; gpointer key, value; struct call_monologue *ml; - GSList *i; + GList *i; if (len<=1) { printlen = snprintf(replybuffer, outbufend-replybuffer, "%s\n", "More parameters required."); @@ -426,7 +426,7 @@ static void cli_incoming_terminate(char* buffer, int len, struct callmaster* m, c = (struct call*)value; if (!c) continue; if (!c->ml_deleted) { - for (i = c->monologues; i; i = i->next) { + for (i = c->monologues.head; i; i = i->next) { ml = i->data; gettimeofday(&(ml->terminated), NULL); ml->term_reason = FORCED; @@ -450,7 +450,7 @@ static void cli_incoming_terminate(char* buffer, int len, struct callmaster* m, } if (!c->ml_deleted) { - for (i = c->monologues; i; i = i->next) { + for (i = c->monologues.head; i; i = i->next) { ml = i->data; gettimeofday(&(ml->terminated), NULL); ml->term_reason = FORCED; @@ -538,40 +538,28 @@ static void control_closed(int fd, void *p, uintptr_t u) { abort(); } -struct cli *cli_new(struct poller *p, u_int32_t ip, u_int16_t port, struct callmaster *m) { +struct cli *cli_new(struct poller *p, const endpoint_t *ep, struct callmaster *m) { struct cli *c; - int fd; - struct sockaddr_in sin; + socket_t sock; struct poller_item i; if (!p || !m) return NULL; - fd = socket(AF_INET, SOCK_STREAM, 0); - if (fd == -1) - return NULL; - - nonblock(fd); - reuseaddr(fd); - - ZERO(sin); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = ip; - sin.sin_port = htons(port); - if (bind(fd, (struct sockaddr *) &sin, sizeof(sin))) - goto fail; + if (open_socket(&sock, SOCK_STREAM, ep->port, &ep->address)) + return NULL; - if (listen(fd, 5)) + if (listen(sock.fd, 5)) goto fail; c = obj_alloc0("cli_udp", sizeof(*c), NULL); - c->fd = fd; + c->sock = sock; c->poller = p; c->callmaster = m; mutex_init(&c->lock); ZERO(i); - i.fd = fd; + i.fd = sock.fd; i.closed = control_closed; i.readable = cli_incoming; i.obj = &c->obj; @@ -584,6 +572,6 @@ struct cli *cli_new(struct poller *p, u_int32_t ip, u_int16_t port, struct callm fail2: obj_put(c); fail: - close(fd); + close_socket(&sock); return NULL; } diff --git a/daemon/cli.h b/daemon/cli.h index af562107a..e5065b4ca 100644 --- a/daemon/cli.h +++ b/daemon/cli.h @@ -1,18 +1,19 @@ #ifndef CLI_UDP_H_ #define CLI_UDP_H_ -#include +#include "socket.h" +#include "obj.h" struct cli { struct obj obj; struct callmaster *callmaster; - int fd; + socket_t sock; struct poller *poller; mutex_t lock; }; -struct cli *cli_new(struct poller *p, u_int32_t ip, u_int16_t port, struct callmaster *m); +struct cli *cli_new(struct poller *p, const endpoint_t *, struct callmaster *m); #endif /* CLI_UDP_H_ */ diff --git a/daemon/control_ng.c b/daemon/control_ng.c index 29ee575e7..1f9735a47 100644 --- a/daemon/control_ng.c +++ b/daemon/control_ng.c @@ -11,6 +11,7 @@ #include "call.h" #include "sdp.h" #include "call_interfaces.h" +#include "socket.h" static void pretty_print(bencode_item_t *el, GString *s) { @@ -58,7 +59,7 @@ static void pretty_print(bencode_item_t *el, GString *s) { } } -struct control_ng_stats* get_control_ng_stats(struct control_ng* c, const struct in6_addr *addr) { +struct control_ng_stats* get_control_ng_stats(struct control_ng* c, const sockaddr_t *addr) { struct callmaster *m = c->callmaster; struct control_ng_stats* cur; @@ -67,24 +68,26 @@ struct control_ng_stats* get_control_ng_stats(struct control_ng* c, const struct if (!cur) { cur = g_slice_alloc0(sizeof(struct control_ng_stats)); cur->proxy = *addr; - ilog(LOG_DEBUG,"Adding a proxy for control ng stats:%s", smart_ntop_p_buf(addr)); + ilog(LOG_DEBUG,"Adding a proxy for control ng stats:%s", sockaddr_print_buf(addr)); g_hash_table_insert(m->cngs_hash, &cur->proxy, cur); } mutex_unlock(&m->cngs_lock); return cur; } -static void control_ng_incoming(struct obj *obj, str *buf, struct sockaddr_in6 *sin, char *addr) { +static void control_ng_incoming(struct obj *obj, str *buf, const endpoint_t *sin, char *addr, + struct udp_listener *ul) +{ struct control_ng *c = (void *) obj; bencode_buffer_t bencbuf; bencode_item_t *dict, *resp; str cmd, cookie, data, reply, *to_send, callid; const char *errstr; - struct msghdr mh; struct iovec iov[3]; + unsigned int iovlen; GString *log_str; - struct control_ng_stats* cur = get_control_ng_stats(c,&sin->sin6_addr); + struct control_ng_stats* cur = get_control_ng_stats(c,&sin->address); str_chr_str(&data, buf, ' '); if (!data.s || data.s == buf->s) { @@ -195,11 +198,7 @@ send_resp: } send_only: - ZERO(mh); - mh.msg_name = sin; - mh.msg_namelen = sizeof(*sin); - mh.msg_iov = iov; - mh.msg_iovlen = 3; + iovlen = 3; iov[0].iov_base = cookie.s; iov[0].iov_len = cookie.len; @@ -208,7 +207,7 @@ send_only: iov[2].iov_base = to_send->s; iov[2].iov_len = to_send->len; - sendmsg(c->udp_listener.fd, &mh, 0); + socket_sendiov(&ul->sock, iov, iovlen, sin); if (resp) cookie_cache_insert(&c->cookie_cache, &cookie, &reply); @@ -224,7 +223,7 @@ out: -struct control_ng *control_ng_new(struct poller *p, struct in6_addr ip, u_int16_t port, struct callmaster *m) { +struct control_ng *control_ng_new(struct poller *p, endpoint_t *ep, struct callmaster *m) { struct control_ng *c; if (!p || !m) @@ -235,7 +234,9 @@ struct control_ng *control_ng_new(struct poller *p, struct in6_addr ip, u_int16_ c->callmaster = m; cookie_cache_init(&c->cookie_cache); - if (udp_listener_init(&c->udp_listener, p, ip, port, control_ng_incoming, &c->obj)) + if (udp_listener_init(&c->udp_listeners[0], p, ep, control_ng_incoming, &c->obj)) + goto fail2; + if (ipv46_any_convert(ep) && udp_listener_init(&c->udp_listeners[1], p, ep, control_ng_incoming, &c->obj)) goto fail2; return c; diff --git a/daemon/control_ng.h b/daemon/control_ng.h index 611ab51e0..919cd872a 100644 --- a/daemon/control_ng.h +++ b/daemon/control_ng.h @@ -4,13 +4,14 @@ #include "obj.h" #include "cookie_cache.h" #include "udp_listener.h" +#include "socket.h" struct poller; struct callmaster; struct control_ng_stats { - struct in6_addr proxy; + sockaddr_t proxy; int ping; int offer; int answer; @@ -24,9 +25,9 @@ struct control_ng { struct obj obj; struct callmaster *callmaster; struct cookie_cache cookie_cache; - struct udp_listener udp_listener; + struct udp_listener udp_listeners[2]; }; -struct control_ng *control_ng_new(struct poller *, struct in6_addr, u_int16_t, struct callmaster *); +struct control_ng *control_ng_new(struct poller *, endpoint_t *, struct callmaster *); #endif diff --git a/daemon/control_tcp.c b/daemon/control_tcp.c index 06ff565dd..e21d6b2f2 100644 --- a/daemon/control_tcp.c +++ b/daemon/control_tcp.c @@ -16,6 +16,7 @@ #include "log.h" #include "call.h" #include "call_interfaces.h" +#include "socket.h" @@ -271,11 +272,10 @@ fail: } -struct control_tcp *control_tcp_new(struct poller *p, u_int32_t ip, u_int16_t port, struct callmaster *m) { - int fd; +struct control_tcp *control_tcp_new(struct poller *p, const endpoint_t *ep, struct callmaster *m) { + socket_t sock; struct control_tcp *c; struct poller_item i; - struct sockaddr_in sin; const char *errptr; int erroff; @@ -284,21 +284,10 @@ struct control_tcp *control_tcp_new(struct poller *p, u_int32_t ip, u_int16_t po if (!m) return NULL; - fd = socket(AF_INET, SOCK_STREAM, 0); - if (fd == -1) + if (open_socket(&sock, SOCK_STREAM, ep->port, &ep->address)) return NULL; - nonblock(fd); - reuseaddr(fd); - - ZERO(sin); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = ip; - sin.sin_port = htons(port); - if (bind(fd, (struct sockaddr *) &sin, sizeof(sin))) - goto fail; - - if (listen(fd, 5)) + if (listen(sock.fd, 5)) goto fail; @@ -310,13 +299,13 @@ struct control_tcp *control_tcp_new(struct poller *p, u_int32_t ip, u_int16_t po PCRE_DOLLAR_ENDONLY | PCRE_DOTALL, &errptr, &erroff, NULL); c->parse_ree = pcre_study(c->parse_re, 0, &errptr); - c->fd = fd; + c->fd = sock.fd; c->poller = p; c->callmaster = m; mutex_init(&c->lock); ZERO(i); - i.fd = fd; + i.fd = sock.fd; i.closed = control_closed; i.readable = control_incoming; i.obj = &c->obj; @@ -329,7 +318,7 @@ struct control_tcp *control_tcp_new(struct poller *p, u_int32_t ip, u_int16_t po fail2: obj_put(c); fail: - close(fd); + close_socket(&sock); return NULL; } diff --git a/daemon/control_tcp.h b/daemon/control_tcp.h index b595fd304..06b3c298f 100644 --- a/daemon/control_tcp.h +++ b/daemon/control_tcp.h @@ -11,6 +11,7 @@ #include "obj.h" #include "aux.h" +#include "socket.h" #define RE_TCP_RL_CMD 1 @@ -35,7 +36,7 @@ struct control_stream; -struct control_tcp *control_tcp_new(struct poller *, u_int32_t, u_int16_t, struct callmaster *); +struct control_tcp *control_tcp_new(struct poller *, const endpoint_t *, struct callmaster *); void control_stream_printf(struct control_stream *, const char *, ...) __attribute__ ((format (printf, 2, 3))); diff --git a/daemon/control_udp.c b/daemon/control_udp.c index 0fcf218ed..48ffa337a 100644 --- a/daemon/control_udp.c +++ b/daemon/control_udp.c @@ -17,15 +17,17 @@ #include "call.h" #include "udp_listener.h" #include "call_interfaces.h" +#include "socket.h" -static void control_udp_incoming(struct obj *obj, str *buf, struct sockaddr_in6 *sin, char *addr) { +static void control_udp_incoming(struct obj *obj, str *buf, const endpoint_t *sin, char *addr, + struct udp_listener *ul) { struct control_udp *u = (void *) obj; int ret; int ovec[100]; char **out; - struct msghdr mh; struct iovec iov[10]; + unsigned int iovlen; str cookie, *reply; ret = pcre_exec(u->parse_re, u->parse_ree, buf->s, buf->len, 0, 0, ovec, G_N_ELEMENTS(ovec)); @@ -40,11 +42,6 @@ static void control_udp_incoming(struct obj *obj, str *buf, struct sockaddr_in6 pcre_get_substring_list(buf->s, ovec, ret, (const char ***) &out); - ZERO(mh); - mh.msg_name = sin; - mh.msg_namelen = sizeof(*sin); - mh.msg_iov = iov; - iov[0].iov_base = (void *) out[RE_UDP_COOKIE]; iov[0].iov_len = strlen(out[RE_UDP_COOKIE]); if (out[RE_UDP_UL_CMD] && (chrtoupper(out[RE_UDP_UL_CMD][0]) == 'U' || chrtoupper(out[RE_UDP_UL_CMD][0]) == 'L')) { @@ -54,15 +51,15 @@ static void control_udp_incoming(struct obj *obj, str *buf, struct sockaddr_in6 iov[2].iov_len = strlen(out[3]); iov[3].iov_base = "\n"; iov[3].iov_len = 1; - mh.msg_iovlen = 4; + iovlen = 4; } else { iov[1].iov_base = " E8\n"; iov[1].iov_len = 4; - mh.msg_iovlen = 2; + iovlen = 2; } - sendmsg(u->udp_listener.fd, &mh, 0); + socket_sendiov(&ul->sock, iov, iovlen, sin); pcre_free(out); @@ -77,7 +74,7 @@ static void control_udp_incoming(struct obj *obj, str *buf, struct sockaddr_in6 reply = cookie_cache_lookup(&u->cookie_cache, &cookie); if (reply) { ilog(LOG_INFO, "Detected command from udp:%s as a duplicate", addr); - sendto(u->udp_listener.fd, reply->s, reply->len, 0, (struct sockaddr *) sin, sizeof(*sin)); + socket_sendto(&ul->sock, reply->s, reply->len, sin); free(reply); goto out; } @@ -96,11 +93,7 @@ static void control_udp_incoming(struct obj *obj, str *buf, struct sockaddr_in6 else if (chrtoupper(out[RE_UDP_DQ_CMD][0]) == 'Q') reply = call_query_udp(out, u->callmaster); else if (chrtoupper(out[RE_UDP_V_CMD][0]) == 'V') { - ZERO(mh); - mh.msg_name = sin; - mh.msg_namelen = sizeof(*sin); - mh.msg_iov = iov; - mh.msg_iovlen = 2; + iovlen = 2; iov[0].iov_base = (void *) out[RE_UDP_COOKIE]; iov[0].iov_len = strlen(out[RE_UDP_COOKIE]); @@ -117,18 +110,18 @@ static void control_udp_incoming(struct obj *obj, str *buf, struct sockaddr_in6 ret = 1; iov[2].iov_base = ret ? "1\n" : "0\n"; iov[2].iov_len = 2; - mh.msg_iovlen++; + iovlen++; } else { iov[2].iov_base = "20040107\n"; iov[2].iov_len = 9; - mh.msg_iovlen++; + iovlen++; } - sendmsg(u->udp_listener.fd, &mh, 0); + socket_sendiov(&ul->sock, iov, iovlen, sin); } if (reply) { - sendto(u->udp_listener.fd, reply->s, reply->len, 0, (struct sockaddr *) sin, sizeof(*sin)); + socket_sendto(&ul->sock, reply->s, reply->len, sin); cookie_cache_insert(&u->cookie_cache, &cookie, reply); free(reply); } @@ -140,7 +133,7 @@ out: log_info_clear(); } -struct control_udp *control_udp_new(struct poller *p, struct in6_addr ip, u_int16_t port, struct callmaster *m) { +struct control_udp *control_udp_new(struct poller *p, endpoint_t *ep, struct callmaster *m) { struct control_udp *c; const char *errptr; int erroff; @@ -172,7 +165,9 @@ struct control_udp *control_udp_new(struct poller *p, struct in6_addr ip, u_int1 cookie_cache_init(&c->cookie_cache); - if (udp_listener_init(&c->udp_listener, p, ip, port, control_udp_incoming, &c->obj)) + if (udp_listener_init(&c->udp_listeners[0], p, ep, control_udp_incoming, &c->obj)) + goto fail2; + if (ipv46_any_convert(ep) && udp_listener_init(&c->udp_listeners[1], p, ep, control_udp_incoming, &c->obj)) goto fail2; return c; diff --git a/daemon/control_udp.h b/daemon/control_udp.h index a4cb6a54a..0381c39e0 100644 --- a/daemon/control_udp.h +++ b/daemon/control_udp.h @@ -13,6 +13,7 @@ #include "aux.h" #include "cookie_cache.h" #include "udp_listener.h" +#include "socket.h" @@ -49,7 +50,7 @@ struct control_udp { struct callmaster *callmaster; struct cookie_cache cookie_cache; - struct udp_listener udp_listener; + struct udp_listener udp_listeners[2]; pcre *parse_re; pcre_extra *parse_ree; @@ -60,7 +61,7 @@ struct control_udp { -struct control_udp *control_udp_new(struct poller *, struct in6_addr, u_int16_t, struct callmaster *); +struct control_udp *control_udp_new(struct poller *, endpoint_t *, struct callmaster *); diff --git a/daemon/dtls.c b/daemon/dtls.c index 94e4904a8..c5f062233 100644 --- a/daemon/dtls.c +++ b/daemon/dtls.c @@ -475,12 +475,12 @@ int dtls_connection_init(struct packet_stream *ps, int active, struct dtls_cert struct dtls_connection *d; unsigned long err; - if (!ps || !ps->sfd) + if (!ps || !ps->selected_sfd) return 0; __DBG("dtls_connection_init(%i)", active); - d = &ps->sfd->dtls; + d = &ps->selected_sfd->dtls; if (d->init) { if ((d->active && active) || (!d->active && !active)) @@ -516,7 +516,7 @@ int dtls_connection_init(struct packet_stream *ps, int active, struct dtls_cert if (!d->r_bio || !d->w_bio) goto error; - SSL_set_app_data(d->ssl, ps->sfd); /* XXX obj reference here? */ + SSL_set_app_data(d->ssl, ps->selected_sfd); /* XXX obj reference here? */ SSL_set_bio(d->ssl, d->r_bio, d->w_bio); SSL_set_mode(d->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); @@ -613,15 +613,15 @@ found: if (d->active) { /* we're the client */ crypto_init(&ps->crypto, &client); - crypto_init(&ps->sfd->crypto, &server); + crypto_init(&ps->selected_sfd->crypto, &server); } else { /* we're the server */ crypto_init(&ps->crypto, &server); - crypto_init(&ps->sfd->crypto, &client); + crypto_init(&ps->selected_sfd->crypto, &client); } - crypto_dump_keys(&ps->crypto, &ps->sfd->crypto); + crypto_dump_keys(&ps->crypto, &ps->selected_sfd->crypto); return 0; @@ -635,20 +635,17 @@ error: } /* called with call locked in W or R with ps->in_lock held */ -int dtls(struct packet_stream *ps, const str *s, struct sockaddr_in6 *fsin) { +int dtls(struct packet_stream *ps, const str *s, const endpoint_t *fsin) { struct dtls_connection *d; int ret; - unsigned char buf[0x10000], ctrl[256]; - struct msghdr mh; - struct iovec iov; - struct sockaddr_in6 sin; + unsigned char buf[0x10000]; - if (!ps || !ps->sfd) + if (!ps || !ps->selected_sfd) return 0; if (!MEDIA_ISSET(ps->media, DTLS)) return 0; - d = &ps->sfd->dtls; + d = &ps->selected_sfd->dtls; if (s) __DBG("dtls packet input: len %u %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", @@ -670,7 +667,7 @@ int dtls(struct packet_stream *ps, const str *s, struct sockaddr_in6 *fsin) { ret = try_connect(d); if (ret == -1) { - ilog(LOG_ERROR, "DTLS error on local port %hu", ps->sfd->fd.localport); + ilog(LOG_ERROR, "DTLS error on local port %u", ps->selected_sfd->socket.local.port); /* fatal error */ dtls_connection_cleanup(d); return 0; @@ -709,30 +706,11 @@ int dtls(struct packet_stream *ps, const str *s, struct sockaddr_in6 *fsin) { buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); - if (!fsin) { - ZERO(sin); - sin.sin6_family = AF_INET6; - sin.sin6_addr = ps->endpoint.ip46; - sin.sin6_port = htons(ps->endpoint.port); - fsin = &sin; - } - - ZERO(mh); - mh.msg_control = ctrl; - mh.msg_controllen = sizeof(ctrl); - mh.msg_name = fsin; - mh.msg_namelen = sizeof(*fsin); - mh.msg_iov = &iov; - mh.msg_iovlen = 1; - - ZERO(iov); - iov.iov_base = buf; - iov.iov_len = ret; - - stream_msg_mh_src(ps, &mh); + if (!fsin) + fsin = &ps->endpoint; ilog(LOG_DEBUG, "Sending DTLS packet"); - sendmsg(ps->sfd->fd.fd, &mh, 0); + socket_sendto(&ps->selected_sfd->socket, buf, ret, fsin); } return 0; @@ -741,25 +719,19 @@ int dtls(struct packet_stream *ps, const str *s, struct sockaddr_in6 *fsin) { /* call must be locked */ void dtls_shutdown(struct packet_stream *ps) { struct dtls_connection *d; - struct sockaddr_in6 sin; - if (!ps || !ps->sfd) + if (!ps || !ps->selected_sfd) return; __DBG("dtls_shutdown"); - d = &ps->sfd->dtls; + d = &ps->selected_sfd->dtls; if (!d->init) return; if (d->connected && d->ssl) { - ZERO(sin); - sin.sin6_family = AF_INET6; - sin.sin6_addr = ps->endpoint.ip46; - sin.sin6_port = htons(ps->endpoint.port); - SSL_shutdown(d->ssl); - dtls(ps, NULL, &sin); + dtls(ps, NULL, &ps->endpoint); } dtls_connection_cleanup(d); @@ -770,7 +742,7 @@ void dtls_shutdown(struct packet_stream *ps) { } crypto_reset(&ps->crypto); - crypto_reset(&ps->sfd->crypto); + crypto_reset(&ps->selected_sfd->crypto); } void dtls_connection_cleanup(struct dtls_connection *c) { diff --git a/daemon/dtls.h b/daemon/dtls.h index e4223615c..17f1541a9 100644 --- a/daemon/dtls.h +++ b/daemon/dtls.h @@ -11,6 +11,7 @@ #include "compat.h" #include "str.h" #include "obj.h" +#include "socket.h" @@ -65,7 +66,7 @@ const struct dtls_hash_func *dtls_find_hash_func(const str *); struct dtls_cert *dtls_cert(void); int dtls_connection_init(struct packet_stream *, int active, struct dtls_cert *cert); -int dtls(struct packet_stream *, const str *s, struct sockaddr_in6 *sin); +int dtls(struct packet_stream *, const str *s, const endpoint_t *sin); void dtls_connection_cleanup(struct dtls_connection *); void dtls_shutdown(struct packet_stream *ps); diff --git a/daemon/graphite.c b/daemon/graphite.c index 00ed46a90..91ba8361b 100644 --- a/daemon/graphite.c +++ b/daemon/graphite.c @@ -18,11 +18,11 @@ #include "log.h" #include "call.h" #include "graphite.h" +#include "socket.h" -static int graphite_sock=-1; +static socket_t graphite_sock; +static const endpoint_t *graphite_ep; static int connectinprogress=0; -static u_int32_t graphite_ipaddress; -static int graphite_port=0; static struct callmaster* cm=0; //struct totalstats totalstats_prev; static time_t next_run; @@ -39,65 +39,33 @@ void set_prefix(char* prefix) { graphite_prefix = prefix; } -int connect_to_graphite_server(u_int32_t ipaddress, int port) { +int connect_to_graphite_server(const endpoint_t *ep) { + graphite_ep = ep; + int rc; - if (graphite_sock>0) - close(graphite_sock); + ilog(LOG_INFO, "Connecting to graphite server %s", endpoint_print_buf(ep)); - graphite_sock=-1; - - int rc=0; - struct sockaddr_in sin; - memset(&sin,0,sizeof(sin)); - int val=1; - - graphite_ipaddress = ipaddress; - graphite_port = port; - - graphite_sock = socket(AF_INET, SOCK_STREAM,0); - if(graphite_sock<0) { - ilog(LOG_ERROR,"Couldn't make socket for connecting to graphite.Reason:%s\n",strerror(errno)); + rc = connect_socket_nb(&graphite_sock, SOCK_STREAM, ep); + if (rc == -1) { + ilog(LOG_ERROR,"Couldn't make socket for connecting to graphite."); return -1; } - - sin.sin_family=AF_INET; - sin.sin_addr.s_addr=graphite_ipaddress; - sin.sin_port=htons(graphite_port); - - rc = setsockopt(graphite_sock,SOL_SOCKET,SO_REUSEADDR, &val,sizeof(val)); - if(rc<0) { - ilog(LOG_ERROR,"Couldn't set sockopt for graphite descriptor."); - goto error; - } - - nonblock(graphite_sock); - - struct in_addr ip; - ip.s_addr = graphite_ipaddress; - ilog(LOG_INFO, "Connecting to graphite server %s at port:%i with fd:%i",inet_ntoa(ip),graphite_port,graphite_sock); - rc = connect(graphite_sock, (struct sockaddr *)&sin, sizeof(sin)); - if (rc==-1) { - ilog(LOG_WARN, "Connection information:%s\n",strerror(errno)); - if (errno==EINPROGRESS) { - connectinprogress=1; - return 0; - } - goto error; + if (rc == 0) + ilog(LOG_INFO, "Graphite server connected."); + else { + /* EINPROGRESS */ + ilog(LOG_INFO, "Connection to graphite is in progress."); + connectinprogress = 1; } return 0; - -error: - close(graphite_sock); - graphite_sock = -1; - return -1; } int send_graphite_data(struct totalstats *sent_data) { int rc=0; - if (graphite_sock < 0) { + if (graphite_sock.fd < 0) { ilog(LOG_ERROR,"Graphite socket is not connected."); return -1; } @@ -186,7 +154,7 @@ int send_graphite_data(struct totalstats *sent_data) { (unsigned long long ) ts->total_calls_duration_interval.tv_usec, (unsigned long long ) g_now.tv_sec); - rc = write(graphite_sock, data_to_send, ptr - data_to_send); + rc = write(graphite_sock.fd, data_to_send, ptr - data_to_send); if (rc<0) { ilog(LOG_ERROR,"Could not write to graphite socket. Disconnecting graphite server."); goto error; @@ -194,7 +162,7 @@ int send_graphite_data(struct totalstats *sent_data) { return 0; error: - close(graphite_sock); graphite_sock=-1; + close_socket(&graphite_sock); return -1; } @@ -213,33 +181,30 @@ void graphite_loop_run(struct callmaster* callmaster, int seconds) { int optval=0; socklen_t optlen=sizeof(optval); - if (connectinprogress && graphite_sock>0) { - FD_SET(graphite_sock,&wfds); + if (connectinprogress && graphite_sock.fd >= 0) { + FD_SET(graphite_sock.fd,&wfds); tv.tv_sec = 0; tv.tv_usec = 1000000; - rc = select (graphite_sock+1, NULL, &wfds, NULL, &tv); + rc = select (graphite_sock.fd+1, NULL, &wfds, NULL, &tv); if ((rc == -1) && (errno == EINTR)) { ilog(LOG_ERROR,"Error on the socket."); - close(graphite_sock); - graphite_sock=-1;connectinprogress=0; + close_socket(&graphite_sock); return; } else if (rc==0) { // timeout return; } else { - if (!FD_ISSET(graphite_sock,&wfds)) { + if (!FD_ISSET(graphite_sock.fd,&wfds)) { ilog(LOG_WARN,"fd active but not the graphite fd."); - close(graphite_sock); - graphite_sock=-1;connectinprogress=0; + close_socket(&graphite_sock); return; } - rc = getsockopt(graphite_sock, SOL_SOCKET, SO_ERROR, &optval, &optlen); + rc = getsockopt(graphite_sock.fd, SOL_SOCKET, SO_ERROR, &optval, &optlen); if (rc) ilog(LOG_ERROR,"getsockopt failure."); if (optval != 0) { - ilog(LOG_ERROR,"Socket connect failed. fd: %i, Reason: %s\n",graphite_sock, strerror(optval)); - close(graphite_sock); - graphite_sock=-1;connectinprogress=0; + ilog(LOG_ERROR,"Socket connect failed. fd: %i, Reason: %s\n",graphite_sock.fd, strerror(optval)); + close_socket(&graphite_sock); return; } ilog(LOG_INFO, "Graphite server connected."); @@ -259,15 +224,11 @@ void graphite_loop_run(struct callmaster* callmaster, int seconds) { if (!cm) cm = callmaster; - if (graphite_sock < 0 && !connectinprogress) { - rc = connect_to_graphite_server(graphite_ipaddress, graphite_port); - if (rc) { - close(graphite_sock); - graphite_sock=-1; - } + if (graphite_sock.fd < 0 && !connectinprogress) { + rc = connect_to_graphite_server(graphite_ep); } - if (graphite_sock>0 && !connectinprogress) { + if (graphite_sock.fd >= 0 && !connectinprogress) { add_total_calls_duration_in_interval(cm, &graphite_interval_tv); rc = send_graphite_data(&graphite_stats); @@ -276,7 +237,7 @@ void graphite_loop_run(struct callmaster* callmaster, int seconds) { ilog(LOG_ERROR,"Sending graphite data failed."); } - copy_with_lock(&cm->totalstats_lastinterval, &graphite_stats, &cm->totalstats_lastinterval_lock); + copy_with_lock(&cm->totalstats_lastinterval, &graphite_stats, &cm->totalstats_lastinterval.total_average_lock); } } @@ -289,7 +250,7 @@ void graphite_loop(void *d) { cm->conf.graphite_interval=1; } - connect_to_graphite_server(cm->conf.graphite_ip,cm->conf.graphite_port); + connect_to_graphite_server(&cm->conf.graphite_ep); while (!g_shutdown) graphite_loop_run(cm,cm->conf.graphite_interval); // time in seconds diff --git a/daemon/graphite.h b/daemon/graphite.h index 784bce274..e6948aa43 100644 --- a/daemon/graphite.h +++ b/daemon/graphite.h @@ -10,7 +10,7 @@ #include "call.h" -int connect_to_graphite_server(u_int32_t ipaddress, int port); +int connect_to_graphite_server(const endpoint_t *ep); int send_graphite_data(); void graphite_loop_run(struct callmaster* cm, int seconds); void set_prefix(char* prefix); diff --git a/daemon/ice.c b/daemon/ice.c index 4d258988a..fbf50c544 100644 --- a/daemon/ice.c +++ b/daemon/ice.c @@ -28,7 +28,7 @@ #define PAIR_FORMAT STR_FORMAT":"STR_FORMAT":%lu" #define PAIR_FMT(p) \ - STR_FMT(&(p)->local_address->ice_foundation), \ + STR_FMT(&(p)->local_intf->spec->ice_foundation), \ STR_FMT(&(p)->remote_candidate->foundation), \ (p)->remote_candidate->component_id @@ -39,7 +39,7 @@ static void __ice_agent_free(void *p); static void create_random_ice_string(struct call *call, str *s, int len); static void __do_ice_checks(struct ice_agent *ag); static struct ice_candidate_pair *__pair_lookup(struct ice_agent *, struct ice_candidate *cand, - struct interface_address *ifa); + const struct local_intf *ifa); static void __recalc_pair_prios(struct ice_agent *ag); static void __role_change(struct ice_agent *ag, int new_controlling); static void __get_complete_components(GQueue *out, struct ice_agent *ag, GTree *t, unsigned int); @@ -88,18 +88,6 @@ enum ice_candidate_type ice_candidate_type(const str *s) { return ICT_UNKNOWN; } -enum ice_transport ice_transport(const str *s) { - if (!str_cmp(s, "udp")) - return ITP_UDP; -// if (!str_cmp(s, "tcp")) -// return ITP_TCP; - if (!str_cmp(s, "UDP")) - return ITP_UDP; -// if (!str_cmp(s, "TCP")) -// return ITP_TCP; - return ITP_UNKNOWN; -} - int ice_has_related(enum ice_candidate_type t) { if (t == ICT_HOST) return 0; @@ -109,12 +97,12 @@ int ice_has_related(enum ice_candidate_type t) { -static u_int64_t __ice_pair_priority(struct interface_address *ifa, struct ice_candidate *cand, +static u_int64_t __ice_pair_priority(const struct local_intf *ifa, struct ice_candidate *cand, int controlling) { u_int64_t g, d; - g = ice_priority(ICT_HOST, ifa->preference, cand->component_id); + g = ice_priority(ICT_HOST, ifa->unique_id, cand->component_id); d = cand->priority; if (!controlling) { @@ -126,7 +114,7 @@ static u_int64_t __ice_pair_priority(struct interface_address *ifa, struct ice_c return (MIN(g,d) << 32) + (MAX(g,d) << 1) + (g > d ? 1 : 0); } static void __do_ice_pair_priority(struct ice_candidate_pair *pair) { - pair->pair_priority = __ice_pair_priority(pair->local_address, pair->remote_candidate, + pair->pair_priority = __ice_pair_priority(pair->local_intf, pair->remote_candidate, AGENT_ISSET(pair->agent, CONTROLLING)); } static void __new_stun_transaction(struct ice_candidate_pair *pair) { @@ -144,20 +132,20 @@ static void __all_pairs_list(struct ice_agent *ag) { } /* agent must be locked */ -static struct ice_candidate_pair *__pair_candidate(struct interface_address *addr, struct ice_agent *ag, - struct ice_candidate *cand, struct packet_stream *ps) +static struct ice_candidate_pair *__pair_candidate(struct stream_fd *sfd, struct ice_agent *ag, + struct ice_candidate *cand) { struct ice_candidate_pair *pair; - if (addr->family != family_from_address(&cand->endpoint.ip46)) + if (sfd->socket.family != cand->endpoint.address.family) return NULL; pair = g_slice_alloc0(sizeof(*pair)); pair->agent = ag; pair->remote_candidate = cand; - pair->local_address = addr; - pair->packet_stream = ps; + pair->local_intf = sfd->local_intf; + pair->sfd = sfd; if (cand->component_id != 1) PAIR_SET(pair, FROZEN); __do_ice_pair_priority(pair); @@ -168,7 +156,8 @@ static struct ice_candidate_pair *__pair_candidate(struct interface_address *add g_tree_insert(ag->all_pairs, pair, pair); ilog(LOG_DEBUG, "Created candidate pair "PAIR_FORMAT" between %s and %s, type %s", PAIR_FMT(pair), - smart_ntop_buf(&addr->addr), smart_ntop_ep_buf(&cand->endpoint), + sockaddr_print_buf(&sfd->socket.local.address), + endpoint_print_buf(&cand->endpoint), ice_candidate_type_str(cand->type)); return pair; @@ -176,22 +165,21 @@ static struct ice_candidate_pair *__pair_candidate(struct interface_address *add static unsigned int __pair_hash(const void *p) { const struct ice_candidate_pair *pair = p; - return g_direct_hash(pair->local_address) ^ g_direct_hash(pair->remote_candidate); + return g_direct_hash(pair->local_intf) ^ g_direct_hash(pair->remote_candidate); } static int __pair_equal(const void *a, const void *b) { const struct ice_candidate_pair *A = a, *B = b; - return A->local_address == B->local_address + return A->local_intf == B->local_intf && A->remote_candidate == B->remote_candidate; } static unsigned int __cand_hash(const void *p) { const struct ice_candidate *cand = p; - return in6_addr_hash(&cand->endpoint.ip46) ^ cand->endpoint.port ^ cand->component_id; + return endpoint_hash(&cand->endpoint) ^ cand->component_id; } static int __cand_equal(const void *a, const void *b) { const struct ice_candidate *A = a, *B = b; - return A->endpoint.port == B->endpoint.port - && A->component_id == B->component_id - && in6_addr_eq(&A->endpoint.ip46, &B->endpoint.ip46); + return endpoint_eq(&A->endpoint, &B->endpoint) + && A->component_id == B->component_id; } static unsigned int __found_hash(const void *p) { const struct ice_candidate *cand = p; @@ -222,10 +210,10 @@ static int __pair_prio_cmp(const void *a, const void *b) { return -1; if (A->remote_candidate->component_id > B->remote_candidate->component_id) return 1; - /* highest local preference first, which is lowest number first */ - if (A->local_address->preference < B->local_address->preference) + /* highest local preference first, which is lowest unique_id first */ + if (A->local_intf->unique_id < B->local_intf->unique_id) return -1; - if (A->local_address->preference > B->local_address->preference) + if (A->local_intf->unique_id > B->local_intf->unique_id) return 1; return 0; } @@ -240,7 +228,7 @@ static void __ice_agent_initialize(struct ice_agent *ag) { ag->foundation_hash = g_hash_table_new(__found_hash, __found_equal); ag->agent_flags = 0; bf_copy(&ag->agent_flags, ICE_AGENT_CONTROLLING, &media->media_flags, MEDIA_FLAG_ICE_CONTROLLING); - ag->local_interface = media->interface; + ag->logical_intf = media->logical_intf; ag->desired_family = media->desired_family; ag->nominated_pairs = g_tree_new(__pair_prio_cmp); ag->valid_pairs = g_tree_new(__pair_prio_cmp); @@ -320,6 +308,7 @@ void ice_update(struct ice_agent *ag, struct stream_params *sp) { unsigned int comps; struct packet_stream *components[MAX_COMPONENTS], *ps; GQueue *candidates; + struct stream_fd *sfd; if (!ag) return; @@ -336,7 +325,7 @@ void ice_update(struct ice_agent *ag, struct stream_params *sp) { __ice_restart(ag); else if (ag->pwd[0].s && sp->ice_pwd.s && str_cmp_str(&ag->pwd[0], &sp->ice_pwd)) __ice_restart(ag); - else if (ag->local_interface != media->interface) + else if (ag->logical_intf != media->logical_intf) __ice_restart(ag); /* update remote info */ @@ -424,11 +413,13 @@ void ice_update(struct ice_agent *ag, struct stream_params *sp) { pair: if (!ps) continue; - for (k = ag->local_interface->list.head; k; k = k->next) { + + for (k = ps->sfds.head; k; k = k->next) { + sfd = k->data; /* skip duplicates here also */ - if (__pair_lookup(ag, dup, k->data)) + if (__pair_lookup(ag, dup, sfd->local_intf)) continue; - __pair_candidate(k->data, ag, dup, ps); + __pair_candidate(sfd, ag, dup); } } @@ -614,8 +605,7 @@ static void __fail_pair(struct ice_candidate_pair *pair) { /* agent must NOT be locked, but call must be locked in R */ static void __do_ice_check(struct ice_candidate_pair *pair) { - struct sockaddr_in6 dst; - struct packet_stream *ps = pair->packet_stream; + struct stream_fd *sfd = pair->sfd; struct ice_agent *ag = pair->agent; u_int32_t prio, transact[3]; @@ -625,12 +615,7 @@ static void __do_ice_check(struct ice_candidate_pair *pair) { if (!ag->pwd[0].s) return; - ZERO(dst); - dst.sin6_port = htons(pair->remote_candidate->endpoint.port); - dst.sin6_addr = pair->remote_candidate->endpoint.ip46; - dst.sin6_family = AF_INET6; - - prio = ice_priority(ICT_PRFLX, pair->local_address->preference, + prio = ice_priority(ICT_PRFLX, pair->local_intf->unique_id, pair->remote_candidate->component_id); mutex_lock(&ag->lock); @@ -661,12 +646,12 @@ static void __do_ice_check(struct ice_candidate_pair *pair) { ilog(LOG_DEBUG, "Sending %sICE/STUN request for candidate pair "PAIR_FORMAT" from %s to %s", PAIR_ISSET(pair, TO_USE) ? "nominating " : "", - PAIR_FMT(pair), smart_ntop_buf(&pair->local_address->addr), - smart_ntop_ep_buf(&pair->remote_candidate->endpoint)); + PAIR_FMT(pair), sockaddr_print_buf(&pair->local_intf->spec->address.addr), + endpoint_print_buf(&pair->remote_candidate->endpoint)); - stun_binding_request(&dst, transact, &ag->pwd[0], ag->ufrag, + stun_binding_request(&pair->remote_candidate->endpoint, transact, &ag->pwd[0], ag->ufrag, AGENT_ISSET(ag, CONTROLLING), tie_breaker, - prio, &pair->local_address->addr, ps->sfd->fd.fd, + prio, &sfd->socket, PAIR_ISSET(pair, TO_USE)); } @@ -721,7 +706,7 @@ static void __nominate_pairs(struct ice_agent *ag) { static void __do_ice_checks(struct ice_agent *ag) { GList *l; struct ice_candidate_pair *pair, *highest = NULL, *frozen = NULL, *valid; - struct packet_stream *ps; + struct stream_fd *sfd; GQueue retransmits = G_QUEUE_INIT; struct timeval next_run = {0,0}; int have_more = 0; @@ -761,8 +746,8 @@ static void __do_ice_checks(struct ice_agent *ag) { pair = l->data; /* skip dead streams */ - ps = pair->packet_stream; - if (!ps || !ps->sfd) + sfd = pair->sfd; + if (!sfd || !sfd->stream || !sfd->stream->selected_sfd) continue; if (PAIR_ISSET(pair, FAILED)) continue; @@ -835,13 +820,12 @@ static void __agent_shutdown(struct ice_agent *ag) { } /* agent must be locked for these */ -static struct ice_candidate *__cand_lookup(struct ice_agent *ag, const struct sockaddr_in6 *sin, +static struct ice_candidate *__cand_lookup(struct ice_agent *ag, const endpoint_t *sin, unsigned int component) { struct ice_candidate d; - d.endpoint.port = ntohs(sin->sin6_port); - d.endpoint.ip46 = sin->sin6_addr; + d.endpoint = *sin; d.component_id = component; return g_hash_table_lookup(ag->candidate_hash, &d); } @@ -855,11 +839,11 @@ static struct ice_candidate *__foundation_lookup(struct ice_agent *ag, const str return g_hash_table_lookup(ag->foundation_hash, &d); } static struct ice_candidate_pair *__pair_lookup(struct ice_agent *ag, struct ice_candidate *cand, - struct interface_address *ifa) + const struct local_intf *ifa) { struct ice_candidate_pair p; - p.local_address = ifa; + p.local_intf = ifa; p.remote_candidate = cand; return g_hash_table_lookup(ag->pair_hash, &p); } @@ -868,29 +852,25 @@ static void __cand_ice_foundation(struct call *call, struct ice_candidate *cand) char buf[64]; int len; - len = sprintf(buf, "%lx%lx%lx%lx%x%x", - (long unsigned) cand->endpoint.ip46.s6_addr32[0], - (long unsigned) cand->endpoint.ip46.s6_addr32[1], - (long unsigned) cand->endpoint.ip46.s6_addr32[2], - (long unsigned) cand->endpoint.ip46.s6_addr32[3], - cand->type, cand->transport); + len = sprintf(buf, "%x%x%x", endpoint_hash(&cand->endpoint), + cand->type, g_direct_hash(cand->transport)); call_str_cpy_len(call, &cand->foundation, buf, len); } /* agent must be locked */ -static struct ice_candidate_pair *__learned_candidate(struct ice_agent *ag, struct packet_stream *ps, - struct sockaddr_in6 *src, struct interface_address *ifa, unsigned long priority) +static struct ice_candidate_pair *__learned_candidate(struct ice_agent *ag, struct stream_fd *sfd, + const endpoint_t *src, unsigned long priority) { struct ice_candidate *cand, *old_cand; struct ice_candidate_pair *pair; struct call *call = ag->call; + struct packet_stream *ps = sfd->stream; cand = g_slice_alloc0(sizeof(*cand)); cand->component_id = ps->component; - cand->transport = ITP_UDP; + cand->transport = sfd->local_intf->spec->address.type; // XXX add socket type into socket_t? cand->priority = priority; - cand->endpoint.ip46 = src->sin6_addr; - cand->endpoint.port = ntohs(src->sin6_port); + cand->endpoint = *src; cand->type = ICT_PRFLX; __cand_ice_foundation(call, cand); @@ -900,7 +880,7 @@ static struct ice_candidate_pair *__learned_candidate(struct ice_agent *ag, stru * address, but from different ports. we cannot distinguish such candidates and * will drop the one with the lower priority */ g_slice_free1(sizeof(*cand), cand); - pair = __pair_lookup(ag, old_cand, ifa); + pair = __pair_lookup(ag, old_cand, sfd->local_intf); if (pair) goto out; /* nothing to do */ cand = old_cand; @@ -912,7 +892,7 @@ static struct ice_candidate_pair *__learned_candidate(struct ice_agent *ag, stru g_hash_table_insert(ag->foundation_hash, cand, cand); pair: - pair = __pair_candidate(ifa, ag, cand, ps); + pair = __pair_candidate(sfd, ag, cand); PAIR_SET(pair, LEARNED); __all_pairs_list(ag); @@ -1005,7 +985,7 @@ static void __get_complete_components(GQueue *out, struct ice_agent *ag, GTree * cand = __foundation_lookup(ag, &pair1->remote_candidate->foundation, i); if (!cand) goto next_foundation; - pairX = __pair_lookup(ag, cand, pair1->local_address); + pairX = __pair_lookup(ag, cand, pair1->local_intf); if (!pairX) goto next_foundation; if (!bf_isset(&pairX->pair_flags, flag)) @@ -1029,10 +1009,11 @@ found: static int __check_valid(struct ice_agent *ag) { struct call_media *media = ag->media; struct packet_stream *ps; - GList *l, *k; + GList *l, *k, *m; GQueue all_compos; struct ice_candidate_pair *pair; - struct interface_address *ifa; +// const struct local_intf *ifa; + struct stream_fd *sfd; if (!ag) { ilog(LOG_ERR, "ice ag is NULL"); @@ -1050,12 +1031,6 @@ static int __check_valid(struct ice_agent *ag) { ilog(LOG_DEBUG, "ICE completed, using pair "PAIR_FORMAT, PAIR_FMT(pair)); AGENT_SET(ag, COMPLETED); - ifa = g_atomic_pointer_get(&media->local_address); - if (ifa != pair->local_address - && g_atomic_pointer_compare_and_exchange(&media->local_address, ifa, - pair->local_address)) - ilog(LOG_INFO, "ICE negotiated: local interface %s", smart_ntop_buf(&pair->local_address->addr)); - for (l = media->streams.head, k = all_compos.head; l && k; l = l->next, k = k->next) { ps = l->data; pair = k->data; @@ -1063,10 +1038,20 @@ static int __check_valid(struct ice_agent *ag) { mutex_lock(&ps->out_lock); if (memcmp(&ps->endpoint, &pair->remote_candidate->endpoint, sizeof(ps->endpoint))) { ilog(LOG_INFO, "ICE negotiated: peer for component %u is %s", ps->component, - smart_ntop_ep_buf(&pair->remote_candidate->endpoint)); + endpoint_print_buf(&pair->remote_candidate->endpoint)); ps->endpoint = pair->remote_candidate->endpoint; } mutex_unlock(&ps->out_lock); + + for (m = ps->sfds.head; m; m = m->next) { + sfd = m->data; + if (sfd->local_intf != pair->local_intf) + continue; + ps->selected_sfd = sfd; + if (ps->component == 1) + ilog(LOG_INFO, "ICE negotiated: local interface %s", + sockaddr_print_buf(&pair->local_intf->spec->address.addr)); + } } call_media_unkernelize(media); @@ -1083,18 +1068,19 @@ static int __check_valid(struct ice_agent *ag) { * -1 = generic error, process packet as normal * -2 = role conflict */ -int ice_request(struct packet_stream *ps, struct sockaddr_in6 *src, struct in6_addr *dst, +int ice_request(struct stream_fd *sfd, const endpoint_t *src, struct stun_attrs *attrs) { + struct packet_stream *ps = sfd->stream; struct call_media *media = ps->media; struct ice_agent *ag; - struct interface_address *ifa; const char *err; struct ice_candidate *cand; struct ice_candidate_pair *pair; int ret; - __DBG("received ICE request from %s on %s", smart_ntop_port_buf(src), smart_ntop_buf(dst)); + __DBG("received ICE request from %s on %s", endpoint_print_buf(src), + endpoint_print_buf(&sfd->socket.local)); ag = media->ice_agent; if (!ag) @@ -1102,20 +1088,15 @@ int ice_request(struct packet_stream *ps, struct sockaddr_in6 *src, struct in6_a atomic64_set(&ag->last_activity, poller_now); - ifa = get_interface_from_address(ag->local_interface, dst); - err = "ICE/STUN binding request received on unknown local interface address"; - if (!ifa) - goto err; - /* determine candidate pair */ mutex_lock(&ag->lock); cand = __cand_lookup(ag, src, ps->component); if (!cand) - pair = __learned_candidate(ag, ps, src, ifa, attrs->priority); + pair = __learned_candidate(ag, sfd, src, attrs->priority); else - pair = __pair_lookup(ag, cand, ifa); + pair = __pair_lookup(ag, cand, sfd->local_intf); err = "Failed to determine ICE candidate from STUN request"; if (!pair) @@ -1167,8 +1148,8 @@ int ice_request(struct packet_stream *ps, struct sockaddr_in6 *src, struct in6_a err_unlock: mutex_unlock(&ag->lock); -err: - ilog(LOG_NOTICE, "%s (from %s on interface %s)", err, smart_ntop_port_buf(src), smart_ntop_buf(dst)); + ilog(LOG_NOTICE, "%s (from %s on interface %s)", err, endpoint_print_buf(src), + endpoint_print_buf(&sfd->socket.local)); return 0; } @@ -1192,19 +1173,21 @@ static int __check_succeeded_complete(struct ice_agent *ag) { } /* call is locked in R */ -int ice_response(struct packet_stream *ps, struct sockaddr_in6 *src, struct in6_addr *dst, +int ice_response(struct stream_fd *sfd, const endpoint_t *src, struct stun_attrs *attrs, u_int32_t transaction[3]) { struct ice_candidate_pair *pair, *opair; struct ice_agent *ag; + struct packet_stream *ps = sfd->stream; struct call_media *media = ps->media; const char *err; unsigned int component; struct ice_candidate *cand; - struct interface_address *ifa; + const struct local_intf *ifa; int ret, was_ctl; - __DBG("received ICE response from %s on %s", smart_ntop_port_buf(src), smart_ntop_buf(dst)); + __DBG("received ICE response from %s on %s", endpoint_print_buf(src), + endpoint_print_buf(&sfd->socket.local)); ag = media->ice_agent; if (!ag) @@ -1222,24 +1205,20 @@ int ice_response(struct packet_stream *ps, struct sockaddr_in6 *src, struct in6_ mutex_unlock(&ag->lock); - ifa = pair->local_address; + ifa = pair->local_intf; ilog(LOG_DEBUG, "Received ICE/STUN response code %u for candidate pair "PAIR_FORMAT" from %s to %s", attrs->error_code, PAIR_FMT(pair), - smart_ntop_ep_buf(&pair->remote_candidate->endpoint), - smart_ntop_buf(&ifa->addr)); + endpoint_print_buf(&pair->remote_candidate->endpoint), + sockaddr_print_buf(&ifa->spec->address.addr)); /* verify endpoints */ err = "ICE/STUN response received, but source address didn't match remote candidate address"; - if (memcmp(&src->sin6_addr, &pair->remote_candidate->endpoint.ip46, sizeof(src->sin6_addr))) - goto err; - if (ntohs(src->sin6_port) != pair->remote_candidate->endpoint.port) + if (!endpoint_eq(src, &pair->remote_candidate->endpoint)) goto err; err = "ICE/STUN response received, but destination address didn't match local interface address"; - if (memcmp(dst, &ifa->addr, sizeof(*dst))) - goto err; - if (pair->packet_stream != ps) + if (pair->sfd != sfd) goto err; PAIR_CLEAR(pair, IN_PROGRESS); @@ -1321,7 +1300,7 @@ err_unlock: err: if (err) ilog(LOG_NOTICE, "%s (from %s on interface %s)", - err, smart_ntop_port_buf(src), smart_ntop_buf(dst)); + err, endpoint_print_buf(src), endpoint_print_buf(&sfd->socket.local)); if (pair && attrs->error_code) __fail_pair(pair); @@ -1402,9 +1381,9 @@ static void create_random_ice_string(struct call *call, str *s, int len) { call_str_cpy_len(call, s, buf, len); } -void ice_foundation(struct interface_address *ifa) { - random_ice_string(ifa->foundation_buf, sizeof(ifa->foundation_buf)); - str_init_len(&ifa->ice_foundation, ifa->foundation_buf, sizeof(ifa->foundation_buf)); +void ice_foundation(str *s) { + str_init_len(s, malloc(ICE_FOUNDATION_LENGTH), ICE_FOUNDATION_LENGTH); + random_ice_string(s->s, ICE_FOUNDATION_LENGTH); } void ice_remote_candidates(GQueue *out, struct ice_agent *ag) { diff --git a/daemon/ice.h b/daemon/ice.h index bbf057123..e3c7d790e 100644 --- a/daemon/ice.h +++ b/daemon/ice.h @@ -11,6 +11,8 @@ #include "obj.h" #include "aux.h" #include "call.h" +#include "media_socket.h" +#include "socket.h" @@ -20,6 +22,7 @@ #define STUN_RETRANSMIT_INTERVAL 100 /* ms, with exponential backoff */ #define STUN_MAX_RETRANSMITS 7 #define MAX_ICE_CANDIDATES 100 +#define ICE_FOUNDATION_LENGTH 16 @@ -52,8 +55,8 @@ -struct local_interface; -struct interface_address; +struct logical_intf; +struct local_intf; struct packet_stream; struct call_media; struct call; @@ -72,27 +75,20 @@ enum ice_candidate_type { __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; + socktype_t *transport; unsigned long priority; - struct endpoint endpoint; + endpoint_t endpoint; enum ice_candidate_type type; - struct in6_addr related_address; - unsigned int related_port; + endpoint_t related; }; struct ice_candidate_pair { struct ice_candidate *remote_candidate; - struct interface_address *local_address; - struct packet_stream *packet_stream; + const struct local_intf *local_intf; + struct stream_fd *sfd; volatile unsigned int pair_flags; u_int32_t stun_transaction[3]; /* belongs to transaction_hash, thus agent->lock */ unsigned int retransmit_ms; @@ -109,8 +105,8 @@ struct ice_agent { struct obj obj; struct call *call; /* main reference */ struct call_media *media; - struct local_interface *local_interface; - int desired_family; + const struct logical_intf *logical_intf; + sockfamily_t *desired_family; atomic64 last_activity; mutex_t lock; /* for elements below. and call must be locked in R */ @@ -150,9 +146,8 @@ 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_foundation(str *); void ice_agent_init(struct ice_agent **agp, struct call_media *media); void ice_update(struct ice_agent *, struct stream_params *); @@ -164,8 +159,8 @@ 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, +int ice_request(struct stream_fd *, const endpoint_t *, struct stun_attrs *); +int ice_response(struct stream_fd *, const endpoint_t *src, struct stun_attrs *attrs, u_int32_t transaction[3]); /* returns 0 if ICE still has work to do, 1 otherwise */ diff --git a/daemon/kernel.c b/daemon/kernel.c index e6cbf9219..407ea88ba 100644 --- a/daemon/kernel.c +++ b/daemon/kernel.c @@ -86,13 +86,13 @@ int kernel_add_stream(int fd, struct rtpengine_target_info *mti, int update) { } -int kernel_del_stream(int fd, u_int16_t p) { +int kernel_del_stream(int fd, const struct re_address *a) { struct rtpengine_message msg; int ret; ZERO(msg); msg.cmd = MMG_DEL; - msg.target.target_port = p; + msg.target.local = *a; ret = write(fd, &msg, sizeof(msg)); if (ret > 0) diff --git a/daemon/kernel.h b/daemon/kernel.h index 9271ef0f4..bc93cac58 100644 --- a/daemon/kernel.h +++ b/daemon/kernel.h @@ -11,6 +11,7 @@ struct rtpengine_target_info; +struct re_address; @@ -18,7 +19,7 @@ int kernel_create_table(unsigned int); int kernel_open_table(unsigned int); int kernel_add_stream(int, struct rtpengine_target_info *, int); -int kernel_del_stream(int, u_int16_t); +int kernel_del_stream(int, const struct re_address *); GList *kernel_list(unsigned int); diff --git a/daemon/log.c b/daemon/log.c index 964f1ae00..3031a5e94 100644 --- a/daemon/log.c +++ b/daemon/log.c @@ -115,9 +115,9 @@ void __ilog(int prio, const char *fmt, ...) { break; case LOG_INFO_STREAM_FD: if (log_info.u.stream_fd->call) - snprintf(prefix, sizeof(prefix), "["STR_FORMAT" port %5hu] ", + snprintf(prefix, sizeof(prefix), "["STR_FORMAT" port %5u] ", STR_FMT(&log_info.u.stream_fd->call->callid), - log_info.u.stream_fd->fd.localport); + log_info.u.stream_fd->socket.local.port); break; case LOG_INFO_STR: snprintf(prefix, sizeof(prefix), "["STR_FORMAT"] ", diff --git a/daemon/main.c b/daemon/main.c index b31260ab0..e3d4fc536 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -27,6 +27,8 @@ #include "cli.h" #include "graphite.h" #include "ice.h" +#include "socket.h" +#include "media_socket.h" @@ -57,14 +59,14 @@ static mutex_t *openssl_locks; static char *pidfile; static gboolean foreground; static GQueue interfaces = G_QUEUE_INIT; -static u_int32_t listenp; -static u_int16_t listenport; -static struct in6_addr udp_listenp; -static u_int16_t udp_listenport; -static struct in6_addr ng_listenp; -static u_int16_t ng_listenport; -static u_int32_t cli_listenp; -static u_int16_t cli_listenport; +endpoint_t tcp_listen_ep; +endpoint_t udp_listen_ep; +endpoint_t ng_listen_ep; +endpoint_t cli_listen_ep; +endpoint_t graphite_ep; +endpoint_t redis_ep; +endpoint_t redis_read_ep; +endpoint_t redis_write_ep; static int tos; static int table = -1; static int no_fallback; @@ -73,21 +75,13 @@ static int silent_timeout; static int port_min = 30000; static int port_max = 40000; static int max_sessions = -1; -static u_int32_t redis_ip; -static u_int16_t redis_port; static int redis_db = -1; -static u_int32_t redis_read_ip; -static u_int32_t redis_write_ip; -static u_int16_t redis_read_port; -static u_int16_t redis_write_port; static int redis_read_db = -1; static int redis_write_db = -1; static char *b2b_url; static enum xmlrpc_format xmlrpc_fmt = XF_SEMS; static int num_threads; static int delete_delay = 30; -static u_int32_t graphite_ip = 0; -static u_int16_t graphite_port; static int graphite_interval = 0; static void sighandler(gpointer x) { @@ -146,10 +140,14 @@ static void signals(void) { } static void resources(void) { + struct rlimit rl; int tryv; rlim(RLIMIT_CORE, RLIM_INFINITY); - for (tryv = ((1<<20) - 1); tryv && rlim(RLIMIT_NOFILE, tryv) == -1; tryv >>= 1) + + if (getrlimit(RLIMIT_NOFILE, &rl)) + rl.rlim_cur = 0; + for (tryv = ((1<<20) - 1); tryv && tryv > rl.rlim_cur && rlim(RLIMIT_NOFILE, tryv) == -1; tryv >>= 1) ; rlim(RLIMIT_DATA, RLIM_INFINITY); @@ -181,12 +179,11 @@ static void print_available_log_facilities () { } -static struct interface_address *if_addr_parse(char *s) { +static struct intf_config *if_addr_parse(char *s) { str name; char *c; - struct in6_addr addr, adv; - struct interface_address *ifa; - int family; + sockaddr_t addr, adv; + struct intf_config *ifa; /* name */ c = strchr(s, '/'); @@ -204,20 +201,21 @@ static struct interface_address *if_addr_parse(char *s) { *c++ = 0; /* address */ - if (pton_46(&addr, s, &family)) + if (sockaddr_parse_any(&addr, s)) return NULL; adv = addr; if (c) { - if (pton_46(&adv, c, NULL)) + if (sockaddr_parse_any(&adv, c)) return NULL; } ifa = g_slice_alloc0(sizeof(*ifa)); - ifa->interface_name = name; - ifa->addr = addr; - ifa->advertised = adv; - ifa->family = family; + ifa->name = name; + ifa->address.addr = addr; + ifa->address.advertised = adv; + ifa->port_min = port_min; + ifa->port_max = port_max; return ifa; } @@ -227,7 +225,7 @@ static struct interface_address *if_addr_parse(char *s) { static void options(int *argc, char ***argv) { char **if_a = NULL; char **iter; - struct interface_address *ifa; + struct intf_config *ifa; char *listenps = NULL; char *listenudps = NULL; char *listenngs = NULL; @@ -306,23 +304,23 @@ static void options(int *argc, char ***argv) { } if (listenps) { - if (parse_ip_port(&listenp, &listenport, listenps)) + if (endpoint_parse_any(&tcp_listen_ep, listenps)) die("Invalid IP or port (--listen-tcp)"); } if (listenudps) { - if (parse_ip6_port(&udp_listenp, &udp_listenport, listenudps)) + if (endpoint_parse_any(&udp_listen_ep, listenudps)) die("Invalid IP or port (--listen-udp)"); } if (listenngs) { - if (parse_ip6_port(&ng_listenp, &ng_listenport, listenngs)) + if (endpoint_parse_any(&ng_listen_ep, listenngs)) die("Invalid IP or port (--listen-ng)"); } - if (listencli) {if (parse_ip_port(&cli_listenp, &cli_listenport, listencli)) + if (listencli) {if (endpoint_parse_any(&cli_listen_ep, listencli)) die("Invalid IP or port (--listen-cli)"); } - if (graphitep) {if (parse_ip_port(&graphite_ip, &graphite_port, graphitep)) + if (graphitep) {if (endpoint_parse_any(&graphite_ep, graphitep)) die("Invalid IP or port (--graphite)"); } @@ -338,21 +336,21 @@ static void options(int *argc, char ***argv) { silent_timeout = 3600; if (redisps) { - if (parse_ip_port(&redis_ip, &redis_port, redisps) || !redis_ip) + if (endpoint_parse_any(&redis_ep, redisps)) die("Invalid IP or port (--redis)"); if (redis_db < 0) die("Must specify Redis DB number (--redis-db) when using Redis"); } if (redisps_read) { - if (parse_ip_port(&redis_read_ip, &redis_read_port, redisps_read) || !redis_read_ip) + if (endpoint_parse_any(&redis_read_ep, redisps_read)) die("Invalid Redis read IP or port (--redis-read)"); if (redis_read_db < 0) die("Must specify Redis read DB number (--redis-read-db) when using Redis"); } if (redisps_write) { - if (parse_ip_port(&redis_write_ip, &redis_write_port, redisps_write) || !redis_write_ip) + if (endpoint_parse_any(&redis_write_ep, redisps_write)) die("Invalid Redis write IP or port (--redis-write)"); if (redis_write_db < 0) die("Must specify Redis write DB number (--redis-write-db) when using Redis"); @@ -451,6 +449,7 @@ static void make_OpenSSL_thread_safe(void) { static void init_everything() { struct timespec ts; + socket_init(); log_init(); clock_gettime(CLOCK_REALTIME, &ts); srandom(ts.tv_sec ^ ts.tv_nsec); @@ -468,19 +467,7 @@ static void init_everything() { sdp_init(); dtls_init(); ice_init(); -} - - -static double time_diff_ms(struct timeval x , struct timeval y) -{ - double x_ms , y_ms , diff; - - x_ms = (double)x.tv_sec * 1000 + (double)x.tv_usec / 1000; - y_ms = (double)y.tv_sec * 1000 + (double)y.tv_usec / 1000; - - diff = (double)y_ms - (double)x_ms; - - return diff; + interfaces_init(&interfaces); } @@ -527,9 +514,6 @@ no_kernel: ZERO(mc); mc.kernelfd = kfd; mc.kernelid = table; - mc.interfaces = &interfaces; - mc.port_min = port_min; - mc.port_max = port_max; if (max_sessions < -1) { max_sessions = -1; } @@ -540,61 +524,59 @@ no_kernel: mc.default_tos = tos; mc.b2b_url = b2b_url; mc.fmt = xmlrpc_fmt; - mc.graphite_port = graphite_port; - mc.graphite_ip = graphite_ip; + mc.graphite_ep = graphite_ep; mc.graphite_interval = graphite_interval; ct = NULL; - if (listenport) { - ct = control_tcp_new(ctx->p, listenp, listenport, ctx->m); + if (tcp_listen_ep.port) { + ct = control_tcp_new(ctx->p, &tcp_listen_ep, ctx->m); if (!ct) die("Failed to open TCP control connection port"); } cu = NULL; - if (udp_listenport) { - callmaster_exclude_port(ctx->m, udp_listenport); - cu = control_udp_new(ctx->p, udp_listenp, udp_listenport, ctx->m); + if (udp_listen_ep.port) { + interfaces_exclude_port(udp_listen_ep.port); + cu = control_udp_new(ctx->p, &udp_listen_ep, ctx->m); if (!cu) die("Failed to open UDP control connection port"); } cn = NULL; - if (ng_listenport) { - callmaster_exclude_port(ctx->m, ng_listenport); - cn = control_ng_new(ctx->p, ng_listenp, ng_listenport, ctx->m); + if (ng_listen_ep.port) { + interfaces_exclude_port(ng_listen_ep.port); + cn = control_ng_new(ctx->p, &ng_listen_ep, ctx->m); if (!cn) die("Failed to open UDP control connection port"); } cl = NULL; - if (cli_listenport) { - callmaster_exclude_port(ctx->m, cli_listenport); - cl = cli_new(ctx->p, cli_listenp, cli_listenport, ctx->m); + if (cli_listen_ep.port) { + interfaces_exclude_port(cli_listen_ep.port); + cl = cli_new(ctx->p, &cli_listen_ep, ctx->m); if (!cl) die("Failed to open UDP CLI connection port"); } - if (redis_ip) { - mc.redis = redis_new(redis_ip, redis_port, redis_db, MASTER_REDIS_ROLE); + if (!is_addr_unspecified(&redis_ep.address)) { + mc.redis = redis_new(&redis_ep, redis_db, MASTER_REDIS_ROLE); if (!mc.redis) die("Cannot start up without Redis database"); } - if (redis_read_ip) { - mc.redis_read = redis_new(redis_read_ip, redis_read_port, redis_read_db, ANY_REDIS_ROLE); + if (!is_addr_unspecified(&redis_read_ep.address)) { + mc.redis_read = redis_new(&redis_read_ep, redis_read_db, ANY_REDIS_ROLE); if (!mc.redis_read) die("Cannot start up without Redis read database"); } - if (redis_write_ip) { - mc.redis_write = redis_new(redis_write_ip, redis_write_port, redis_write_db, ANY_REDIS_ROLE); + if (!is_addr_unspecified(&redis_write_ep.address)) { + mc.redis_write = redis_new(&redis_write_ep, redis_write_db, ANY_REDIS_ROLE); if (!mc.redis_write) die("Cannot start up without Redis write database"); } ctx->m->conf = mc; - callmaster_config_init(ctx->m); if (!foreground) daemonize(); @@ -603,6 +585,7 @@ no_kernel: // start redis restore timer gettimeofday(&redis_start, NULL); + // restore if (mc.redis_read) { if (redis_restore(ctx->m, mc.redis_read, ANY_REDIS_ROLE)) die("Refusing to continue without working Redis read database"); @@ -615,12 +598,12 @@ no_kernel: gettimeofday(&redis_stop, NULL); // print redis restore duration - redis_diff += time_diff_ms(redis_start, redis_stop); + redis_diff += timeval_diff(&redis_start, &redis_stop) / 1000.0; ilog(LOG_INFO, "Redis restore time = %.0lf ms", redis_diff); gettimeofday(&ctx->m->latest_graphite_interval_start, NULL); - timeval_from_ms(&tmp_tv, graphite_interval*1000000); + timeval_from_us(&tmp_tv, graphite_interval*1000000); set_graphite_interval_tv(&tmp_tv); } @@ -637,7 +620,7 @@ int main(int argc, char **argv) { thread_create_detach(sighandler, NULL); thread_create_detach(poller_timer_loop, ctx.p); - if (graphite_ip) + if (!is_addr_unspecified(&graphite_ep.address)) thread_create_detach(graphite_loop, ctx.m); thread_create_detach(ice_thread_run, NULL); diff --git a/daemon/media_socket.c b/daemon/media_socket.c new file mode 100644 index 000000000..9a9ed52ac --- /dev/null +++ b/daemon/media_socket.c @@ -0,0 +1,1443 @@ +#include "media_socket.h" +#include +#include +#include +#include +#include "str.h" +#include "ice.h" +#include "socket.h" +#include "redis.h" +#include "rtp.h" +#include "ice.h" +#include "stun.h" +#include "kernel.h" +#include "xt_RTPENGINE.h" +#include "rtcp.h" +#include "sdp.h" +#include "aux.h" + + + + +#ifndef PORT_RANDOM_MIN +#define PORT_RANDOM_MIN 6 +#define PORT_RANDOM_MAX 20 +#endif + +#ifndef MAX_RECV_ITERS +#define MAX_RECV_ITERS 50 +#endif + + +typedef int (*rewrite_func)(str *, struct packet_stream *); + + +struct streamhandler_io { + rewrite_func rtp; + rewrite_func rtcp; + int (*kernel)(struct rtpengine_srtp *, struct packet_stream *); +}; +struct streamhandler { + const struct streamhandler_io *in; + const struct streamhandler_io *out; +}; + + +static void determine_handler(struct packet_stream *in, const struct packet_stream *out); + +static int __k_null(struct rtpengine_srtp *s, struct packet_stream *); +static int __k_srtp_encrypt(struct rtpengine_srtp *s, struct packet_stream *); +static int __k_srtp_decrypt(struct rtpengine_srtp *s, struct packet_stream *); + +static int call_avp2savp_rtp(str *s, struct packet_stream *); +static int call_savp2avp_rtp(str *s, struct packet_stream *); +static int call_avp2savp_rtcp(str *s, struct packet_stream *); +static int call_savp2avp_rtcp(str *s, struct packet_stream *); +static int call_avpf2avp_rtcp(str *s, struct packet_stream *); +//static int call_avpf2savp_rtcp(str *s, struct packet_stream *); +static int call_savpf2avp_rtcp(str *s, struct packet_stream *); +//static int call_savpf2savp_rtcp(str *s, struct packet_stream *); + + + + + +static const struct streamhandler_io __shio_noop = { + .kernel = __k_null, +}; +static const struct streamhandler_io __shio_decrypt = { + .kernel = __k_srtp_decrypt, + .rtp = call_savp2avp_rtp, + .rtcp = call_savp2avp_rtcp, +}; +static const struct streamhandler_io __shio_encrypt = { + .kernel = __k_srtp_encrypt, + .rtp = call_avp2savp_rtp, + .rtcp = call_avp2savp_rtcp, +}; +static const struct streamhandler_io __shio_avpf_strip = { + .kernel = __k_null, + .rtcp = call_avpf2avp_rtcp, +}; +static const struct streamhandler_io __shio_decrypt_avpf_strip = { + .kernel = __k_srtp_decrypt, + .rtp = call_savp2avp_rtp, + .rtcp = call_savpf2avp_rtcp, +}; + +/* ********** */ + +static const struct streamhandler __sh_noop = { + .in = &__shio_noop, + .out = &__shio_noop, +}; +static const struct streamhandler __sh_savp2avp = { + .in = &__shio_decrypt, + .out = &__shio_noop, +}; +static const struct streamhandler __sh_avp2savp = { + .in = &__shio_noop, + .out = &__shio_encrypt, +}; +static const struct streamhandler __sh_avpf2avp = { + .in = &__shio_avpf_strip, + .out = &__shio_noop, +}; +static const struct streamhandler __sh_avpf2savp = { + .in = &__shio_avpf_strip, + .out = &__shio_encrypt, +}; +static const struct streamhandler __sh_savpf2avp = { + .in = &__shio_decrypt_avpf_strip, + .out = &__shio_noop, +}; +static const struct streamhandler __sh_savp2savp = { + .in = &__shio_decrypt, + .out = &__shio_encrypt, +}; +static const struct streamhandler __sh_savpf2savp = { + .in = &__shio_decrypt_avpf_strip, + .out = &__shio_encrypt, +}; + +/* ********** */ + +static const struct streamhandler *__sh_matrix_in_rtp_avp[] = { + [PROTO_RTP_AVP] = &__sh_noop, + [PROTO_RTP_AVPF] = &__sh_noop, + [PROTO_RTP_SAVP] = &__sh_avp2savp, + [PROTO_RTP_SAVPF] = &__sh_avp2savp, + [PROTO_UDP_TLS_RTP_SAVP] = &__sh_avp2savp, + [PROTO_UDP_TLS_RTP_SAVPF] = &__sh_avp2savp, + [PROTO_UDPTL] = &__sh_noop, +}; +static const struct streamhandler *__sh_matrix_in_rtp_avpf[] = { + [PROTO_RTP_AVP] = &__sh_avpf2avp, + [PROTO_RTP_AVPF] = &__sh_noop, + [PROTO_RTP_SAVP] = &__sh_avpf2savp, + [PROTO_RTP_SAVPF] = &__sh_avp2savp, + [PROTO_UDP_TLS_RTP_SAVP] = &__sh_avpf2savp, + [PROTO_UDP_TLS_RTP_SAVPF] = &__sh_avp2savp, + [PROTO_UDPTL] = &__sh_noop, +}; +static const struct streamhandler *__sh_matrix_in_rtp_savp[] = { + [PROTO_RTP_AVP] = &__sh_savp2avp, + [PROTO_RTP_AVPF] = &__sh_savp2avp, + [PROTO_RTP_SAVP] = &__sh_noop, + [PROTO_RTP_SAVPF] = &__sh_noop, + [PROTO_UDP_TLS_RTP_SAVP] = &__sh_noop, + [PROTO_UDP_TLS_RTP_SAVPF] = &__sh_noop, + [PROTO_UDPTL] = &__sh_noop, +}; +static const struct streamhandler *__sh_matrix_in_rtp_savpf[] = { + [PROTO_RTP_AVP] = &__sh_savpf2avp, + [PROTO_RTP_AVPF] = &__sh_savp2avp, + [PROTO_RTP_SAVP] = &__sh_savpf2savp, + [PROTO_RTP_SAVPF] = &__sh_noop, + [PROTO_UDP_TLS_RTP_SAVP] = &__sh_savpf2savp, + [PROTO_UDP_TLS_RTP_SAVPF] = &__sh_noop, + [PROTO_UDPTL] = &__sh_noop, +}; +static const struct streamhandler *__sh_matrix_in_rtp_savp_recrypt[] = { + [PROTO_RTP_AVP] = &__sh_savp2avp, + [PROTO_RTP_AVPF] = &__sh_savp2avp, + [PROTO_RTP_SAVP] = &__sh_savp2savp, + [PROTO_RTP_SAVPF] = &__sh_savp2savp, + [PROTO_UDP_TLS_RTP_SAVP] = &__sh_savp2savp, + [PROTO_UDP_TLS_RTP_SAVPF] = &__sh_savp2savp, + [PROTO_UDPTL] = &__sh_noop, +}; +static const struct streamhandler *__sh_matrix_in_rtp_savpf_recrypt[] = { + [PROTO_RTP_AVP] = &__sh_savpf2avp, + [PROTO_RTP_AVPF] = &__sh_savp2avp, + [PROTO_RTP_SAVP] = &__sh_savpf2savp, + [PROTO_RTP_SAVPF] = &__sh_savp2savp, + [PROTO_UDP_TLS_RTP_SAVP] = &__sh_savpf2savp, + [PROTO_UDP_TLS_RTP_SAVPF] = &__sh_savp2savp, + [PROTO_UDPTL] = &__sh_noop, +}; +static const struct streamhandler *__sh_matrix_noop[] = { + [PROTO_RTP_AVP] = &__sh_noop, + [PROTO_RTP_AVPF] = &__sh_noop, + [PROTO_RTP_SAVP] = &__sh_noop, + [PROTO_RTP_SAVPF] = &__sh_noop, + [PROTO_UDP_TLS_RTP_SAVP] = &__sh_noop, + [PROTO_UDP_TLS_RTP_SAVPF] = &__sh_noop, + [PROTO_UDPTL] = &__sh_noop, +}; + +/* ********** */ + +static const struct streamhandler **__sh_matrix[] = { + [PROTO_RTP_AVP] = __sh_matrix_in_rtp_avp, + [PROTO_RTP_AVPF] = __sh_matrix_in_rtp_avpf, + [PROTO_RTP_SAVP] = __sh_matrix_in_rtp_savp, + [PROTO_RTP_SAVPF] = __sh_matrix_in_rtp_savpf, + [PROTO_UDP_TLS_RTP_SAVP] = __sh_matrix_in_rtp_savp, + [PROTO_UDP_TLS_RTP_SAVPF] = __sh_matrix_in_rtp_savpf, + [PROTO_UDPTL] = __sh_matrix_noop, +}; +/* special case for DTLS as we can't pass through SRTP<>SRTP */ +static const struct streamhandler **__sh_matrix_recrypt[] = { + [PROTO_RTP_AVP] = __sh_matrix_in_rtp_avp, + [PROTO_RTP_AVPF] = __sh_matrix_in_rtp_avpf, + [PROTO_RTP_SAVP] = __sh_matrix_in_rtp_savp_recrypt, + [PROTO_RTP_SAVPF] = __sh_matrix_in_rtp_savpf_recrypt, + [PROTO_UDP_TLS_RTP_SAVP] = __sh_matrix_in_rtp_savp_recrypt, + [PROTO_UDP_TLS_RTP_SAVPF] = __sh_matrix_in_rtp_savpf_recrypt, + [PROTO_UDPTL] = __sh_matrix_noop, +}; + +/* ********** */ + +static const struct rtpengine_srtp __res_null = { + .cipher = REC_NULL, + .hmac = REH_NULL, +}; + + + + +static GQueue *__interface_list_for_family(sockfamily_t *fam); + + +static GHashTable *__logical_intf_name_family_hash; +static GHashTable *__intf_spec_addr_type_hash; +static GQueue __preferred_lists_for_family[__SF_LAST]; + +static __thread unsigned int selection_index = 0; +static __thread unsigned int selection_count = 0; + + +/* checks for free no_ports on a local interface */ +static int has_free_ports_loc(struct local_intf *loc, unsigned int num_ports) { + if (loc == NULL) { + ilog(LOG_ERR, "has_free_ports_loc - NULL local interface"); + return 0; + } + + if (num_ports > loc->spec->port_pool.free_ports) { + ilog(LOG_ERR, "Didn't found %d ports available for %.*s/%s", + num_ports, loc->logical->name.len, loc->logical->name.s, + sockaddr_print_buf(&loc->spec->address.addr)); + return 0; + } + + __C_DBG("Found %d ports available for %.*s/%s from total of %d free ports", + num_ports, loc->logical->name.len, loc->logical->name.s, + sockaddr_print_buf(&loc->spec->address.addr), + loc->spec->port_pool.free_ports); + + return 1; +} + +#if 0 +/* checks for free num_ports on at least one local interface of a logical interface */ +static int has_free_ports_log_any(struct logical_intf *log, unsigned int num_ports) { + if (log == NULL) { + ilog(LOG_ERR, "has_free_ports_log_any - NULL logical interface"); + return 0; + } + + struct local_intf *loc; + GList *l; + + for (l = log->list.head; l; l = l->next) { + loc = l->data; + + if (has_free_ports_loc(loc, num_ports)) { + return 1; + } + } + + return 0; +} +#endif + +/* checks for free num_ports on all local interfaces of a logical interface */ +static int has_free_ports_log_all(struct logical_intf *log, unsigned int num_ports) { + if (log == NULL) { + ilog(LOG_ERR, "has_free_ports_log_all - NULL logical interface"); + return 0; + } + + struct local_intf *loc; + GList *l; + + for (l = log->list.head; l; l = l->next) { + loc = l->data; + + if (!has_free_ports_loc(loc, num_ports)) { + return 0; + } + } + + return 1; +} + +/* run round-robin-calls algorithm */ +static struct logical_intf* run_round_robin_calls(GQueue *q, unsigned int num_ports) { + struct logical_intf *log = NULL; + volatile unsigned int nr_tries = 0; + unsigned int nr_logs = 0; + + nr_logs = g_queue_get_length(q); + +select_log: + // choose the next logical interface + log = g_queue_peek_nth(q, selection_index); + if (!log) { + if (selection_index == 0) + return NULL; + selection_index = 0; + goto select_log; + } + __C_DBG("Trying %d ports on logical interface %.*s", num_ports, log->name.len, log->name.s); + + // test for free ports for the logical interface + if(!has_free_ports_log_all(log, num_ports)) { + // count the logical interfaces tried + nr_tries++; + + // the logical interface selected has no ports available, try another one + selection_index ++; + selection_index = selection_index % nr_logs; + + // all the logical interfaces have no ports available + if (nr_tries == nr_logs) { + ilog(LOG_ERR, "No logical interface with free ports found; fallback to default behaviour"); + return NULL; + } + + goto select_log; + } + + __C_DBG("Round Robin Calls algorithm found logical %.*s; count=%u index=%u", log->name.len, log->name.s, selection_count, selection_index); + + // 1 stream => 2 x get_logical_interface calls at offer + // 2 streams => 4 x get_logical_interface calls at offer + selection_count ++; + if (selection_count % (num_ports / 2) == 0) { + selection_count = 0; + selection_index ++; + selection_index = selection_index % nr_logs; + } + + return log; +} + +struct logical_intf *get_logical_interface(const str *name, sockfamily_t *fam, int num_ports) { + struct logical_intf d, *log = NULL; + + __C_DBG("Get logical interface for %d ports", num_ports); + + if (G_UNLIKELY(!name || !name->s || + str_cmp(name, ALGORITHM_ROUND_ROBIN_CALLS) == 0)) { + + GQueue *q; + if (fam) + q = __interface_list_for_family(fam); + else { + for (int i = 0; i < __SF_LAST; i++) { + q = &__preferred_lists_for_family[i]; + if (q->length) + goto got_some; + } + abort(); +got_some: + ; + } + + // round-robin-calls behaviour - return next interface with free ports + if (name && name->s && str_cmp(name, ALGORITHM_ROUND_ROBIN_CALLS) == 0 && num_ports > 0) { + log = run_round_robin_calls(q, num_ports); + if (log) { + __C_DBG("Choose logical interface %.*s because of direction %.*s", + log->name.len, log->name.s, + name->len, name->s); + } else { + __C_DBG("Choose logical interface NULL because of direction %.*s", + name->len, name->s); + } + return log; + } + + // default behaviour - return first logical interface + return q->head ? q->head->data : NULL; + } + + d.name = *name; + d.preferred_family = fam; + + log = g_hash_table_lookup(__logical_intf_name_family_hash, &d); + if (log) { + __C_DBG("Choose logical interface %.*s because of direction %.*s", + log->name.len, log->name.s, + name->len, name->s); + } else { + __C_DBG("Choose logical interface NULL because of direction %.*s", + name->len, name->s); + } + + return log; +} + +static unsigned int __name_family_hash(const void *p) { + const struct logical_intf *lif = p; + return str_hash(&lif->name) ^ g_direct_hash(lif->preferred_family); +} +static int __name_family_eq(const void *a, const void *b) { + const struct logical_intf *A = a, *B = b; + return str_equal(&A->name, &B->name) && A->preferred_family == B->preferred_family; +} + +static unsigned int __addr_type_hash(const void *p) { + const struct intf_address *addr = p; + return sockaddr_hash(&addr->addr) ^ g_direct_hash(addr->type); +} +static int __addr_type_eq(const void *a, const void *b) { + const struct intf_address *A = a, *B = b; + return sockaddr_eq(&A->addr, &B->addr) && A->type == B->type; +} + +static GQueue *__interface_list_for_family(sockfamily_t *fam) { + return &__preferred_lists_for_family[fam->idx]; +} +static void __interface_append(struct intf_config *ifa, sockfamily_t *fam) { + struct logical_intf *lif; + GQueue *q; + struct local_intf *ifc; + struct intf_spec *spec; + + lif = get_logical_interface(&ifa->name, fam, 0); + + if (!lif) { + lif = g_slice_alloc0(sizeof(*lif)); + lif->name = ifa->name; + lif->preferred_family = fam; + lif->addr_hash = g_hash_table_new(__addr_type_hash, __addr_type_eq); + g_hash_table_insert(__logical_intf_name_family_hash, lif, lif); + if (ifa->address.addr.family == fam) { + q = __interface_list_for_family(fam); + g_queue_push_tail(q, lif); + } + } + + spec = g_hash_table_lookup(__intf_spec_addr_type_hash, &ifa->address); + if (!spec) { + spec = g_slice_alloc0(sizeof(*spec)); + spec->address = ifa->address; + ice_foundation(&spec->ice_foundation); + spec->port_pool.min = ifa->port_min; + spec->port_pool.max = ifa->port_max; + spec->port_pool.free_ports = spec->port_pool.max - spec->port_pool.min + 1; + g_hash_table_insert(__intf_spec_addr_type_hash, &spec->address, spec); + } + + ifc = uid_slice_alloc(ifc, &lif->list); + ifc->spec = spec; + ifc->logical = lif; + + g_hash_table_insert(lif->addr_hash, (void *) &ifc->spec->address, ifc); +} + +void interfaces_init(GQueue *interfaces) { + int i; + GList *l; + struct intf_config *ifa; + sockfamily_t *fam; + + /* init everything */ + __logical_intf_name_family_hash = g_hash_table_new(__name_family_hash, __name_family_eq); + __intf_spec_addr_type_hash = g_hash_table_new(__addr_type_hash, __addr_type_eq); + + for (i = 0; i < G_N_ELEMENTS(__preferred_lists_for_family); i++) + g_queue_init(&__preferred_lists_for_family[i]); + + /* build primary lists first */ + for (l = interfaces->head; l; l = l->next) { + ifa = l->data; + __interface_append(ifa, ifa->address.addr.family); + } + + /* then append to each other as lower-preference alternatives */ + for (i = 0; i < __SF_LAST; i++) { + fam = get_socket_family_enum(i); + for (l = interfaces->head; l; l = l->next) { + ifa = l->data; + if (ifa->address.addr.family == fam) + continue; + __interface_append(ifa, fam); + } + } +} + +void interfaces_exclude_port(unsigned int port) { + GList *vals, *l; + struct intf_spec *spec; + + vals = g_hash_table_get_values(__intf_spec_addr_type_hash); + + for (l = vals; l; l = l->next) { + spec = l->data; + bit_array_set(spec->port_pool.ports_used, port); + } + + g_list_free(vals); +} + +struct local_intf *get_interface_address(const struct logical_intf *lif, sockfamily_t *fam) { + const GQueue *q; + + if (!fam) + return NULL; + q = &lif->list; + if (!q->head) + return NULL; + return q->head->data; +} + +/* safety fallback */ +struct local_intf *get_any_interface_address(const struct logical_intf *lif, sockfamily_t *fam) { + struct local_intf *ifa; + + ifa = get_interface_address(lif, fam); + if (ifa) + return ifa; + ifa = get_interface_address(lif, __get_socket_family_enum(SF_IP4)); + if (ifa) + return ifa; + return get_interface_address(lif, __get_socket_family_enum(SF_IP6)); +} + + + +/* XXX family specific? unify? */ +static int get_port6(socket_t *r, unsigned int port, struct intf_spec *spec) { + if (open_socket(r, SOCK_DGRAM, port, &spec->address.addr)) + return -1; + + return 0; +} + +static int get_port(socket_t *r, unsigned int port, struct intf_spec *spec) { + int ret; + struct port_pool *pp; + + __C_DBG("attempting to open port %u", port); + + pp = &spec->port_pool; + + if (bit_array_set(pp->ports_used, port)) { + __C_DBG("port %d in use", port); + return -1; + } + __C_DBG("port %d locked", port); + + ret = get_port6(r, port, spec); + + if (ret) { + __C_DBG("couldn't open port %d", port); + bit_array_clear(pp->ports_used, port); + return ret; + } + + g_atomic_int_dec_and_test(&pp->free_ports); + __C_DBG("%d free ports remaining on interface %s", pp->free_ports, sockaddr_print_buf(&spec->address.addr)); + + return 0; +} + +static void release_port(socket_t *r, struct intf_spec *spec) { + unsigned int port = r->local.port; + + __C_DBG("trying to release port %u", port); + + if (close_socket(r) == 0) { + __C_DBG("port %u is released", port); + bit_array_clear(spec->port_pool.ports_used, port); + g_atomic_int_inc(&spec->port_pool.free_ports); + } else { + __C_DBG("port %u is NOT released", port); + } +} +static void free_port(socket_t *r, struct intf_spec *spec) { + release_port(r, spec); + g_slice_free1(sizeof(*r), r); +} + + + +/* puts list of socket_t into "out" */ +int __get_consecutive_ports(GQueue *out, unsigned int num_ports, unsigned int wanted_start_port, + struct intf_spec *spec) +{ + int i, cycle = 0; + socket_t *sk; + int port; + struct port_pool *pp; + + if (num_ports == 0) + return 0; + + pp = &spec->port_pool; + + __C_DBG("wanted_start_port=%d", wanted_start_port); + + if (wanted_start_port > 0) { + port = wanted_start_port; + __C_DBG("port=%d", port); + } else { + port = g_atomic_int_get(&pp->last_used); + __C_DBG("before randomization port=%d", port); +#if PORT_RANDOM_MIN && PORT_RANDOM_MAX + port += PORT_RANDOM_MIN + (random() % (PORT_RANDOM_MAX - PORT_RANDOM_MIN)); +#endif + __C_DBG("after randomization port=%d", port); + } + + // debug msg if port is in the given interval + if (bit_array_isset(pp->ports_used, port)) { + __C_DBG("port %d is USED in port pool", port); + } else { + __C_DBG("port %d is NOOT USED in port pool", port); + } + + while (1) { + __C_DBG("cycle=%d, port=%d", cycle, port); + if (!wanted_start_port) { + if (port < pp->min) + port = pp->min; + if ((port & 1)) + port++; + } + + for (i = 0; i < num_ports; i++) { + sk = g_slice_alloc0(sizeof(*sk)); + // fd=0 is a valid file descriptor that may be closed + // accidentally by free_port if previously bounded + sk->fd = -1; + g_queue_push_tail(out, sk); + + if (!wanted_start_port && port > pp->max) { + port = 0; + cycle++; + goto release_restart; + } + + if (get_port(sk, port++, spec)) + goto release_restart; + } + break; + +release_restart: + while ((sk = g_queue_pop_head(out))) + free_port(sk, spec); + + if (cycle >= 2 || wanted_start_port > 0) + goto fail; + } + + /* success */ + g_atomic_int_set(&pp->last_used, port); + + __C_DBG("Opened ports %u.. on interface %s for media relay", + ((socket_t *) out->head->data)->local.port, sockaddr_print_buf(&spec->address.addr)); + return 0; + +fail: + ilog(LOG_ERR, "Failed to get %u consecutive ports on interface %s for media relay", + num_ports, sockaddr_print_buf(&spec->address.addr)); + return -1; +} + +/* puts a list of "struct intf_list" into "out", containing socket_t list */ +int get_consecutive_ports(GQueue *out, unsigned int num_ports, const struct logical_intf *log) { + GList *l; + struct intf_list *il; + const struct local_intf *loc; + + for (l = log->list.head; l; l = l->next) { + loc = l->data; + + il = g_slice_alloc0(sizeof(*il)); + il->local_intf = loc; + g_queue_push_tail(out, il); + if (G_LIKELY(!__get_consecutive_ports(&il->list, num_ports, 0, loc->spec))) { + // success - found available ports on local interfaces, so far + continue; + } + + // error - found at least one local interface with no ports available + goto error_ports; + } + + return 0; + +error_ports: + ilog(LOG_ERR, "Failed to get %d consecutive ports on all locals of logical '"STR_FORMAT"'", + num_ports, STR_FMT(&log->name)); + + // free all ports alloc'ed so far for the previous local interfaces + while ((il = g_queue_pop_head(out))) { + free_socket_intf_list(il); + } + + return -1; +} +void free_socket_intf_list(struct intf_list *il) { + socket_t *sock; + + while ((sock = g_queue_pop_head(&il->list))) + free_port(sock, il->local_intf->spec); + g_slice_free1(sizeof(*il), il); +} +void free_intf_list(struct intf_list *il) { + g_queue_clear(&il->list); + g_slice_free1(sizeof(*il), il); +} + + + +/* called lock-free */ +static void stream_fd_closed(int fd, void *p, uintptr_t u) { + struct stream_fd *sfd = p; + struct call *c; + int i; + socklen_t j; + + assert(sfd->socket.fd == fd); + c = sfd->call; + if (!c) + return; + + j = sizeof(i); + getsockopt(fd, SOL_SOCKET, SO_ERROR, &i, &j); + ilog(LOG_WARNING, "Read error on media socket: %i (%s) -- closing call", i, strerror(i)); + + call_destroy(c); +} + + + +/* returns: 0 = not a muxed stream, 1 = muxed, RTP, 2 = muxed, RTCP */ +static int rtcp_demux(str *s, struct call_media *media) { + if (!MEDIA_ISSET(media, RTCP_MUX)) + return 0; + return rtcp_demux_is_rtcp(s) ? 2 : 1; +} + +static int call_avpf2avp_rtcp(str *s, struct packet_stream *stream) { + return rtcp_avpf2avp(s); +} +static int call_avp2savp_rtp(str *s, struct packet_stream *stream) { + return rtp_avp2savp(s, &stream->crypto); +} +static int call_avp2savp_rtcp(str *s, struct packet_stream *stream) { + return rtcp_avp2savp(s, &stream->crypto); +} +static int call_savp2avp_rtp(str *s, struct packet_stream *stream) { + return rtp_savp2avp(s, &stream->selected_sfd->crypto); +} +static int call_savp2avp_rtcp(str *s, struct packet_stream *stream) { + return rtcp_savp2avp(s, &stream->selected_sfd->crypto); +} +static int call_savpf2avp_rtcp(str *s, struct packet_stream *stream) { + int ret; + ret = rtcp_savp2avp(s, &stream->selected_sfd->crypto); + if (ret < 0) + return ret; + return rtcp_avpf2avp(s); +} + + +static int __k_null(struct rtpengine_srtp *s, struct packet_stream *stream) { + *s = __res_null; + return 0; +} +static int __k_srtp_crypt(struct rtpengine_srtp *s, struct crypto_context *c) { + if (!c->params.crypto_suite) + return -1; + + *s = (struct rtpengine_srtp) { + .cipher = c->params.crypto_suite->kernel_cipher, + .hmac = c->params.crypto_suite->kernel_hmac, + .mki_len = c->params.mki_len, + .last_index = c->last_index, + .auth_tag_len = c->params.crypto_suite->srtp_auth_tag, + }; + if (c->params.mki_len) + memcpy(s->mki, c->params.mki, c->params.mki_len); + memcpy(s->master_key, c->params.master_key, c->params.crypto_suite->master_key_len); + memcpy(s->master_salt, c->params.master_salt, c->params.crypto_suite->master_salt_len); + + if (c->params.session_params.unencrypted_srtp) + s->cipher = REC_NULL; + if (c->params.session_params.unauthenticated_srtp) + s->auth_tag_len = 0; + + return 0; +} +static int __k_srtp_encrypt(struct rtpengine_srtp *s, struct packet_stream *stream) { + return __k_srtp_crypt(s, &stream->crypto); +} +static int __k_srtp_decrypt(struct rtpengine_srtp *s, struct packet_stream *stream) { + return __k_srtp_crypt(s, &stream->selected_sfd->crypto); +} + +INLINE void __re_address_translate_ep(struct re_address *o, const endpoint_t *ep) { + ep->address.family->endpoint2kernel(o, ep); +} + +static int __rtp_stats_pt_sort(const void *ap, const void *bp) { + const struct rtp_stats *a = ap, *b = bp; + + if (a->payload_type < b->payload_type) + return -1; + if (a->payload_type > b->payload_type) + return 1; + return 0; +} + + +/* called with in_lock held */ +void kernelize(struct packet_stream *stream) { + struct rtpengine_target_info reti; + struct call *call = stream->call; + struct callmaster *cm = call->callmaster; + struct packet_stream *sink = NULL; + const char *nk_warn_msg; + + if (PS_ISSET(stream, KERNELIZED)) + return; + if (cm->conf.kernelid < 0) + goto no_kernel; + nk_warn_msg = "interface to kernel module not open"; + if (cm->conf.kernelfd < 0) + goto no_kernel_warn; + if (!PS_ISSET(stream, RTP)) + goto no_kernel; + if (!stream->selected_sfd) + goto no_kernel; + + ilog(LOG_INFO, "Kernelizing media stream: %s:%d", sockaddr_print_buf(&stream->endpoint.address), stream->endpoint.port); + + sink = packet_stream_sink(stream); + if (!sink) { + ilog(LOG_WARNING, "Attempt to kernelize stream without sink"); + goto no_kernel; + } + + determine_handler(stream, sink); + + if (is_addr_unspecified(&sink->advertised_endpoint.address) + || !sink->advertised_endpoint.port) + goto no_kernel; + nk_warn_msg = "protocol not supported by kernel module"; + if (!stream->handler->in->kernel + || !stream->handler->out->kernel) + goto no_kernel_warn; + + ZERO(reti); + + if (PS_ISSET2(stream, STRICT_SOURCE, MEDIA_HANDOVER)) { + mutex_lock(&stream->out_lock); + __re_address_translate_ep(&reti.expected_src, &stream->endpoint); + mutex_unlock(&stream->out_lock); + if (PS_ISSET(stream, STRICT_SOURCE)) + reti.src_mismatch = MSM_DROP; + else if (PS_ISSET(stream, MEDIA_HANDOVER)) + reti.src_mismatch = MSM_PROPAGATE; + } + + mutex_lock(&sink->out_lock); + + __re_address_translate_ep(&reti.local, &stream->selected_sfd->socket.local); + reti.tos = call->tos; + reti.rtcp_mux = MEDIA_ISSET(stream->media, RTCP_MUX); + reti.dtls = MEDIA_ISSET(stream->media, DTLS); + reti.stun = stream->media->ice_agent ? 1 : 0; + + __re_address_translate_ep(&reti.dst_addr, &sink->endpoint); + __re_address_translate_ep(&reti.src_addr, &sink->selected_sfd->socket.local); + reti.ssrc = sink->crypto.ssrc; + + stream->handler->in->kernel(&reti.decrypt, stream); + stream->handler->out->kernel(&reti.encrypt, sink); + + mutex_unlock(&sink->out_lock); + + nk_warn_msg = "encryption cipher or HMAC not supported by kernel module"; + if (!reti.encrypt.cipher || !reti.encrypt.hmac) + goto no_kernel_warn; + nk_warn_msg = "decryption cipher or HMAC not supported by kernel module"; + if (!reti.decrypt.cipher || !reti.decrypt.hmac) + goto no_kernel_warn; + + ZERO(stream->kernel_stats); + + if (stream->media->protocol && stream->media->protocol->rtp) { + GList *values, *l; + struct rtp_stats *rs; + + reti.rtp = 1; + values = g_hash_table_get_values(stream->rtp_stats); + values = g_list_sort(values, __rtp_stats_pt_sort); + for (l = values; l; l = l->next) { + if (reti.num_payload_types >= G_N_ELEMENTS(reti.payload_types)) { + ilog(LOG_WARNING, "Too many RTP payload types for kernel module"); + break; + } + rs = l->data; + reti.payload_types[reti.num_payload_types++] = rs->payload_type; + } + g_list_free(values); + } + + kernel_add_stream(cm->conf.kernelfd, &reti, 0); + PS_SET(stream, KERNELIZED); + + return; + +no_kernel_warn: + ilog(LOG_WARNING, "No support for kernel packet forwarding available (%s)", nk_warn_msg); +no_kernel: + PS_SET(stream, KERNELIZED); + PS_SET(stream, NO_KERNEL_SUPPORT); +} + +/* must be called with in_lock held or call->master_lock held in W */ +void __unkernelize(struct packet_stream *p) { + struct re_address rea; + + if (!PS_ISSET(p, KERNELIZED)) + return; + if (PS_ISSET(p, NO_KERNEL_SUPPORT)) + return; + + if (p->call->callmaster->conf.kernelfd >= 0) { + __re_address_translate_ep(&rea, &p->selected_sfd->socket.local); + kernel_del_stream(p->call->callmaster->conf.kernelfd, &rea); + } + + PS_CLEAR(p, KERNELIZED); +} + + +void __stream_unconfirm(struct packet_stream *ps) { + __unkernelize(ps); + PS_CLEAR(ps, CONFIRMED); + ps->handler = NULL; +} +static void stream_unconfirm(struct packet_stream *ps) { + if (!ps) + return; + mutex_lock(&ps->in_lock); + __stream_unconfirm(ps); + mutex_unlock(&ps->in_lock); +} +void unkernelize(struct packet_stream *ps) { + if (!ps) + return; + mutex_lock(&ps->in_lock); + __unkernelize(ps); + mutex_unlock(&ps->in_lock); +} + + + +/* must be called with call->master_lock held in R, and in->in_lock held */ +static void determine_handler(struct packet_stream *in, const struct packet_stream *out) { + const struct streamhandler **sh_pp, *sh; + const struct streamhandler ***matrix; + + if (in->handler) + return; + if (MEDIA_ISSET(in->media, PASSTHRU)) + goto noop; + + if (!in->media->protocol) + goto err; + if (!out->media->protocol) + goto err; + + matrix = __sh_matrix; + if (MEDIA_ISSET(in->media, DTLS) || MEDIA_ISSET(out->media, DTLS)) + matrix = __sh_matrix_recrypt; + else if (in->media->protocol->srtp && out->media->protocol->srtp + && in->selected_sfd && out->selected_sfd + && (crypto_params_cmp(&in->crypto.params, &out->selected_sfd->crypto.params) + || crypto_params_cmp(&out->crypto.params, &in->selected_sfd->crypto.params))) + matrix = __sh_matrix_recrypt; + + + sh_pp = matrix[in->media->protocol->index]; + if (!sh_pp) + goto err; + sh = sh_pp[out->media->protocol->index]; + if (!sh) + goto err; + in->handler = sh; + + return; + +err: + ilog(LOG_WARNING, "Unknown transport protocol encountered"); +noop: + in->handler = &__sh_noop; + return; +} + + +/* XXX split this function into pieces */ +/* called lock-free */ +static int stream_packet(struct stream_fd *sfd, str *s, const endpoint_t *fsin) { + struct packet_stream *stream, + *sink = NULL, + *in_srtp, *out_srtp; + struct call_media *media; + int ret = 0, update = 0, stun_ret = 0, handler_ret = 0, muxed_rtcp = 0, rtcp = 0, + unk = 0; + int i; + struct call *call; + struct callmaster *cm; + /*unsigned char cc;*/ + struct endpoint endpoint; + rewrite_func rwf_in, rwf_out; + //struct local_intf *loc_addr; + struct rtp_header *rtp_h; + struct rtp_stats *rtp_s; + + call = sfd->call; + cm = call->callmaster; + + rwlock_lock_r(&call->master_lock); + + stream = sfd->stream; + if (!stream) + goto unlock_out; + __C_DBG("Try to Kernelizing media stream: %s:%d", sockaddr_print_buf(&stream->endpoint.address), stream->endpoint.port); + + + media = stream->media; + + if (!stream->selected_sfd) + 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 unlock_out; + } + + if (media->ice_agent && is_stun(s)) { + stun_ret = stun(s, sfd, fsin); + if (!stun_ret) + goto unlock_out; + if (stun_ret == 1) { + call_media_state_machine(media); + 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; + if (memcmp(stream->lp_buf[i].buf, s->s, MIN(s->len, RTP_LOOP_PROTECT))) + continue; + + __C_DBG("packet dupe"); + if (stream->lp_count >= RTP_LOOP_MAX_COUNT) { + ilog(LOG_WARNING, "More than %d duplicate packets detected, dropping packet " + "to avoid potential loop", RTP_LOOP_MAX_COUNT); + goto done; + } + + stream->lp_count++; + goto loop_ok; + } + + /* not a dupe */ + stream->lp_count = 0; + stream->lp_buf[stream->lp_idx].len = s->len; + 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: + mutex_unlock(&stream->in_lock); +#endif + + + /* demux RTCP */ + + in_srtp = stream; + sink = stream->rtp_sink; + if (!sink && PS_ISSET(stream, RTCP)) { + sink = stream->rtcp_sink; + rtcp = 1; + } + else if (stream->rtcp_sink) { + muxed_rtcp = rtcp_demux(s, media); + if (muxed_rtcp == 2) { + sink = stream->rtcp_sink; + rtcp = 1; + in_srtp = stream->rtcp_sibling; + } + } + out_srtp = sink; + if (rtcp && sink && sink->rtcp_sibling) + out_srtp = sink->rtcp_sibling; + + + /* stats per RTP payload type */ + + if (media->protocol && media->protocol->rtp && !rtcp && !rtp_payload(&rtp_h, NULL, s)) { + i = (rtp_h->m_pt & 0x7f); + + rtp_s = g_hash_table_lookup(stream->rtp_stats, &i); + if (!rtp_s) { + ilog(LOG_WARNING | LOG_FLAG_LIMIT, + "RTP packet with unknown payload type %u received", i); + atomic64_inc(&stream->stats.errors); + atomic64_inc(&cm->statsps.errors); + } + + else { + atomic64_inc(&rtp_s->packets); + atomic64_add(&rtp_s->bytes, s->len); + } + } + + + /* do we have somewhere to forward it to? */ + + if (!sink || !sink->selected_sfd || !out_srtp->selected_sfd || !in_srtp->selected_sfd) { + ilog(LOG_WARNING, "RTP packet from %s discarded", endpoint_print_buf(fsin)); + atomic64_inc(&stream->stats.errors); + atomic64_inc(&cm->statsps.errors); + goto unlock_out; + } + + + /* transcoding stuff, in and out */ + + mutex_lock(&in_srtp->in_lock); + + determine_handler(in_srtp, sink); + + if (!rtcp) { + rwf_in = in_srtp->handler->in->rtp; + rwf_out = in_srtp->handler->out->rtp; + } + else { + rwf_in = in_srtp->handler->in->rtcp; + rwf_out = in_srtp->handler->out->rtcp; + } + + mutex_lock(&out_srtp->out_lock); + + /* return values are: 0 = forward packet, -1 = error/dont forward, + * 1 = forward and push update to redis */ + if (rwf_in) + handler_ret = rwf_in(s, in_srtp); + if (handler_ret >= 0) { + if (rtcp && _log_facility_rtcp) + parse_and_log_rtcp_report(sfd, s->s, s->len); + if (rwf_out) + handler_ret += rwf_out(s, out_srtp); + } + + if (handler_ret > 0) { + __unkernelize(stream); + update = 1; + } + + mutex_unlock(&out_srtp->out_lock); + mutex_unlock(&in_srtp->in_lock); + + + /* endpoint address handling */ + + mutex_lock(&stream->in_lock); + + /* 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 */ + if (!PS_ISSET(stream, FILLED)) { + __C_DBG("stream %s:%d not FILLED", sockaddr_print_buf(&stream->endpoint.address), stream->endpoint.port); + goto forward; + } + + /* do not pay attention to source addresses of incoming packets for asymmetric streams */ + if (MEDIA_ISSET(media, ASYMMETRIC)) + PS_SET(stream, CONFIRMED); + + /* 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_ISSET2(stream, STRICT_SOURCE, MEDIA_HANDOVER)) { + endpoint = *fsin; + mutex_lock(&stream->out_lock); + + int tmp = memcmp(&endpoint, &stream->endpoint, sizeof(endpoint)); + if (tmp && PS_ISSET(stream, MEDIA_HANDOVER)) { + /* out_lock remains locked */ + ilog(LOG_INFO, "Peer address changed to %s", endpoint_print_buf(fsin)); + unk = 1; + goto update_addr; + } + + mutex_unlock(&stream->out_lock); + + if (tmp && PS_ISSET(stream, STRICT_SOURCE)) { + ilog(LOG_INFO, "Drop due to strict-source attribute; got %s:%d, expected %s:%d", + sockaddr_print_buf(&endpoint.address), endpoint.port, + sockaddr_print_buf(&stream->endpoint.address), stream->endpoint.port); + atomic64_inc(&stream->stats.errors); + goto drop; + } + } + goto kernel_check; + } + + /* wait at least 3 seconds after last signal before committing to a particular + * endpoint address */ + if (!call->last_signal || poller_now <= call->last_signal + 3) + goto update_peerinfo; + + ilog(LOG_INFO, "Confirmed peer address as %s", endpoint_print_buf(fsin)); + + PS_SET(stream, CONFIRMED); + update = 1; + +update_peerinfo: + mutex_lock(&stream->out_lock); +update_addr: + endpoint = stream->endpoint; + stream->endpoint = *fsin; + if (memcmp(&endpoint, &stream->endpoint, sizeof(endpoint))) + update = 1; + mutex_unlock(&stream->out_lock); + + /* check the destination address of the received packet against what we think our + * local interface to use is */ + if (sfd && stream->selected_sfd && sfd != stream->selected_sfd) { + ilog(LOG_INFO, "Switching local interface to %s", endpoint_print_buf(&sfd->socket.local)); + stream->selected_sfd = sfd; + update = 1; + } + + +kernel_check: + if (PS_ISSET(stream, NO_KERNEL_SUPPORT)) { + __C_DBG("stream %s:%d NO_KERNEL_SUPPORT", sockaddr_print_buf(&stream->endpoint.address), stream->endpoint.port); + goto forward; + } + + if (!PS_ISSET(stream, CONFIRMED)) { + __C_DBG("stream %s:%d not CONFIRMED", sockaddr_print_buf(&stream->endpoint.address), stream->endpoint.port); + goto forward; + } + + if (!sink) { + __C_DBG("sink is NULL for stream %s:%d", sockaddr_print_buf(&stream->endpoint.address), stream->endpoint.port); + goto forward; + } + + if (!PS_ISSET(sink, CONFIRMED)) { + __C_DBG("sink not CONFIRMED for stream %s:%d", sockaddr_print_buf(&stream->endpoint.address), stream->endpoint.port); + goto forward; + } + + if (!PS_ISSET(sink, FILLED)) { + __C_DBG("sink not FILLED for stream %s:%d", sockaddr_print_buf(&stream->endpoint.address), stream->endpoint.port); + goto forward; + } + + kernelize(stream); + +forward: + if (sink) + mutex_lock(&sink->out_lock); + + if (!sink + || !sink->advertised_endpoint.port + || (is_addr_unspecified(&sink->advertised_endpoint.address) + && !is_trickle_ice_address(&sink->advertised_endpoint)) + || stun_ret || handler_ret < 0) + goto drop; + + ret = socket_sendto(&sink->selected_sfd->socket, s->s, s->len, &sink->endpoint); + __C_DBG("Forward to sink endpoint: %s:%d", sockaddr_print_buf(&sink->endpoint.address), sink->endpoint.port); + + mutex_unlock(&sink->out_lock); + + if (ret == -1) { + ret = -errno; + ilog(LOG_DEBUG,"Error when sending message. Error: %s",strerror(errno)); + atomic64_inc(&stream->stats.errors); + atomic64_inc(&cm->statsps.errors); + goto out; + } + + sink = NULL; + +drop: + if (sink) + mutex_unlock(&sink->out_lock); + ret = 0; + atomic64_inc(&stream->stats.packets); + atomic64_add(&stream->stats.bytes, s->len); + atomic64_set(&stream->last_packet, poller_now); + atomic64_inc(&cm->statsps.packets); + atomic64_add(&cm->statsps.bytes, s->len); + +out: + if (ret == 0 && update) + ret = 1; + +done: + if (unk) + __stream_unconfirm(stream); + mutex_unlock(&stream->in_lock); + if (unk) { + stream_unconfirm(stream->rtp_sink); + stream_unconfirm(stream->rtcp_sink); + } +unlock_out: + rwlock_unlock_r(&call->master_lock); + + return ret; +} + + + + +static void stream_fd_readable(int fd, void *p, uintptr_t u) { + struct stream_fd *sfd = p; + char buf[RTP_BUFFER_SIZE]; + int ret, iters; + int update = 0; + struct call *ca; + str s; + endpoint_t ep; + + if (sfd->socket.fd != fd) + goto out; + + log_info_stream_fd(sfd); + + for (iters = 0; ; iters++) { +#if MAX_RECV_ITERS + if (iters >= MAX_RECV_ITERS) { + ilog(LOG_ERROR, "Too many packets in UDP receive queue (more than %d), " + "aborting loop. Dropped packets possible", iters); + break; + } +#endif + + ret = socket_recvfrom(&sfd->socket, buf + RTP_BUFFER_HEAD_ROOM, MAX_RTP_PACKET_SIZE, &ep); + + if (ret < 0) { + if (errno == EINTR) + continue; + if (errno == EAGAIN || errno == EWOULDBLOCK) + break; + stream_fd_closed(fd, sfd, 0); + goto done; + } + if (ret >= MAX_RTP_PACKET_SIZE) + ilog(LOG_WARNING, "UDP packet possibly truncated"); + + str_init_len(&s, buf + RTP_BUFFER_HEAD_ROOM, ret); + ret = stream_packet(sfd, &s, &ep); + if (ret < 0) { + ilog(LOG_WARNING, "Write error on RTP socket: %s", strerror(-ret)); + call_destroy(sfd->call); + goto done; + } + if (ret == 1) + update = 1; + } + +out: + ca = sfd->call ? : NULL; + + if (ca && update) { + if (ca->callmaster->conf.redis_write) { + redis_update(ca, ca->callmaster->conf.redis, ANY_REDIS_ROLE); + } else if (ca->callmaster->conf.redis) { + redis_update(ca, ca->callmaster->conf.redis, MASTER_REDIS_ROLE); + } + } +done: + log_info_clear(); +} + + + + +static void stream_fd_free(void *p) { + struct stream_fd *f = p; + + release_port(&f->socket, f->local_intf->spec); + crypto_cleanup(&f->crypto); + dtls_connection_cleanup(&f->dtls); + + obj_put(f->call); +} + +struct stream_fd *stream_fd_new(socket_t *fd, struct call *call, const struct local_intf *lif) { + struct stream_fd *sfd; + struct poller_item pi; + struct poller *po = call->callmaster->poller; + + sfd = obj_alloc0("stream_fd", sizeof(*sfd), stream_fd_free); + sfd->unique_id = g_queue_get_length(&call->stream_fds); + sfd->socket = *fd; + sfd->call = obj_get(call); + sfd->local_intf = lif; + g_queue_push_tail(&call->stream_fds, sfd); /* hand over ref */ + + __C_DBG("stream_fd_new localport=%d", sfd->socket.local.port); + + ZERO(pi); + pi.fd = sfd->socket.fd; + pi.obj = &sfd->obj; + pi.readable = stream_fd_readable; + pi.closed = stream_fd_closed; + + poller_add_item(po, &pi); + + return sfd; +} diff --git a/daemon/media_socket.h b/daemon/media_socket.h new file mode 100644 index 000000000..04e96682b --- /dev/null +++ b/daemon/media_socket.h @@ -0,0 +1,112 @@ +#ifndef _MEDIA_SOCKET_H_ +#define _MEDIA_SOCKET_H_ + + +#include +#include +#include +#include "str.h" +#include "obj.h" +#include "aux.h" +#include "dtls.h" +#include "crypto.h" +#include "socket.h" + + + + + + +struct logical_intf { + str name; + sockfamily_t *preferred_family; + GQueue list; /* struct local_intf */ + GHashTable *addr_hash; +}; +struct port_pool { + BIT_ARRAY_DECLARE(ports_used, 0x10000); + volatile unsigned int last_used; + volatile unsigned int free_ports; + + unsigned int min, max; +}; +struct intf_address { + socktype_t *type; + sockaddr_t addr; + sockaddr_t advertised; +}; +struct intf_config { + str name; + struct intf_address address; + unsigned int port_min, port_max; +}; +struct intf_spec { + struct intf_address address; + str ice_foundation; + struct port_pool port_pool; +}; +struct local_intf { + struct intf_spec *spec; + unsigned int unique_id; /* starting with 0 - serves as preference */ + const struct logical_intf *logical; +}; +struct intf_list { + const struct local_intf *local_intf; + GQueue list; +}; +struct stream_fd { + struct obj obj; + unsigned int unique_id; /* RO */ + socket_t socket; /* RO */ + const struct local_intf *local_intf; /* RO */ + struct call *call; /* RO */ + struct packet_stream *stream; /* LOCK: call->master_lock */ + struct crypto_context crypto; /* IN direction, LOCK: stream->in_lock */ + struct dtls_connection dtls; /* LOCK: stream->in_lock */ +}; + + + +void interfaces_init(GQueue *interfaces); + +struct logical_intf *get_logical_interface(const str *name, sockfamily_t *fam, int num_ports); +struct local_intf *get_interface_address(const struct logical_intf *lif, sockfamily_t *fam); +struct local_intf *get_any_interface_address(const struct logical_intf *lif, sockfamily_t *fam); +void interfaces_exclude_port(unsigned int port); + +//int get_port(socket_t *r, unsigned int port, const struct local_intf *lif, const struct call *c); +//void release_port(socket_t *r, const struct local_intf *); +INLINE void set_tos(socket_t *s, unsigned int tos) { + s->family->tos(s, tos); +} +int __get_consecutive_ports(GQueue *out, unsigned int num_ports, unsigned int wanted_start_port, + struct intf_spec *spec); +int get_consecutive_ports(GQueue *out, unsigned int num_ports, const struct logical_intf *log); +struct stream_fd *stream_fd_new(socket_t *fd, struct call *call, const struct local_intf *lif); + +void free_intf_list(struct intf_list *il); +void free_socket_intf_list(struct intf_list *il); + +INLINE int open_intf_socket(socket_t *r, unsigned int port, const struct local_intf *lif) { + return open_socket(r, SOCK_DGRAM, port, &lif->spec->address.addr); +} + +void kernelize(struct packet_stream *); +void __unkernelize(struct packet_stream *); +void unkernelize(struct packet_stream *); +void __stream_unconfirm(struct packet_stream *); + +/* XXX shouldnt be necessary */ +/* +INLINE struct local_intf *get_interface_from_address(const struct logical_intf *lif, + const sockaddr_t *addr, socktype_t *type) +{ + struct intf_address a; + a.type = type; + a.addr = *addr; + return g_hash_table_lookup(lif->addr_hash, &a); +} +*/ + + +#endif diff --git a/daemon/redis.c b/daemon/redis.c index 1f091db4d..104644756 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "redis.h" #include "compat.h" @@ -134,7 +135,7 @@ static int redis_connect(struct redis *r, int wait, int role) { tv.tv_sec = 1; tv.tv_usec = 0; - r->ctx = redisConnectWithTimeout(r->host, r->port, tv); + r->ctx = redisConnectWithTimeout(r->host, r->endpoint.port, tv); if (!r->ctx) goto err; @@ -205,14 +206,13 @@ err: -struct redis *redis_new(u_int32_t ip, u_int16_t port, int db, int role) { +struct redis *redis_new(const endpoint_t *ep, int db, int role) { struct redis *r; r = g_slice_alloc0(sizeof(*r)); - r->ip = ip; - sprintf(r->host, IPF, IPP(ip)); - r->port = port; + r->endpoint = *ep; + sockaddr_print(&ep->address, r->host, sizeof(r->host)); r->db = db; mutex_init(&r->lock); @@ -247,63 +247,31 @@ static void redis_check_conn(struct redis *r, int role) { abort(); } -/* -static void _redis_pipe(struct redis *r, const char *fmt, ...) { - va_list ap; - char interm[200]; - va_start(ap, fmt); - sprintf(interm, fmt, ap); - va_end(ap); - ilog(LOG_ERR, "<< %s >>> \n", interm); + +static void redis_delete_list(struct redis *r, const str *callid, const char *prefix, GQueue *q) { + unsigned int i; + + for (i = 0; i < g_queue_get_length(q); i++) + redis_pipe(r, "DEL %s-"PB"-%u", prefix, STR(callid), i); } -*/ /* called with r->lock held and c->master_lock held */ static void redis_delete_call(struct call *c, struct redis *r) { - GSList *l, *n; - GList *k; - struct call_monologue *ml; - struct call_media *media; - struct stream_fd *sfd; - struct packet_stream *ps; - struct endpoint_map *em; - char *mono_key, *media_key, *em_key; - redis_pipe(r, "SREM calls "PB"", STR(&c->callid)); - redis_pipe(r, "DEL call-"PB" tags-"PB" sfds-"PB" streams-"PB"", - STR(&c->callid), STR(&c->callid), STR(&c->callid), STR(&c->callid)); - - for (l = c->stream_fds; l; l = l->next) { - sfd = l->data; - redis_pipe(r, "DEL sfd-%s", HKEY(sfd)); - } - for (l = c->streams; l; l = l->next) { - ps = l->data; - redis_pipe(r, "DEL stream-%s", HKEY(ps)); - } - - for (l = c->monologues; l; l = l->next) { - ml = l->data; - mono_key = HKEY(ml); - - redis_pipe(r, "DEL tag-%s other_tags-%s medias-%s", mono_key, mono_key, - mono_key); - - for (k = ml->medias.head; k; k = k->next) { - media = k->data; - media_key = HKEY(media); - - redis_pipe(r, "DEL media-%s streams-%s maps-%s payload_types-%s", - media_key, media_key, media_key, media_key); - - for (n = media->endpoint_maps; n; n = n->next) { - em = n->data; - em_key = HKEY(em); - redis_pipe(r, "DEL map-%s sfds-%s", em_key, em_key); - } - } - } + redis_pipe(r, "DEL call-"PB"", STR(&c->callid)); + redis_delete_list(r, &c->callid, "sfd", &c->stream_fds); + redis_delete_list(r, &c->callid, "stream", &c->streams); + redis_delete_list(r, &c->callid, "stream_sfds", &c->streams); + redis_delete_list(r, &c->callid, "tag", &c->monologues); + redis_delete_list(r, &c->callid, "other_tags", &c->monologues); + redis_delete_list(r, &c->callid, "medias", &c->monologues); + redis_delete_list(r, &c->callid, "media", &c->medias); + redis_delete_list(r, &c->callid, "streams", &c->medias); + redis_delete_list(r, &c->callid, "maps", &c->medias); + redis_delete_list(r, &c->callid, "payload_types", &c->medias); + redis_delete_list(r, &c->callid, "map", &c->endpoint_maps); + redis_delete_list(r, &c->callid, "map_sfds", &c->endpoint_maps); redis_consume(r); } @@ -311,14 +279,19 @@ static void redis_delete_call(struct call *c, struct redis *r) { -static int redis_get_hash(struct redis_hash *out, struct redis *r, const char *key, const redisReply *which) { +static int redis_get_hash(struct redis_hash *out, struct redis *r, const char *key, const redisReply *which, + unsigned int id) +{ redisReply *k, *v; int i; out->ht = g_hash_table_new(g_str_hash, g_str_equal); if (!out->ht) goto err; - out->rr = redis_get(r, REDIS_REPLY_ARRAY, "HGETALL %s-"PB"", key, STR_R(which)); + if (id == -1) + out->rr = redis_get(r, REDIS_REPLY_ARRAY, "HGETALL %s-"PB"", key, STR_R(which)); + else + out->rr = redis_get(r, REDIS_REPLY_ARRAY, "HGETALL %s-"PB"-%u", key, STR_R(which), id); if (!out->rr) goto err2; @@ -341,90 +314,20 @@ err2: err: return -1; } -/* -static struct redis_hash *redis_get_hash_new(struct redis *r, const char *key, const redisReply *which) { - struct redis_hash *out; - - out = g_slice_alloc(sizeof(*out)); - if (!out) - return NULL; - if (!redis_get_hash(out, r, key, which)) - return out; - g_slice_free1(sizeof(*out), out); - return NULL; -} -*/ - static void redis_destroy_hash(struct redis_hash *rh) { freeReplyObject(rh->rr); g_hash_table_destroy(rh->ht); } -/* -static void redis_free_hash(struct redis_hash *rh) { - redis_destroy_hash(rh); - g_slice_free1(sizeof(*rh), rh); -} -*/ static void redis_destroy_list(struct redis_list *rl) { - struct list_item *it; - - redis_destroy_hash(&rl->rh); - while ((it = g_queue_pop_head(&rl->q))) - g_slice_free1(sizeof(*it), it); -} - - - -static int redis_get_list_hash(struct redis_list *out, struct redis *r, const char *key, const redisReply *id, - const char *sub) -{ - redisReply *el; - int i; - struct list_item *it; - - g_queue_init(&out->q); - out->rh.ht = g_hash_table_new(g_str_hash, g_str_equal); - if (!out->rh.ht) - return -1; - - out->rh.rr = redis_get(r, REDIS_REPLY_ARRAY, "LRANGE %s-"PB" 0 -1", key, STR_R(id)); - if (!out->rh.rr) - goto err; - - for (i = 0; i < out->rh.rr->elements; i++) { - el = out->rh.rr->element[i]; - if (el->type != REDIS_REPLY_STRING) - continue; - - it = g_slice_alloc(sizeof(*it)); - if (!it) - goto err2; - - it->id = el; - - if (redis_get_hash(&it->rh, r, sub, el)) - goto err3; - - if (g_hash_table_insert_check(out->rh.ht, el->str, it) != TRUE) - goto err4; + unsigned int i; - g_queue_push_tail(&out->q, it); + for (i = 0; i < rl->len; i++) { + redis_destroy_hash(&rl->rh[i]); } - - return 0; - -err4: - redis_destroy_hash(&it->rh); -err3: - g_slice_free1(sizeof(*it), it); -err2: - freeReplyObject(out->rh.rr); -err: - g_hash_table_destroy(out->rh.ht); - g_queue_clear(&out->q); - return -1; + free(rl->rh); + free(rl->ptrs); } @@ -456,12 +359,14 @@ static atomic64 strtoa64(const char *c, char **endp, int base) { define_get_int_type(time_t, time_t, strtoull); define_get_int_type(int, int, strtol); define_get_int_type(unsigned, unsigned int, strtol); -define_get_int_type(u16, u_int16_t, strtol); +//define_get_int_type(u16, u_int16_t, strtol); define_get_int_type(u64, u_int64_t, strtoull); define_get_int_type(a64, atomic64, strtoa64); define_get_type_format(str, str); -define_get_type_format(u16, u_int16_t); +define_get_type_format(int, int); +//define_get_type_format(unsigned, unsigned int); +//define_get_type_format(u16, u_int16_t); //define_get_type_format(u64, u_int64_t); define_get_type_format(a64, atomic64); @@ -501,11 +406,9 @@ static int redis_hash_get_bool_flag(const struct redis_hash *h, const char *k) { static int redis_hash_get_endpoint(struct endpoint *out, const struct redis_hash *h, const char *k) { str s; - if (redis_hash_get_str_f(&s, h, "%s-addr", k)) - return -1; - if (inet_pton(AF_INET6, s.s, &out->ip46) != 1) + if (redis_hash_get_str(&s, h, k)) return -1; - if (redis_hash_get_u16_f(&out->port, h, "%s-port", k)) + if (endpoint_parse_any(out, s.s)) return -1; return 0; @@ -519,32 +422,88 @@ static int redis_hash_get_stats(struct stats *out, const struct redis_hash *h, c return -1; return 0; } -static void *redis_hash_get_ptr(struct redis_list *list, const char *key) { - struct list_item *it; - - if (!strcmp(key, "0")) - return NULL; - it = g_hash_table_lookup(list->rh.ht, key); - if (!it) +static void *redis_list_get_idx_ptr(struct redis_list *list, unsigned int idx) { + if (idx > list->len) return NULL; - return it->ptr; + return list->ptrs[idx]; } -static void *redis_hash_get_ptr_rr(struct redis_list *list, const redisReply *rr) { - if (rr->type != REDIS_REPLY_STRING) +static void *redis_list_get_ptr(struct redis_list *list, struct redis_hash *rh, const char *key) { + unsigned int idx; + if (redis_hash_get_unsigned(&idx, rh, key)) return NULL; - return redis_hash_get_ptr(list, rr->str); + return redis_list_get_idx_ptr(list, idx); } -static void *redis_hash_get_ptr_hash(struct redis_list *list, struct redis_hash *rh, const char *key) { +static int redis_build_list_cb(GQueue *q, struct redis *r, const char *key, const str *callid, + unsigned int idx, struct redis_list *list, + int (*cb)(str *, GQueue *, struct redis_list *, void *), void *ptr) +{ + redisReply *rr; + int i; str s; - if (redis_hash_get_str(&s, rh, key)) - return NULL; - return redis_hash_get_ptr(list, s.s); + rr = redis_get(r, REDIS_REPLY_ARRAY, "LRANGE %s-"PB"-%u 0 -1", key, STR(callid), idx); + if (!rr) + return -1; + + for (i = 0; i < rr->elements; i++) { + if (rr->element[i]->type != REDIS_REPLY_STRING) + return -1; + str_init_len(&s, rr->element[i]->str, rr->element[i]->len); + if (cb(&s, q, list, ptr)) + return -1; + } + return 0; +} +static int rbl_cb_simple(str *s, GQueue *q, struct redis_list *list, void *ptr) { + int j; + j = str_to_i(s, 0); + g_queue_push_tail(q, redis_list_get_idx_ptr(list, (unsigned) j)); + return 0; +} +static int redis_build_list(GQueue *q, struct redis *r, const char *key, const str *callid, + unsigned int idx, struct redis_list *list) +{ + return redis_build_list_cb(q, r, key, callid, idx, list, rbl_cb_simple, NULL); +} +static int redis_get_list_hash(struct redis_list *out, struct redis *r, const char *key, const redisReply *id, + const struct redis_hash *rh, const char *rh_num_key) +{ + unsigned int i; + + if (redis_hash_get_unsigned(&out->len, rh, rh_num_key)) + return -1; + out->rh = malloc(sizeof(*out->rh) * out->len); + if (!out->rh) + return -1; + out->ptrs = malloc(sizeof(*out->ptrs) * out->len); + if (!out->ptrs) + goto err1; + + for (i = 0; i < out->len; i++) { + if (redis_get_hash(&out->rh[i], r, key, id, i)) + goto err2; + } + + return 0; + +err2: + free(out->ptrs); + while (i) { + i--; + redis_destroy_hash(&out->rh[i]); + } +err1: + free(out->rh); + return -1; } + + + /* can return 1, 0 or -1 */ static int redis_hash_get_crypto_params(struct crypto_params *out, const struct redis_hash *h, const char *k) { str s; + int i; if (redis_hash_get_str_f(&s, h, "%s-crypto_suite", k)) return 1; @@ -562,8 +521,16 @@ static int redis_hash_get_crypto_params(struct crypto_params *out, const struct return -1; out->mki = malloc(s.len); memcpy(out->mki, s.s, s.len); + out->mki_len = s.len; } + if (!redis_hash_get_int_f(&i, h, "%s-unenc-srtp", k)) + out->session_params.unencrypted_srtp = i; + if (!redis_hash_get_int_f(&i, h, "%s-unenc-srtcp", k)) + out->session_params.unencrypted_srtcp = i; + if (!redis_hash_get_int_f(&i, h, "%s-unauth-srtp", k)) + out->session_params.unauthenticated_srtp = i; + return 0; } static int redis_hash_get_crypto_context(struct crypto_context *out, const struct redis_hash *h) { @@ -574,108 +541,91 @@ static int redis_hash_get_crypto_context(struct crypto_context *out, const struc return 0; else if (ret) return -1; + if (redis_hash_get_u64(&out->last_index, h, "last_index")) return -1; + redis_hash_get_unsigned(&out->ssrc, h, "ssrc"); return 0; } -static int redis_hash_build_list(struct redis *r, const char *key, redisReply *tag, struct redis_list *list, - int (*func)(void *, void *), void *up) { - redisReply *rr; - void *ptr; - int i, ret = -1; - - rr = redis_get(r, REDIS_REPLY_ARRAY, "LRANGE %s-"PB" 0 -1", key, STR_R(tag)); - if (!rr) - return -1; - - for (i = 0; i < rr->elements; i++) { - ptr = redis_hash_get_ptr_rr(list, rr->element[i]); - if (!ptr) - goto out; - if (func(up, ptr)) - goto out; - } - - ret = 0; -out: - freeReplyObject(rr); - return ret; -} -static int redis_build_other_tags(void *a, void *b) { - struct call_monologue *A = a, *B = b; - - g_hash_table_insert(A->other_tags, &B->tag, B); - return 0; -} -static int redis_build_streams(void *a, void *b) { - struct call_media *med = a; - struct packet_stream *ps = b; - - g_queue_push_tail(&med->streams, ps); - ps->media = med; - return 0; -} -static int redis_build_em_sfds(void *a, void *b) { - struct endpoint_map *em = a; - - g_queue_push_tail(&em->sfds, b); - return 0; -} - - static int redis_sfds(struct call *c, struct redis_list *sfds) { - GList *l; - struct list_item *it; + unsigned int i; + str family, intf_name; + struct redis_hash *rh; + sockfamily_t *fam; + struct logical_intf *lif; + struct local_intf *loc; + GQueue q = G_QUEUE_INIT; + unsigned int loc_uid; struct stream_fd *sfd; - struct udp_fd fd; + socket_t *sock; int port; - for (l = sfds->q.head; l; l = l->next) { - it = l->data; + for (i = 0; i < sfds->len; i++) { + rh = &sfds->rh[i]; - if (redis_hash_get_int(&port, &it->rh, "localport")) + if (redis_hash_get_int(&port, rh, "localport")) + return -1; + if (redis_hash_get_str(&family, rh, "pref_family")) + return -1; + if (redis_hash_get_str(&intf_name, rh, "logical_intf")) return -1; - if (__get_consecutive_ports(&fd, 1, port, c)) + if (redis_hash_get_unsigned(&loc_uid, rh, "local_intf_uid")) return -1; - sfd = __stream_fd_new(&fd, c); - redis_hkey_cpy( sfd->redis_hkey, it->id->str); - if (redis_hash_get_crypto_context(&sfd->crypto, &it->rh)) + fam = get_socket_family_rfc(&family); + if (!fam) return -1; - it->ptr = sfd; + lif = get_logical_interface(&intf_name, fam, 0); + if (!lif) + return -1; + loc = g_queue_peek_nth(&lif->list, loc_uid); + if (!loc) + return -1; + + if (__get_consecutive_ports(&q, 1, port, loc->spec)) + return -1; + sock = g_queue_pop_head(&q); + if (!sock) + return -1; + sfd = stream_fd_new(sock, c, loc); + // XXX tos + if (redis_hash_get_crypto_context(&sfd->crypto, rh)) + return -1; + + sfds->ptrs[i] = sfd; } return 0; } static int redis_streams(struct call *c, struct redis_list *streams) { - GList *l; - struct list_item *it; + unsigned int i; + struct redis_hash *rh; struct packet_stream *ps; - for (l = streams->q.head; l; l = l->next) { - it = l->data; + for (i = 0; i < streams->len; i++) { + rh = &streams->rh[i]; ps = __packet_stream_new(c); if (!ps) return -1; atomic64_set_na(&ps->last_packet, time(NULL)); - if (redis_hash_get_unsigned((unsigned int *) &ps->ps_flags, &it->rh, "ps_flags")) + if (redis_hash_get_unsigned((unsigned int *) &ps->ps_flags, rh, "ps_flags")) return -1; - if (redis_hash_get_endpoint(&ps->endpoint, &it->rh, "endpoint")) + if (redis_hash_get_endpoint(&ps->endpoint, rh, "endpoint")) return -1; - if (redis_hash_get_endpoint(&ps->advertised_endpoint, &it->rh, "advertised_endpoint")) + if (redis_hash_get_endpoint(&ps->advertised_endpoint, rh, "advertised_endpoint")) return -1; - if (redis_hash_get_stats(&ps->stats, &it->rh, "stats")) + if (redis_hash_get_stats(&ps->stats, rh, "stats")) return -1; - if (redis_hash_get_crypto_context(&ps->crypto, &it->rh)) + if (redis_hash_get_crypto_context(&ps->crypto, rh)) return -1; - it->ptr = ps; - redis_hkey_cpy(ps->redis_hkey, it->id->str); + + streams->ptrs[i] = ps; PS_CLEAR(ps, KERNELIZED); } @@ -683,222 +633,272 @@ static int redis_streams(struct call *c, struct redis_list *streams) { } static int redis_tags(struct call *c, struct redis_list *tags) { - GList *l; - struct list_item *it; + unsigned int i; + struct redis_hash *rh; struct call_monologue *ml; str s; - for (l = tags->q.head; l; l = l->next) { - it = l->data; + for (i = 0; i < tags->len; i++) { + rh = &tags->rh[i]; ml = __monologue_create(c); if (!ml) return -1; - redis_hkey_cpy(ml->redis_hkey, it->id->str); - - if (redis_hash_get_time_t(&ml->created, &it->rh, "created")) + if (redis_hash_get_time_t(&ml->created, rh, "created")) return -1; - if (!redis_hash_get_str(&s, &it->rh, "tag")) + if (!redis_hash_get_str(&s, rh, "tag")) __monologue_tag(ml, &s); - if (!redis_hash_get_str(&s, &it->rh, "via-branch")) + if (!redis_hash_get_str(&s, rh, "via-branch")) __monologue_viabranch(ml, &s); - redis_hash_get_time_t(&ml->deleted, &it->rh, "deleted"); - it->ptr = ml; + redis_hash_get_time_t(&ml->deleted, rh, "deleted"); + + tags->ptrs[i] = ml; } return 0; } -static int redis_link_sfds(struct redis_list *sfds, struct redis_list *streams) { - GList *l; - struct list_item *it; - struct stream_fd *sfd; - - for (l = sfds->q.head; l; l = l->next) { - it = l->data; - sfd = it->ptr; - sfd->stream = redis_hash_get_ptr_hash(streams, &it->rh, "stream"); - if (!sfd->stream) - return -1; - } +static int rbl_cb_plts(str *s, GQueue *q, struct redis_list *list, void *ptr) { + struct rtp_payload_type *pt; + str ptype, enc, clock, parms; + struct call_media *med = ptr; + struct call *call = med->call; + if (str_token(&ptype, s, '/')) + return -1; + if (str_token(&enc, s, '/')) + return -1; + if (str_token(&clock, s, '/')) + return -1; + parms = *s; + + // from call.c + // XXX remove all the duplicate code + pt = g_slice_alloc0(sizeof(*pt)); + pt->payload_type = str_to_ui(&ptype, 0); + call_str_cpy(call, &pt->encoding, &enc); + pt->clock_rate = str_to_ui(&clock, 0); + call_str_cpy(call, &pt->encoding_parameters, &parms); + g_hash_table_replace(med->rtp_payload_types, &pt->payload_type, pt); return 0; } - -static int redis_tags_populate(struct redis *r, struct redis_list *tags, struct redis_list *streams, - struct redis_list *sfds) -{ - GList *l_tags, *l_medias, *l_ems, *l_streams; - struct list_item *it_tag, *it_media, *it_em; - struct packet_stream *it_stream; - struct call_monologue *ml; - struct redis_list rl_medias, rl_ems; - int i; +static int redis_medias(struct redis *r, struct call *c, struct redis_list *medias) { + unsigned int i; + struct redis_hash *rh; struct call_media *med; str s; - struct endpoint_map *em = NULL; - struct callmaster *cm; - struct in6_addr in6a; - struct rtp_payload_type *pt = NULL; - struct redis_hash pt_hash; - unsigned int pt_index, pt_totals; - char hash_key[32]; - for (l_tags = tags->q.head; l_tags; l_tags = l_tags->next) { - it_tag = l_tags->data; - ml = it_tag->ptr; + for (i = 0; i < medias->len; i++) { + rh = &medias->rh[i]; - cm = ml->call->callmaster; + /* from call.c:__get_media() */ + med = uid_slice_alloc0(med, &c->medias); + med->call = c; + med->rtp_payload_types = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, + __payload_type_free); - if (redis_hash_build_list(r, "other_tags", it_tag->id, tags, redis_build_other_tags, ml)) + if (redis_hash_get_unsigned(&med->index, rh, "index")) + return -1; + if (redis_hash_get_str(&s, rh, "type")) return -1; - ml->active_dialogue = redis_hash_get_ptr_hash(tags, &it_tag->rh, "active"); + call_str_cpy(c, &med->type, &s); - if (redis_get_list_hash(&rl_medias, r, "medias", it_tag->id, "media")) + if (redis_hash_get_str(&s, rh, "protocol")) return -1; + med->protocol = transport_protocol(&s); - for (i = 1, l_medias = rl_medias.q.head; l_medias; i++, l_medias = l_medias->next) { - it_media = l_medias->data; + if (redis_hash_get_str(&s, rh, "desired_family")) + return -1; + med->desired_family = get_socket_family_rfc(&s); - /* from call.c:__get_media() */ - med = g_slice_alloc0(sizeof(*med)); - med->monologue = ml; - med->call = ml->call; - med->index = i; - med->rtp_payload_types = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, __payload_type_free); + if (redis_hash_get_str(&s, rh, "logical_intf") + || !(med->logical_intf = get_logical_interface(&s, med->desired_family, 0))) + { + rlog(LOG_ERR, "unable to find specified local interface"); + med->logical_intf = get_logical_interface(NULL, med->desired_family, 0); + } - redis_hkey_cpy(med->redis_hkey, it_media->id->str); + if (redis_hash_get_unsigned(&med->sdes_in.tag, rh, "sdes_in_tag")) + return -1; + if (redis_hash_get_unsigned(&med->sdes_out.tag, rh, "sdes_out_tag")) + return -1; + if (redis_hash_get_unsigned((unsigned int *) &med->media_flags, rh, + "media_flags")) + return -1; + if (redis_hash_get_crypto_params(&med->sdes_in.params, rh, "sdes_in") < 0) + return -1; + if (redis_hash_get_crypto_params(&med->sdes_out.params, rh, "sdes_out") < 0) + return -1; - g_queue_push_tail(&ml->medias, med); + redis_build_list_cb(NULL, r, "payload_types", &c->callid, i, NULL, rbl_cb_plts, med); + /* XXX dtls */ - if (redis_hash_get_str(&s, &it_media->rh, "type")) - goto free1; - call_str_cpy(ml->call, &med->type, &s); + medias->ptrs[i] = med; + } - if (redis_hash_get_str(&s, &it_media->rh, "protocol")) - goto free1; - med->protocol = transport_protocol(&s); + return 0; +} - if (redis_hash_get_int(&med->desired_family, &it_media->rh, "desired_family")) - goto free1; +static int redis_maps(struct call *c, struct redis_list *maps) { + unsigned int i; + struct redis_hash *rh; + struct endpoint_map *em; + str s, t; + sockfamily_t *fam; - if (redis_hash_get_str(&s, &it_media->rh, "interface") - || !(med->interface = get_local_interface(cm, &s, med->desired_family))) - { - rlog(LOG_ERR, "unable to find specified local interface"); - med->interface = get_local_interface(cm, NULL, med->desired_family); - } + for (i = 0; i < maps->len; i++) { + rh = &maps->rh[i]; - if (redis_hash_get_str(&s, &it_media->rh, "local_address") - || inet_pton(AF_INET6, s.s, &in6a) != 1 - || !(med->local_address = get_interface_from_address(med->interface, - &in6a))) - { - rlog(LOG_ERR, "unable to find specified local address"); - med->local_address = get_any_interface_address(med->interface, - med->desired_family); - } + /* from call.c:__get_endpoint_map() */ + em = uid_slice_alloc0(em, &c->endpoint_maps); + g_queue_init(&em->intf_sfds); - if (redis_hash_get_unsigned(&med->sdes_in.tag, &it_media->rh, "sdes_in_tag")) - goto free1; - if (redis_hash_get_unsigned(&med->sdes_out.tag, &it_media->rh, "sdes_out_tag")) - goto free1; - if (redis_hash_get_unsigned((unsigned int *) &med->media_flags, &it_media->rh, - "media_flags")) - goto free1; - if (redis_hash_get_crypto_params(&med->sdes_in.params, &it_media->rh, "sdes_in") < 0) - goto free1; - if (redis_hash_get_crypto_params(&med->sdes_out.params, &it_media->rh, "sdes_out") < 0) - goto free1; - /* XXX dtls */ - - if (redis_hash_build_list(r, "streams", it_media->id, streams, redis_build_streams, med)) - goto free1; - - if (redis_get_list_hash(&rl_ems, r, "maps", it_media->id, "map")) - goto free1; - - for (l_ems = rl_ems.q.head; l_ems; l_ems = l_ems->next) { - it_em = l_ems->data; - - /* from call.c:__get_endpoint_map() */ - em = g_slice_alloc0(sizeof(*em)); - g_queue_init(&em->sfds); - med->endpoint_maps = g_slist_prepend(med->endpoint_maps, em); - - redis_hkey_cpy(em->redis_hkey, it_em->id->str); - - if (redis_hash_get_endpoint(&em->endpoint, &it_em->rh, "endpoint")) - goto free2; - if (redis_hash_build_list(r, "sfds", it_em->id, sfds, redis_build_em_sfds, em)) - goto free2; - em->wildcard = redis_hash_get_bool_flag(&it_em->rh, "wildcard"); - } + em->wildcard = redis_hash_get_bool_flag(rh, "wildcard"); + if (redis_hash_get_unsigned(&em->num_ports, rh, "num_ports")) + return -1; + if (redis_hash_get_str(&t, rh, "intf_preferred_family")) + return -1; + fam = get_socket_family_rfc(&t); + if (!fam) + return -1; + if (redis_hash_get_str(&s, rh, "logical_intf") + || !(em->logical_intf = get_logical_interface(&s, fam, 0))) + { + rlog(LOG_ERR, "unable to find specified local interface"); + em->logical_intf = get_logical_interface(NULL, fam, 0); + } + if (redis_hash_get_endpoint(&em->endpoint, rh, "endpoint")) + return -1; - // get payload_types hash content (e.g 0:pt0, 1:pt1, ...) - if (redis_get_hash(&pt_hash, r, "payload_types", it_media->id)) - goto free3; + maps->ptrs[i] = em; + } - // fill media rtp_payload_types - pt_totals = (unsigned int)g_hash_table_size(pt_hash.ht); - for (pt_index = 0; pt_index < pt_totals; pt_index++) { - pt = g_slice_alloc0(sizeof(*pt)); - if (!pt) { - goto free3; - } + return 0; +} - sprintf(hash_key,"%u", pt_index); - if (redis_hash_get_unsigned(&pt->payload_type, &pt_hash, hash_key)) { - goto free3; - } +static int redis_link_sfds(struct redis_list *sfds, struct redis_list *streams) { + unsigned int i; + struct stream_fd *sfd; - g_hash_table_insert(med->rtp_payload_types, &pt->payload_type, pt); - } + for (i = 0; i < sfds->len; i++) { + sfd = sfds->ptrs[i]; - // fill streams rtp_stats - for (l_streams = med->streams.head; l_streams; l_streams = l_streams->next) { - it_stream = l_streams->data; - __rtp_stats_update(it_stream->rtp_stats, med->rtp_payload_types); - } + sfd->stream = redis_list_get_ptr(streams, &sfds->rh[i], "stream"); + if (!sfd->stream) + return -1; + } + return 0; +} + +static int redis_link_tags(struct redis *r, struct call *c, struct redis_list *tags, struct redis_list *medias) +{ + unsigned int i; + struct call_monologue *ml, *other_ml; + GQueue q = G_QUEUE_INIT; + GList *l; + + for (i = 0; i < tags->len; i++) { + ml = tags->ptrs[i]; + + ml->active_dialogue = redis_list_get_ptr(tags, &tags->rh[i], "active"); + + if (redis_build_list(&q, r, "other_tags", &c->callid, i, tags)) + return -1; + for (l = q.head; l; l = l->next) { + other_ml = l->data; + g_hash_table_insert(ml->other_tags, &other_ml->tag, other_ml); } + g_queue_clear(&q); + + if (redis_build_list(&ml->medias, r, "medias", &c->callid, i, medias)) + return -1; } return 0; +} -free3: - redis_destroy_hash(&pt_hash); - if (pt) { - g_slice_free1(sizeof(*pt), pt); - } -free2: - med->endpoint_maps = g_slist_delete_link(med->endpoint_maps, med->endpoint_maps); - if (em) { - g_slice_free1(sizeof(*em), em); +static int redis_link_streams(struct redis *r, struct call *c, struct redis_list *streams, + struct redis_list *sfds, struct redis_list *medias) +{ + unsigned int i; + struct packet_stream *ps; + + for (i = 0; i < streams->len; i++) { + ps = streams->ptrs[i]; + + ps->media = redis_list_get_ptr(medias, &streams->rh[i], "media"); + ps->selected_sfd = redis_list_get_ptr(sfds, &streams->rh[i], "sfd"); + ps->rtp_sink = redis_list_get_ptr(streams, &streams->rh[i], "rtp_sink"); + ps->rtcp_sink = redis_list_get_ptr(streams, &streams->rh[i], "rtcp_sink"); + ps->rtcp_sibling = redis_list_get_ptr(streams, &streams->rh[i], "rtcp_sibling"); + + if (redis_build_list(&ps->sfds, r, "stream_sfds", &c->callid, i, sfds)) + return -1; + + if (ps->media) + __rtp_stats_update(ps->rtp_stats, ps->media->rtp_payload_types); } -free1: - g_queue_pop_tail(&ml->medias); - g_queue_clear(&med->streams); - g_slice_free1(sizeof(*med), med); - return -1; + + return 0; } -static int redis_link_streams(struct redis_list *streams, struct redis_list *sfds) { - GList *l; - struct list_item *it; - struct packet_stream *ps; +static int redis_link_medias(struct redis *r, struct call *c, struct redis_list *medias, + struct redis_list *streams, struct redis_list *maps, struct redis_list *tags) +{ + unsigned int i; + struct call_media *med; + + for (i = 0; i < medias->len; i++) { + med = medias->ptrs[i]; + + med->monologue = redis_list_get_ptr(tags, &medias->rh[i], "tag"); + if (!med->monologue) + return -1; + if (redis_build_list(&med->streams, r, "streams", &c->callid, i, streams)) + return -1; + if (redis_build_list(&med->endpoint_maps, r, "maps", &c->callid, i, maps)) + return -1; + } + return 0; +} - for (l = streams->q.head; l; l = l->next) { - it = l->data; - ps = it->ptr; +static int rbl_cb_intf_sfds(str *s, GQueue *q, struct redis_list *list, void *ptr) { + int i; + struct intf_list *il; + struct endpoint_map *em; - ps->sfd = redis_hash_get_ptr_hash(sfds, &it->rh, "sfd"); - ps->rtp_sink = redis_hash_get_ptr_hash(streams, &it->rh, "rtp_sink"); - ps->rtcp_sink = redis_hash_get_ptr_hash(streams, &it->rh, "rtcp_sink"); - ps->rtcp_sibling = redis_hash_get_ptr_hash(streams, &it->rh, "rtcp_sibling"); + if (!strncmp(s->s, "loc-", 4)) { + il = g_slice_alloc0(sizeof(*il)); + em = ptr; + i = atoi(s->s+4); + il->local_intf = g_queue_peek_nth((GQueue*) &em->logical_intf->list, i); + if (!il->local_intf) + return -1; + g_queue_push_tail(q, il); + return 0; } + il = g_queue_peek_tail(q); + if (!il) + return -1; + g_queue_push_tail(&il->list, redis_list_get_idx_ptr(list, atoi(s->s))); + return 0; +} +static int redis_link_maps(struct redis *r, struct call *c, struct redis_list *maps, + struct redis_list *sfds) +{ + unsigned int i; + struct endpoint_map *em; + + for (i = 0; i < maps->len; i++) { + em = maps->ptrs[i]; + + if (redis_build_list_cb(&em->intf_sfds, r, "map_sfds", &c->callid, em->unique_id, sfds, + rbl_cb_intf_sfds, em)) + return -1; + } return 0; } @@ -906,33 +906,41 @@ static int redis_link_streams(struct redis_list *streams, struct redis_list *sfd + static void redis_restore_call(struct redis *r, struct callmaster *m, const redisReply *id) { struct redis_hash call; - struct redis_list tags, sfds, streams; + struct redis_list tags, sfds, streams, medias, maps; struct call *c = NULL; str s; const char *err; int i; err = "'call' data incomplete"; - if (redis_get_hash(&call, r, "call", id)) + if (redis_get_hash(&call, r, "call", id, -1)) goto err1; err = "'tags' incomplete"; - if (redis_get_list_hash(&tags, r, "tags", id, "tag")) + if (redis_get_list_hash(&tags, r, "tag", id, &call, "num_tags")) goto err2; err = "'sfds' incomplete"; - if (redis_get_list_hash(&sfds, r, "sfds", id, "sfd")) + if (redis_get_list_hash(&sfds, r, "sfd", id, &call, "num_sfds")) goto err3; err = "'streams' incomplete"; - if (redis_get_list_hash(&streams, r, "streams", id, "stream")) + if (redis_get_list_hash(&streams, r, "stream", id, &call, "num_streams")) goto err4; + err = "'medias' incomplete"; + if (redis_get_list_hash(&medias, r, "media", id, &call, "num_medias")) + goto err5; + err = "'maps' incomplete"; + if (redis_get_list_hash(&maps, r, "map", id, &call, "num_maps")) + goto err7; - s.s = id->str; - s.len = id->len; + str_init_len(&s, id->str, id->len); + //s.s = id->str; + //s.len = id->len; c = call_get_or_create(&s, m); err = "failed to create call struct"; if (!c) - goto err5; + goto err8; err = "missing 'created' timestamp"; if (redis_hash_get_time_t(&c->created, &call, "created")) @@ -948,11 +956,8 @@ static void redis_restore_call(struct redis *r, struct callmaster *m, const redi redis_hash_get_time_t(&c->ml_deleted, &call, "ml_deleted"); if (!redis_hash_get_str(&s, &call, "created_from")) c->created_from = call_strdup(c, s.s); - if (!redis_hash_get_str(&s, &call, "created_from_addr")) { - parse_ip6_port(&c->created_from_addr.sin6_addr, &c->created_from_addr.sin6_port, s.s); - c->created_from_addr.sin6_port = htons(c->created_from_addr.sin6_port); - c->created_from_addr.sin6_family = AF_INET6; - } + if (!redis_hash_get_str(&s, &call, "created_from_addr")) + sockaddr_parse_any_str(&c->created_from_addr, &s); err = "failed to create sfds"; if (redis_sfds(c, &sfds)) @@ -963,15 +968,27 @@ static void redis_restore_call(struct redis *r, struct callmaster *m, const redi err = "failed to create tags"; if (redis_tags(c, &tags)) goto err6; + err = "failed to create medias"; + if (redis_medias(r, c, &medias)) + goto err6; + err = "failed to create maps"; + if (redis_maps(c, &maps)) + goto err6; err = "failed to link sfds"; if (redis_link_sfds(&sfds, &streams)) goto err6; - err = "failed to populate tags"; - if (redis_tags_populate(r, &tags, &streams, &sfds)) - goto err6; err = "failed to link streams"; - if (redis_link_streams(&streams, &sfds)) + if (redis_link_streams(r, c, &streams, &sfds, &medias)) + goto err6; + err = "failed to link tags"; + if (redis_link_tags(r, c, &tags, &medias)) + goto err6; + err = "failed to link medias"; + if (redis_link_medias(r, c, &medias, &streams, &maps, &tags)) + goto err6; + err = "failed to link maps"; + if (redis_link_maps(r, c, &maps, &sfds)) goto err6; err = NULL; @@ -979,6 +996,10 @@ static void redis_restore_call(struct redis *r, struct callmaster *m, const redi err6: rwlock_unlock_w(&c->master_lock); +err8: + redis_destroy_list(&maps); +err7: + redis_destroy_list(&medias); err5: redis_destroy_list(&streams); err4: @@ -995,6 +1016,8 @@ err1: call_destroy(c); obj_put(c); } + else + redisCommandNR(r->ctx, "SREM calls "PB"", STR_R(id)); } } @@ -1050,7 +1073,7 @@ int redis_restore(struct callmaster *m, struct redis *r, int role) { mutex_init(&ctx.r_m); g_queue_init(&ctx.r_q); for (i = 0; i < RESTORE_NUM_THREADS; i++) - g_queue_push_tail(&ctx.r_q, redis_new(r->ip, r->port, r->db, role)); + g_queue_push_tail(&ctx.r_q, redis_new(&r->endpoint, r->db, role)); gtp = g_thread_pool_new(restore_thread, &ctx, RESTORE_NUM_THREADS, TRUE, NULL); for (i = 0; i < calls->elements; i++) { @@ -1073,76 +1096,104 @@ err: -static int redis_update_crypto_params(struct redis *r, const char *pref, void *suff, +static int redis_update_crypto_params(struct redis *r, const char *pref, const str *callid, + unsigned int unique_id, const char *key, const struct crypto_params *p) { if (!p->crypto_suite) return -1; - redis_pipe(r, "HMSET %s-%s %s-crypto_suite %s %s-master_key "PB" %s-master_salt "PB"", - pref, suff, + redis_pipe(r, "HMSET %s-"PB"-%u %s-crypto_suite %s %s-master_key "PB" %s-master_salt "PB" " + "%s-unenc-srtp %i %s-unenc-srtcp %i %s-unauth-srtp %i", + pref, STR(callid), unique_id, key, p->crypto_suite->name, key, S_LEN(p->master_key, sizeof(p->master_key)), - key, S_LEN(p->master_salt, sizeof(p->master_salt))); + key, S_LEN(p->master_salt, sizeof(p->master_salt)), + key, p->session_params.unencrypted_srtp, + key, p->session_params.unencrypted_srtcp, + key, p->session_params.unauthenticated_srtp); if (p->mki) - redis_pipe(r, "HMSET %s-%s %s-mki "PB"", pref, suff, key, S_LEN(p->mki, p->mki_len)); + redis_pipe(r, "HMSET %s-"PB"-%u %s-mki "PB"", + pref, STR(callid), unique_id, + key, + S_LEN(p->mki, p->mki_len)); return 0; } -static void redis_update_crypto_context(struct redis *r, const char *pref, void *suff, +static void redis_update_crypto_context(struct redis *r, const char *pref, const str *callid, + unsigned int unique_id, const struct crypto_context *c) { - if (redis_update_crypto_params(r, pref, suff, "", &c->params)) + if (redis_update_crypto_params(r, pref, callid, unique_id, "", &c->params)) return; - redis_pipe(r, "HMSET %s-%s last_index "UINT64F"", - pref, suff, c->last_index); + redis_pipe(r, "HMSET %s-"PB"-%u last_index "UINT64F" ssrc %u", + pref, STR(callid), unique_id, + c->last_index, (unsigned) c->ssrc); } -static void redis_update_endpoint(struct redis *r, const char *pref, void *suff, +static void redis_update_endpoint(struct redis *r, const char *pref, const str *callid, + unsigned int unique_id, const char *key, const struct endpoint *e) { - char a[64]; - - inet_ntop(AF_INET6, &e->ip46, a, sizeof(a)); - redis_pipe(r, "HMSET %s-%s %s-addr %s %s-port %hu", - pref, suff, key, a, key, (short unsigned) e->port); + redis_pipe(r, "HMSET %s-"PB"-%u %s %s", + pref, STR(callid), unique_id, + key, endpoint_print_buf(e)); } -static void redis_update_stats(struct redis *r, const char *pref, void *suff, +static void redis_update_stats(struct redis *r, const char *pref, const str *callid, + unsigned int unique_id, const char *key, const struct stats *s) { - redis_pipe(r, "HMSET %s-%s %s-packets "UINT64F" %s-bytes "UINT64F" %s-errors "UINT64F"", - pref, suff, + redis_pipe(r, "HMSET %s-"PB"-%u %s-packets "UINT64F" %s-bytes "UINT64F" %s-errors "UINT64F"", + pref, STR(callid), unique_id, key, atomic64_get(&s->packets), key, atomic64_get(&s->bytes), key, atomic64_get(&s->errors)); } -static void redis_update_dtls_fingerprint(struct redis *r, const char *pref, void *suff, +static void redis_update_dtls_fingerprint(struct redis *r, const char *pref, const str *callid, + unsigned int unique_id, const struct dtls_fingerprint *f) { if (!f->hash_func) return; - redis_pipe(r, "HMSET %s-%s hash_func %s fingerprint "PB"", - pref, suff, + redis_pipe(r, "HMSET %s-"PB"-%u hash_func %s fingerprint "PB"", + pref, STR(callid), unique_id, f->hash_func->name, S_LEN(f->digest, sizeof(f->digest))); } +/* + * Redis data structure: + * + * SET: calls %s %s %s ... + * + * HASH: call-$callid num_sfds %u num_streams %u num_medias %u num_tags %u num_maps %u + * + * HASH: sfd-$callid-$num stream %u + * + * HASH: stream-$callid-$num media %u sfd %u rtp_sink %u rtcp_sink %u rtcp_sibling %u + * LIST: stream_sfds-$callid-$num %u %u ... + * + * HASH: tag-$callid-$num + * LIST: other_tags-$callid-$num %u %u ... + * LIST: medias-$callid-$num %u %u ... + * + * HASH: media-$callid-$num tag %u + * LIST: streams-$callid-$num %u %u ... + * LIST: maps-$callid-$num %u %u ... + * + * HASH: map-$callid-$num + * LIST: map_sfds-$callid-$num %u %u ... + */ /* must be called lock-free */ void redis_update(struct call *c, struct redis *r, int role) { - GSList *l, *n; - GList *pt_list, *pt_iter; - GList *k, *m; - struct call_monologue *ml; + GList *l, *n, *k, *m; + struct call_monologue *ml, *ml2; struct call_media *media; struct packet_stream *ps; struct stream_fd *sfd; + struct intf_list *il; struct endpoint_map *ep; - struct rtp_payload_type *pt; - char a[64]; - unsigned int pt_index; - char *sfd_key, *ps_key, *mono_key, *active_mono_key, *other_mono_key, - *media_key, *em_key, *ps_rtp_sink_key, *ps_rtcp_sink_key, - *ps_rtcp_sibling_key; + struct rtp_payload_type *pt; if (!r) return; @@ -1153,158 +1204,234 @@ void redis_update(struct call *c, struct redis *r, int role) { rwlock_lock_r(&c->master_lock); redis_pipe(r, "SREM calls "PB"", STR(&c->callid)); - redis_pipe(r, "DEL call-"PB" tags-"PB" sfds-"PB" streams-"PB"", STR(&c->callid), STR(&c->callid), - STR(&c->callid), STR(&c->callid)); - smart_ntop_port(a, &c->created_from_addr, sizeof(a)); + redis_pipe(r, "DEL call-"PB"", STR(&c->callid)); redis_pipe(r, "HMSET call-"PB" created %llu last_signal %llu tos %i deleted %llu " + "num_sfds %u num_streams %u num_medias %u num_tags %u " + "num_maps %u " "ml_deleted %llu created_from %s created_from_addr %s", STR(&c->callid), (long long unsigned) c->created, (long long unsigned) c->last_signal, - (int) c->tos, (long long unsigned) c->deleted, (long long unsigned) c->ml_deleted, - c->created_from, a); + (int) c->tos, (long long unsigned) c->deleted, + g_queue_get_length(&c->stream_fds), g_queue_get_length(&c->streams), + g_queue_get_length(&c->medias), g_queue_get_length(&c->monologues), + g_queue_get_length(&c->endpoint_maps), + (long long unsigned) c->ml_deleted, + c->created_from, sockaddr_print_buf(&c->created_from_addr)); /* XXX DTLS cert?? */ - for (l = c->stream_fds; l; l = l->next) { + redis_pipe(r, "DEL sfd-"PB"-0", STR(&c->callid)); + + for (l = c->stream_fds.head; l; l = l->next) { sfd = l->data; - sfd_key = HKEY(sfd) ; - ps_key = HKEY(sfd->stream); - redis_pipe(r, "DEL sfd-%s", sfd_key); - redis_pipe(r, "HMSET sfd-%s localport %hu stream %s", - sfd_key, (short unsigned) sfd->fd.localport, ps_key); - redis_update_crypto_context(r, "sfd", sfd, &sfd->crypto); + redis_pipe(r, "HMSET sfd-"PB"-%u pref_family %s localport %u logical_intf "PB" " + "local_intf_uid %u " + "stream %u", + STR(&c->callid), sfd->unique_id, + sfd->local_intf->logical->preferred_family->rfc_name, + sfd->socket.local.port, + STR(&sfd->local_intf->logical->name), + sfd->local_intf->unique_id, + sfd->stream->unique_id); + redis_update_crypto_context(r, "sfd", &c->callid, sfd->unique_id, &sfd->crypto); /* XXX DTLS?? */ - redis_pipe(r, "EXPIRE sfd-%s 86400", sfd_key); - redis_pipe(r, "LPUSH sfds-"PB" %s", STR(&c->callid), sfd_key); + redis_pipe(r, "EXPIRE sfd-"PB"-%u 86400", STR(&c->callid), sfd->unique_id); + + redis_pipe(r, "DEL sfd-"PB"-%u", STR(&c->callid), sfd->unique_id + 1); } - for (l = c->streams; l; l = l->next) { + redis_pipe(r, "DEL stream-"PB"-0 stream_sfds-"PB"-0", STR(&c->callid), STR(&c->callid)); + + for (l = c->streams.head; l; l = l->next) { ps = l->data; mutex_lock(&ps->in_lock); mutex_lock(&ps->out_lock); - ps_key = HKEY(ps); - ps_rtp_sink_key = HKEY(ps->rtp_sink); - ps_rtcp_sink_key = HKEY(ps->rtcp_sink); - ps_rtcp_sibling_key = HKEY(ps->rtcp_sibling); - media_key = HKEY(ps->media); - sfd_key = HKEY(ps->sfd); - - redis_pipe(r, "DEL stream-%s", ps_key); - redis_pipe(r, "HMSET stream-%s media %s sfd %s rtp_sink %s " - "rtcp_sink %s rtcp_sibling %s last_packet "UINT64F" " + redis_pipe(r, "HMSET stream-"PB"-%u media %u sfd %u rtp_sink %u " + "rtcp_sink %u rtcp_sibling %u last_packet "UINT64F" " "ps_flags %u", - ps_key, media_key, sfd_key, ps_rtp_sink_key, - ps_rtcp_sink_key, ps_rtcp_sibling_key, atomic64_get(&ps->last_packet), + STR(&c->callid), ps->unique_id, + ps->media->unique_id, + ps->selected_sfd ? ps->selected_sfd->unique_id : -1, + ps->rtp_sink ? ps->rtp_sink->unique_id : -1, + ps->rtcp_sink ? ps->rtcp_sink->unique_id : -1, + ps->rtcp_sibling ? ps->rtcp_sibling->unique_id : -1, + atomic64_get(&ps->last_packet), ps->ps_flags); - redis_update_endpoint(r, "stream", ps_key, "endpoint", &ps->endpoint); - redis_update_endpoint(r, "stream", ps_key, "advertised_endpoint", &ps->advertised_endpoint); - redis_update_stats(r, "stream", ps_key, "stats", &ps->stats); - redis_update_crypto_context(r, "stream", ps_key, &ps->crypto); + redis_update_endpoint(r, "stream", &c->callid, ps->unique_id, "endpoint", &ps->endpoint); + redis_update_endpoint(r, "stream", &c->callid, ps->unique_id, "advertised_endpoint", + &ps->advertised_endpoint); + redis_update_stats(r, "stream", &c->callid, ps->unique_id, "stats", &ps->stats); + redis_update_crypto_context(r, "stream", &c->callid, ps->unique_id, &ps->crypto); /* XXX DTLS?? */ + for (k = ps->sfds.head; k; k = k->next) { + sfd = k->data; + redis_pipe(r, "RPUSH stream_sfds-"PB"-%u %u", + STR(&c->callid), ps->unique_id, + sfd->unique_id); + } + mutex_unlock(&ps->in_lock); mutex_unlock(&ps->out_lock); - redis_pipe(r, "EXPIRE stream-%s 86400", ps_key); - redis_pipe(r, "LPUSH streams-"PB" %s", STR(&c->callid), ps_key); + redis_pipe(r, "EXPIRE stream-"PB"-%u 86400", STR(&c->callid), ps->unique_id); + redis_pipe(r, "EXPIRE stream_sfds-"PB"-%u 86400", STR(&c->callid), ps->unique_id); + + redis_pipe(r, "DEL stream-"PB"-%u stream_sfds-"PB"-%u", + STR(&c->callid), ps->unique_id + 1, + STR(&c->callid), ps->unique_id + 1); } - for (l = c->monologues; l; l = l->next) { + redis_pipe(r, "DEL tag-"PB"-0 other_tags-"PB"-0 medias-"PB"-0", + STR(&c->callid), STR(&c->callid), STR(&c->callid)); + + for (l = c->monologues.head; l; l = l->next) { ml = l->data; - mono_key = HKEY(ml); - active_mono_key = HKEY(ml->active_dialogue); - - redis_pipe(r, "DEL tag-%s other_tags-%s medias-%s", - mono_key, mono_key, mono_key); - redis_pipe(r, "HMSET tag-%s created %llu active %s deleted %llu", - mono_key, (long long unsigned) ml->created, - active_mono_key, (long long unsigned) ml->deleted); + + redis_pipe(r, "HMSET tag-"PB"-%u created %llu active %u deleted %llu", + STR(&c->callid), ml->unique_id, + (long long unsigned) ml->created, + ml->active_dialogue ? ml->active_dialogue->unique_id : -1, + (long long unsigned) ml->deleted); if (ml->tag.s) - redis_pipe(r, "HMSET tag-%s tag "PB"", mono_key, STR(&ml->tag)); + redis_pipe(r, "HMSET tag-"PB"-%u tag "PB"", + STR(&c->callid), ml->unique_id, + STR(&ml->tag)); if (ml->viabranch.s) - redis_pipe(r, "HMSET tag-%s via-branch "PB"", mono_key, STR(&ml->viabranch)); + redis_pipe(r, "HMSET tag-"PB"-%u via-branch "PB"", + STR(&c->callid), ml->unique_id, + STR(&ml->viabranch)); k = g_hash_table_get_values(ml->other_tags); for (m = k; m; m = m->next) { - other_mono_key = HKEY(((struct call_monologue *) m->data)); - redis_pipe(r, "RPUSH other_tags-%s %s", mono_key, other_mono_key); + ml2 = m->data; + redis_pipe(r, "RPUSH other_tags-"PB"-%u %u", + STR(&c->callid), ml->unique_id, + ml2->unique_id); } g_list_free(k); for (k = ml->medias.head; k; k = k->next) { media = k->data; - media_key = HKEY(media); - - redis_pipe(r, "DEL media-%s streams-%s maps-%s payload_types-%s", - media_key, media_key, - media_key, media_key); - redis_pipe(r, "HMSET media-%s " - "type "PB" protocol %s desired_family %i " - "sdes_in_tag %u sdes_out_tag %u interface "PB" local_address "IP6F" " - "media_flags %u", - media_key, - STR(&media->type), media->protocol ? media->protocol->name : "", - media->desired_family, - media->sdes_in.tag, media->sdes_out.tag, - STR(&media->interface->name), IP6P(&media->local_address->addr.s6_addr), - media->media_flags); - redis_update_crypto_params(r, "media", media_key, "sdes_in", &media->sdes_in.params); - redis_update_crypto_params(r, "media", media_key, "sdes_out", &media->sdes_out.params); - redis_update_dtls_fingerprint(r, "media", media_key, &media->fingerprint); - - for (m = media->streams.head; m; m = m->next) { - ps_key = HKEY(((struct packet_stream *) m->data)); - redis_pipe(r, "RPUSH streams-%s %s", media_key, ps_key); - } + redis_pipe(r, "RPUSH medias-"PB"-%u %u", + STR(&c->callid), ml->unique_id, + media->unique_id); + } - for (n = media->endpoint_maps; n; n = n->next) { - ep = n->data; - em_key = HKEY(ep); + redis_pipe(r, "EXPIRE tag-"PB"-%u 86400", STR(&c->callid), ml->unique_id); + redis_pipe(r, "EXPIRE other_tags-"PB"-%u 86400", STR(&c->callid), ml->unique_id); + redis_pipe(r, "EXPIRE medias-"PB"-%u 86400", STR(&c->callid), ml->unique_id); - redis_pipe(r, "DEL map-%s sfds-%s", em_key, em_key); - redis_pipe(r, "HMSET map-%s wildcard %i", em_key, ep->wildcard); - redis_update_endpoint(r, "map", em_key, "endpoint", &ep->endpoint); + redis_pipe(r, "DEL tag-"PB"-%u other_tags-"PB"-%u medias-"PB"-%u", + STR(&c->callid), ml->unique_id + 1, + STR(&c->callid), ml->unique_id + 1, + STR(&c->callid), ml->unique_id + 1); + } - for (m = ep->sfds.head; m; m = m->next) { - sfd_key = HKEY(((struct stream_fd *) m->data)); - redis_pipe(r, "RPUSH sfds-%s %s", em_key, sfd_key); - } + redis_pipe(r, "DEL media-"PB"-0 streams-"PB"-0 maps-"PB"-0 payload_types-"PB"-0", + STR(&c->callid), STR(&c->callid), STR(&c->callid), STR(&c->callid)); - redis_pipe(r, "EXPIRE map-%s 86400", em_key); - redis_pipe(r, "EXPIRE sfds-%s 86400", em_key); - redis_pipe(r, "LPUSH maps-%s %s", media_key, em_key); - } + for (l = c->medias.head; l; l = l->next) { + media = l->data; + + redis_pipe(r, "HMSET media-"PB"-%u " + "tag %u " + "index %u " + "type "PB" protocol %s desired_family %s " + "sdes_in_tag %u sdes_out_tag %u logical_intf "PB" " + "media_flags %u", + STR(&c->callid), media->unique_id, + media->monologue->unique_id, + media->index, + STR(&media->type), media->protocol ? media->protocol->name : "", + media->desired_family ? media->desired_family->rfc_name : "", + media->sdes_in.tag, media->sdes_out.tag, + STR(&media->logical_intf->name), + media->media_flags); + redis_update_crypto_params(r, "media", &c->callid, media->unique_id, "sdes_in", + &media->sdes_in.params); + redis_update_crypto_params(r, "media", &c->callid, media->unique_id, "sdes_out", + &media->sdes_out.params); + redis_update_dtls_fingerprint(r, "media", &c->callid, media->unique_id, &media->fingerprint); + + for (m = media->streams.head; m; m = m->next) { + ps = m->data; + redis_pipe(r, "RPUSH streams-"PB"-%u %u", + STR(&c->callid), media->unique_id, + ps->unique_id); + } + + for (m = media->endpoint_maps.head; m; m = m->next) { + ep = m->data; + redis_pipe(r, "RPUSH maps-"PB"-%u %u", + STR(&c->callid), media->unique_id, + ep->unique_id); + } - pt_list = g_hash_table_get_values(media->rtp_payload_types); - pt_index = 0; - for (pt_iter = pt_list; pt_iter; pt_iter = pt_iter->next) { - pt = pt_iter->data; - redis_pipe(r, "HSET payload_types-%s %u %u", - media_key, - (unsigned int) pt_index, - (unsigned int) pt->payload_type); - pt_index++; + k = g_hash_table_get_values(media->rtp_payload_types); + for (m = k; m; m = m->next) { + pt = m->data; + redis_pipe(r, "RPUSH payload_types-"PB"-%u %u/"PB"/%u/"PB"", + STR(&c->callid), media->unique_id, + pt->payload_type, STR(&pt->encoding), + pt->clock_rate, STR(&pt->encoding_parameters)); + } + g_list_free(k); + + redis_pipe(r, "EXPIRE media-"PB"-%u", STR(&c->callid), media->unique_id); + redis_pipe(r, "EXPIRE streams-"PB"-%u", STR(&c->callid), media->unique_id); + redis_pipe(r, "EXPIRE maps-"PB"-%u", STR(&c->callid), media->unique_id); + redis_pipe(r, "EXPIRE payload_types-"PB"-%u", STR(&c->callid), media->unique_id); + + redis_pipe(r, "DEL media-"PB"-%u streams-"PB"-%u maps-"PB"-%u payload_types-"PB"-%u", + STR(&c->callid), media->unique_id + 1, + STR(&c->callid), media->unique_id + 1, + STR(&c->callid), media->unique_id + 1, + STR(&c->callid), media->unique_id + 1); + } + + redis_pipe(r, "DEL map-"PB"-0 map_sfds-"PB"-0", + STR(&c->callid), STR(&c->callid)); + + for (l = c->endpoint_maps.head; l; l = l->next) { + ep = l->data; + + redis_pipe(r, "HMSET map-"PB"-%u wildcard %i num_ports %u intf_preferred_family %s " + "logical_intf "PB"", + STR(&c->callid), ep->unique_id, + ep->wildcard, + ep->num_ports, + ep->logical_intf->preferred_family->rfc_name, + STR(&ep->logical_intf->name)); + redis_update_endpoint(r, "map", &c->callid, ep->unique_id, "endpoint", &ep->endpoint); + + for (m = ep->intf_sfds.head; m; m = m->next) { + il = m->data; + + redis_pipe(r, "RPUSH map_sfds-"PB"-%u loc-%u", + STR(&c->callid), ep->unique_id, + il->local_intf->unique_id); + + for (n = il->list.head; n; n = n->next) { + sfd = n->data; + + redis_pipe(r, "RPUSH map_sfds-"PB"-%u %u", + STR(&c->callid), ep->unique_id, + sfd->unique_id); } - g_list_free(pt_list); - redis_pipe(r, "EXPIRE media-%s 86400", media_key); - redis_pipe(r, "EXPIRE streams-%s 86400", media_key); - redis_pipe(r, "EXPIRE maps-%s 86400", media_key); - redis_pipe(r, "EXPIRE payload_types-%s 86400", media_key); - redis_pipe(r, "LPUSH medias-%s %s", mono_key, media_key); } - redis_pipe(r, "EXPIRE tag-%s 86400", mono_key); - redis_pipe(r, "EXPIRE other_tags-%s 86400", mono_key); - redis_pipe(r, "EXPIRE medias-%s 86400", mono_key); - redis_pipe(r, "LPUSH tags-"PB" %s", STR(&c->callid), mono_key); + redis_pipe(r, "EXPIRE map-"PB"-%u 86400", STR(&c->callid), ep->unique_id); + redis_pipe(r, "EXPIRE map_sfds-"PB"-%u 86400", STR(&c->callid), ep->unique_id); + + redis_pipe(r, "DEL map-"PB"-%u map_sfds-"PB"-%u", + STR(&c->callid), ep->unique_id + 1, + STR(&c->callid), ep->unique_id + 1); } redis_pipe(r, "EXPIRE call-"PB" 86400", STR(&c->callid)); - redis_pipe(r, "EXPIRE tags-"PB" 86400", STR(&c->callid)); - redis_pipe(r, "EXPIRE sfds-"PB" 86400", STR(&c->callid)); - redis_pipe(r, "EXPIRE streams-"PB" 86400", STR(&c->callid)); redis_pipe(r, "SADD calls "PB"", STR(&c->callid)); redis_consume(r); diff --git a/daemon/redis.h b/daemon/redis.h index 92c0c87e4..d0bd7de7c 100644 --- a/daemon/redis.h +++ b/daemon/redis.h @@ -4,6 +4,9 @@ +#include +#include "compat.h" +#include "socket.h" #include "aux.h" #include @@ -21,9 +24,8 @@ struct call; struct redis { - u_int32_t ip; - char host[32]; - int port; + endpoint_t endpoint; + char host[64]; redisContext *ctx; int db; @@ -35,17 +37,13 @@ struct redis_hash { GHashTable *ht; }; struct redis_list { - GQueue q; - struct redis_hash rh; -}; -struct list_item { - redisReply *id; - struct redis_hash rh; - void *ptr; + unsigned int len; + struct redis_hash *rh; + void **ptrs; }; -#define HKEY(ptr) ptr ? ptr->redis_hkey : "0" + @@ -77,7 +75,7 @@ INLINE gboolean g_hash_table_insert_check(GHashTable *h, gpointer k, gpointer v) -struct redis *redis_new(u_int32_t, u_int16_t, int, int); +struct redis *redis_new(const endpoint_t *, int, int); int redis_restore(struct callmaster *, struct redis *, int); void redis_update(struct call *, struct redis *, int); void redis_delete(struct call *, struct redis *, int); diff --git a/daemon/sdp.c b/daemon/sdp.c index 3be85b735..a77410636 100644 --- a/daemon/sdp.c +++ b/daemon/sdp.c @@ -15,12 +15,13 @@ #include "dtls.h" #include "rtp.h" #include "ice.h" +#include "socket.h" struct network_address { str network_type; str address_type; str address; - struct in6_addr parsed; + sockaddr_t parsed; }; struct sdp_origin { @@ -223,36 +224,8 @@ static struct sdp_attribute *attr_get_by_id_m_s(struct sdp_media *m, int id) { } -/* hack hack */ -INLINE int inet_pton_str(int af, str *src, void *dst) { - char *s = src->s; - char p; - int ret; - p = s[src->len]; - s[src->len] = '\0'; - ret = smart_pton(af, src->s, dst); - s[src->len] = p; - return ret; -} - -int address_family(const str *s) { - if (s->len != 3) - return 0; - - if (!memcmp(s->s, "IP4", 3) - || !memcmp(s->s, "ip4", 3)) - return AF_INET; - - if (!memcmp(s->s, "IP6", 3) - || !memcmp(s->s, "ip6", 3)) - return AF_INET6; - - return 0; -} - -static int __parse_address(struct in6_addr *out, str *network_type, str *address_type, str *address) { - struct in_addr in4; - int af; +static int __parse_address(sockaddr_t *out, str *network_type, str *address_type, str *address) { + sockfamily_t *af; if (network_type) { if (network_type->len != 2) @@ -263,26 +236,13 @@ static int __parse_address(struct in6_addr *out, str *network_type, str *address } if (!address_type) { - if (inet_pton_str(AF_INET, address, &in4) == 1) - goto ip4; - if (inet_pton_str(AF_INET6, address, out) == 1) - return 0; - return -1; - } - - af = address_family(address_type); - - if (af == AF_INET) { - if (inet_pton_str(AF_INET, address, &in4) != 1) - return -1; -ip4: - in4_to_6(out, in4.s_addr); - } - else if (af == AF_INET6) { - if (inet_pton_str(AF_INET6, address, out) != 1) + if (sockaddr_parse_any_str(out, address)) return -1; + return 0; } - else + + af = get_socket_family_rfc(address_type); + if (sockaddr_parse_str(out, af, address)) return -1; return 0; @@ -322,7 +282,10 @@ INLINE int extract_token(char **sp, char *end, str *out) { if (parse_address(&output->field)) return -1 #define EXTRACT_NETWORK_ADDRESS_NF(field) \ EXTRACT_NETWORK_ADDRESS_NP(field); \ - if (parse_address(&output->field)) output->field.parsed.s6_addr32[0] = 0xfe + if (parse_address(&output->field)) do { \ + output->field.parsed.family = get_socket_family_enum(SF_IP4); \ + output->field.parsed.u.ipv4.s_addr = 1; \ + } while (0) #define PARSE_DECL char *end, *start #define PARSE_INIT start = output->value.s; end = start + output->value.len @@ -607,7 +570,7 @@ static int parse_attribute_candidate(struct sdp_attribute *output) { if (ep == c->component_str.s) return -1; - c->cand_parsed.transport = ice_transport(&c->transport_str); + c->cand_parsed.transport = get_socket_type(&c->transport_str); if (!c->cand_parsed.transport) return 0; @@ -615,7 +578,7 @@ static int parse_attribute_candidate(struct sdp_attribute *output) { if (ep == c->priority_str.s) return -1; - if (__parse_address(&c->cand_parsed.endpoint.ip46, NULL, NULL, &c->address_str)) + if (__parse_address(&c->cand_parsed.endpoint.address, NULL, NULL, &c->address_str)) return 0; c->cand_parsed.endpoint.port = strtoul(c->port_str.s, &ep, 10); @@ -642,10 +605,10 @@ static int parse_attribute_candidate(struct sdp_attribute *output) { if (str_cmp(&c->rport_str, "rport")) return -1; - if (__parse_address(&c->cand_parsed.related_address, NULL, NULL, &c->related_address_str)) + 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); + c->cand_parsed.related.port = strtoul(c->related_port_str.s, &ep, 10); if (ep == c->related_port_str.s) return -1; @@ -1072,14 +1035,14 @@ static int fill_endpoint(struct endpoint *ep, const struct sdp_media *media, str &flags->received_from_address)) return -1; } - ep->ip46 = flags->parsed_received_from; + ep->address = flags->parsed_received_from; } else if (address && !is_addr_unspecified(&address->parsed)) - ep->ip46 = address->parsed; + ep->address = address->parsed; else if (media->connection.parsed) - ep->ip46 = media->connection.address.parsed; + ep->address = media->connection.address.parsed; else if (session->connection.parsed) - ep->ip46 = session->connection.address.parsed; + ep->address = session->connection.address.parsed; else return -1; @@ -1437,7 +1400,7 @@ static int replace_media_port(struct sdp_chopper *chop, struct sdp_media *media, if (copy_up_to(chop, port)) return -1; - p = ps->sfd ? ps->sfd->fd.localport : 0; + p = ps->selected_sfd ? ps->selected_sfd->socket.local.port : 0; chopper_append_printf(chop, "%u", p); if (skip_over(chop, port)) @@ -1452,7 +1415,7 @@ static int replace_consecutive_port_count(struct sdp_chopper *chop, struct sdp_m int cons; struct packet_stream *ps_n; - if (media->port_count == 1 || !ps->sfd) + if (media->port_count == 1 || !ps->selected_sfd) return 0; for (cons = 1; cons < media->port_count; cons++) { @@ -1460,7 +1423,7 @@ static int replace_consecutive_port_count(struct sdp_chopper *chop, struct sdp_m if (!j) goto warn; ps_n = j->data; - if (ps_n->sfd->fd.localport != ps->sfd->fd.localport + cons * 2) { + if (ps_n->selected_sfd->socket.local.port != ps->selected_sfd->socket.local.port + cons * 2) { warn: ilog(LOG_WARN, "Failed to handle consecutive ports"); break; @@ -1472,18 +1435,18 @@ warn: return 0; } -static int insert_ice_address(struct sdp_chopper *chop, struct packet_stream *ps, struct interface_address *ifa) { +static int insert_ice_address(struct sdp_chopper *chop, struct stream_fd *sfd) { char buf[64]; int len; - call_stream_address46(buf, ps, SAF_ICE, &len, ifa); + call_stream_address46(buf, sfd->stream, SAF_ICE, &len, sfd->local_intf); chopper_append_dup(chop, buf, len); - chopper_append_printf(chop, " %hu", ps->sfd->fd.localport); + chopper_append_printf(chop, " %u", sfd->socket.local.port); return 0; } -static int insert_raddr_rport(struct sdp_chopper *chop, struct packet_stream *ps, struct interface_address *ifa) { +static int insert_raddr_rport(struct sdp_chopper *chop, struct packet_stream *ps, const struct local_intf *ifa) { char buf[64]; int len; @@ -1491,7 +1454,7 @@ static int insert_raddr_rport(struct sdp_chopper *chop, struct packet_stream *ps call_stream_address46(buf, ps, SAF_ICE, &len, ifa); chopper_append_dup(chop, buf, len); chopper_append_c(chop, " rport "); - chopper_append_printf(chop, "%hu", ps->sfd->fd.localport); + chopper_append_printf(chop, "%u", ps->selected_sfd->socket.local.port); return 0; } @@ -1514,17 +1477,10 @@ static int replace_network_address(struct sdp_chopper *chop, struct network_addr if (flags->media_address.s && is_addr_unspecified(&flags->parsed_media_address)) __parse_address(&flags->parsed_media_address, NULL, NULL, &flags->media_address); - if (!is_addr_unspecified(&flags->parsed_media_address)) { - if (IN6_IS_ADDR_V4MAPPED(&flags->parsed_media_address)) - len = sprintf(buf, "IP4 " IPF, IPP(flags->parsed_media_address.s6_addr32[3])); - else { - memcpy(buf, "IP6 ", 4); - inet_ntop(AF_INET6, &flags->parsed_media_address, buf + 4, sizeof(buf)-4); - len = strlen(buf); - } - } + if (!is_addr_unspecified(&flags->parsed_media_address)) + len = sprintf(buf, "%s", sockaddr_print_buf(&flags->parsed_media_address)); else - call_stream_address(buf, ps, SAF_NG, &len); + call_stream_address46(buf, ps, SAF_NG, &len, NULL); chopper_append_dup(chop, buf, len); if (skip_over(chop, &address->address)) @@ -1725,17 +1681,21 @@ out: *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) +static void insert_candidate(struct sdp_chopper *chop, struct stream_fd *sfd, + unsigned int type_pref, unsigned int local_pref, enum ice_candidate_type type) { unsigned long priority; + struct packet_stream *ps = sfd->stream; + const struct local_intf *ifa = sfd->local_intf; - priority = ice_priority_pref(type_pref, local_pref, component); + if (local_pref == -1) + local_pref = ifa->unique_id; + + priority = ice_priority_pref(type_pref, local_pref, ps->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_str(chop, &ifa->spec->ice_foundation); + chopper_append_printf(chop, " %u UDP %lu ", ps->component, priority); + insert_ice_address(chop, sfd); chopper_append_c(chop, " typ "); chopper_append_c(chop, ice_candidate_type_str(type)); /* raddr and rport are required for non-host candidates: rfc5245 section-15.1 */ @@ -1744,14 +1704,26 @@ static void insert_candidate(struct sdp_chopper *chop, struct packet_stream *ps, chopper_append_c(chop, "\r\n"); } +static void insert_sfd_candidates(struct sdp_chopper *chop, struct packet_stream *ps, + unsigned int type_pref, unsigned int local_pref, enum ice_candidate_type type) +{ + GList *l; + struct stream_fd *sfd; + + for (l = ps->sfds.head; l; l = l->next) { + sfd = l->data; + insert_candidate(chop, sfd, type_pref, local_pref, type); + + if (local_pref != -1) + local_pref++; + } +} + static void insert_candidates(struct sdp_chopper *chop, struct packet_stream *rtp, struct packet_stream *rtcp, struct sdp_ng_flags *flags, struct sdp_media *sdp_media) { - GList *l; - struct interface_address *ifa; - unsigned int pref; + const struct local_intf *ifa; struct call_media *media; - struct local_interface *lif; struct ice_agent *ag; unsigned int type_pref, local_pref; enum ice_candidate_type cand_type; @@ -1770,13 +1742,12 @@ static void insert_candidates(struct sdp_chopper *chop, struct packet_stream *rt } 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); + ifa = rtp->selected_sfd->local_intf; + insert_candidate(chop, rtp->selected_sfd, type_pref, ifa->unique_id, cand_type); if (rtcp) /* rtcp-mux only possible in answer */ - insert_candidate(chop, rtcp, 2, type_pref, ifa->preference, cand_type, ifa); + insert_candidate(chop, rtcp->selected_sfd, type_pref, ifa->unique_id, cand_type); if (flags->opmode == OP_OFFER && AGENT_ISSET(ag, CONTROLLING)) { GQueue rc; @@ -1788,7 +1759,7 @@ static void insert_candidates(struct sdp_chopper *chop, struct packet_stream *rt 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); + sockaddr_print_buf(&cand->endpoint.address), cand->endpoint.port); } chopper_append_c(chop, "\r\n"); g_queue_clear(&rc); @@ -1796,18 +1767,10 @@ static void insert_candidates(struct sdp_chopper *chop, struct packet_stream *rt return; } - for (l = lif->list.head; l; l = l->next) { - ifa = l->data; - pref = (local_pref == -1) ? ifa->preference : local_pref; - - insert_candidate(chop, rtp, 1, type_pref, pref, cand_type, ifa); + insert_sfd_candidates(chop, rtp, type_pref, local_pref, cand_type); - if (rtcp) /* rtcp-mux only possible in answer */ - insert_candidate(chop, rtcp, 2, type_pref, pref, cand_type, ifa); - - if (local_pref != -1) - local_pref++; - } + if (rtcp) /* rtcp-mux only possible in answer */ + insert_sfd_candidates(chop, rtcp, type_pref, local_pref, cand_type); } static void insert_dtls(struct call_media *media, struct sdp_chopper *chop) { @@ -1985,7 +1948,7 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call_monologu assert(j->data == ps_rtcp); } - if (!sdp_media->port_num || !ps->sfd) + if (!sdp_media->port_num || !ps->selected_sfd) goto next; if (MEDIA_ARESET2(call_media, SEND, RECV)) @@ -2000,13 +1963,13 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call_monologu if (call_media->protocol && call_media->protocol->rtp) { if (MEDIA_ISSET(call_media, RTCP_MUX) && flags->opmode == OP_ANSWER) { chopper_append_c(chop, "a=rtcp:"); - chopper_append_printf(chop, "%hu", ps->sfd->fd.localport); + chopper_append_printf(chop, "%u", ps->selected_sfd->socket.local.port); chopper_append_c(chop, "\r\na=rtcp-mux\r\n"); ps_rtcp = NULL; } else if (ps_rtcp && !flags->ice_force_relay) { chopper_append_c(chop, "a=rtcp:"); - chopper_append_printf(chop, "%hu", ps_rtcp->sfd->fd.localport); + chopper_append_printf(chop, "%u", ps_rtcp->selected_sfd->socket.local.port); if (!MEDIA_ISSET(call_media, RTCP_MUX)) chopper_append_c(chop, "\r\n"); else diff --git a/daemon/sdp.h b/daemon/sdp.h index 3449e0982..c8e78facc 100644 --- a/daemon/sdp.h +++ b/daemon/sdp.h @@ -4,6 +4,7 @@ #include #include "str.h" #include "call.h" +#include "media_socket.h" struct sdp_ng_flags { @@ -14,10 +15,10 @@ struct sdp_ng_flags { str transport_protocol_str; str address_family_str; const struct transport_protocol *transport_protocol; - struct in6_addr parsed_received_from; - struct in6_addr parsed_media_address; + sockaddr_t parsed_received_from; + sockaddr_t parsed_media_address; str direction[2]; - int address_family; + sockfamily_t *address_family; int tos; int asymmetric:1, trust_address:1, @@ -63,10 +64,8 @@ int sdp_replace(struct sdp_chopper *, GQueue *, struct call_monologue *, struct struct sdp_chopper *sdp_chopper_new(str *input); void sdp_chopper_destroy(struct sdp_chopper *chop); -int address_family(const str *s); - INLINE int is_trickle_ice_address(const struct endpoint *ep) { - if (is_addr_unspecified(&ep->ip46) && ep->port == 9) + if (is_addr_unspecified(&ep->address) && ep->port == 9) return 1; return 0; } diff --git a/daemon/socket.c b/daemon/socket.c new file mode 100644 index 000000000..653411997 --- /dev/null +++ b/daemon/socket.c @@ -0,0 +1,550 @@ +#include "socket.h" +#include +#include +#include +#include "str.h" +#include "media_socket.h" +#include "xt_RTPENGINE.h" +#include "call.h" + +static int __ip4_addr_parse(sockaddr_t *dst, const char *src); +static int __ip6_addr_parse(sockaddr_t *dst, const char *src); +static int __ip4_addr_print(const sockaddr_t *a, char *buf, size_t len); +static int __ip6_addr_print(const sockaddr_t *a, char *buf, size_t len); +static int __ip6_addr_print_p(const sockaddr_t *a, char *buf, size_t len); +static unsigned int __ip4_hash(const sockaddr_t *a); +static unsigned int __ip6_hash(const sockaddr_t *a); +static int __ip4_eq(const sockaddr_t *a, const sockaddr_t *b); +static int __ip6_eq(const sockaddr_t *a, const sockaddr_t *b); +static int __ip4_is_specified(const sockaddr_t *a); +static int __ip6_is_specified(const sockaddr_t *a); +static int __ip_bind(socket_t *s, unsigned int, const sockaddr_t *); +static int __ip_connect(socket_t *s, const endpoint_t *); +static int __ip4_sockaddr2endpoint(endpoint_t *, const void *); +static int __ip6_sockaddr2endpoint(endpoint_t *, const void *); +static int __ip4_endpoint2sockaddr(void *, const endpoint_t *); +static int __ip6_endpoint2sockaddr(void *, const endpoint_t *); +static int __ip4_addrport2sockaddr(void *, const sockaddr_t *, unsigned int); +static int __ip6_addrport2sockaddr(void *, const sockaddr_t *, unsigned int); +static ssize_t __ip_recvfrom(socket_t *s, void *buf, size_t len, endpoint_t *ep); +static ssize_t __ip_sendmsg(socket_t *s, struct msghdr *mh, const endpoint_t *ep); +static ssize_t __ip_sendto(socket_t *s, const void *buf, size_t len, const endpoint_t *ep); +static int __ip4_tos(socket_t *, unsigned int); +static int __ip6_tos(socket_t *, unsigned int); +static void __ip4_endpoint2kernel(struct re_address *, const endpoint_t *); +static void __ip6_endpoint2kernel(struct re_address *, const endpoint_t *); +static void __ip4_kernel2endpoint(endpoint_t *ep, const struct re_address *ra); +static void __ip6_kernel2endpoint(endpoint_t *ep, const struct re_address *ra); + + + +static socktype_t __socket_types[] = { + { + .name = "udp", + .name_uc = "UDP", + }, +}; + +static struct socket_family __socket_families[__SF_LAST] = { + [SF_IP4] = { + .af = AF_INET, + .sockaddr_size = sizeof(struct sockaddr_in), + .name = "IPv4", + .rfc_name = "IP4", + .unspec_string = "0.0.0.0", + .hash = __ip4_hash, + .eq = __ip4_eq, + .addr_parse = __ip4_addr_parse, + .addr_print = __ip4_addr_print, + .addr_print_p = __ip4_addr_print, + .is_specified = __ip4_is_specified, + .sockaddr2endpoint = __ip4_sockaddr2endpoint, + .endpoint2sockaddr = __ip4_endpoint2sockaddr, + .addrport2sockaddr = __ip4_addrport2sockaddr, + .bind = __ip_bind, + .connect = __ip_connect, + .recvfrom = __ip_recvfrom, + .sendmsg = __ip_sendmsg, + .sendto = __ip_sendto, + .tos = __ip4_tos, + .endpoint2kernel = __ip4_endpoint2kernel, + .kernel2endpoint = __ip4_kernel2endpoint, + }, + [SF_IP6] = { + .af = AF_INET6, + .sockaddr_size = sizeof(struct sockaddr_in6), + .name = "IPv6", + .rfc_name = "IP6", + .unspec_string = "::", + .hash = __ip6_hash, + .eq = __ip6_eq, + .addr_parse = __ip6_addr_parse, + .addr_print = __ip6_addr_print, + .addr_print_p = __ip6_addr_print_p, + .is_specified = __ip6_is_specified, + .sockaddr2endpoint = __ip6_sockaddr2endpoint, + .endpoint2sockaddr = __ip6_endpoint2sockaddr, + .addrport2sockaddr = __ip6_addrport2sockaddr, + .bind = __ip_bind, + .connect = __ip_connect, + .recvfrom = __ip_recvfrom, + .sendmsg = __ip_sendmsg, + .sendto = __ip_sendto, + .tos = __ip6_tos, + .endpoint2kernel = __ip6_endpoint2kernel, + .kernel2endpoint = __ip6_kernel2endpoint, + }, +}; + + +static int __ip4_addr_parse(sockaddr_t *dst, const char *src) { + if (inet_pton(AF_INET, src, &dst->u.ipv4) == 1) + return 0; + return -1; +} +static int __ip6_addr_parse(sockaddr_t *dst, const char *src) { + if (src[0] != '[') { + if (inet_pton(AF_INET6, src, &dst->u.ipv6) == 1) + return 0; + return -1; + } + + const char *ep; + ep = strchr(src, ']'); + if (!ep) + return -1; + + unsigned int len = ep - src - 1; + char buf[64]; + memcpy(buf, src+1, len); + buf[len] = '\0'; + + if (inet_pton(AF_INET6, buf, &dst->u.ipv6) == 1) + return 0; + return -1; +} +static int __ip4_addr_print(const sockaddr_t *a, char *buf, size_t len) { + buf[0] = '\0'; + if (!inet_ntop(AF_INET, &a->u.ipv4, buf, len)) + return -1; + return 0; +} +static int __ip6_addr_print(const sockaddr_t *a, char *buf, size_t len) { + buf[0] = '\0'; + if (!inet_ntop(AF_INET6, &a->u.ipv6, buf, len)) + return -1; + return 0; +} +static int __ip6_addr_print_p(const sockaddr_t *a, char *buf, size_t len) { + buf[0] = '\0'; + if (!inet_ntop(AF_INET6, &a->u.ipv6, buf+1, len-2)) + return -1; + buf[0] = '['; + strcpy(buf + strlen(buf), "]"); + return 0; +} +static unsigned int __ip4_hash(const sockaddr_t *a) { + return a->u.ipv4.s_addr; +} +static unsigned int __ip6_hash(const sockaddr_t *a) { + return in6_addr_hash(&a->u.ipv6); +} +static int __ip4_eq(const sockaddr_t *a, const sockaddr_t *b) { + return !memcmp(&a->u.ipv4, &b->u.ipv4, sizeof(a->u.ipv4)); +} +static int __ip6_eq(const sockaddr_t *a, const sockaddr_t *b) { + return !memcmp(&a->u.ipv6, &b->u.ipv6, sizeof(a->u.ipv6)); +} +static int __ip4_is_specified(const sockaddr_t *a) { + return a->u.ipv4.s_addr != 0; +} +static int __ip6_is_specified(const sockaddr_t *a) { + return a->u.ipv6.s6_addr32[0] != 0 + && a->u.ipv6.s6_addr32[1] != 0 + && a->u.ipv6.s6_addr32[2] != 0 + && a->u.ipv6.s6_addr32[3] != 0; +} +static int __ip4_sockaddr2endpoint(endpoint_t *ep, const void *p) { + const struct sockaddr_in *sin = p; + if (sin->sin_family != AF_INET) + return -1; + ZERO(*ep); + ep->address.family = &__socket_families[SF_IP4]; + ep->address.u.ipv4 = sin->sin_addr; + ep->port = ntohs(sin->sin_port); + return 0; +} +static int __ip6_sockaddr2endpoint(endpoint_t *ep, const void *p) { + const struct sockaddr_in6 *sin = p; + if (sin->sin6_family != AF_INET6) + return -1; + ZERO(*ep); + ep->address.family = &__socket_families[SF_IP6]; + ep->address.u.ipv6 = sin->sin6_addr; + ep->port = ntohs(sin->sin6_port); + return 0; +} +static int __ip4_endpoint2sockaddr(void *p, const endpoint_t *ep) { + return __ip4_addrport2sockaddr(p, &ep->address, ep->port); +} +static int __ip6_endpoint2sockaddr(void *p, const endpoint_t *ep) { + return __ip6_addrport2sockaddr(p, &ep->address, ep->port); +} +static int __ip4_addrport2sockaddr(void *p, const sockaddr_t *sa, unsigned int port) { + struct sockaddr_in *sin = p; + ZERO(*sin); + sin->sin_family = AF_INET; + sin->sin_port = htons(port); + if (sa) + sin->sin_addr = sa->u.ipv4; + return 0; +} +static int __ip6_addrport2sockaddr(void *p, const sockaddr_t *sa, unsigned int port) { + struct sockaddr_in6 *sin = p; + ZERO(*sin); + sin->sin6_family = AF_INET6; + sin->sin6_port = htons(port); + if (sa) + sin->sin6_addr = sa->u.ipv6; + return 0; +} +static int __ip_bind(socket_t *s, unsigned int port, const sockaddr_t *a) { + struct sockaddr_storage sin; + + s->family->addrport2sockaddr(&sin, a, port); + if (bind(s->fd, (struct sockaddr *) &sin, s->family->sockaddr_size)) { + __C_DBG("bind fail, fd=%d, port=%d", s->fd, s->local.port); + return -1; + } else { + __C_DBG("bind success, fd=%d, port=%d", s->fd, s->local.port); + } + + return 0; +} +static int __ip_connect(socket_t *s, const endpoint_t *ep) { + struct sockaddr_storage sin; + + s->family->endpoint2sockaddr(&sin, ep); + if (connect(s->fd, (struct sockaddr *) &sin, s->family->sockaddr_size)) { + __C_DBG("connect fail, fd=%d, port=%d", s->fd, s->local.port); + return -1; + } else { + __C_DBG("connect succes, fd=%d, port=%d", s->fd, s->local.port); + } + return 0; +} +static ssize_t __ip_recvfrom(socket_t *s, void *buf, size_t len, endpoint_t *ep) { + ssize_t ret; + struct sockaddr_storage sin; + socklen_t sinlen; + + sinlen = s->family->sockaddr_size; + ret = recvfrom(s->fd, buf, len, 0, (void *) &sin, &sinlen); + if (ret < 0) + return ret; + s->family->sockaddr2endpoint(ep, &sin); + return ret; +} +static ssize_t __ip_sendmsg(socket_t *s, struct msghdr *mh, const endpoint_t *ep) { + struct sockaddr_storage sin; + + s->family->endpoint2sockaddr(&sin, ep); + mh->msg_name = &sin; + mh->msg_namelen = s->family->sockaddr_size; + + return sendmsg(s->fd, mh, 0); +} +static ssize_t __ip_sendto(socket_t *s, const void *buf, size_t len, const endpoint_t *ep) { + struct sockaddr_storage sin; + + s->family->endpoint2sockaddr(&sin, ep); + return sendto(s->fd, buf, len, 0, (void *) &sin, s->family->sockaddr_size); +} +static int __ip4_tos(socket_t *s, unsigned int tos) { + unsigned char ctos; + ctos = tos; + setsockopt(s->fd, IPPROTO_IP, IP_TOS, &ctos, sizeof(ctos)); + return 0; +} +static int __ip6_tos(socket_t *s, unsigned int tos) { + setsockopt(s->fd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos)); + return 0; +} +static void __ip4_endpoint2kernel(struct re_address *ra, const endpoint_t *ep) { + ZERO(*ra); + ra->family = AF_INET; + ra->u.ipv4 = ep->address.u.ipv4.s_addr; + ra->port = ep->port; +} +static void __ip6_endpoint2kernel(struct re_address *ra, const endpoint_t *ep) { + ZERO(*ra); + ra->family = AF_INET6; + memcpy(ra->u.ipv6, &ep->address.u.ipv6, sizeof(ra->u.ipv6)); + ra->port = ep->port; +} +void kernel2endpoint(endpoint_t *ep, const struct re_address *ra) { + ZERO(*ep); + if (ra->family == AF_INET) + ep->address.family = __get_socket_family_enum(SF_IP4); + else if (ra->family == AF_INET6) + ep->address.family = __get_socket_family_enum(SF_IP6); + else + abort(); + ep->port = ra->port; + ep->address.family->kernel2endpoint(ep, ra); +} +static void __ip4_kernel2endpoint(endpoint_t *ep, const struct re_address *ra) { + ep->address.u.ipv4.s_addr = ra->u.ipv4; +} +static void __ip6_kernel2endpoint(endpoint_t *ep, const struct re_address *ra) { + memcpy(&ep->address.u.ipv6, ra->u.ipv6, sizeof(ep->address.u.ipv6)); +} + + + +unsigned int sockaddr_hash(const sockaddr_t *a) { + return a->family->hash(a) ^ g_direct_hash(a->family); +} +int sockaddr_eq(const sockaddr_t *a, const sockaddr_t *b) { + return a->family == b->family && a->family->eq(a, b); +} +unsigned int g_sockaddr_hash(const void *a) { + return sockaddr_hash(a); +} +int g_sockaddr_eq(const void *a, const void *b) { + return sockaddr_eq(a, b); +} + + +unsigned int endpoint_hash(const endpoint_t *a) { + return sockaddr_hash(&a->address) ^ a->port; +} +int endpoint_eq(const endpoint_t *a, const endpoint_t *b) { + return sockaddr_eq(&a->address, &b->address) && a->port == b->port; +} +unsigned int g_endpoint_hash(const void *a) { + return endpoint_hash(a); +} +int g_endpoint_eq(const void *a, const void *b) { + return endpoint_eq(a, b); +} + + + +int sockaddr_parse_any(sockaddr_t *dst, const char *src) { + int i; + sockfamily_t *fam; + + for (i = 0; i < __SF_LAST; i++) { + fam = &__socket_families[i]; + if (!fam->addr_parse(dst, src)) { + dst->family = fam; + return 0; + } + } + return -1; +} +int sockaddr_parse_any_str(sockaddr_t *dst, const str *src) { + char buf[64]; + if (src->len >= sizeof(buf)) + return -1; + sprintf(buf, STR_FORMAT, STR_FMT(src)); + return sockaddr_parse_any(dst, buf); +} +int sockaddr_parse_str(sockaddr_t *dst, sockfamily_t *fam, const str *src) { + char buf[64]; + if (src->len >= sizeof(buf)) + return -1; + if (!fam) + return -1; + sprintf(buf, STR_FORMAT, STR_FMT(src)); + dst->family = fam; + return fam->addr_parse(dst, buf); +} +sockfamily_t *get_socket_family_rfc(const str *s) { + int i; + sockfamily_t *fam; + + for (i = 0; i < __SF_LAST; i++) { + fam = &__socket_families[i]; + if (!str_cmp(s, fam->rfc_name)) + return fam; + } + return NULL; +} +sockfamily_t *__get_socket_family_enum(enum socket_families i) { + return &__socket_families[i]; +} +int endpoint_parse_any(endpoint_t *d, const char *s) { + int i; + sockfamily_t *fam; + unsigned int len; + const char *ep; + char buf[64]; + + ep = strrchr(s, ':'); + if (!ep) { + if (strchr(s, '.')) + return -1; + /* just a port number */ + d->port = atoi(s); + ZERO(d->address); + d->address.family = __get_socket_family_enum(SF_IP4); + return 0; + } + len = ep - s; + if (len >= sizeof(buf)) + return -1; + d->port = atoi(ep+1); + if (d->port > 0xffff) + return -1; + sprintf(buf, "%.*s", len, s); + + for (i = 0; i < __SF_LAST; i++) { + fam = &__socket_families[i]; + if (!fam->addr_parse(&d->address, buf)) { + d->address.family = fam; + return 0; + } + } + return -1; +} + +static int __socket(socket_t *r, int type, sockfamily_t *fam) { + ZERO(*r); + r->family = fam; + r->fd = socket(fam->af, type, 0); + if (r->fd == -1) { + __C_DBG("socket() syscall fail, fd=%d", r->fd); + return -1; + } else { + __C_DBG("socket() syscall success, fd=%d", r->fd); + } + + return 0; +} + +int open_socket(socket_t *r, int type, unsigned int port, const sockaddr_t *sa) { + sockfamily_t *fam; + + fam = sa->family; + + if (__socket(r, type, fam)) { + __C_DBG("open socket fail, fd=%d", r->fd); + return -1; + } + + nonblock(r->fd); + reuseaddr(r->fd); + + if (port > 0xffff) { + __C_DBG("open socket fail, port=%d > 0xfffffd", port); + goto fail; + } + + if (fam->bind(r, port, sa)) { + __C_DBG("open socket fail, fd=%d, port=%d", r->fd, port); + goto fail; + } + + r->local.port = port; + r->local.address = *sa; + + __C_DBG("open socket success, fd=%d, port=%d", r->fd, port); + + return 0; + +fail: + close_socket(r); + return -1; +} + +int connect_socket(socket_t *r, int type, const endpoint_t *ep) { + sockfamily_t *fam; + + fam = ep->address.family; + + if (__socket(r, type, fam)) + return -1; + if (fam->connect(r, ep)) + goto fail; + + r->remote = *ep; + + return 0; + +fail: + close_socket(r); + return -1; +} + +int connect_socket_nb(socket_t *r, int type, const endpoint_t *ep) { + sockfamily_t *fam; + int ret = 0; + + fam = ep->address.family; + + if (__socket(r, type, fam)) + return -1; + nonblock(r->fd); + if (fam->connect(r, ep)) { + if (errno != EINPROGRESS) + goto fail; + ret = 1; + } + + r->remote = *ep; + + return ret; + +fail: + close_socket(r); + return -1; +} + +int close_socket(socket_t *r) { + if (!r || r->fd == -1) { + __C_DBG("close() syscall not called, fd=%d", r->fd); + return -1; + } + + if (close(r->fd) != 0) { + __C_DBG("close() syscall fail, fd=%d", r->fd); + return -1; + } + + __C_DBG("close() syscall success, fd=%d", r->fd); + + r->fd = -1; + ZERO(r->local); + ZERO(r->remote); + + return 0; +} + + + + +socktype_t *get_socket_type(const str *s) { + int i; + socktype_t *tp; + + for (i = 0; i < G_N_ELEMENTS(__socket_types); i++) { + tp = &__socket_types[i]; + if (!str_cmp(s, tp->name)) + return tp; + if (!str_cmp(s, tp->name_uc)) + return tp; + } + return NULL; +} + + + + +void socket_init(void) { + int i; + + for (i = 0; i < __SF_LAST; i++) + __socket_families[i].idx = i; +} diff --git a/daemon/socket.h b/daemon/socket.h new file mode 100644 index 000000000..771ca68d7 --- /dev/null +++ b/daemon/socket.h @@ -0,0 +1,224 @@ +#ifndef _SOCKET_H_ +#define _SOCKET_H_ + + +#include +#include + + + + +enum socket_families { + SF_IP4 = 0, + SF_IP6, + __SF_LAST +}; + + + +struct socket_address; +struct socket_type; +struct socket_family; +struct endpoint; +struct socket; +struct re_address; + +typedef struct socket_address sockaddr_t; +typedef struct endpoint endpoint_t; +typedef struct socket socket_t; +typedef const struct socket_type socktype_t; +typedef const struct socket_family sockfamily_t; + + +#include "str.h" + + + +struct local_intf; + + +struct socket_type { + const char *name; /* lower case */ + const char *name_uc; /* upper case */ +}; +struct socket_family { + int idx; + int af; + size_t sockaddr_size; + const char *name; /* "IPv4" */ + const char *rfc_name; /* "IP4" */ + const char *unspec_string; /* 0.0.0.0 or :: */ + unsigned int (*hash)(const sockaddr_t *); + int (*eq)(const sockaddr_t *, const sockaddr_t *); + int (*addr_parse)(sockaddr_t *, const char *); + int (*addr_print)(const sockaddr_t *, char *, size_t); + int (*addr_print_p)(const sockaddr_t *, char *, size_t); + int (*is_specified)(const sockaddr_t *); + int (*sockaddr2endpoint)(endpoint_t *, const void *); + int (*endpoint2sockaddr)(void *, const endpoint_t *); + int (*addrport2sockaddr)(void *, const sockaddr_t *, unsigned int); + int (*bind)(socket_t *, unsigned int, const sockaddr_t *); + int (*connect)(socket_t *, const endpoint_t *); + ssize_t (*recvfrom)(socket_t *, void *, size_t, endpoint_t *); + ssize_t (*sendmsg)(socket_t *, struct msghdr *, const endpoint_t *); + ssize_t (*sendto)(socket_t *, const void *, size_t, const endpoint_t *); + int (*tos)(socket_t *, unsigned int); + void (*endpoint2kernel)(struct re_address *, const endpoint_t *); + void (*kernel2endpoint)(endpoint_t *, const struct re_address *); +}; +struct socket_address { + sockfamily_t *family; + union { + struct in_addr ipv4; + struct in6_addr ipv6; + } u; +}; +struct endpoint { + sockaddr_t address; + unsigned int port; +}; +struct socket { + int fd; + sockfamily_t *family; + endpoint_t local; + endpoint_t remote; +}; + + + +#include "aux.h" + + +INLINE int sockaddr_print(const sockaddr_t *a, char *buf, size_t len) { + if (!a->family) { + buf[0] = '\0'; + return 0; + } + return a->family->addr_print(a, buf, len); +} +INLINE char *sockaddr_print_buf(const sockaddr_t *a) { + char *buf = get_thread_buf(); + if (!a->family) { + buf[0] = '\0'; + return 0; + } + sockaddr_print(a, buf, THREAD_BUF_SIZE); + return buf; +} +INLINE int sockaddr_print_p(const sockaddr_t *a, char *buf, size_t len) { + if (!a->family) { + buf[0] = '\0'; + return 0; + } + return a->family->addr_print_p(a, buf, len); +} +INLINE char *sockaddr_print_p_buf(const sockaddr_t *a) { + char *buf = get_thread_buf(); + sockaddr_print_p(a, buf, THREAD_BUF_SIZE); + return buf; +} +INLINE int sockaddr_print_port(const sockaddr_t *a, unsigned int port, char *buf, size_t len) { + if (!a->family) { + buf[0] = '\0'; + return 0; + } + if (a->family->addr_print_p(a, buf, len-6)) + return -1; + sprintf(buf + strlen(buf), ":%u", port); + return 0; +} +INLINE char *sockaddr_print_port_buf(const sockaddr_t *a, unsigned int port) { + char *buf = get_thread_buf(); + sockaddr_print_port(a, port, buf, THREAD_BUF_SIZE); + return buf; +} +INLINE int endpoint_print(const endpoint_t *ep, char *buf, size_t len) { + return sockaddr_print_port(&ep->address, ep->port, buf, len); +} +INLINE char *endpoint_print_buf(const endpoint_t *ep) { + return sockaddr_print_port_buf(&ep->address, ep->port); +} +INLINE int is_addr_unspecified(const sockaddr_t *a) { + if (!a || !a->family) + return 1; + return !a->family->is_specified(a); +} +#define socket_recvfrom(s,a...) (s)->family->recvfrom((s), a) +#define socket_sendmsg(s,a...) (s)->family->sendmsg((s), a) +#define socket_sendto(s,a...) (s)->family->sendto((s), a) +INLINE ssize_t socket_sendiov(socket_t *s, const struct iovec *v, unsigned int len, const endpoint_t *dst) { + struct msghdr mh; + ZERO(mh); + mh.msg_iov = (void *) v; + mh.msg_iovlen = len; + return socket_sendmsg(s, &mh, dst); +} + + + +/* XXX obsolete these? */ +INLINE void nonblock(int fd) { + fcntl(fd, F_SETFL, O_NONBLOCK); +} +INLINE void reuseaddr(int fd) { + int one = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); +} +INLINE void ipv6only(int fd, int yn) { + setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yn, sizeof(yn)); +} + + + +void socket_init(void); + +int open_socket(socket_t *r, int type, unsigned int port, const sockaddr_t *); +int connect_socket(socket_t *r, int type, const endpoint_t *ep); +int connect_socket_nb(socket_t *r, int type, const endpoint_t *ep); +int close_socket(socket_t *r); + +sockfamily_t *get_socket_family_rfc(const str *s); +sockfamily_t *__get_socket_family_enum(enum socket_families); +int sockaddr_parse_any(sockaddr_t *dst, const char *src); +int sockaddr_parse_any_str(sockaddr_t *dst, const str *src); +int sockaddr_parse_str(sockaddr_t *dst, sockfamily_t *fam, const str *src); +int endpoint_parse_any(endpoint_t *, const char *); +void kernel2endpoint(endpoint_t *ep, const struct re_address *ra); + +unsigned int sockaddr_hash(const sockaddr_t *); +int sockaddr_eq(const sockaddr_t *, const sockaddr_t *); /* true/false */ +unsigned int g_sockaddr_hash(const void *); +int g_sockaddr_eq(const void *, const void *); /* true/false */ + +unsigned int endpoint_hash(const endpoint_t *); +int endpoint_eq(const endpoint_t *, const endpoint_t *); /* true/false */ +unsigned int g_endpoint_hash(const void *); +int g_endpoint_eq(const void *, const void *); /* true/false */ + +INLINE sockfamily_t *get_socket_family_enum(enum socket_families i) { + if (i >= __SF_LAST) + return NULL; + return __get_socket_family_enum(i); +} +INLINE int endpoint_parse_port_any(endpoint_t *e, const char *p, unsigned int port) { + if (port > 0xffff) + return -1; + e->port = port; + return sockaddr_parse_any(&e->address, p); +} +INLINE int ipv46_any_convert(endpoint_t *ep) { + if (ep->address.family->af != AF_INET) + return 0; + if (!is_addr_unspecified(&ep->address)) + return 0; + ep->address.family = __get_socket_family_enum(SF_IP6); + ZERO(ep->address.u.ipv6); + return 1; +} + + + +socktype_t *get_socket_type(const str *s); + + +#endif diff --git a/daemon/str.h b/daemon/str.h index ff017445c..c18c60557 100644 --- a/daemon/str.h +++ b/daemon/str.h @@ -65,6 +65,8 @@ INLINE void str_swap(str *a, str *b); INLINE int str_to_i(str *s, int def); /* parses a string uinto an int, returns default if conversion fails */ INLINE uint str_to_ui(str *s, int def); +/* extracts the first/next token into "new_token" and modifies "ori_and_remainer" in place */ +INLINE int str_token(str *new_token, str *ori_and_remainder, int sep); /* asprintf() analogs */ #define str_sprintf(fmt, a...) __str_sprintf(STR_MALLOC_PADDING fmt, a) @@ -278,4 +280,15 @@ INLINE unsigned int str_to_ui(str *s, int def) { return ret; } +// XXX use this for sdp.c token extraction +INLINE int str_token(str *new_token, str *ori_and_remainder, int sep) { + *new_token = *ori_and_remainder; + if (!str_chr_str(ori_and_remainder, ori_and_remainder, sep)) + return -1; + new_token->len = ori_and_remainder->s - new_token->s; + if (str_shift(ori_and_remainder, 1)) + return -1; + return 0; +} + #endif diff --git a/daemon/stun.c b/daemon/stun.c index 3a5de6273..0a03a03fd 100644 --- a/daemon/stun.c +++ b/daemon/stun.c @@ -188,18 +188,21 @@ static int stun_attributes(struct stun_attrs *out, str *s, u_int16_t *unknowns, 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); + out->mapped.port = ntohs(*((u_int16_t *) (&attr.s[2]))) ^ (STUN_COOKIE >> 16); + if (attr.len == 8 && ntohs(*((u_int16_t *) attr.s)) == 1) { + out->mapped.address.family = get_socket_family_enum(SF_IP4); + out->mapped.address.u.ipv4.s_addr = + 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] + out->mapped.address.family = get_socket_family_enum(SF_IP6); + out->mapped.address.u.ipv6.s6_addr32[0] = *((u_int32_t *) (&attr.s[4])) ^ htonl(STUN_COOKIE); - out->mapped_address.s6_addr32[1] + out->mapped.address.u.ipv6.s6_addr32[1] = *((u_int32_t *) (&attr.s[8])) ^ req->transaction[0]; - out->mapped_address.s6_addr32[2] + out->mapped.address.u.ipv6.s6_addr32[2] = *((u_int32_t *) (&attr.s[12])) ^ req->transaction[1]; - out->mapped_address.s6_addr32[3] + out->mapped.address.u.ipv6.s6_addr32[3] = *((u_int32_t *) (&attr.s[16])) ^ req->transaction[2]; } break; @@ -228,17 +231,11 @@ out: return uc ? -1 : 0; } -static void output_init(struct msghdr *mh, struct iovec *iov, struct sockaddr_in6 *sin, - struct header *hdr, unsigned short code, u_int32_t *transaction, - unsigned char *buf, int buflen) +static void output_init(struct msghdr *mh, struct iovec *iov, + struct header *hdr, unsigned short code, u_int32_t *transaction) { ZERO(*mh); - mh->msg_control = buf; - mh->msg_controllen = buflen; - - mh->msg_name = sin; - mh->msg_namelen = sizeof(*sin); mh->msg_iov = iov; mh->msg_iovlen = 1; @@ -293,13 +290,8 @@ static void __output_finish(struct msghdr *mh) { hdr = mh->msg_iov->iov_base; hdr->msg_len = htons(hdr->msg_len); } -//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) { +static void output_finish_src(struct msghdr *mh) { __output_finish(mh); - msg_mh_src(src, mh); } static void fingerprint(struct msghdr *mh, struct fingerprint *fp) { @@ -352,7 +344,7 @@ 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 in6_addr *dst, +static void stun_error_len(struct stream_fd *sfd, const endpoint_t *sin, struct header *req, int code, char *reason, int len, u_int16_t add_attr, void *attr_cont, int attr_len) @@ -364,27 +356,26 @@ static void stun_error_len(struct packet_stream *ps, struct sockaddr_in6 *sin, s struct generic aa; struct msghdr mh; 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)); + output_init(&mh, iov, &hdr, STUN_BINDING_ERROR_RESPONSE, req->transaction); ec.codes = htonl(((code / 100) << 8) | (code % 100)); output_add_data(&mh, &ec, STUN_ERROR_CODE, reason, len); if (attr_cont) output_add_data(&mh, &aa, add_attr, attr_cont, attr_len); - integrity(&mh, &mi, &ps->media->ice_agent->pwd[0]); + integrity(&mh, &mi, &sfd->stream->media->ice_agent->pwd[0]); fingerprint(&mh, &fp); - output_finish_src(&mh, dst); - sendmsg(ps->sfd->fd.fd, &mh, 0); + output_finish_src(&mh); + socket_sendmsg(&sfd->socket, &mh, sin); } -#define stun_error(ps, sin, dst, req, code, reason) \ - stun_error_len(ps, sin, dst, req, code, reason "\0\0\0", strlen(reason), \ +#define stun_error(sfd, sin, req, code, reason) \ + stun_error_len(sfd, sin, req, code, reason "\0\0\0", strlen(reason), \ 0, NULL, 0) -#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), \ +#define stun_error_attrs(sfd, sin, req, code, reason, type, content, len) \ + stun_error_len(sfd, sin, req, code, reason "\0\0\0", strlen(reason), \ type, content, len) @@ -434,11 +425,11 @@ static int check_auth(str *msg, struct stun_attrs *attrs, struct call_media *med lenX = htons((attrs->msg_integrity_attr - msg->s) - 20 + 24); iov[0].iov_base = msg->s; - iov[0].iov_len = OFFSET_OF(struct header, msg_len); + iov[0].iov_len = G_STRUCT_OFFSET(struct header, msg_len); iov[1].iov_base = &lenX; iov[1].iov_len = sizeof(lenX); - iov[2].iov_base = msg->s + OFFSET_OF(struct header, cookie); - iov[2].iov_len = ntohs(lenX) + - 24 + 20 - OFFSET_OF(struct header, cookie); + iov[2].iov_base = msg->s + G_STRUCT_OFFSET(struct header, cookie); + iov[2].iov_len = ntohs(lenX) + - 24 + 20 - G_STRUCT_OFFSET(struct header, cookie); __integrity(iov, G_N_ELEMENTS(iov), &ag->pwd[dst], digest); @@ -446,8 +437,8 @@ static int check_auth(str *msg, struct stun_attrs *attrs, struct call_media *med } /* 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 in6_addr *dst) +static int stun_binding_success(struct stream_fd *sfd, struct header *req, struct stun_attrs *attrs, + const endpoint_t *sin) { struct header hdr; struct xor_mapped_address xma; @@ -455,30 +446,29 @@ static int stun_binding_success(struct packet_stream *ps, struct header *req, st struct fingerprint fp; struct msghdr mh; struct iovec iov[4]; /* hdr, xma, mi, fp */ - unsigned char buf[256]; - output_init(&mh, iov, sin, &hdr, STUN_BINDING_SUCCESS_RESPONSE, req->transaction, buf, sizeof(buf)); + output_init(&mh, iov, &hdr, STUN_BINDING_SUCCESS_RESPONSE, req->transaction); - xma.port = sin->sin6_port ^ htons(STUN_COOKIE >> 16); - if (IN6_IS_ADDR_V4MAPPED(&sin->sin6_addr)) { + xma.port = htons(sin->port) ^ (STUN_COOKIE >> 16); + if (sin->address.family->af == AF_INET) { xma.family = htons(0x01); - xma.address[0] = sin->sin6_addr.s6_addr32[3] ^ htonl(STUN_COOKIE); + xma.address[0] = sin->address.u.ipv4.s_addr ^ htonl(STUN_COOKIE); output_add_len(&mh, &xma, STUN_XOR_MAPPED_ADDRESS, 8); } else { xma.family = htons(0x02); - xma.address[0] = sin->sin6_addr.s6_addr32[0] ^ htonl(STUN_COOKIE); - xma.address[1] = sin->sin6_addr.s6_addr32[1] ^ req->transaction[0]; - xma.address[2] = sin->sin6_addr.s6_addr32[2] ^ req->transaction[1]; - xma.address[3] = sin->sin6_addr.s6_addr32[3] ^ req->transaction[2]; + xma.address[0] = sin->address.u.ipv6.s6_addr32[0] ^ htonl(STUN_COOKIE); + xma.address[1] = sin->address.u.ipv6.s6_addr32[1] ^ req->transaction[0]; + xma.address[2] = sin->address.u.ipv6.s6_addr32[2] ^ req->transaction[1]; + xma.address[3] = sin->address.u.ipv6.s6_addr32[3] ^ req->transaction[2]; output_add(&mh, &xma, STUN_XOR_MAPPED_ADDRESS); } - integrity(&mh, &mi, &ps->media->ice_agent->pwd[1]); + integrity(&mh, &mi, &sfd->stream->media->ice_agent->pwd[1]); fingerprint(&mh, &fp); - output_finish_src(&mh, dst); - sendmsg(ps->sfd->fd.fd, &mh, 0); + output_finish_src(&mh); + socket_sendmsg(&sfd->socket, &mh, sin); return 0; } @@ -493,36 +483,36 @@ 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) +#define SLP endpoint_print_buf(sin) +static int __stun_request(struct stream_fd *sfd, const endpoint_t *sin, + struct header *req, struct stun_attrs *attrs) { int ret; - ret = ice_request(ps, sin, dst, attrs); + ret = ice_request(sfd, sin, attrs); if (ret == -2) { ilog(LOG_DEBUG, "ICE role conflict detected"); - stun_error(ps, sin, dst, req, 487, "Role conflict"); + stun_error(sfd, sin, 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); + stun_binding_success(sfd, req, attrs, sin); 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) +static int __stun_success(struct stream_fd *sfd, const endpoint_t *sin, + struct header *req, struct stun_attrs *attrs) { - return ice_response(ps, sin, dst, attrs, req->transaction); + return ice_response(sfd, sin, 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) +static int __stun_error(struct stream_fd *sfd, const endpoint_t *sin, + struct header *req, struct stun_attrs *attrs) { - return ice_response(ps, sin, dst, attrs, req->transaction); + return ice_response(sfd, sin, attrs, req->transaction); } @@ -533,7 +523,7 @@ static int __stun_error(struct packet_stream *ps, struct sockaddr_in6 *sin, * * call is locked in R */ -int stun(str *b, struct packet_stream *ps, struct sockaddr_in6 *sin, struct in6_addr *dst) { +int stun(str *b, struct stream_fd *sfd, const endpoint_t *sin) { struct header *req = (void *) b->s; int msglen, method, class; str attr_str; @@ -541,6 +531,7 @@ int stun(str *b, struct packet_stream *ps, struct sockaddr_in6 *sin, struct in6_ u_int16_t unknowns[UNKNOWNS_COUNT]; const char *err; int dst_idx, src_idx; + struct packet_stream *ps = sfd->stream; msglen = ntohs(req->msg_len); err = "message-length mismatch"; @@ -564,7 +555,7 @@ int stun(str *b, struct packet_stream *ps, struct sockaddr_in6 *sin, struct in6_ goto ignore; ilog(LOG_WARNING, "STUN packet contained unknown " "\"comprehension required\" attribute(s)" SLF, SLP); - stun_error_attrs(ps, sin, dst, req, 420, "Unknown attribute", + stun_error_attrs(sfd, sin, req, 420, "Unknown attribute", STUN_UNKNOWN_ATTRIBUTES, unknowns, u_int16_t_arr_len(unknowns) * 2); return 0; @@ -597,11 +588,11 @@ int stun(str *b, struct packet_stream *ps, struct sockaddr_in6 *sin, struct in6_ switch (class) { case STUN_CLASS_REQUEST: - return __stun_request(ps, sin, dst, req, &attrs); + return __stun_request(sfd, sin, req, &attrs); case STUN_CLASS_SUCCESS: - return __stun_success(ps, sin, dst, req, &attrs); + return __stun_success(sfd, sin, req, &attrs); case STUN_CLASS_ERROR: - return __stun_error(ps, sin, dst, req, &attrs); + return __stun_error(sfd, sin, req, &attrs); default: return -1; } @@ -609,25 +600,24 @@ int stun(str *b, struct packet_stream *ps, struct sockaddr_in6 *sin, struct in6_ bad_req: ilog(LOG_NOTICE, "Received invalid STUN packet" SLF ": %s", SLP, err); - stun_error(ps, sin, dst, req, 400, "Bad request"); + stun_error(sfd, sin, req, 400, "Bad request"); return 0; unauth: ilog(LOG_NOTICE, "STUN authentication mismatch" SLF, SLP); - stun_error(ps, sin, dst, req, 401, "Unauthorized"); + stun_error(sfd, sin, 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, +int stun_binding_request(const endpoint_t *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) + socket_t *sock, 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; @@ -637,7 +627,7 @@ int stun_binding_request(struct sockaddr_in6 *dst, u_int32_t transaction[3], str struct fingerprint fp; struct msg_integrity mi; - output_init(&mh, iov, dst, &hdr, STUN_BINDING_REQUEST, transaction, buf, sizeof(buf)); + output_init(&mh, iov, &hdr, STUN_BINDING_REQUEST, transaction); i = snprintf(username_buf, sizeof(username_buf), STR_FORMAT":"STR_FORMAT, STR_FMT(&ufrags[0]), STR_FMT(&ufrags[1])); @@ -657,8 +647,8 @@ int stun_binding_request(struct sockaddr_in6 *dst, u_int32_t transaction[3], str integrity(&mh, &mi, pwd); fingerprint(&mh, &fp); - output_finish_src(&mh, src); - sendmsg(fd, &mh, 0); + output_finish_src(&mh); + socket_sendmsg(sock, &mh, dst); return 0; } diff --git a/daemon/stun.h b/daemon/stun.h index 9a52336fb..7cddb6e28 100644 --- a/daemon/stun.h +++ b/daemon/stun.h @@ -8,6 +8,7 @@ #include "compat.h" #include "call.h" #include "str.h" +#include "socket.h" #define STUN_COOKIE 0x2112A442UL @@ -22,8 +23,7 @@ struct stun_attrs { char *fingerprint_attr; u_int32_t fingerprint; u_int64_t tiebreaker; - struct in6_addr mapped_address; - unsigned int mapped_port; /* XXX use struct endpoint */ + endpoint_t mapped; unsigned int error_code; int use:1, controlled:1, @@ -49,10 +49,10 @@ INLINE int is_stun(const str *s) { } -int stun(str *, struct packet_stream *, struct sockaddr_in6 *, struct in6_addr *); +int stun(str *, struct stream_fd *, const endpoint_t *); -int stun_binding_request(struct sockaddr_in6 *dst, u_int32_t transaction[3], str *pwd, +int stun_binding_request(const endpoint_t *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); + socket_t *, int); #endif diff --git a/daemon/udp_listener.c b/daemon/udp_listener.c index ec7a66d40..209038dc4 100644 --- a/daemon/udp_listener.c +++ b/daemon/udp_listener.c @@ -12,10 +12,12 @@ #include "str.h" #include "log.h" #include "obj.h" +#include "socket.h" struct udp_listener_callback { struct obj obj; udp_listener_callback_t func; + struct udp_listener *ul; struct obj *p; }; @@ -25,18 +27,20 @@ static void udp_listener_closed(int fd, void *p, uintptr_t x) { static void udp_listener_incoming(int fd, void *p, uintptr_t x) { struct udp_listener_callback *cb = p; - struct sockaddr_in6 sin; - socklen_t sin_len; int len; char buf[0x10000]; char addr[64]; str str; + struct udp_listener *ul; + socket_t *listener; + endpoint_t sin; str.s = buf; + ul = cb->ul; + listener = &ul->sock; for (;;) { - sin_len = sizeof(sin); - len = recvfrom(fd, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &sin, &sin_len); + len = socket_recvfrom(listener, buf, sizeof(buf)-1, &sin); if (len < 0) { if (errno == EINTR) continue; @@ -46,39 +50,31 @@ static void udp_listener_incoming(int fd, void *p, uintptr_t x) { } buf[len] = '\0'; - smart_ntop_port(addr, &sin, sizeof(addr)); + endpoint_print(&sin, addr, sizeof(addr)); str.len = len; - cb->func(cb->p, &str, &sin, addr); + cb->func(cb->p, &str, &sin, addr, ul); } } -int udp_listener_init(struct udp_listener *u, struct poller *p, struct in6_addr ip, u_int16_t port, udp_listener_callback_t func, struct obj *obj) { - struct sockaddr_in6 sin; +int udp_listener_init(struct udp_listener *u, struct poller *p, const endpoint_t *ep, + udp_listener_callback_t func, struct obj *obj) +{ struct poller_item i; struct udp_listener_callback *cb; cb = obj_alloc("udp_listener_callback", sizeof(*cb), NULL); cb->func = func; cb->p = obj_get_o(obj); + cb->ul = u; - u->fd = socket(AF_INET6, SOCK_DGRAM, 0); - if (u->fd == -1) + if (open_socket(&u->sock, SOCK_DGRAM, ep->port, &ep->address)) goto fail; - nonblock(u->fd); - reuseaddr(u->fd); - ipv6only(u->fd, 0); - - ZERO(sin); - sin.sin6_family = AF_INET6; - sin.sin6_addr = ip; - sin.sin6_port = htons(port); - if (bind(u->fd, (struct sockaddr *) &sin, sizeof(sin))) - goto fail; + ipv6only(u->sock.fd, 1); ZERO(i); - i.fd = u->fd; + i.fd = u->sock.fd; i.closed = udp_listener_closed; i.readable = udp_listener_incoming; i.obj = &cb->obj; @@ -88,8 +84,7 @@ int udp_listener_init(struct udp_listener *u, struct poller *p, struct in6_addr return 0; fail: - if (u->fd != -1) - close(u->fd); + close_socket(&u->sock); obj_put_o(obj); obj_put(cb); return -1; diff --git a/daemon/udp_listener.h b/daemon/udp_listener.h index f0b85b6a8..d69ef6d82 100644 --- a/daemon/udp_listener.h +++ b/daemon/udp_listener.h @@ -4,18 +4,21 @@ #include #include "poller.h" #include "str.h" +#include "socket.h" struct poller; struct obj; +struct udp_listener; -typedef void (*udp_listener_callback_t)(struct obj *p, str *buf, struct sockaddr_in6 *sin, char *addr); +typedef void (*udp_listener_callback_t)(struct obj *p, str *buf, const endpoint_t *ep, char *addr, + struct udp_listener *); struct udp_listener { - int fd; + socket_t sock; struct poller *poller; }; -int udp_listener_init(struct udp_listener *, struct poller *p, struct in6_addr ip, u_int16_t port, udp_listener_callback_t, struct obj *); +int udp_listener_init(struct udp_listener *, struct poller *p, const endpoint_t *, udp_listener_callback_t, struct obj *); #endif From 6b5c4e8b0c86372d67213638b97b879234fb772d Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Wed, 2 Dec 2015 10:22:52 -0500 Subject: [PATCH 2/6] Mass update for non-daemon code (see `6d3865b`) Change-Id: I7c1f3816c20b1e7e44d11838d841c26e15de380b --- README.md | 19 ++ kernel-module/xt_RTPENGINE.c | 396 +++++++++++++++++++++++++++-------- kernel-module/xt_RTPENGINE.h | 2 +- 3 files changed, 323 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index d8e4596ef..e2b48bcaa 100644 --- a/README.md +++ b/README.md @@ -731,6 +731,25 @@ Optionally included keys are: However, this mechanism for selecting the address family is now obsolete and the `address family` dictionary key should be used instead. + A direction keyword is *round-robin-calls*. If this is received, a round robin algorithm runs for + choosing the logical interface for the current stream(e.g. audio, video). + The algorithm checks that all local interfaces of the tried logical interface have free ports for + call streams. If a logical interface fails the check, the next one is tried. If there is no logical + interface found with this property, it fallbacks to the default behaviour (e.g. return first logical + interface in --interface list even if no free ports are available). The attribute is ignored for + answers() because the logical interface was already selected at offers(). + Naming an interface "round-robin-calls" and trying to select it using direction will + __run the above algorithm__! + + Round robin for both legs of the stream: + { ..., "direction": [ "round-robin-calls", "round-robin-calls" ], ... } + + Round robin for first leg and and select "pub" for the second leg of the stream: + { ..., "direction": [ "round-robin-calls", "pub" ], ... } + + Round robin for first leg and and default behaviour for the second leg of the stream: + { ..., "direction": [ "round-robin-calls" ], ... } + * `received from` Contains a list of exactly two elements. The first element denotes the address family and the second diff --git a/kernel-module/xt_RTPENGINE.c b/kernel-module/xt_RTPENGINE.c index de2cc2d76..67adfc9e6 100644 --- a/kernel-module/xt_RTPENGINE.c +++ b/kernel-module/xt_RTPENGINE.c @@ -132,7 +132,8 @@ static void *proc_main_list_next(struct seq_file *, void *, loff_t *); static int proc_main_list_show(struct seq_file *, void *); static void table_push(struct rtpengine_table *); -static struct rtpengine_target *get_target(struct rtpengine_table *, u_int16_t); +static struct rtpengine_target *get_target(struct rtpengine_table *, const struct re_address *); +static int is_valid_address(const struct re_address *rea); static int aes_f8_session_key_init(struct re_crypto_context *, struct rtpengine_srtp *); static int srtp_encrypt_aes_cm(struct re_crypto_context *, struct rtpengine_srtp *, @@ -188,8 +189,19 @@ struct re_bitfield { }; struct re_bucket { - struct re_bitfield targets; - struct rtpengine_target *target[256]; + struct re_bitfield ports_lo_bf; + struct rtpengine_target *ports_lo[256]; +}; + +struct re_dest_addr { + struct re_address destination; + struct re_bitfield ports_hi_bf; + struct re_bucket *ports_hi[256]; +}; + +struct re_dest_addr_hash { + struct re_bitfield addrs_bf; + struct re_dest_addr *addrs[256]; }; struct rtpengine_table { @@ -204,10 +216,9 @@ struct rtpengine_table { struct proc_dir_entry *list; struct proc_dir_entry *blist; - struct re_bitfield buckets; - struct re_bucket *bucket[256]; + struct re_dest_addr_hash dest_addr_hash; - unsigned int targets; + unsigned int num_targets; }; struct re_cipher { @@ -536,7 +547,8 @@ static void clear_proc(struct proc_dir_entry **e) { static void table_push(struct rtpengine_table *t) { - int i, j; + int i, j, k; + struct re_dest_addr *rda; struct re_bucket *b; if (!t) @@ -547,23 +559,33 @@ static void table_push(struct rtpengine_table *t) { DBG("Freeing table\n"); - for (i = 0; i < 256; i++) { - b = t->bucket[i]; - if (!b) + for (k = 0; k < 256; k++) { + rda = t->dest_addr_hash.addrs[k]; + if (!rda) continue; - for (j = 0; j < 256; j++) { - if (!b->target[j]) + for (i = 0; i < 256; i++) { + b = rda->ports_hi[i]; + if (!b) continue; - b->target[j]->table = -1; - target_push(b->target[j]); - b->target[j] = NULL; + + for (j = 0; j < 256; j++) { + if (!b->ports_lo[j]) + continue; + b->ports_lo[j]->table = -1; + target_push(b->ports_lo[j]); + b->ports_lo[j] = NULL; + } + + kfree(b); + rda->ports_hi[i] = NULL; } - kfree(b); - t->bucket[i] = NULL; + kfree(rda); + t->dest_addr_hash.addrs[k] = NULL; } + clear_proc(&t->status); clear_proc(&t->control); clear_proc(&t->list); @@ -656,8 +678,7 @@ static ssize_t proc_status(struct file *f, char __user *b, size_t l, loff_t *o) read_lock_irqsave(&t->target_lock, flags); len += sprintf(buf + len, "Refcount: %u\n", atomic_read(&t->refcnt) - 1); len += sprintf(buf + len, "Control PID: %u\n", t->pid); - len += sprintf(buf + len, "Targets: %u\n", t->targets); - len += sprintf(buf + len, "Buckets: %u\n", t->buckets.used); + len += sprintf(buf + len, "Targets: %u\n", t->num_targets); read_unlock_irqrestore(&t->target_lock, flags); table_push(t); @@ -758,43 +779,69 @@ static inline void bitfield_clear(struct re_bitfield *bf, unsigned char i) { bf->b[b] &= ~k; bf->used--; } -static inline struct rtpengine_target *find_next_target(struct rtpengine_table *t, int *port) { +static inline struct rtpengine_target *find_next_target(struct rtpengine_table *t, int *addr_bucket, + int *port) +{ unsigned long flags; + struct re_dest_addr *rda; struct re_bucket *b; - unsigned char hi, lo; - unsigned int hi_b, lo_b; + unsigned char hi, lo, ab; + unsigned int rda_b, hi_b, lo_b; struct rtpengine_target *g; - if (*port < 0 || *port > 0xffff) + if (*port < 0) + return NULL; + if (*port > 0xffff) { + *port = 0; + (*addr_bucket)++; + } + if (*addr_bucket < 0 || *addr_bucket > 255) return NULL; hi = (*port & 0xff00) >> 8; lo = *port & 0xff; + ab = *addr_bucket; read_lock_irqsave(&t->target_lock, flags); for (;;) { + rda_b = bitfield_slot(ab); + if (!t->dest_addr_hash.addrs_bf.b[rda_b]) { + ab = bitfield_next_slot(rda_b); + hi = 0; + lo = 0; + goto next_rda; + } + + rda = t->dest_addr_hash.addrs[ab]; + if (!rda) { + ab++; + hi = 0; + lo = 0; + goto next_rda; + } + hi_b = bitfield_slot(hi); - if (!t->buckets.b[hi_b]) { + if (!rda->ports_hi_bf.b[hi_b]) { hi = bitfield_next_slot(hi_b); lo = 0; - goto next; + goto next_hi; } - b = t->bucket[hi]; + b = rda->ports_hi[hi]; if (!b) { hi++; lo = 0; - goto next; + goto next_hi; } lo_b = bitfield_slot(lo); - if (!b->targets.b[lo_b]) { + if (!b->ports_lo_bf.b[lo_b]) { lo = bitfield_next_slot(lo_b); goto next_lo; } - g = b->target[lo]; + g = b->ports_lo[lo]; if (!g) { lo++; goto next_lo; @@ -806,13 +853,17 @@ static inline struct rtpengine_target *find_next_target(struct rtpengine_table * next_lo: if (!lo) hi++; -next: +next_hi: if (!hi && !lo) + ab++; +next_rda: + if (!ab && !hi && !lo) break; } read_unlock_irqrestore(&t->target_lock, flags); + *addr_bucket = ab; *port = (hi << 8) | lo; (*port)++; @@ -854,7 +905,7 @@ static ssize_t proc_blist_read(struct file *f, char __user *b, size_t l, loff_t u_int32_t id; struct rtpengine_table *t; struct rtpengine_list_entry op; - int err, port, i; + int err, port, addr_bucket, i; struct rtpengine_target *g; unsigned long flags; @@ -869,9 +920,10 @@ static ssize_t proc_blist_read(struct file *f, char __user *b, size_t l, loff_t if (!t) return -ENOENT; - port = (int) *o; - g = find_next_target(t, &port); - *o = port; + addr_bucket = ((int) *o) >> 17; + port = ((int) *o) & 0x1ffff; + g = find_next_target(t, &addr_bucket, &port); + *o = (addr_bucket << 17) | port; err = 0; if (!g) goto err; @@ -950,34 +1002,34 @@ static void *proc_list_next(struct seq_file *f, void *v, loff_t *o) { /* v is in u_int32_t id = (u_int32_t) (unsigned long) f->private; struct rtpengine_table *t; struct rtpengine_target *g; - int port; + int port, addr_bucket; - port = (int) *o; + addr_bucket = ((int) *o) >> 17; + port = ((int) *o) & 0x1ffff; t = get_table(id); if (!t) return NULL; - g = find_next_target(t, &port); + g = find_next_target(t, &addr_bucket, &port); - *o = port; + *o = (addr_bucket << 17) | port; table_push(t); return g; } -static void proc_list_addr_print(struct seq_file *f, const char *s, const struct re_address *a) { +static void seq_addr_print(struct seq_file *f, const struct re_address *a) { if (!a->family) return; - seq_printf(f, " %6s ", s); switch (a->family) { case AF_INET: - seq_printf(f, "inet4 %u.%u.%u.%u:%u\n", a->u.u8[0], a->u.u8[1], a->u.u8[2], + seq_printf(f, "inet4 %u.%u.%u.%u:%u", a->u.u8[0], a->u.u8[1], a->u.u8[2], a->u.u8[3], a->port); break; case AF_INET6: - seq_printf(f, "inet6 [%x:%x:%x:%x:%x:%x:%x:%x]:%u\n", + seq_printf(f, "inet6 [%x:%x:%x:%x:%x:%x:%x:%x]:%u", htons(a->u.u16[0]), htons(a->u.u16[1]), htons(a->u.u16[2]), htons(a->u.u16[3]), htons(a->u.u16[4]), htons(a->u.u16[5]), htons(a->u.u16[6]), htons(a->u.u16[7]), a->port); @@ -988,6 +1040,15 @@ static void proc_list_addr_print(struct seq_file *f, const char *s, const struct } } +static void proc_list_addr_print(struct seq_file *f, const char *s, const struct re_address *a) { + if (!a->family) + return; + + seq_printf(f, " %6s ", s); + seq_addr_print(f, a); + seq_printf(f, "\n"); +} + static void proc_list_crypto_print(struct seq_file *f, struct re_crypto_context *c, struct rtpengine_srtp *s, const char *label) { @@ -1015,7 +1076,9 @@ static int proc_list_show(struct seq_file *f, void *v) { struct rtpengine_target *g = v; int i; - seq_printf(f, "port %5u:\n", g->target.target_port); + seq_printf(f, "local "); + seq_addr_print(f, &g->target.local); + seq_printf(f, "\n"); proc_list_addr_print(f, "src", &g->target.src_addr); proc_list_addr_print(f, "dst", &g->target.dst_addr); proc_list_addr_print(f, "mirror", &g->target.mirror_addr); @@ -1046,37 +1109,119 @@ static int proc_list_show(struct seq_file *f, void *v) { +static unsigned int re_address_hash(const struct re_address *a) { + u_int32_t ret = 0; + + if (!a) + goto out; + + ret += a->family; + + switch (a->family) { + case AF_INET: + ret += a->u.ipv4; + break; + case AF_INET6: + ret += a->u.u32[0]; + ret += a->u.u32[1]; + ret += a->u.u32[2]; + ret += a->u.u32[3]; + break; + default: + goto out; + } + + ret = (ret & 0xffff) ^ ((ret & 0xffff0000) >> 16); + ret = (ret & 0xff) ^ ((ret & 0xff00) >> 8); + +out: + return ret; +} + +static int re_address_match(const struct re_address *a, const struct re_address *b) { + if (!a || !b) + return 0; + if (a->family != b->family) + return 0; + + switch (a->family) { + case AF_INET: + if (a->u.ipv4 == b->u.ipv4) + return 1; + break; + case AF_INET6: + if (!memcmp(a->u.ipv6, b->u.ipv6, sizeof(a->u.ipv6))) + return 1; + break; + default: + return 0; + } + + return 0; +} + +static struct re_dest_addr *find_dest_addr(const struct re_dest_addr_hash *h, const struct re_address *local) { + unsigned int rda_hash, i; + struct re_dest_addr *rda; + + i = rda_hash = re_address_hash(local); + + while (1) { + rda = h->addrs[i]; + if (!rda) + return NULL; + if (re_address_match(local, &rda->destination)) + return rda; + + i++; + if (i >= 256) + i = 0; + if (i == rda_hash) + return NULL; + } +} + + + + -static int table_del_target(struct rtpengine_table *t, u_int16_t port) { +static int table_del_target(struct rtpengine_table *t, const struct re_address *local) { unsigned char hi, lo; + struct re_dest_addr *rda; struct re_bucket *b; struct rtpengine_target *g = NULL; unsigned long flags; - if (!port) + if (!local || !is_valid_address(local)) return -EINVAL; - hi = (port & 0xff00) >> 8; - lo = port & 0xff; + hi = (local->port & 0xff00) >> 8; + lo = local->port & 0xff; write_lock_irqsave(&t->target_lock, flags); - b = t->bucket[hi]; + + rda = find_dest_addr(&t->dest_addr_hash, local); + if (!rda) + goto out; + b = rda->ports_hi[hi]; if (!b) goto out; - g = b->target[lo]; + g = b->ports_lo[lo]; if (!g) goto out; - b->target[lo] = NULL; - bitfield_clear(&b->targets, lo); - t->targets--; - if (!b->targets.used) { - t->bucket[hi] = NULL; - bitfield_clear(&t->buckets, hi); + b->ports_lo[lo] = NULL; + bitfield_clear(&b->ports_lo_bf, lo); + t->num_targets--; + if (!b->ports_lo_bf.used) { + rda->ports_hi[hi] = NULL; + bitfield_clear(&rda->ports_hi_bf, hi); } else b = NULL; + /* not freeing or NULLing the re_dest_addr due to hash collision logic */ + out: write_unlock_irqrestore(&t->target_lock, flags); @@ -1093,7 +1238,7 @@ out: -static int is_valid_address(struct re_address *rea) { +static int is_valid_address(const struct re_address *rea) { switch (rea->family) { case AF_INET: if (!rea->u.ipv4) @@ -1418,13 +1563,17 @@ static void crypto_context_init(struct re_crypto_context *c, struct rtpengine_sr static int table_new_target(struct rtpengine_table *t, struct rtpengine_target_info *i, int update) { unsigned char hi, lo; + unsigned int rda_hash, rh_it; struct rtpengine_target *g; + struct re_dest_addr *rda; struct re_bucket *b, *ba = NULL; struct rtpengine_target *og = NULL; int err, j; unsigned long flags; - if (!i->target_port) + /* validation */ + + if (!is_valid_address(&i->local)) return -EINVAL; if (!is_valid_address(&i->src_addr)) return -EINVAL; @@ -1445,6 +1594,8 @@ static int table_new_target(struct rtpengine_table *t, struct rtpengine_target_i DBG("Creating new target\n"); + /* initializing */ + err = -ENOMEM; g = kmalloc(sizeof(*g), GFP_KERNEL); if (!g) @@ -1465,37 +1616,86 @@ static int table_new_target(struct rtpengine_table *t, struct rtpengine_target_i if (err) goto fail2; - hi = (i->target_port & 0xff00) >> 8; - lo = i->target_port & 0xff; + /* find or allocate re_dest_addr */ + + rda_hash = re_address_hash(&i->local); + hi = (i->local.port & 0xff00) >> 8; + lo = i->local.port & 0xff; +retry: + rh_it = rda_hash; write_lock_irqsave(&t->target_lock, flags); - if (!(b = t->bucket[hi])) { - err = -ENOENT; - if (update) + + rda = t->dest_addr_hash.addrs[rh_it]; + while (rda) { + if (re_address_match(&rda->destination, &i->local)) + goto got_rda; + rh_it++; + if (rh_it >= 256) + rh_it = 0; + err = -ENXIO; + if (rh_it == rda_hash) goto fail4; + rda = t->dest_addr_hash.addrs[rh_it]; + } + err = -ENOENT; + if (update) + goto fail4; + + write_unlock_irqrestore(&t->target_lock, flags); + + rda = kmalloc(sizeof(*rda), GFP_KERNEL); + err = -ENOMEM; + if (!rda) + goto fail2; + memset(rda, 0, sizeof(*rda)); + memcpy(&rda->destination, &i->local, sizeof(rda->destination)); + + write_lock_irqsave(&t->target_lock, flags); + + if (t->dest_addr_hash.addrs[rh_it]) { write_unlock_irqrestore(&t->target_lock, flags); + kfree(rda); + goto retry; + } + + t->dest_addr_hash.addrs[rh_it] = rda; + bitfield_set(&t->dest_addr_hash.addrs_bf, rh_it); - b = kmalloc(sizeof(*b), GFP_KERNEL); - err = -ENOMEM; - if (!b) - goto fail2; - memset(b, 0, sizeof(*b)); +got_rda: + /* find or allocate re_bucket */ - write_lock_irqsave(&t->target_lock, flags); + if ((b = rda->ports_hi[hi])) + goto got_bucket; - if (!t->bucket[hi]) { - t->bucket[hi] = b; - bitfield_set(&t->buckets, hi); - } - else { - ba = b; - b = t->bucket[hi]; - } + err = -ENOENT; + if (update) + goto fail4; + + write_unlock_irqrestore(&t->target_lock, flags); + + b = kmalloc(sizeof(*b), GFP_KERNEL); + err = -ENOMEM; + if (!b) + goto fail2; + memset(b, 0, sizeof(*b)); + + write_lock_irqsave(&t->target_lock, flags); + + if (!rda->ports_hi[hi]) { + rda->ports_hi[hi] = b; + bitfield_set(&rda->ports_hi_bf, hi); + } + else { + ba = b; + b = rda->ports_hi[hi]; } + +got_bucket: if (update) { err = -ENOENT; - og = b->target[lo]; + og = b->ports_lo[lo]; if (!og) goto fail4; @@ -1514,13 +1714,13 @@ static int table_new_target(struct rtpengine_table *t, struct rtpengine_target_i } else { err = -EEXIST; - if (b->target[lo]) + if (b->ports_lo[lo]) goto fail4; - bitfield_set(&b->targets, lo); - t->targets++; + bitfield_set(&b->ports_lo_bf, lo); + t->num_targets++; } - b->target[lo] = g; + b->ports_lo[lo] = g; g = NULL; write_unlock_irqrestore(&t->target_lock, flags); @@ -1545,21 +1745,24 @@ fail1: -static struct rtpengine_target *get_target(struct rtpengine_table *t, u_int16_t port) { +static struct rtpengine_target *get_target(struct rtpengine_table *t, const struct re_address *local) { unsigned char hi, lo; + struct re_dest_addr *rda; struct rtpengine_target *r; unsigned long flags; if (!t) return NULL; - if (!port) + if (!local) return NULL; - hi = (port & 0xff00) >> 8; - lo = port & 0xff; + hi = (local->port & 0xff00) >> 8; + lo = local->port & 0xff; read_lock_irqsave(&t->target_lock, flags); - r = t->bucket[hi] ? t->bucket[hi]->target[lo] : NULL; + + rda = find_dest_addr(&t->dest_addr_hash, local); + r = rda ? (rda->ports_hi[hi] ? rda->ports_hi[hi]->ports_lo[lo] : NULL) : NULL; if (r) target_hold(r); read_unlock_irqrestore(&t->target_lock, flags); @@ -1706,7 +1909,7 @@ static ssize_t proc_control_write(struct file *file, const char __user *buf, siz break; case MMG_DEL: - err = table_del_target(t, msg.target.target_port); + err = table_del_target(t, &msg.target.local); if (err) goto err; break; @@ -2225,7 +2428,9 @@ static inline int rtp_payload_type(const struct rtp_header *hdr, const struct rt } #endif -static unsigned int rtpengine46(struct sk_buff *skb, struct rtpengine_table *t, struct re_address *src, u_int8_t in_tos) { +static unsigned int rtpengine46(struct sk_buff *skb, struct rtpengine_table *t, struct re_address *src, + struct re_address *dst, u_int8_t in_tos) +{ struct udphdr *uh; struct rtpengine_target *g; struct sk_buff *skb2; @@ -2254,8 +2459,9 @@ static unsigned int rtpengine46(struct sk_buff *skb, struct rtpengine_table *t, skb_trim(skb, datalen); src->port = ntohs(uh->source); + dst->port = ntohs(uh->dest); - g = get_target(t, ntohs(uh->dest)); + g = get_target(t, dst); if (!g) goto skip2; @@ -2428,7 +2634,7 @@ static unsigned int rtpengine4(struct sk_buff *oskb, const struct xt_action_para struct sk_buff *skb; struct iphdr *ih; struct rtpengine_table *t; - struct re_address src; + struct re_address src, dst; t = get_table(pinfo->id); if (!t) @@ -2447,8 +2653,10 @@ static unsigned int rtpengine4(struct sk_buff *oskb, const struct xt_action_para memset(&src, 0, sizeof(src)); src.family = AF_INET; src.u.ipv4 = ih->saddr; + dst.family = AF_INET; + dst.u.ipv4 = ih->daddr; - return rtpengine46(skb, t, &src, (u_int8_t)ih->tos); + return rtpengine46(skb, t, &src, &dst, (u_int8_t)ih->tos); skip2: kfree_skb(skb); @@ -2470,7 +2678,7 @@ static unsigned int rtpengine6(struct sk_buff *oskb, const struct xt_action_para struct sk_buff *skb; struct ipv6hdr *ih; struct rtpengine_table *t; - struct re_address src; + struct re_address src, dst; t = get_table(pinfo->id); if (!t) @@ -2490,8 +2698,10 @@ static unsigned int rtpengine6(struct sk_buff *oskb, const struct xt_action_para memset(&src, 0, sizeof(src)); src.family = AF_INET6; memcpy(&src.u.ipv6, &ih->saddr, sizeof(src.u.ipv6)); + dst.family = AF_INET6; + memcpy(&dst.u.ipv6, &ih->daddr, sizeof(dst.u.ipv6)); - return rtpengine46(skb, t, &src, ipv6_get_dsfield(ih)); + return rtpengine46(skb, t, &src, &dst, ipv6_get_dsfield(ih)); skip2: kfree_skb(skb); diff --git a/kernel-module/xt_RTPENGINE.h b/kernel-module/xt_RTPENGINE.h index f30c5320b..70367bdd8 100644 --- a/kernel-module/xt_RTPENGINE.h +++ b/kernel-module/xt_RTPENGINE.h @@ -75,7 +75,7 @@ enum rtpengine_src_mismatch { }; struct rtpengine_target_info { - u_int16_t target_port; + struct re_address local; struct re_address expected_src; /* for incoming packets */ enum rtpengine_src_mismatch src_mismatch; From 548b71076ca6ced916844ea774887c0a6aedda6f Mon Sep 17 00:00:00 2001 From: Camille Oudot Date: Thu, 3 Dec 2015 16:18:52 +0100 Subject: [PATCH 3/6] disable procfs uid/gid for kernels prior to 3.10 --- kernel-module/xt_RTPENGINE.c | 38 +++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/kernel-module/xt_RTPENGINE.c b/kernel-module/xt_RTPENGINE.c index 67adfc9e6..7fffaf11f 100644 --- a/kernel-module/xt_RTPENGINE.c +++ b/kernel-module/xt_RTPENGINE.c @@ -81,7 +81,7 @@ struct re_cipher; struct rtp_parsed; struct re_crypto_context; - +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) kuid_t proc_kuid; uint proc_uid = 0; module_param(proc_uid, uint, 0); @@ -92,7 +92,7 @@ kgid_t proc_kgid; uint proc_gid = 0; module_param(proc_gid, uint, 0); MODULE_PARM_DESC(proc_gid, "rtpengine procfs tree group id"); - +#endif static struct proc_dir_entry *my_proc_root; static struct proc_dir_entry *proc_list; @@ -416,37 +416,37 @@ static int table_create_proc(struct rtpengine_table *t, u_int32_t id) { #endif if (!t->proc) return -1; - +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) proc_set_user(t->proc, proc_kuid, proc_kgid); - +#endif t->status = proc_create_data("status", S_IFREG | S_IRUGO, t->proc, &proc_status_ops, (void *) (unsigned long) id); if (!t->status) return -1; - +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) proc_set_user(t->status, proc_kuid, proc_kgid); - +#endif t->control = proc_create_data("control", S_IFREG | S_IWUSR | S_IWGRP, t->proc, &proc_control_ops, (void *) (unsigned long) id); if (!t->control) return -1; - +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) proc_set_user(t->control, proc_kuid, proc_kgid); - +#endif t->list = proc_create_data("list", S_IFREG | S_IRUGO, t->proc, &proc_list_ops, (void *) (unsigned long) id); if (!t->list) return -1; - +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) proc_set_user(t->list, proc_kuid, proc_kgid); - +#endif t->blist = proc_create_data("blist", S_IFREG | S_IRUGO, t->proc, &proc_blist_ops, (void *) (unsigned long) id); if (!t->blist) return -1; - +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) proc_set_user(t->blist, proc_kuid, proc_kgid); - +#endif return 0; } @@ -2769,10 +2769,11 @@ static int __init init(void) { const char *err; printk(KERN_NOTICE "Registering xt_RTPENGINE module - version %s\n", RTPENGINE_VERSION); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) printk(KERN_DEBUG "using uid %u, gid %d\n", proc_uid, proc_gid); proc_kuid = KUIDT_INIT(proc_uid); proc_kgid = KGIDT_INIT(proc_gid); - +#endif rwlock_init(&table_lock); ret = -ENOMEM; @@ -2780,23 +2781,24 @@ static int __init init(void) { my_proc_root = proc_mkdir("rtpengine", NULL); if (!my_proc_root) goto fail; - +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) proc_set_user(my_proc_root, proc_kuid, proc_kgid); +#endif /* my_proc_root->owner = THIS_MODULE; */ proc_control = proc_create("control", S_IFREG | S_IWUSR | S_IWGRP, my_proc_root, &proc_main_control_ops); if (!proc_control) goto fail; - +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) proc_set_user(proc_control, proc_kuid, proc_kgid); - +#endif proc_list = proc_create("list", S_IFREG | S_IRUGO, my_proc_root, &proc_main_list_ops); if (!proc_list) goto fail; - +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) proc_set_user(proc_list, proc_kuid, proc_kgid); - +#endif err = "could not register xtables target"; ret = xt_register_targets(xt_rtpengine_regs, ARRAY_SIZE(xt_rtpengine_regs)); if (ret) From 31d28a3babd352956149c9fb36be7606282566f3 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Mon, 30 Nov 2015 10:23:59 -0500 Subject: [PATCH 4/6] MT#16783 support via-branch matching in `delete` messages Change-Id: I8383742ec11983d82ab4e2e92f1e20cbda798d71 --- daemon/call.c | 60 +++++++++++++++++++++++----------------- daemon/call_interfaces.c | 3 +- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index 4e35a0b69..0e830a694 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -2416,13 +2416,14 @@ static int monologue_destroy(struct call_monologue *ml) { __monologue_destroy(ml); if (!g_hash_table_size(c->tags)) { - ilog(LOG_INFO, "Call branch '"STR_FORMAT"' deleted, no more branches remaining", - STR_FMT(&ml->tag)); + ilog(LOG_INFO, "Call branch '"STR_FORMAT"' (via-branch '"STR_FORMAT"') " + "deleted, no more branches remaining", + STR_FMT(&ml->tag), STR_FMT0(&ml->viabranch)); return 1; /* destroy call */ } - ilog(LOG_INFO, "Call branch "STR_FORMAT" deleted", - STR_FMT(&ml->tag)); + ilog(LOG_INFO, "Call branch '"STR_FORMAT"' (via-branch '"STR_FORMAT"') deleted", + STR_FMT(&ml->tag), STR_FMT0(&ml->viabranch)); return 0; } @@ -2575,46 +2576,55 @@ int call_delete_branch(struct callmaster *m, const str *callid, const str *branc ml->term_reason = REGULAR; } - if (!fromtag || !fromtag->s || !fromtag->len) + if (!fromtag || !fromtag->len) goto del_all; - match_tag = (totag && totag->s && totag->len) ? totag : fromtag; + if ((!totag || !totag->len) && branch && branch->len) { + // try a via-branch match + ml = g_hash_table_lookup(c->viabranches, branch); + if (ml) + goto do_delete; + } + + match_tag = (totag && totag->len) ? totag : fromtag; ml = g_hash_table_lookup(c->tags, match_tag); if (!ml) { + if (branch && branch->len) { + // also try a via-branch match here + ml = g_hash_table_lookup(c->viabranches, branch); + if (ml) + goto do_delete; + } + + // last resort: try the from-tag if we tried the to-tag before and see + // if the associated dialogue has an empty tag (unknown) + if (match_tag == totag) { + ml = g_hash_table_lookup(c->tags, fromtag); + if (ml && ml->active_dialogue && ml->active_dialogue->tag.len == 0) + goto do_delete; + } + ilog(LOG_INFO, "Tag '"STR_FORMAT"' in delete message not found, ignoring", STR_FMT(match_tag)); goto err; } +do_delete: if (output) ng_call_stats(c, fromtag, totag, output, NULL); -/* - if (branch && branch->len) { - if (!g_hash_table_remove(c->branches, branch)) { - ilog(LOG_INFO, LOG_PREFIX_CI "Branch to delete doesn't exist", STR_FMT(&c->callid), STR_FMT(branch)); - goto err; - } - - ilog(LOG_INFO, LOG_PREFIX_CI "Branch deleted", LOG_PARAMS_CI(c)); - if (g_hash_table_size(c->branches)) - goto success_unlock; - else - DBG("no branches left, deleting full call"); - } -*/ - if (delete_delay > 0) { - ilog(LOG_INFO, "Scheduling deletion of call branch '"STR_FORMAT"' in %d seconds", - STR_FMT(&ml->tag), delete_delay); + ilog(LOG_INFO, "Scheduling deletion of call branch '"STR_FORMAT"' " + "(via-branch '"STR_FORMAT"') in %d seconds", + STR_FMT(&ml->tag), STR_FMT0(branch), delete_delay); ml->deleted = poller_now + delete_delay; if (!c->ml_deleted || c->ml_deleted > ml->deleted) c->ml_deleted = ml->deleted; } else { - ilog(LOG_INFO, "Deleting call branch '"STR_FORMAT"'", - STR_FMT(&ml->tag)); + ilog(LOG_INFO, "Deleting call branch '"STR_FORMAT"' (via-branch '"STR_FORMAT"')", + STR_FMT(&ml->tag), STR_FMT0(branch)); if (monologue_destroy(ml)) goto del_all; } diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 7824ae718..6991e5539 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -776,8 +776,7 @@ const char *call_delete_ng(bencode_item_t *input, struct callmaster *m, bencode_ if (!bencode_dictionary_get_str(input, "call-id", &callid)) return "No call-id in message"; - if (!bencode_dictionary_get_str(input, "from-tag", &fromtag)) - return "No from-tag in message"; + bencode_dictionary_get_str(input, "from-tag", &fromtag); bencode_dictionary_get_str(input, "to-tag", &totag); bencode_dictionary_get_str(input, "via-branch", &viabranch); From cd34dde934048a30f3c9905ebd15d3069fc4bfdb Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Thu, 3 Dec 2015 12:48:14 -0500 Subject: [PATCH 5/6] fix missing redis expiry times Change-Id: Id38d3760f2eb0bcbf6c8912be244662c5ce11855 --- daemon/redis.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/daemon/redis.c b/daemon/redis.c index 104644756..fa65aa2bf 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1379,10 +1379,10 @@ void redis_update(struct call *c, struct redis *r, int role) { } g_list_free(k); - redis_pipe(r, "EXPIRE media-"PB"-%u", STR(&c->callid), media->unique_id); - redis_pipe(r, "EXPIRE streams-"PB"-%u", STR(&c->callid), media->unique_id); - redis_pipe(r, "EXPIRE maps-"PB"-%u", STR(&c->callid), media->unique_id); - redis_pipe(r, "EXPIRE payload_types-"PB"-%u", STR(&c->callid), media->unique_id); + redis_pipe(r, "EXPIRE media-"PB"-%u 86400", STR(&c->callid), media->unique_id); + redis_pipe(r, "EXPIRE streams-"PB"-%u 86400", STR(&c->callid), media->unique_id); + redis_pipe(r, "EXPIRE maps-"PB"-%u 86400", STR(&c->callid), media->unique_id); + redis_pipe(r, "EXPIRE payload_types-"PB"-%u 86400", STR(&c->callid), media->unique_id); redis_pipe(r, "DEL media-"PB"-%u streams-"PB"-%u maps-"PB"-%u payload_types-"PB"-%u", STR(&c->callid), media->unique_id + 1, From cb53218a904d1ccd9cae35762434657cd9e87dda Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Thu, 3 Dec 2015 12:48:33 -0500 Subject: [PATCH 6/6] fix incomplete dialogue association when initial offer contains to-tag Change-Id: I6da6399a8f754438ed9c41f4b851797a233e1e02 --- daemon/call.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index 0e830a694..776e8f692 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -2427,6 +2427,20 @@ static int monologue_destroy(struct call_monologue *ml) { return 0; } +/* must be called with call->master_lock held in W */ +static void __fix_other_tags(struct call_monologue *one) { + struct call_monologue *two; + + if (!one || !one->tag.len) + return; + two = one->active_dialogue; + if (!two || !two->tag.len) + return; + + g_hash_table_insert(one->other_tags, &two->tag, two); + g_hash_table_insert(two->other_tags, &one->tag, one); +} + /* must be called with call->master_lock held in W */ static struct call_monologue *call_get_monologue(struct call *call, const str *fromtag, const str *totag, const str *viabranch) @@ -2437,7 +2451,6 @@ static struct call_monologue *call_get_monologue(struct call *call, const str *f STR_FMT(fromtag), STR_FMT(&call->callid)); ret = g_hash_table_lookup(call->tags, fromtag); if (!ret) { - __C_DBG("creating new monologue"); ret = __monologue_create(call); __monologue_tag(ret, fromtag); goto new_branch; @@ -2479,8 +2492,11 @@ new_branch: __monologue_viabranch(os, viabranch); ok_check_tag: - if (totag && totag->s && !ret->active_dialogue->tag.s) - __monologue_tag(ret->active_dialogue, totag); + os = ret->active_dialogue; + if (totag && totag->s && !os->tag.s) { + __monologue_tag(os, totag); + __fix_other_tags(ret); + } return ret; } @@ -2527,12 +2543,11 @@ static struct call_monologue *call_get_dialogue(struct call *call, const str *fr if (!ft->tag.s) __monologue_tag(ft, fromtag); - g_hash_table_insert(ft->other_tags, &tt->tag, tt); - g_hash_table_insert(tt->other_tags, &ft->tag, ft); __monologue_unkernelize(ft->active_dialogue); __monologue_unkernelize(tt->active_dialogue); ft->active_dialogue = tt; tt->active_dialogue = ft; + __fix_other_tags(ft); done: __monologue_unkernelize(ft);