diff --git a/daemon/call.c b/daemon/call.c index e4352b51d..e78820583 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -123,6 +123,60 @@ const char *transport_protocol_strings[__PROTO_LAST] = { +static int __k_null(struct mediaproxy_srtp *s, struct streamrelay *r); +static int __k_srtp_encrypt(struct mediaproxy_srtp *s, struct streamrelay *r); +static int __k_srtp_decrypt(struct mediaproxy_srtp *s, struct streamrelay *r); + +static int call_avp2savp_rtp(str *s, struct streamrelay *r); +static int call_savp2avp_rtp(str *s, struct streamrelay *r); +static int call_avp2savp_rtcp(str *s, struct streamrelay *r); +static int call_savp2avp_rtcp(str *s, struct streamrelay *r); +static int call_avpf2avp_rtcp(str *s, struct streamrelay *r); +static int call_avpf2savp_rtcp(str *s, struct streamrelay *r); +static int call_savpf2avp_rtcp(str *s, struct streamrelay *r); +static int call_savpf2savp_rtcp(str *s, struct streamrelay *r); + +static const struct streamhandler __sh_noop = { + .kernel_decrypt = __k_null, + .kernel_encrypt = __k_null, +}; +static const struct streamhandler __sh_rtp_avp2savp = { + .rewrite = call_avp2savp_rtp, + .kernel_decrypt = __k_null, + .kernel_encrypt = __k_srtp_encrypt, +}; +static const struct streamhandler __sh_rtp_savp2avp = { + .rewrite = call_savp2avp_rtp, + .kernel_decrypt = __k_srtp_decrypt, + .kernel_encrypt = __k_null, +}; +static const struct streamhandler __sh_rtcp_avp2savp = { + .rewrite = call_avp2savp_rtcp, +}; +static const struct streamhandler __sh_rtcp_savp2avp = { + .rewrite = call_savp2avp_rtcp, +}; +static const struct streamhandler __sh_rtcp_avpf2avp = { + .rewrite = call_avpf2avp_rtcp, +}; +static const struct streamhandler __sh_rtcp_avpf2savp = { + .rewrite = call_avpf2savp_rtcp, +}; +static const struct streamhandler __sh_rtcp_savpf2avp = { + .rewrite = call_savpf2avp_rtcp, +}; +static const struct streamhandler __sh_rtcp_savpf2savp = { + .rewrite = call_savpf2savp_rtcp, +}; + +static const struct mediaproxy_srtp __mps_null = { + .cipher = MPC_NULL, + .hmac = MPH_NULL, +}; + + + + static void call_destroy(struct call *); @@ -161,7 +215,7 @@ void kernelize(struct callstream *c) { int i, j; struct peer *p, *pp; struct streamrelay *r, *rp; - struct kernel_stream ks; + struct mediaproxy_target_info mpt; struct callmaster *cm = c->call->callmaster; if (cm->conf.kernelfd < 0 || cm->conf.kernelid == -1) @@ -169,7 +223,7 @@ void kernelize(struct callstream *c) { mylog(LOG_DEBUG, LOG_PREFIX_C "Kernelizing RTP streams", LOG_PARAMS_C(c->call)); - ZERO(ks); + ZERO(mpt); for (i = 0; i < 2; i++) { p = &c->peers[i]; @@ -184,29 +238,40 @@ void kernelize(struct callstream *c) { if (is_addr_unspecified(&r->peer_advertised.ip46) || !r->fd.fd_family || !r->peer_advertised.port) - continue; + goto no_kernel_stream; + if (!r->handler->kernel_decrypt + || !r->handler->kernel_encrypt) + goto no_kernel_stream; - ks.local_port = r->fd.localport; - ks.tos = cm->conf.tos; - ks.src.port = rp->fd.localport; - ks.dest.port = r->peer.port; + mpt.target_port = r->fd.localport; + mpt.tos = cm->conf.tos; + mpt.src_addr.port = rp->fd.localport; + mpt.dst_addr.port = r->peer.port; if (IN6_IS_ADDR_V4MAPPED(&r->peer.ip46)) { - ks.src.family = AF_INET; - ks.src.ipv4 = cm->conf.ipv4; - ks.dest.family = AF_INET; - ks.dest.ipv4 = r->peer.ip46.s6_addr32[3]; + mpt.src_addr.family = AF_INET; + mpt.src_addr.ipv4 = cm->conf.ipv4; + mpt.dst_addr.family = AF_INET; + mpt.dst_addr.ipv4 = r->peer.ip46.s6_addr32[3]; } else { - ks.src.family = AF_INET6; - ks.src.ipv6 = cm->conf.ipv6; - ks.dest.family = AF_INET6; - ks.dest.ipv6 = r->peer.ip46; + mpt.src_addr.family = AF_INET6; + memcpy(mpt.src_addr.ipv6, &cm->conf.ipv6, sizeof(mpt.src_addr.ipv6)); + mpt.dst_addr.family = AF_INET6; + memcpy(mpt.dst_addr.ipv6, &r->peer.ip46, sizeof(mpt.src_addr.ipv6)); } + r->handler->kernel_decrypt(&mpt.decrypt, r); + r->handler->kernel_encrypt(&mpt.encrypt, r); + ZERO(r->kstats); - kernel_add_stream(cm->conf.kernelfd, &ks, 0); + kernel_add_stream(cm->conf.kernelfd, &mpt, 0); + + continue; + +no_kernel_stream: + r->no_kernel_support = 1; } p->kernelized = 1; @@ -216,10 +281,7 @@ void kernelize(struct callstream *c) { -static int __dummy_stream_handler(str *s, struct streamrelay *r) { - return 0; -} -static int call_avpf2avp(str *s, struct streamrelay *r) { +static int call_avpf2avp_rtcp(str *s, struct streamrelay *r) { return rtcp_avpf2avp(s); } static int call_avp2savp_rtp(str *s, struct streamrelay *r) { @@ -260,25 +322,76 @@ static int call_savpf2savp_rtcp(str *s, struct streamrelay *r) { } -static stream_handler determine_handler(struct streamrelay *in) { - if (in->peer.protocol == in->peer_advertised.protocol) - goto dummy; - if (in->peer_advertised.protocol == PROTO_UNKNOWN) - goto dummy; +static int __k_null(struct mediaproxy_srtp *s, struct streamrelay *r) { + *s = __mps_null; + return 0; +} +static int __k_srtp_crypt(struct mediaproxy_srtp *s, struct crypto_context *c) { + *s = (struct mediaproxy_srtp) { + .cipher = c->crypto_suite->kernel_cipher, + .hmac = c->crypto_suite->kernel_hmac, + .mki = c->mki, + .mki_len = c->mki_len, + .last_index = c->s_l, + .auth_tag_len = c->crypto_suite->srtp_auth_tag, + }; + memcpy(s->master_key, c->master_key, c->crypto_suite->master_key_len); + memcpy(s->master_salt, c->master_salt, c->crypto_suite->master_salt_len); + return 0; +} +static int __k_srtp_encrypt(struct mediaproxy_srtp *s, struct streamrelay *r) { + return __k_srtp_crypt(s, &r->crypto.out); +} +static int __k_srtp_decrypt(struct mediaproxy_srtp *s, struct streamrelay *r) { + return __k_srtp_crypt(s, &r->other->crypto.in); +} +static const struct streamhandler *determine_handler_rtp(struct streamrelay *in) { switch (in->peer.protocol) { - case PROTO_UNKNOWN: - goto dummy; + case PROTO_RTP_AVP: + case PROTO_RTP_AVPF: + switch (in->peer_advertised.protocol) { + case PROTO_RTP_AVP: + case PROTO_RTP_AVPF: + return NULL; + case PROTO_RTP_SAVP: + case PROTO_RTP_SAVPF: + return &__sh_rtp_avp2savp; + + default: + abort(); + } + + case PROTO_RTP_SAVP: + case PROTO_RTP_SAVPF: + switch (in->peer_advertised.protocol) { + case PROTO_RTP_AVP: + case PROTO_RTP_AVPF: + return &__sh_rtp_savp2avp; + + case PROTO_RTP_SAVPF: + case PROTO_RTP_SAVP: + return NULL; + + default: + abort(); + } + + default: + abort(); + } +} +static const struct streamhandler *determine_handler_rtcp(struct streamrelay *in) { + switch (in->peer.protocol) { case PROTO_RTP_AVP: switch (in->peer_advertised.protocol) { case PROTO_RTP_AVPF: - goto dummy; + return NULL; case PROTO_RTP_SAVP: case PROTO_RTP_SAVPF: - return in->rtcp ? call_avp2savp_rtcp - : call_avp2savp_rtp; + return &__sh_rtcp_avp2savp; default: abort(); @@ -288,11 +401,10 @@ static stream_handler determine_handler(struct streamrelay *in) { switch (in->peer_advertised.protocol) { case PROTO_RTP_AVP: case PROTO_RTP_AVPF: - return in->rtcp ? call_savp2avp_rtcp - : call_savp2avp_rtp; + return &__sh_rtcp_savp2avp; case PROTO_RTP_SAVPF: - goto dummy; + return NULL; default: abort(); @@ -301,17 +413,14 @@ static stream_handler determine_handler(struct streamrelay *in) { case PROTO_RTP_AVPF: switch (in->peer_advertised.protocol) { case PROTO_RTP_AVP: - if (!in->rtcp) - goto dummy; - return call_avpf2avp; + return &__sh_rtcp_avpf2avp; case PROTO_RTP_SAVP: - return in->rtcp ? call_avpf2savp_rtcp - : call_avp2savp_rtp; + return &__sh_rtcp_avpf2savp; case PROTO_RTP_SAVPF: - return in->rtcp ? call_avp2savp_rtcp - : call_avp2savp_rtp; + return &__sh_rtcp_avp2savp; + default: abort(); } @@ -319,17 +428,13 @@ static stream_handler determine_handler(struct streamrelay *in) { case PROTO_RTP_SAVPF: switch (in->peer_advertised.protocol) { case PROTO_RTP_AVP: - return in->rtcp ? call_savpf2avp_rtcp - : call_savp2avp_rtp; + return &__sh_rtcp_savpf2avp; case PROTO_RTP_AVPF: - return in->rtcp ? call_savp2avp_rtcp - : call_savp2avp_rtp; + return &__sh_rtcp_savp2avp; case PROTO_RTP_SAVP: - if (!in->rtcp) - goto dummy; - return call_savpf2savp_rtcp; + return &__sh_rtcp_savpf2savp; default: abort(); @@ -338,9 +443,28 @@ static stream_handler determine_handler(struct streamrelay *in) { default: abort(); } +} +static const struct streamhandler *determine_handler(struct streamrelay *in) { + const struct streamhandler *ret; + + if (in->peer.protocol == in->peer_advertised.protocol) + goto dummy; + if (in->peer.protocol == PROTO_UNKNOWN) + goto dummy; + if (in->peer_advertised.protocol == PROTO_UNKNOWN) + goto dummy; + + if (in->rtcp) + ret = determine_handler_rtcp(in); + else + ret = determine_handler_rtp(in); + + if (!ret) + goto dummy; + return ret; dummy: - return __dummy_stream_handler; + return &__sh_noop; } /* called with r->up (== cs) locked */ @@ -392,7 +516,8 @@ static int stream_packet(struct streamrelay *sr_incoming, str *s, struct sockadd if (!sr_incoming->handler) sr_incoming->handler = determine_handler(sr_incoming); - handler_ret = sr_incoming->handler(s, sr_incoming); + if (sr_incoming->handler->rewrite) + handler_ret = sr_incoming->handler->rewrite(s, sr_incoming); use_cand: if (p_incoming->confirmed || !p_incoming->filled || sr_incoming->idx != 0) @@ -424,7 +549,7 @@ peerinfo: update = 1; - if (sr_incoming->handler != __dummy_stream_handler) + if (sr_incoming->no_kernel_support) goto forward; if (p_incoming->confirmed && p_outgoing->confirmed && p_outgoing->filled) @@ -1592,6 +1717,7 @@ static void unkernelize(struct peer *p) { for (i = 0; i < 2; i++) { r = &p->rtps[i]; kernel_del_stream(p->up->call->callmaster->conf.kernelfd, r->fd.localport); + r->no_kernel_support = 0; } p->kernelized = 0; diff --git a/daemon/call.h b/daemon/call.h index 3df041204..eb3fda759 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -93,7 +93,12 @@ struct udp_fd { }; struct streamrelay; -typedef int (*stream_handler)(str *, struct streamrelay *); +struct mediaproxy_srtp; +struct streamhandler { + int (*rewrite)(str *, struct streamrelay *); + int (*kernel_decrypt)(struct mediaproxy_srtp *, struct streamrelay *); + int (*kernel_encrypt)(struct mediaproxy_srtp *, struct streamrelay *); +}; struct streamrelay { struct udp_fd fd; @@ -105,10 +110,11 @@ struct streamrelay { struct stats stats; struct stats kstats; time_t last; - stream_handler handler; + const struct streamhandler *handler; struct crypto_context_pair crypto; int stun:1; int rtcp:1; + int no_kernel_support:1; }; struct relays_cache { struct udp_fd relays_A[16]; diff --git a/daemon/crypto.c b/daemon/crypto.c index d56d4fdce..3f1dd916d 100644 --- a/daemon/crypto.c +++ b/daemon/crypto.c @@ -5,6 +5,8 @@ #include #include +#include "xt_MEDIAPROXY.h" + #include "str.h" #include "aux.h" #include "rtp.h" @@ -37,8 +39,8 @@ const struct crypto_suite crypto_suites[] = { .session_salt_len = 14, .srtp_lifetime = 1ULL << 48, .srtcp_lifetime = 1ULL << 31, - .cipher = CIPHER_AES_CM, - .mac = MAC_HMAC_SHA1, + .kernel_cipher = MPC_AES_CM, + .kernel_hmac = MPH_HMAC_SHA1, .srtp_auth_tag = 10, .srtcp_auth_tag = 10, .srtp_auth_key_len = 20, @@ -60,8 +62,8 @@ const struct crypto_suite crypto_suites[] = { .session_salt_len = 14, .srtp_lifetime = 1ULL << 48, .srtcp_lifetime = 1ULL << 31, - .cipher = CIPHER_AES_CM, - .mac = MAC_HMAC_SHA1, + .kernel_cipher = MPC_AES_CM, + .kernel_hmac = MPH_HMAC_SHA1, .srtp_auth_tag = 4, .srtcp_auth_tag = 10, .srtp_auth_key_len = 20, @@ -81,8 +83,8 @@ const struct crypto_suite crypto_suites[] = { .session_salt_len = 14, .srtp_lifetime = 1ULL << 48, .srtcp_lifetime = 1ULL << 31, - .cipher = CIPHER_AES_F8, - .mac = MAC_HMAC_SHA1, + .kernel_cipher = 0, + .kernel_hmac = MPH_HMAC_SHA1, .srtp_auth_tag = 10, .srtcp_auth_tag = 10, .srtp_auth_key_len = 20, diff --git a/daemon/crypto.h b/daemon/crypto.h index 8118444ab..7cae45ed6 100644 --- a/daemon/crypto.h +++ b/daemon/crypto.h @@ -8,22 +8,6 @@ -/* XXX get rid of the enums and replace with struct pointers? */ -enum cipher { - CIPHER_UNKNOWN = 0, - CIPHER_AES_CM, - CIPHER_AES_F8, - - __CIPHER_LAST -}; - -enum mac { - MAC_UNKNOWN = 0, - MAC_HMAC_SHA1, - - __MAC_LAST -}; - struct crypto_context; struct rtp_header; struct rtcp_packet; @@ -49,8 +33,8 @@ struct crypto_suite { unsigned long long int srtp_lifetime, srtcp_lifetime; - enum cipher cipher; - enum mac mac; + int kernel_cipher; + int kernel_hmac; crypto_func_rtp encrypt_rtp, decrypt_rtp; crypto_func_rtcp encrypt_rtcp, diff --git a/daemon/kernel.c b/daemon/kernel.c index a33d79ef4..b6f085f72 100644 --- a/daemon/kernel.c +++ b/daemon/kernel.c @@ -7,10 +7,12 @@ #include #include #include +#include #include "xt_MEDIAPROXY.h" #include "aux.h" +#include "log.h" @@ -72,50 +74,36 @@ fail: } -static void addr_copy(struct mp_address *mp, struct ip_port *ap) { - mp->family = ap->family; - mp->port = ap->port; - switch (mp->family) { - case AF_INET: - mp->ipv4 = ap->ipv4; - break; - case AF_INET6: - memcpy(mp->ipv6, &ap->ipv6, 16); - break; - default: - /* XXX panic */ - break; - } -} - - -int kernel_add_stream(int fd, struct kernel_stream *info, int update) { +int kernel_add_stream(int fd, struct mediaproxy_target_info *mti, int update) { struct mediaproxy_message msg; + int ret; - ZERO(msg); msg.cmd = update ? MMG_UPDATE : MMG_ADD; - msg.target.target_port = info->local_port; - addr_copy(&msg.target.src_addr, &info->src); - addr_copy(&msg.target.dst_addr, &info->dest); - addr_copy(&msg.target.mirror_addr, &info->mirror); - msg.target.tos = info->tos; - msg.target.decrypt.cipher = MPC_NULL; - msg.target.decrypt.hmac = MPH_NULL; - msg.target.encrypt.cipher = MPC_NULL; - msg.target.encrypt.hmac = MPH_NULL; - - return write(fd, &msg, sizeof(msg)) <= 0 ? -1 : 0; + msg.target = *mti; + + ret = write(fd, &msg, sizeof(msg)); + if (ret > 0) + return 0; + + mylog(LOG_ERROR, "Failed to push relay stream to kernel: %s", strerror(errno)); + return -1; } int kernel_del_stream(int fd, u_int16_t p) { struct mediaproxy_message msg; + int ret; ZERO(msg); msg.cmd = MMG_DEL; msg.target.target_port = p; - return write(fd, &msg, sizeof(msg)) <= 0 ? -1 : 0; + ret = write(fd, &msg, sizeof(msg)); + if (ret > 0) + return 0; + + mylog(LOG_ERROR, "Failed to delete relay stream from kernel: %s", strerror(errno)); + return -1; } diff --git a/daemon/kernel.h b/daemon/kernel.h index 00427969b..8d00b6952 100644 --- a/daemon/kernel.h +++ b/daemon/kernel.h @@ -10,30 +10,14 @@ -struct ip_port { - int family; - union { - u_int32_t ipv4; - struct in6_addr ipv6; - }; - u_int16_t port; -}; - -struct kernel_stream { - u_int16_t local_port; - struct ip_port src; - struct ip_port dest; - struct ip_port mirror; - unsigned char tos; -}; - +struct mediaproxy_target_info; int kernel_create_table(unsigned int); int kernel_open_table(unsigned int); -int kernel_add_stream(int, struct kernel_stream *, int); +int kernel_add_stream(int, struct mediaproxy_target_info *, int); int kernel_del_stream(int, u_int16_t); GList *kernel_list(unsigned int); diff --git a/kernel-module/xt_MEDIAPROXY.c b/kernel-module/xt_MEDIAPROXY.c index f0da1c4e9..4c62329bc 100644 --- a/kernel-module/xt_MEDIAPROXY.c +++ b/kernel-module/xt_MEDIAPROXY.c @@ -809,18 +809,29 @@ static void proc_list_addr_print(struct seq_file *f, const char *s, const struct } } +static void proc_list_crypto_print(struct seq_file *f, struct mp_crypto_context *c, struct mediaproxy_srtp *s) { + seq_printf(f, " cipher: %s\n", c->cipher ? c->cipher->name : ""); + seq_printf(f, " MKI: %llu length %u\n", (unsigned long long) s->mki, s->mki_len); + seq_printf(f, " HMAC: %s\n", c->hmac ? c->hmac->name : ""); + seq_printf(f, " auth tag length: %u\n", s->auth_tag_len); +} + static int proc_list_show(struct seq_file *f, void *v) { struct mediaproxy_target *g = v; unsigned long flags; - spin_lock_irqsave(&g->stats_lock, flags); seq_printf(f, "port %5u:\n", g->target.target_port); proc_list_addr_print(f, "src", &g->target.src_addr); proc_list_addr_print(f, "dst", &g->target.dst_addr); proc_list_addr_print(f, "mirror", &g->target.mirror_addr); + spin_lock_irqsave(&g->stats_lock, flags); seq_printf(f, " stats: %20llu bytes, %20llu packets, %20llu errors\n", g->stats.bytes, g->stats.packets, g->stats.errors); spin_unlock_irqrestore(&g->stats_lock, flags); + seq_printf(f, " SRTP in:\n"); + proc_list_crypto_print(f, &g->decrypt, &g->target.decrypt); + seq_printf(f, " SRTP out:\n"); + proc_list_crypto_print(f, &g->encrypt, &g->target.encrypt); target_push(g);