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/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 16399407c..d9a6ded7f 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -66,8 +66,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 62b47c61f..a90a3c34c 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,942 +121,18 @@ 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, OP_OTHER); - } else if (sfd->call->callmaster->conf.redis) { - redis_update(ca, sfd->call->callmaster->conf.redis, MASTER_REDIS_ROLE, OP_OTHER); - } - } -done: - log_info_clear(); -} + +static void __monologue_destroy(struct call_monologue *monologue); +static int monologue_destroy(struct call_monologue *ml); /* 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; @@ -1090,7 +141,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) @@ -1122,7 +173,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; @@ -1130,7 +181,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; @@ -1148,17 +198,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; @@ -1169,10 +219,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) @@ -1198,8 +247,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; @@ -1321,7 +370,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; @@ -1357,7 +406,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 @@ -1365,7 +414,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)); @@ -1406,7 +455,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; @@ -1414,8 +463,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); @@ -1433,14 +484,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; } @@ -1519,16 +571,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); @@ -1572,7 +626,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; @@ -1583,157 +637,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) @@ -1752,65 +662,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; @@ -1821,85 +702,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 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 (first) + g_queue_clear(&ps->sfds); + + sfd->stream = ps; + g_queue_push_tail(&ps->sfds, sfd); + + 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; } @@ -1911,15 +815,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; } @@ -1931,7 +833,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; @@ -1964,7 +865,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 */ @@ -1972,14 +872,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)) @@ -1987,7 +889,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; @@ -1998,7 +900,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) @@ -2010,9 +912,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)); @@ -2314,7 +1217,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; } } @@ -2398,12 +1302,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); } } @@ -2428,36 +1332,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; +// } } @@ -2521,6 +1430,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) @@ -2529,15 +1465,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) { @@ -2553,6 +1494,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 */ @@ -2622,7 +1564,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; @@ -2631,8 +1573,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); @@ -2660,7 +1606,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); @@ -2670,22 +1616,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: @@ -2701,22 +1655,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; + return ERROR_NO_FREE_PORTS; - if (p->call->callmaster->conf.kernelfd >= 0) - kernel_del_stream(p->call->callmaster->conf.kernelfd, p->sfd->fd.localport); - - 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) { @@ -2827,7 +1772,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 { @@ -2908,7 +1855,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) { @@ -2980,7 +1927,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"; @@ -2997,7 +1944,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, @@ -3052,7 +2000,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, @@ -3068,9 +2017,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), @@ -3097,7 +2046,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 @@ -3148,8 +2097,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); @@ -3175,22 +2124,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); } @@ -3199,78 +2148,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; } @@ -3289,19 +2195,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); @@ -3316,18 +2220,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) { @@ -3345,7 +2247,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; } @@ -3417,7 +2318,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; @@ -3425,8 +2326,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; } @@ -3451,26 +2350,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; @@ -3540,16 +2419,31 @@ 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; } +/* 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) @@ -3559,53 +2453,53 @@ 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) { + 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); 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; } @@ -3643,10 +2537,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 @@ -3654,12 +2546,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); @@ -3686,7 +2577,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; @@ -3697,52 +2588,61 @@ 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; } - 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; } @@ -3851,115 +2751,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 154d76663..38a029e8e 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); @@ -777,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); @@ -822,18 +820,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 +841,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 +954,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 +976,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 28173c633..a240532fc 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,11 +620,12 @@ int main(int argc, char **argv) { thread_create_detach(sighandler, NULL); thread_create_detach(poller_timer_loop, ctx.p); - // TODO: if redis notify enabled. - thread_create_detach(redis_notify, ctx.m); + if (!is_addr_unspecified(&redis_read_ep.address)) + thread_create_detach(redis_notify, ctx.m); - if (graphite_ip) + if (!is_addr_unspecified(&graphite_ep.address)) thread_create_detach(graphite_loop, ctx.m); + thread_create_detach(ice_thread_run, NULL); if (num_threads < 1) { diff --git a/daemon/media_socket.c b/daemon/media_socket.c new file mode 100644 index 000000000..5dd7dd798 --- /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, OP_OTHER); + } else if (ca->callmaster->conf.redis) { + redis_update(ca, ca->callmaster->conf.redis, MASTER_REDIS_ROLE, OP_OTHER); + } + } +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 b3797860a..f4d75b8db 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "redis.h" @@ -133,7 +134,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; @@ -268,7 +269,7 @@ void redis_notify(void *d) { struct event_base *base = event_base_new(); - redisAsyncContext *c = redisAsyncConnect(r->host, r->port); + redisAsyncContext *c = redisAsyncConnect(r->host, r->endpoint.port); if (c->err) { printf("error: %s\n", c->errstr); return; @@ -280,14 +281,13 @@ void redis_notify(void *d) { event_base_dispatch(base); } -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); @@ -322,64 +322,32 @@ 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, "DEL notifier-"PB"", STR(&c->callid)); 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); } @@ -387,14 +355,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; @@ -417,90 +390,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); } @@ -532,12 +435,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); @@ -577,11 +482,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; @@ -595,32 +498,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; @@ -638,8 +597,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) { @@ -650,108 +617,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; - - 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) + if (redis_hash_get_u64(&out->last_index, h, "last_index")) return -1; + redis_hash_get_unsigned(&out->ssrc, h, "ssrc"); - 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, 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 (redis_hash_get_unsigned(&loc_uid, rh, "local_intf_uid")) + return -1; - if (redis_hash_get_int(&port, &it->rh, "localport")) + fam = get_socket_family_rfc(&family); + if (!fam) return -1; - if (__get_consecutive_ports(&fd, 1, port, c)) + 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; - 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)) + if (__get_consecutive_ports(&q, 1, port, loc->spec)) + return -1; + sock = g_queue_pop_head(&q); + if (!sock) return -1; - it->ptr = sfd; + 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); } @@ -759,222 +709,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; - ml->active_dialogue = redis_hash_get_ptr_hash(tags, &it_tag->rh, "active"); + if (redis_hash_get_str(&s, rh, "type")) + return -1; + 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; } @@ -982,33 +982,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")) @@ -1024,11 +1032,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)) @@ -1039,15 +1044,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; @@ -1055,6 +1072,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: @@ -1071,6 +1092,8 @@ err1: call_destroy(c); obj_put(c); } + else + redisCommandNR(r->ctx, "SREM calls "PB"", STR_R(id)); } } @@ -1126,7 +1149,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++) { @@ -1149,76 +1172,106 @@ 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, enum call_opmode opmode) { - 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; @@ -1230,158 +1283,234 @@ void redis_update(struct call *c, struct redis *r, int role, enum call_opmode op redis_pipe(r, "DEL notifier-"PB"", STR(&c->callid)); 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 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, + 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)); if (opmode==OP_ANSWER) { redis_pipe(r, "SADD notifier-"PB" "PB"", STR(&c->callid), STR(&c->callid)); diff --git a/daemon/redis.h b/daemon/redis.h index f7f626b16..43b00c4ed 100644 --- a/daemon/redis.h +++ b/daemon/redis.h @@ -4,6 +4,9 @@ +#include +#include "compat.h" +#include "socket.h" #include "aux.h" #include @@ -22,9 +25,8 @@ struct call; struct redis { - u_int32_t ip; - char host[32]; - int port; + endpoint_t endpoint; + char host[64]; redisContext *ctx; int db; @@ -36,17 +38,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" + @@ -79,7 +77,7 @@ INLINE gboolean g_hash_table_insert_check(GHashTable *h, gpointer k, gpointer v) void redis_notify(void *d); -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, enum call_opmode); 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 diff --git a/kernel-module/xt_RTPENGINE.c b/kernel-module/xt_RTPENGINE.c index de2cc2d76..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; @@ -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 { @@ -405,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; } @@ -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); @@ -2559,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; @@ -2570,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) 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;