From a009f764566d6d3f6f5d2697f644931ff15abb6f Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Wed, 28 Apr 2021 09:11:55 -0400 Subject: [PATCH] TT#14008 support media echo and blackhole Change-Id: I9df4680188709867db2b61d97cc5f1e30c59e0d7 --- README.md | 28 ++++++++++++++++++++++++++ daemon/call.c | 38 ++++++++++++++++++++++++++++++++++++ daemon/call_interfaces.c | 25 ++++++++++++++++++++++++ daemon/codec.c | 5 ++++- daemon/media_socket.c | 12 +++++++++--- include/call.h | 2 ++ include/call_interfaces.h | 7 +++++++ kernel-module/xt_RTPENGINE.c | 13 +++++++++--- kernel-module/xt_RTPENGINE.h | 1 + utils/rtpengine-ng-client | 3 ++- 10 files changed, 126 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 9c9bfe4c5..c88fc9e11 100644 --- a/README.md +++ b/README.md @@ -1433,6 +1433,34 @@ Optionally included keys are: If specified, then this address will be used as destination address for the XMLRPC timeout callback (see `b2b-url` option). +* `media echo` or `media-echo` + + Contains a string to enable a special media echo mode. Recognised values are: + + - `blackhole` or `sinkhole` + + Media arriving from either side of the call is simply discarded + and not forwarded. + + - `forward` + + Enables media echo towards the receiver of this message (e.g. + the called party if the message is an `offer` from the caller). + Media arriving from that side is echoed back to its sender + (with a new SSRC if it's RTP). Media arriving from the opposite + side is discarded. + + - `backwards` + + Enables media echo towards the sender of this message (i.e. the + opposite of `forward`). Media arriving from the other side is + discarded. + + - `both` + + Enables media echo towards both the sender and the receiver of + this message. + An example of a complete `offer` request dictionary could be (SDP body abbreviated): { "command": "offer", "call-id": "cfBXzDSZqhYNcXM", "from-tag": "mS9rSAn0Cr", diff --git a/daemon/call.c b/daemon/call.c index af16640cb..2f887668c 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -1179,6 +1179,10 @@ static int __init_streams(struct call_media *A, struct call_media *B, const stru /* RTP */ a->rtp_sink = b; + // reflect media - pretent reflection also for blackhole, as otherwise + // we get SSRC flip-flops on the opposite side + if (MEDIA_ISSET(A, ECHO) || MEDIA_ISSET(A, BLACKHOLE)) + a->rtp_sink = a; PS_SET(a, RTP); /* XXX technically not correct, could be udptl too */ __rtp_stats_update(a->rtp_stats, A->codecs_recv); @@ -1213,6 +1217,8 @@ static int __init_streams(struct call_media *A, struct call_media *B, const stru } else { a->rtcp_sink = b; + if (MEDIA_ISSET(A, ECHO) || MEDIA_ISSET(A, BLACKHOLE)) + a->rtcp_sink = a->rtcp_sibling; PS_SET(a, RTCP); PS_CLEAR(a, IMPLICIT_RTCP); } @@ -1227,6 +1233,8 @@ static int __init_streams(struct call_media *A, struct call_media *B, const stru a->rtp_sink = NULL; a->rtcp_sink = b; + if (MEDIA_ISSET(A, ECHO) || MEDIA_ISSET(A, BLACKHOLE)) + a->rtcp_sink = a; PS_CLEAR(a, RTP); PS_SET(a, RTCP); a->rtcp_sibling = NULL; @@ -2197,6 +2205,36 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, MEDIA_CLEAR(other_media, RTCP_GEN); } + if (flags) { + switch (flags->media_echo) { + case MEO_FWD: + MEDIA_SET(media, ECHO); + MEDIA_SET(other_media, BLACKHOLE); + MEDIA_CLEAR(media, BLACKHOLE); + MEDIA_CLEAR(other_media, ECHO); + break; + case MEO_BKW: + MEDIA_SET(media, BLACKHOLE); + MEDIA_SET(other_media, ECHO); + MEDIA_CLEAR(media, ECHO); + MEDIA_CLEAR(other_media, BLACKHOLE); + break; + case MEO_BOTH: + MEDIA_SET(media, ECHO); + MEDIA_SET(other_media, ECHO); + MEDIA_CLEAR(media, BLACKHOLE); + MEDIA_CLEAR(other_media, BLACKHOLE); + break; + case MEO_BLACKHOLE: + MEDIA_SET(media, BLACKHOLE); + MEDIA_SET(other_media, BLACKHOLE); + MEDIA_CLEAR(media, ECHO); + MEDIA_CLEAR(other_media, ECHO); + case MEO_DEFAULT: + break; + } + } + __update_media_protocol(media, other_media, sp, flags); __update_media_id(media, other_media, sp, flags); __endpoint_loop_protect(sp, other_media); diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 5a0875ce7..8ffc04eb1 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -1154,6 +1154,31 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *inpu else if (!str_cmp(&s, "off")) out->generate_rtcp_off = 1; } + + if (bencode_get_alt(input, "media-echo", "media echo", &s)) { + switch (__csh_lookup(&s)) { + case CSH_LOOKUP("blackhole"): + case CSH_LOOKUP("sinkhole"): + out->media_echo = MEO_BLACKHOLE; + break; + case CSH_LOOKUP("forward"): + case CSH_LOOKUP("fwd"): + case CSH_LOOKUP("fw"): + out->media_echo = MEO_FWD; + break; + case CSH_LOOKUP("backward"): + case CSH_LOOKUP("backwards"): + case CSH_LOOKUP("reverse"): + case CSH_LOOKUP("back"): + case CSH_LOOKUP("bkw"): + case CSH_LOOKUP("bk"): + out->media_echo = MEO_BKW; + break; + case CSH_LOOKUP("both"): + out->media_echo = MEO_BOTH; + break; + } + } } static void call_ng_free_flags(struct sdp_ng_flags *flags) { if (flags->codec_strip) diff --git a/daemon/codec.c b/daemon/codec.c index ddb4c8b57..af2ca5fef 100644 --- a/daemon/codec.c +++ b/daemon/codec.c @@ -407,7 +407,10 @@ static GList *__delete_send_codec(struct call_media *sender, GList *link) { // only called from codec_handlers_update() static void __make_passthrough_gsl(struct codec_handler *handler, GSList **handlers) { - __make_passthrough(handler); + if (MEDIA_ISSET(handler->media, ECHO)) + __make_passthrough_ssrc(handler); + else + __make_passthrough(handler); *handlers = g_slist_prepend(*handlers, handler); } diff --git a/daemon/media_socket.c b/daemon/media_socket.c index 258d8ab0d..01db693e4 100644 --- a/daemon/media_socket.c +++ b/daemon/media_socket.c @@ -1141,6 +1141,8 @@ void kernelize(struct packet_stream *stream) { goto no_kernel; if (!stream->endpoint.address.family) goto no_kernel; + if (MEDIA_ISSET(media, BLACKHOLE)) + non_forwarding = 1; ilog(LOG_INFO, "Kernelizing media stream: %s%s:%d%s", FMT_M(sockaddr_print_buf(&stream->endpoint.address), stream->endpoint.port)); @@ -1183,13 +1185,14 @@ void kernelize(struct packet_stream *stream) { reti.dtls = MEDIA_ISSET(media, DTLS); reti.stun = media->ice_agent ? 1 : 0; reti.non_forwarding = non_forwarding; + reti.blackhole = MEDIA_ISSET(media, BLACKHOLE) ? 1 : 0; reti.rtp_stats = MEDIA_ISSET(media, RTCP_GEN) ? 1 : 0; __re_address_translate_ep(&reti.dst_addr, &sink->endpoint); __re_address_translate_ep(&reti.src_addr, &sink->selected_sfd->socket.local); if (stream->ssrc_in) { reti.ssrc = htonl(stream->ssrc_in->parent->h.ssrc); - if (MEDIA_ISSET(media, TRANSCODE)) { + if (MEDIA_ISSET(media, TRANSCODE) || MEDIA_ISSET(media, ECHO)) { reti.ssrc_out = htonl(stream->ssrc_in->ssrc_map_out); reti.transcoding = 1; } @@ -1473,7 +1476,7 @@ static void __stream_ssrc(struct packet_stream *in_srtp, struct packet_stream *o } // make sure we reset the output SSRC if we're not transcoding - if (!MEDIA_ISSET(in_srtp->media, TRANSCODE)) + if (!MEDIA_ISSET(in_srtp->media, TRANSCODE) && !MEDIA_ISSET(in_srtp->media, ECHO)) (*ssrc_in_p)->ssrc_map_out = in_ssrc; out_ssrc = (*ssrc_in_p)->ssrc_map_out; @@ -2118,7 +2121,10 @@ static int stream_packet(struct packet_handler_ctx *phc) { goto drop; } - ret = media_socket_dequeue(&phc->mp, phc->sink); + if (!MEDIA_ISSET(phc->mp.media, BLACKHOLE)) + ret = media_socket_dequeue(&phc->mp, phc->sink); + else + ret = media_socket_dequeue(&phc->mp, NULL); mutex_unlock(&phc->sink->out_lock); diff --git a/include/call.h b/include/call.h index 0815a0dd4..1cfe9980c 100644 --- a/include/call.h +++ b/include/call.h @@ -157,6 +157,8 @@ enum call_stream_state { #define MEDIA_FLAG_GENERATOR 0x02000000 #define MEDIA_FLAG_ICE_LITE_SELF 0x04000000 #define MEDIA_FLAG_RTCP_GEN 0x08000000 +#define MEDIA_FLAG_ECHO 0x10000000 +#define MEDIA_FLAG_BLACKHOLE 0x20000000 /* access macros */ #define SP_ISSET(p, f) bf_isset(&(p)->sp_flags, SP_FLAG_ ## f) diff --git a/include/call_interfaces.h b/include/call_interfaces.h index 5bbc32a89..3b5fab13e 100644 --- a/include/call_interfaces.h +++ b/include/call_interfaces.h @@ -64,6 +64,13 @@ struct sdp_ng_flags { ICE_LITE_BKW, ICE_LITE_BOTH, } ice_lite_option:3; + enum { + MEO_DEFAULT = 0, + MEO_BLACKHOLE, + MEO_FWD, + MEO_BKW, + MEO_BOTH, + } media_echo:3; unsigned int asymmetric:1, protocol_accept:1, no_redis_update:1, diff --git a/kernel-module/xt_RTPENGINE.c b/kernel-module/xt_RTPENGINE.c index c54cc6a97..5fb5dc621 100644 --- a/kernel-module/xt_RTPENGINE.c +++ b/kernel-module/xt_RTPENGINE.c @@ -1581,8 +1581,10 @@ static int proc_list_show(struct seq_file *f, void *v) { 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); + if (!g->target.non_forwarding) { + 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); proc_list_addr_print(f, "expect", &g->target.expected_src); if (g->target.src_mismatch > 0 && g->target.src_mismatch <= ARRAY_SIZE(re_msm_strings)) @@ -1612,6 +1614,8 @@ static int proc_list_show(struct seq_file *f, void *v) { seq_printf(f, " option: transcoding\n"); if (g->target.non_forwarding) seq_printf(f, " option: non forwarding\n"); + if (g->target.blackhole) + seq_printf(f, " option: blackhole\n"); if (g->target.rtp_stats) seq_printf(f, " option: RTP stats\n"); @@ -4273,8 +4277,11 @@ not_stun: goto skip_error; src_check_ok: - if (g->target.non_forwarding) + if (g->target.non_forwarding) { + if (g->target.blackhole) + error_nf_action = NF_DROP; goto skip1; + } if (g->target.dtls && is_dtls(skb)) goto skip1; diff --git a/kernel-module/xt_RTPENGINE.h b/kernel-module/xt_RTPENGINE.h index d86f0cebd..a693e0ae6 100644 --- a/kernel-module/xt_RTPENGINE.h +++ b/kernel-module/xt_RTPENGINE.h @@ -120,6 +120,7 @@ struct rtpengine_target_info { do_intercept:1, transcoding:1, // SSRC subst and RTP PT filtering non_forwarding:1, // empty src/dst addr + blackhole:1, rtp_stats:1; // requires SSRC and clock_rates to be set }; diff --git a/utils/rtpengine-ng-client b/utils/rtpengine-ng-client index 0198e501d..78688b04a 100755 --- a/utils/rtpengine-ng-client +++ b/utils/rtpengine-ng-client @@ -89,13 +89,14 @@ GetOptions( 'generate-RTCP' => \$options{'generate RTCP'}, 'single-codec' => \$options{'single codec'}, 'reorder-codecs' => \$options{'reorder codecs'}, + 'media-echo=s' => \$options{'media echo'}, ) or die; my $cmd = shift(@ARGV) or die; my %packet = (command => $cmd); -for my $x (split(/,/, 'from-tag,to-tag,call-id,transport protocol,media address,ICE,address family,DTLS,via-branch,media address,ptime,xmlrpc-callback,metadata,address,file,db-id,code,DTLS-fingerprint,ICE-lite')) { +for my $x (split(/,/, 'from-tag,to-tag,call-id,transport protocol,media address,ICE,address family,DTLS,via-branch,media address,ptime,xmlrpc-callback,metadata,address,file,db-id,code,DTLS-fingerprint,ICE-lite,media echo')) { defined($options{$x}) and $packet{$x} = \$options{$x}; } for my $x (split(/,/, 'TOS,delete-delay')) {