diff --git a/README.md b/README.md index c88fc9e11..ecab89ed4 100644 --- a/README.md +++ b/README.md @@ -842,6 +842,13 @@ Optionally included keys are: Enabled full debug logging for this call, regardless of global log level settings. + - `pierce NAT` + + Sends empty UDP packets to the remote RTP peer as soon as an + endpoint address is available from a received SDP, for as long + as no incoming packets have been received. Useful to create an + initial NAT mapping. Not needed when ICE is in use. + * `generate RTCP` Contains a string, either `on` or `off`. If enabled for a call, diff --git a/daemon/call.c b/daemon/call.c index 2f887668c..31b072816 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -1004,7 +1004,7 @@ static int __num_media_streams(struct call_media *media, unsigned int num_ports) } static void __fill_stream(struct packet_stream *ps, const struct endpoint *epp, unsigned int port_off, - const struct stream_params *sp) + const struct stream_params *sp, const struct sdp_ng_flags *flags) { struct endpoint ep; struct call_media *media = ps->media; @@ -1035,6 +1035,9 @@ static void __fill_stream(struct packet_stream *ps, const struct endpoint *epp, ilog(LOG_DEBUG, "set FILLED flag for stream %s%s:%d%s", FMT_M(sockaddr_print_buf(&ps->endpoint.address), ps->endpoint.port)); PS_SET(ps, FILLED); + + if (flags && flags->pierce_nat) + PS_SET(ps, PIERCE_NAT); } /* called with call locked in R or W, but ps not locked */ @@ -1070,6 +1073,16 @@ enum call_stream_state call_stream_state_machine(struct packet_stream *ps) { mutex_unlock(&ps->in_lock); } + if (PS_ISSET(ps, PIERCE_NAT) && PS_ISSET(ps, FILLED) && !PS_ISSET(ps, CONFIRMED)) { + for (GList *l = ps->sfds.head; l; l = l->next) { + static const str fake_rtp = STR_CONST_INIT("\x80\x7f\xff\xff\x00\x00\x00\x00" + "\x00\x00\x00\x00"); + struct stream_fd *sfd = l->data; + socket_sendto(&sfd->socket, fake_rtp.s, fake_rtp.len, &ps->endpoint); + } + ret = CSS_PIERCE_NAT; + } + return ret; } @@ -1188,7 +1201,7 @@ static int __init_streams(struct call_media *A, struct call_media *B, const stru __rtp_stats_update(a->rtp_stats, A->codecs_recv); if (sp) { - __fill_stream(a, &sp->rtp_endpoint, port_off, sp); + __fill_stream(a, &sp->rtp_endpoint, port_off, sp, flags); bf_copy_same(&a->ps_flags, &sp->sp_flags, SHARED_FLAG_STRICT_SOURCE | SHARED_FLAG_MEDIA_HANDOVER); } @@ -1244,11 +1257,11 @@ static int __init_streams(struct call_media *A, struct call_media *B, const stru if (sp) { if (!SP_ISSET(sp, IMPLICIT_RTCP)) { - __fill_stream(a, &sp->rtcp_endpoint, port_off, sp); + __fill_stream(a, &sp->rtcp_endpoint, port_off, sp, flags); PS_CLEAR(a, IMPLICIT_RTCP); } else { - __fill_stream(a, &sp->rtp_endpoint, port_off + 1, sp); + __fill_stream(a, &sp->rtp_endpoint, port_off + 1, sp, flags); PS_SET(a, IMPLICIT_RTCP); } bf_copy_same(&a->ps_flags, &sp->sp_flags, diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 8ffc04eb1..3f6226571 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -888,6 +888,9 @@ static void call_ng_flags_flags(struct sdp_ng_flags *out, str *s, void *dummy) { case CSH_LOOKUP("no-jitter-buffer"): out->disable_jb = 1; break; + case CSH_LOOKUP("pierce-NAT"): + out->pierce_nat = 1; + break; default: // handle values aliases from other dictionaries if (call_ng_flags_prefix(out, s, "SDES-no-", call_ng_flags_str_ht, &out->sdes_no)) diff --git a/include/call.h b/include/call.h index 1cfe9980c..54e8366b6 100644 --- a/include/call.h +++ b/include/call.h @@ -62,6 +62,7 @@ enum call_stream_state { CSS_SHUTDOWN, CSS_ICE, CSS_DTLS, + CSS_PIERCE_NAT, CSS_RUNNING, }; @@ -132,6 +133,7 @@ enum call_stream_state { #define PS_FLAG_MEDIA_HANDOVER SHARED_FLAG_MEDIA_HANDOVER #define PS_FLAG_ICE SHARED_FLAG_ICE #define PS_FLAG_ZERO_ADDR 0x04000000 +#define PS_FLAG_PIERCE_NAT 0x08000000 /* struct call_media */ #define MEDIA_FLAG_INITIALIZED 0x00010000 diff --git a/include/call_interfaces.h b/include/call_interfaces.h index 3b5fab13e..7166ba482 100644 --- a/include/call_interfaces.h +++ b/include/call_interfaces.h @@ -138,7 +138,8 @@ struct sdp_ng_flags { drop_traffic_stop:1, passthrough_on:1, passthrough_off:1, - disable_jb:1; + disable_jb:1, + pierce_nat:1; }; diff --git a/utils/rtpengine-ng-client b/utils/rtpengine-ng-client index 78688b04a..1a1ccce7c 100755 --- a/utils/rtpengine-ng-client +++ b/utils/rtpengine-ng-client @@ -90,6 +90,7 @@ GetOptions( 'single-codec' => \$options{'single codec'}, 'reorder-codecs' => \$options{'reorder codecs'}, 'media-echo=s' => \$options{'media echo'}, + 'pierce-NAT' => \$options{'pierce NAT'}, ) or die; my $cmd = shift(@ARGV) or die; @@ -102,7 +103,7 @@ for my $x (split(/,/, 'from-tag,to-tag,call-id,transport protocol,media address, for my $x (split(/,/, 'TOS,delete-delay')) { defined($options{$x}) and $packet{$x} = $options{$x}; } -for my $x (split(/,/, 'trust address,symmetric,asymmetric,unidirectional,force,strict source,media handover,sip source address,reset,port latching,no rtcp attribute,full rtcp attribute,loop protect,record call,always transcode,all,pad crypto,generate mid,fragment,original sendrecv,symmetric codecs,asymmetric codecs,inject DTMF,generate RTCP,single codec,reorder codecs')) { +for my $x (split(/,/, 'trust address,symmetric,asymmetric,unidirectional,force,strict source,media handover,sip source address,reset,port latching,no rtcp attribute,full rtcp attribute,loop protect,record call,always transcode,all,pad crypto,generate mid,fragment,original sendrecv,symmetric codecs,asymmetric codecs,inject DTMF,generate RTCP,single codec,reorder codecs,pierce NAT')) { defined($options{$x}) and push(@{$packet{flags}}, $x); } for my $x (split(/,/, 'origin,session connection,sdp version,username,session-name,zero-address')) {