diff --git a/README.md b/README.md index 0975f8ace..3a66692bb 100644 --- a/README.md +++ b/README.md @@ -824,6 +824,14 @@ Optionally included keys are: as no incoming packets have been received. Useful to create an initial NAT mapping. Not needed when ICE is in use. + - `NAT-wait` + + Prevents forwarding media packets to the respective endpoint + until at least one media packet has been received from that + endpoint. This is to allow a NAT binding to open in the ingress + direction before sending packets out, which could result in an + automated firewall block. + - `trickle ICE` Useful for `offer` messages when ICE as advertised to also advertise diff --git a/daemon/call.c b/daemon/call.c index b3d00b7a1..d104c2ecf 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -1060,6 +1060,8 @@ static void __fill_stream(struct packet_stream *ps, const struct endpoint *epp, if (flags && flags->pierce_nat) PS_SET(ps, PIERCE_NAT); + if (flags && flags->nat_wait) + PS_SET(ps, NAT_WAIT); } void call_stream_crypto_reset(struct packet_stream *ps) { diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index a26fd083e..54c58213b 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -900,6 +900,9 @@ static void call_ng_flags_flags(struct sdp_ng_flags *out, str *s, void *dummy) { case CSH_LOOKUP("pierce-NAT"): out->pierce_nat = 1; break; + case CSH_LOOKUP("NAT-wait"): + out->nat_wait = 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/daemon/media_socket.c b/daemon/media_socket.c index 69bfc802d..e519a6d7f 100644 --- a/daemon/media_socket.c +++ b/daemon/media_socket.c @@ -1326,6 +1326,9 @@ void kernelize(struct packet_stream *stream) { else { for (GList *l = sinks->head; l; l = l->next) { struct sink_handler *sh = l->data; + struct packet_stream *sink = sh->sink; + if (PS_ISSET(sink, NAT_WAIT) && !PS_ISSET(sink, RECEIVED)) + continue; const char *err = kernelize_one(&reti, &outputs, stream, sh, sinks); if (err) ilog(LOG_WARNING, "No support for kernel packet forwarding available (%s)", err); @@ -1939,6 +1942,8 @@ static int media_packet_address_check(struct packet_handler_ctx *phc) } } + PS_SET(phc->mp.stream, RECEIVED); + /* do not pay attention to source addresses of incoming packets for asymmetric streams */ if (MEDIA_ISSET(phc->mp.media, ASYMMETRIC) || rtpe_config.endpoint_learning == EL_OFF) PS_SET(phc->mp.stream, CONFIRMED); @@ -2328,6 +2333,13 @@ static int stream_packet(struct packet_handler_ctx *phc) { goto next; } + if (PS_ISSET(sh->sink, NAT_WAIT) && !PS_ISSET(sh->sink, RECEIVED)) { + ilog(LOG_DEBUG | LOG_FLAG_LIMIT, + "Media packet from %s%s%s discarded due to `NAT-wait` flag", + FMT_M(endpoint_print_buf(&phc->mp.fsin))); + goto next; + } + if (G_UNLIKELY(!sh->sink->selected_sfd || !phc->out_srtp || !phc->out_srtp->selected_sfd || !phc->in_srtp->selected_sfd)) { diff --git a/include/call.h b/include/call.h index 07ff84953..5a28b81ca 100644 --- a/include/call.h +++ b/include/call.h @@ -134,7 +134,7 @@ enum { #define PS_FLAG_RTCP 0x00020000 #define PS_FLAG_IMPLICIT_RTCP SHARED_FLAG_IMPLICIT_RTCP #define PS_FLAG_FALLBACK_RTCP 0x00040000 -#define PS_FLAG_UNUSED2 0x00080000 +#define PS_FLAG_RECEIVED 0x00080000 #define PS_FLAG_FILLED 0x00100000 #define PS_FLAG_CONFIRMED 0x00200000 #define PS_FLAG_KERNELIZED 0x00400000 @@ -146,6 +146,7 @@ enum { #define PS_FLAG_ICE SHARED_FLAG_ICE #define PS_FLAG_ZERO_ADDR 0x04000000 #define PS_FLAG_PIERCE_NAT 0x08000000 +#define PS_FLAG_NAT_WAIT 0x10000000 /* struct call_media */ #define MEDIA_FLAG_INITIALIZED 0x00010000 diff --git a/include/call_interfaces.h b/include/call_interfaces.h index 99156263f..2d17cd889 100644 --- a/include/call_interfaces.h +++ b/include/call_interfaces.h @@ -144,6 +144,7 @@ struct sdp_ng_flags { passthrough_on:1, passthrough_off:1, disable_jb:1, + nat_wait:1, pierce_nat:1; };