Commit a38cd80733 for freeswitch.com

commit a38cd8073314f7da29b16743b8dbe52fd8505ba7
Author: Jakub Karolczyk <jakub.karolczyk@signalwire.com>
Date:   Sat Aug 8 15:10:31 2026 +0100

    Merge commit from fork

    * [core] Add protection for RTP inject DoS

    * [core] Wipe malicious packet out

    * [core] Introduce rtp_auto_adjustment_wait_for_advertised_ms chanvar to keep the auto-adjustment window opened for X ms or until packet from the source IP advertised in the SDP is received

    * [core] Perform auto adjustment logic before bytes can be zeroed by flush

    * [core] Add DDoS protection for auto-adjustment window with configurable threshold of packets per ptime from non-advertised source to be rejected. Should be carefully used in bursty environments. Disabled by default.

    * [core] Track auto-adjust packets-per-ptime per source IP; wipe and yield CPU on rate-reject of flooding sources

    * [core] Harden RTP per-source rate-reject: O(1) LRU eviction, dynamic age window, timer-independent ptime, size guards

diff --git a/src/switch_rtp.c b/src/switch_rtp.c
index cf3c55f9d6..9ffcce84e0 100644
--- a/src/switch_rtp.c
+++ b/src/switch_rtp.c
@@ -317,6 +317,49 @@ typedef struct ts_normalize_s {
 	int last_external;
 } ts_normalize_t;

+typedef struct switch_rtp_inject_auto_adj_source_s switch_rtp_inject_auto_adj_source_t;
+
+struct switch_rtp_inject_auto_adj_source_s {
+	int count;
+	int pppt;
+	switch_time_t ts;
+	uint8_t init;
+	switch_time_t last_seen;
+	switch_rtp_inject_auto_adj_source_t *lru_prev;
+	switch_rtp_inject_auto_adj_source_t *lru_next;
+	char key[50];
+};
+
+typedef struct {
+	const char *name;
+	int count;
+	int pps;
+	switch_time_t ts;
+	switch_sockaddr_t *last_address;
+	switch_time_t last_alert_log;
+	uint8_t init;
+	switch_hash_t *auto_adj_sources;
+	switch_rtp_inject_auto_adj_source_t *lru_head;
+	switch_rtp_inject_auto_adj_source_t *lru_tail;
+	uint32_t auto_adj_sources_count;
+} switch_rtp_inject_dos_packet_t;
+
+typedef struct {
+	switch_rtp_inject_dos_packet_t rtp;
+	switch_rtp_inject_dos_packet_t rtcp;
+	switch_rtp_inject_dos_packet_t dtls;
+	switch_rtp_inject_dos_packet_t stun;
+	switch_rtp_inject_dos_packet_t unknown;
+	uint16_t packet_not_advertised_reject_thr;
+} switch_rtp_inject_dos_t;
+
+#define RTP_PACKET_NOT_ADVERTISED_REJECT_THR_MIN 5
+#define RTP_AUTO_ADJ_SOURCE_AGE_LIMIT_MAX_US 5000000
+#define RTP_AUTO_ADJ_SOURCE_AGE_LIMIT_MIN_US 500000
+#define RTP_AUTO_ADJ_SOURCE_PRESSURE_LOW 1024
+#define RTP_AUTO_ADJ_SOURCE_PRESSURE_HIGH 16384
+#define RTP_AUTO_ADJ_SOURCE_AGE_LIMIT_PTIME_FACTOR 4
+
 struct switch_rtp {
 	/*
 	 * Two sockets are needed because we might be transcoding protocol families
@@ -350,6 +393,8 @@ struct switch_rtp {
 	uint32_t autoadj_window;
 	uint32_t autoadj_threshold;
 	uint32_t autoadj_tally;
+	uint32_t autoadj_wait_for_advertised_interval;
+	switch_time_t autoadj_last_ts;

 	uint32_t rtcp_autoadj_window;
 	uint32_t rtcp_autoadj_threshold;
@@ -405,6 +450,7 @@ struct switch_rtp {
 	char *local_host_str;
 	char *remote_host_str;
 	char *eff_remote_host_str;
+	char from_host_str[50];
 	switch_time_t first_stun;
 	switch_time_t last_stun;
 	uint32_t samples_per_interval;
@@ -487,11 +533,13 @@ struct switch_rtp {
 	uint8_t has_rtp;
 	uint8_t has_rtcp;
 	uint8_t has_ice;
+	uint8_t has_dtls;
 	uint8_t punts;
 	uint8_t clean;
 	uint32_t last_max_vb_frames;
 	int skip_timer;
 	uint32_t prev_nacks_inflight;
+	switch_rtp_inject_dos_t inject_dos;
 };

 struct switch_rtcp_report_block {
@@ -4550,6 +4598,18 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_create(switch_rtp_t **new_rtp_session
 	switch_sockaddr_create(&rtp_session->from_addr, pool);
 	switch_sockaddr_create(&rtp_session->rtp_from_addr, pool);

+	switch_sockaddr_create(&rtp_session->inject_dos.rtp.last_address, pool);
+	switch_sockaddr_create(&rtp_session->inject_dos.rtcp.last_address, pool);
+	switch_sockaddr_create(&rtp_session->inject_dos.dtls.last_address, pool);
+	switch_sockaddr_create(&rtp_session->inject_dos.stun.last_address, pool);
+	switch_sockaddr_create(&rtp_session->inject_dos.unknown.last_address, pool);
+
+	rtp_session->inject_dos.rtp.name = "RTP";
+	rtp_session->inject_dos.rtcp.name = "RTCP";
+	rtp_session->inject_dos.dtls.name = "DTLS";
+	rtp_session->inject_dos.stun.name = "STUN";
+	rtp_session->inject_dos.unknown.name = "UNKNOWN";
+
 	if (rtp_session->flags[SWITCH_RTP_FLAG_ENABLE_RTCP]) {
 		switch_sockaddr_create(&rtp_session->rtcp_from_addr, pool);
 	}
@@ -4653,6 +4713,31 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_create(switch_rtp_t **new_rtp_session
 	rtp_session->ready = 1;
 	*new_rtp_session = rtp_session;

+	rtp_session->autoadj_wait_for_advertised_interval = 0;
+	rtp_session->inject_dos.packet_not_advertised_reject_thr = 0;
+
+	if (channel) {
+		const char *var = switch_channel_get_variable(channel, "rtp_auto_adjustment_wait_for_advertised_ms");
+
+		if (!zstr(var) && switch_is_number(var)) {
+			rtp_session->autoadj_wait_for_advertised_interval = atoi(var) * 1000;
+		}
+
+		var = switch_channel_get_variable(channel, "rtp_packet_not_advertised_reject_threshold");
+		if (!zstr(var) && switch_is_number(var)) {
+			int tmp = atoi(var);
+
+			if (tmp >= RTP_PACKET_NOT_ADVERTISED_REJECT_THR_MIN && tmp <= UINT16_MAX) {
+				rtp_session->inject_dos.packet_not_advertised_reject_thr = (uint16_t)tmp;
+				switch_core_hash_init(&rtp_session->inject_dos.rtp.auto_adj_sources);
+			} else if (tmp) {
+				switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_WARNING,
+					"Ignoring rtp_packet_not_advertised_reject_threshold. Value [%d] is out of range [%d, %d]; feature not enabled\n",
+					tmp, RTP_PACKET_NOT_ADVERTISED_REJECT_THR_MIN, UINT16_MAX);
+			}
+		}
+	}
+
 	return SWITCH_STATUS_SUCCESS;
 }

@@ -5358,6 +5443,13 @@ SWITCH_DECLARE(void) switch_rtp_destroy(switch_rtp_t **rtp_session)
 	switch_rtp_release_port((*rtp_session)->rx_host, (*rtp_session)->rx_port);
 	switch_mutex_unlock((*rtp_session)->flag_mutex);

+	if ((*rtp_session)->inject_dos.rtp.auto_adj_sources) {
+		switch_core_hash_destroy(&(*rtp_session)->inject_dos.rtp.auto_adj_sources);
+		(*rtp_session)->inject_dos.rtp.lru_head = NULL;
+		(*rtp_session)->inject_dos.rtp.lru_tail = NULL;
+		(*rtp_session)->inject_dos.rtp.auto_adj_sources_count = 0;
+	}
+
 	return;
 }

@@ -5886,6 +5978,243 @@ static int get_recv_payload(switch_rtp_t *rtp_session)
 	return r;
 }

+static void switch_rtp_inject_dos_alert_log(switch_rtp_t *rtp_session, switch_rtp_inject_dos_packet_t *packet)
+{
+	char host[100] = { 0 };
+	switch_time_t now = switch_time_now();
+
+	if (now - packet->last_alert_log >= 1000000) {
+		switch_print_host(packet->last_address, host, sizeof(host));
+		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_ERROR,
+							"Inject DoS of %s packets detected! PPS: [%d] (last packet from: [%s])\n",
+								packet->name, packet->pps, host);
+
+		packet->last_alert_log = now;
+	}
+
+	return;
+}
+
+#define INJECT_DOS_PPS_RTP_ALERT_THRESHOLD 40000 /* accounts with a surplus for high res, high fps VP8 video */
+#define INJECT_DOS_PPS_OTHER_ALERT_THRESHOLD 250
+#define INJECT_DOS_PPS_STUN_REJECT_THRESHOLD 500 /* we don't expect more STUN packets arrving within a second */
+
+static inline void switch_rtp_inject_dos_wipe_recv_msg(switch_rtp_t *rtp_session, switch_size_t *bytes)
+{
+	memset(&rtp_session->recv_msg, 0, sizeof(rtp_session->recv_msg));
+	*bytes = 0;
+
+	return;
+}
+
+static void switch_rtp_inject_dos_check(switch_rtp_t *rtp_session, switch_rtp_inject_dos_packet_t *packet, switch_bool_t check_only)
+{
+	switch_time_t now = switch_time_now();
+
+	if (!packet->ts) {
+		packet->ts = now;
+	}
+
+	if (!packet->pps && !check_only) {
+		packet->init = 1;
+		packet->ts = now;
+	}
+
+	if (!check_only) {
+		packet->count++;
+		switch_cp_addr(packet->last_address, rtp_session->from_addr);
+	}
+
+	if (now - packet->ts >= 1000000) {
+		packet->pps = packet->count;
+		packet->count = 0;
+		packet->init = 0;
+		packet->ts = now;
+	}
+
+	if (packet->init) {
+		packet->pps = packet->count;
+	}
+
+	return;
+}
+
+static inline void switch_rtp_inject_auto_adj_lru_unlink(switch_rtp_inject_dos_packet_t *packet, switch_rtp_inject_auto_adj_source_t *src)
+{
+	if (src->lru_prev) {
+		src->lru_prev->lru_next = src->lru_next;
+	} else {
+		packet->lru_head = src->lru_next;
+	}
+
+	if (src->lru_next) {
+		src->lru_next->lru_prev = src->lru_prev;
+	} else {
+		packet->lru_tail = src->lru_prev;
+	}
+
+	src->lru_prev = NULL;
+	src->lru_next = NULL;
+}
+
+static inline void switch_rtp_inject_auto_adj_lru_append_tail(switch_rtp_inject_dos_packet_t *packet, switch_rtp_inject_auto_adj_source_t *src)
+{
+	src->lru_prev = packet->lru_tail;
+	src->lru_next = NULL;
+
+	if (packet->lru_tail) {
+		packet->lru_tail->lru_next = src;
+	} else {
+		packet->lru_head = src;
+	}
+
+	packet->lru_tail = src;
+}
+
+static int switch_rtp_inject_auto_adj_check(switch_rtp_t *rtp_session, switch_rtp_inject_dos_packet_t *packet, const char *src_host)
+{
+	switch_time_t now;
+	switch_rtp_inject_auto_adj_source_t *src = NULL;
+	int pppt = 0;
+
+	if (!packet->auto_adj_sources) {
+		return 0;
+	}
+
+	now = switch_time_now();
+
+	src = switch_core_hash_find(packet->auto_adj_sources, src_host);
+	if (!src) {
+		switch_zmalloc(src, sizeof(*src));
+		switch_copy_string(src->key, src_host, sizeof(src->key));
+		if (switch_core_hash_insert_destructor(packet->auto_adj_sources, src->key, src, free) != SWITCH_STATUS_SUCCESS) {
+			free(src);
+
+			return 0;
+		}
+		switch_rtp_inject_auto_adj_lru_append_tail(packet, src);
+		packet->auto_adj_sources_count++;
+	} else if (src != packet->lru_tail) {
+		switch_rtp_inject_auto_adj_lru_unlink(packet, src);
+		switch_rtp_inject_auto_adj_lru_append_tail(packet, src);
+	}
+
+	src->last_seen = now;
+
+	if (!src->ts) {
+		src->ts = now;
+	}
+
+	if (!src->pppt) {
+		src->init = 1;
+		src->ts = now;
+	}
+
+	src->count++;
+
+	if (now - src->ts >= rtp_session->ms_per_packet) {
+		src->pppt = src->count;
+		src->count = 0;
+		src->init = 0;
+		src->ts = now;
+		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG2,
+			"[%d] packets per [%ums] ptime from [%s]. SDP advertised IP: [%s]\n",
+			src->pppt, rtp_session->ms_per_packet / 1000, src_host, rtp_session->remote_host_str);
+	}
+
+	if (src->init) {
+		src->pppt = src->count;
+	}
+
+	pppt = src->pppt;
+
+	return pppt;
+}
+
+static void switch_rtp_inject_auto_adj_cleanup(switch_rtp_inject_dos_packet_t *packet, switch_time_t ptime_us)
+{
+	switch_time_t now;
+	switch_rtp_inject_auto_adj_source_t *src;
+	switch_time_t age_limit;
+	switch_time_t safe_min = 0;
+	uint32_t count;
+
+	if (!packet->auto_adj_sources || packet->auto_adj_sources_count == 0) {
+		return;
+	}
+
+	count = packet->auto_adj_sources_count;
+	if (count <= RTP_AUTO_ADJ_SOURCE_PRESSURE_LOW) {
+		age_limit = RTP_AUTO_ADJ_SOURCE_AGE_LIMIT_MAX_US;
+	} else if (count >= RTP_AUTO_ADJ_SOURCE_PRESSURE_HIGH) {
+		age_limit = RTP_AUTO_ADJ_SOURCE_AGE_LIMIT_MIN_US;
+	} else {
+		age_limit = (switch_time_t)RTP_AUTO_ADJ_SOURCE_AGE_LIMIT_MAX_US -
+			((switch_time_t)(count - RTP_AUTO_ADJ_SOURCE_PRESSURE_LOW) *
+			 ((switch_time_t)RTP_AUTO_ADJ_SOURCE_AGE_LIMIT_MAX_US - (switch_time_t)RTP_AUTO_ADJ_SOURCE_AGE_LIMIT_MIN_US)) /
+			(RTP_AUTO_ADJ_SOURCE_PRESSURE_HIGH - RTP_AUTO_ADJ_SOURCE_PRESSURE_LOW);
+	}
+
+	safe_min = ptime_us * RTP_AUTO_ADJ_SOURCE_AGE_LIMIT_PTIME_FACTOR;
+	if (safe_min > age_limit) {
+		age_limit = safe_min;
+	}
+
+	now = switch_time_now();
+
+	while ((src = packet->lru_head) != NULL && now - src->last_seen > age_limit) {
+		packet->lru_head = src->lru_next;
+		if (packet->lru_head) {
+			packet->lru_head->lru_prev = NULL;
+		} else {
+			packet->lru_tail = NULL;
+		}
+
+		switch_core_hash_delete(packet->auto_adj_sources, src->key);
+		packet->auto_adj_sources_count--;
+	}
+}
+
+static inline void switch_rtp_inject_dos_tick(switch_rtp_t *rtp_session)
+{
+	switch_rtp_inject_dos_check(rtp_session, &rtp_session->inject_dos.rtp, SWITCH_TRUE);
+	switch_rtp_inject_dos_check(rtp_session, &rtp_session->inject_dos.rtcp, SWITCH_TRUE);
+	switch_rtp_inject_dos_check(rtp_session, &rtp_session->inject_dos.dtls, SWITCH_TRUE);
+	switch_rtp_inject_dos_check(rtp_session, &rtp_session->inject_dos.stun, SWITCH_TRUE);
+	switch_rtp_inject_dos_check(rtp_session, &rtp_session->inject_dos.unknown, SWITCH_TRUE);
+
+	if (rtp_session->inject_dos.rtp.pps > INJECT_DOS_PPS_RTP_ALERT_THRESHOLD) {
+		switch_rtp_inject_dos_alert_log(rtp_session, &rtp_session->inject_dos.rtp);
+	}
+
+	if (rtp_session->inject_dos.rtcp.pps > INJECT_DOS_PPS_OTHER_ALERT_THRESHOLD) {
+		switch_rtp_inject_dos_alert_log(rtp_session, &rtp_session->inject_dos.rtcp);
+	}
+
+	if (rtp_session->inject_dos.dtls.pps > INJECT_DOS_PPS_OTHER_ALERT_THRESHOLD) {
+		switch_rtp_inject_dos_alert_log(rtp_session, &rtp_session->inject_dos.dtls);
+	}
+
+	if (rtp_session->inject_dos.stun.pps > INJECT_DOS_PPS_OTHER_ALERT_THRESHOLD) {
+		switch_rtp_inject_dos_alert_log(rtp_session, &rtp_session->inject_dos.stun);
+	}
+
+	if (rtp_session->inject_dos.unknown.pps > INJECT_DOS_PPS_OTHER_ALERT_THRESHOLD) {
+		switch_rtp_inject_dos_alert_log(rtp_session, &rtp_session->inject_dos.unknown);
+	}
+
+	if (rtp_session->inject_dos.packet_not_advertised_reject_thr >= RTP_PACKET_NOT_ADVERTISED_REJECT_THR_MIN) {
+		switch_rtp_inject_auto_adj_cleanup(&rtp_session->inject_dos.rtp, (switch_time_t)rtp_session->ms_per_packet);
+	}
+
+	return;
+}
+
+static switch_bool_t rtp_is_remote_address_advertised_host(switch_rtp_t *rtp_session, const char *tx_host)
+{
+	return (!zstr(rtp_session->remote_host_str) && !strcasecmp(rtp_session->remote_host_str, tx_host));
+}
+
 #define return_cng_frame() do_cng = 1; goto timer_check

 static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t *bytes, switch_frame_flag_t *flags,
@@ -5946,6 +6275,7 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t
 	rtp_session->has_rtp = 0;
 	rtp_session->has_ice = 0;
 	rtp_session->has_rtcp = 0;
+	rtp_session->has_dtls = 0;

 	switch_mutex_lock(rtp_session->ice_mutex);
 	if (rtp_session->dtls) {
@@ -5982,6 +6312,7 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t

 			switch_mutex_unlock(rtp_session->ice_mutex);

+			rtp_session->has_dtls = 1;
 			rtp_session->has_ice = 0;
 			rtp_session->has_rtp = 0;
 			rtp_session->has_rtcp = 0;
@@ -5989,6 +6320,7 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t
 			rtp_session->has_ice = 1;
 			rtp_session->has_rtp = 0;
 			rtp_session->has_rtcp = 0;
+			rtp_session->has_dtls = 0;
 		} else {
 			if (rtp_session->flags[SWITCH_RTP_FLAG_RTCP_MUX]) {
 				switch(rtp_session->recv_msg.header.pt) {
@@ -6004,6 +6336,7 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t
 					rtp_session->has_rtcp = 1;
 					rtp_session->has_rtp = 0;
 					rtp_session->has_ice = 0;
+					rtp_session->has_dtls = 0;
 					break;
 				default:
 					if (rtp_session->rtcp_recv_msg_p->header.version == 2 &&
@@ -6011,12 +6344,130 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t
 						rtp_session->has_rtcp = 1;
 						rtp_session->has_rtp = 0;
 						rtp_session->has_ice = 0;
+						rtp_session->has_dtls = 0;
 					}
 					break;
 				}
 			}
 		}

+		if ((!using_ice(rtp_session) && !(rtp_session->rtp_bugs & RTP_BUG_ACCEPT_ANY_PACKETS)) || using_ice(rtp_session)) {
+
+			if (!switch_cmp_addr(rtp_session->from_addr, rtp_session->remote_addr, SWITCH_FALSE)) {
+				/* got packet which seems to not belong to us, let's make more checks */
+				switch_rtp_inject_dos_packet_t *packet = NULL;
+
+				if ((rtp_session->has_rtp || rtp_session->has_rtcp) && !switch_rtp_test_flag(rtp_session, SWITCH_RTP_FLAG_AUTOADJ)) {
+					/* it's RTP or RTCP (rtcpmux) packet and we aren't in auto-adjustment window anymore. Ignore it! */
+					packet = rtp_session->has_rtp ? &rtp_session->inject_dos.rtp : &rtp_session->inject_dos.rtcp;
+					switch_rtp_inject_dos_check(rtp_session, packet, SWITCH_FALSE);
+					switch_rtp_inject_dos_wipe_recv_msg(rtp_session, bytes);
+
+					return SWITCH_STATUS_NOOP;
+				}
+
+				if ((rtp_session->has_dtls || rtp_session->has_ice) && using_ice(rtp_session)) {
+						/**
+						* check if this packet is being sourced from any of the ICE candidate,
+						* which currently exists on the ICE cand list.
+						* Accept it if exists.
+						**/
+
+					switch_rtp_ice_t *ice = &rtp_session->ice;
+					int i = 0;
+					char tmp_buf[80] = "";
+					const char *from_host = switch_get_addr(tmp_buf, sizeof(tmp_buf), rtp_session->from_addr);
+					uint16_t from_port = switch_sockaddr_get_port(rtp_session->from_addr);
+					int found = 0;
+
+					switch_mutex_lock(rtp_session->ice_mutex);
+
+					for (i = 0; i < ice->ice_params->cand_idx[ice->proto]; i++) {
+						if (!strcmp(ice->ice_params->cands[i][ice->proto].con_addr, from_host) &&
+							ice->ice_params->cands[i][ice->proto].con_port == from_port) {
+
+							/* this packet is already known legit ICE candidate, accept it! */
+							found++;
+							break;
+						}
+					}
+
+					switch_mutex_unlock(rtp_session->ice_mutex);
+
+					if (found) {
+						goto done_inject_dos_checks;
+					}
+
+					if (rtp_session->has_dtls) {
+							/**
+							* further along the path, we only accept DTLS from ICE candidate, which currently
+							* exists on the ICE candidates list. We couldn't find this one, so it must be either
+							* malicious one or came from prflx candidate. prflx must be added to the ICE
+							* cand list by STUN binding prior we start accepting DTLS from it.
+							* Ignore it!
+							**/
+
+						switch_rtp_inject_dos_check(rtp_session, &rtp_session->inject_dos.dtls, SWITCH_FALSE);
+						switch_rtp_inject_dos_wipe_recv_msg(rtp_session, bytes);
+
+						return SWITCH_STATUS_NOOP;
+					}
+
+					if (rtp_session->has_ice) {
+							/**
+							* this can be malicious packet, but it can be also prflx candidate.
+							* Further along the path it's allowed to adjust to prflx candidate,
+							* so we can't simply ignore it here.
+							* Let's check the rate of those packets and decide.
+							**/
+						switch_rtp_inject_dos_check(rtp_session, &rtp_session->inject_dos.stun, SWITCH_FALSE);
+
+						if (rtp_session->inject_dos.stun.pps < INJECT_DOS_PPS_STUN_REJECT_THRESHOLD) {
+							goto done_inject_dos_checks;
+						}
+
+						switch_rtp_inject_dos_wipe_recv_msg(rtp_session, bytes);
+
+						return SWITCH_STATUS_NOOP;
+					}
+				} else if (rtp_session->has_dtls || rtp_session->has_ice) {
+					/* we don't expect STUN/DTLS if not using ICE. Ignore it! */
+					packet = rtp_session->has_dtls ? &rtp_session->inject_dos.dtls : &rtp_session->inject_dos.stun;
+					switch_rtp_inject_dos_check(rtp_session, packet, SWITCH_FALSE);
+					switch_rtp_inject_dos_wipe_recv_msg(rtp_session, bytes);
+
+					return SWITCH_STATUS_NOOP;
+				}
+
+				if (!rtp_session->has_rtp && !rtp_session->has_rtcp) {
+					/* we don't want any other non-rtp packet at all. Ignore it! */
+					switch_rtp_inject_dos_check(rtp_session, &rtp_session->inject_dos.unknown, SWITCH_FALSE);
+					switch_rtp_inject_dos_wipe_recv_msg(rtp_session, bytes);
+
+					return SWITCH_STATUS_NOOP;
+				}
+			}
+
+			if (!using_ice(rtp_session) && rtp_session->has_rtp && switch_rtp_test_flag(rtp_session, SWITCH_RTP_FLAG_AUTOADJ) &&
+					(rtp_session->inject_dos.packet_not_advertised_reject_thr >= RTP_PACKET_NOT_ADVERTISED_REJECT_THR_MIN ||
+					rtp_session->autoadj_wait_for_advertised_interval)) {
+				switch_get_addr(rtp_session->from_host_str, sizeof(rtp_session->from_host_str), rtp_session->from_addr);
+
+				/* being in auto-adjustment window, for packets from a source not advertised in SDP, track per-source packets-per-ptime and discard packets from sources exceeding the threshold */
+				if (rtp_session->inject_dos.packet_not_advertised_reject_thr >= RTP_PACKET_NOT_ADVERTISED_REJECT_THR_MIN &&
+						!rtp_is_remote_address_advertised_host(rtp_session, rtp_session->from_host_str)) {
+					int src_pppt = switch_rtp_inject_auto_adj_check(rtp_session, &rtp_session->inject_dos.rtp, rtp_session->from_host_str);
+
+					if (src_pppt >= rtp_session->inject_dos.packet_not_advertised_reject_thr) {
+						switch_rtp_inject_dos_wipe_recv_msg(rtp_session, bytes);
+						switch_cond_next();
+
+						return SWITCH_STATUS_NOOP;
+					}
+				}
+			}
+		}
+
 		if (rtp_session->has_rtp || rtp_session->flags[SWITCH_RTP_FLAG_UDPTL]) {
 			rtp_session->missed_count = 0;
 			switch_cp_addr(rtp_session->rtp_from_addr, rtp_session->from_addr);
@@ -6074,8 +6525,25 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t
 				}
 			}
 		}
+	} else if (!switch_cmp_addr(rtp_session->from_addr, rtp_session->remote_addr, SWITCH_FALSE) &&
+				!switch_rtp_test_flag(rtp_session, SWITCH_RTP_FLAG_AUTOADJ) &&
+				switch_core_timer_check(&rtp_session->timer, SWITCH_FALSE) == SWITCH_STATUS_FALSE) {
+			/**
+			* nothing has been read from socket. We are not in auto-adjustment window anymore
+			* and previous packet did not belong to us.
+			* Don't process further (esp. don't generate CNG further along the path for RTP).
+			* Come back right away and check for more malicious packets (for no longer than one timer step)
+			* because it can be some villain hitting us!
+			**/
+			switch_cond_next();
+
+		return SWITCH_STATUS_NOOP;
 	}

+ done_inject_dos_checks:
+
+	switch_rtp_inject_dos_tick(rtp_session);
+
 	if (!rtp_session->vb && (!rtp_session->jb || rtp_session->pause_jb || !jb_valid(rtp_session))) {
 		if (*bytes > rtp_header_len && (rtp_session->has_rtp && check_recv_payload(rtp_session))) {
 			xcheck_jitter = *bytes;
@@ -7237,6 +7705,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_
 	int read_loops = 0;
 	int slept = 0;
 	switch_bool_t got_jb = SWITCH_FALSE;
+	switch_bool_t remote_addr_advertised = SWITCH_FALSE;

 	if (!switch_rtp_ready(rtp_session)) {
 		return -1;
@@ -7298,6 +7767,11 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_
 						ret = -1;
 						goto end;
 					}
+
+					if (status == SWITCH_STATUS_NOOP) {
+						continue;
+					}
+
 					if ((*flags & SFF_RTCP)) {
 						*flags &= ~SFF_RTCP;
 						has_rtcp = 1;
@@ -7462,6 +7936,10 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_
 					goto end;
 				}

+				if (status == SWITCH_STATUS_NOOP) {
+					goto recvfrom;
+				}
+
 				if (rtp_session->max_missed_packets && read_loops == 1 && !rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] &&
 					!rtp_session->flags[SWITCH_RTP_FLAG_UDPTL]) {
 					if (bytes && status == SWITCH_STATUS_SUCCESS) {
@@ -7644,46 +8122,6 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_
 			goto end;
 		}

-		check = !bytes;
-
-		if (rtp_session->flags[SWITCH_RTP_FLAG_FLUSH]) {
-			bytes = do_flush(rtp_session, SWITCH_FALSE, bytes);
-			switch_rtp_clear_flag(rtp_session, SWITCH_RTP_FLAG_FLUSH);
-		}
-
-		if ((!bytes && rtp_session->flags[SWITCH_RTP_FLAG_BREAK]) || (bytes && bytes == 4 && *((int *) &rtp_session->recv_msg) == UINT_MAX)) {
-			switch_rtp_clear_flag(rtp_session, SWITCH_RTP_FLAG_BREAK);
-
-			if (!rtp_session->flags[SWITCH_RTP_FLAG_NOBLOCK] || !rtp_session->flags[SWITCH_RTP_FLAG_USE_TIMER] ||
-				rtp_session->flags[SWITCH_RTP_FLAG_PROXY_MEDIA] || rtp_session->flags[SWITCH_RTP_FLAG_UDPTL] ||
-				(bytes && bytes < 5) || (!bytes && poll_loop)) {
-				bytes = 0;
-				reset_jitter_seq(rtp_session);
-				return_cng_frame();
-			}
-		}
-
-		if (bytes && bytes < 5) {
-			continue;
-		}
-
-		if (!bytes && poll_loop) {
-			goto recvfrom;
-		}
-
-		if (bytes && rtp_session->last_rtp_hdr.m && rtp_session->last_rtp_hdr.pt != rtp_session->recv_te &&
-			!rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] &&
-			!rtp_session->flags[SWITCH_RTP_FLAG_TEXT] &&
-			!(rtp_session->rtp_bugs & RTP_BUG_IGNORE_MARK_BIT)) {
-			rtp_flush_read_buffer(rtp_session, SWITCH_RTP_FLUSH_ONCE);
-		}
-
-		if (rtp_session->last_rtp_hdr.pt == rtp_session->cng_pt || rtp_session->last_rtp_hdr.pt == 13) {
-			*flags |= SFF_NOT_AUDIO;
-		} else {
-			*flags &= ~SFF_NOT_AUDIO; /* If this flag was already set, make sure to remove it when we get real audio */
-		}
-
 		/* ignore packets not meant for us unless the auto-adjust window is open (ice mode has its own alternatives to this) */
 		if (!using_ice(rtp_session) && bytes) {
 			if (rtp_session->flags[SWITCH_RTP_FLAG_AUTOADJ]) {
@@ -7696,9 +8134,17 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_
 			}
 		}

-		if (bytes && rtp_session->flags[SWITCH_RTP_FLAG_AUTOADJ] && switch_sockaddr_get_port(rtp_session->rtp_from_addr)) {
+		remote_addr_advertised = SWITCH_FALSE;
+
+		if (rtp_session->autoadj_wait_for_advertised_interval && switch_rtp_test_flag(rtp_session, SWITCH_RTP_FLAG_AUTOADJ) &&
+				!using_ice(rtp_session) && !(rtp_session->rtp_bugs & RTP_BUG_ACCEPT_ANY_PACKETS)) {
+			remote_addr_advertised = rtp_is_remote_address_advertised_host(rtp_session, rtp_session->from_host_str);
+		}
+
+		if (bytes >= 5 && rtp_session->flags[SWITCH_RTP_FLAG_AUTOADJ] && switch_sockaddr_get_port(rtp_session->rtp_from_addr)) {
+
 			if (!switch_cmp_addr(rtp_session->rtp_from_addr, rtp_session->remote_addr, SWITCH_FALSE)) {
-				if (++rtp_session->autoadj_tally >= rtp_session->autoadj_threshold) {
+				if (++rtp_session->autoadj_tally >= rtp_session->autoadj_threshold || (rtp_session->autoadj_wait_for_advertised_interval && remote_addr_advertised)) {
 					const char *err;
 					uint32_t old = rtp_session->eff_remote_port;
 					const char *tx_host;
@@ -7713,6 +8159,8 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_
 									  "Auto Changing %s port from %s:%u to %s:%u\n", rtp_type(rtp_session), old_host, old, tx_host,
 									  switch_sockaddr_get_port(rtp_session->rtp_from_addr));

+					rtp_session->autoadj_last_ts = switch_time_now();
+
 					if (channel) {
 						char varname[80] = "";

@@ -7732,36 +8180,84 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_
 						switch_snprintf(varname, sizeof(varname), "rtp_auto_adjust_%s", rtp_type(rtp_session));
 						switch_channel_set_variable(channel, varname, "true");
 					}
+
 					rtp_session->auto_adj_used = 1;
 					switch_rtp_set_remote_address(rtp_session, tx_host, switch_sockaddr_get_port(rtp_session->rtp_from_addr), 0, SWITCH_FALSE, &err);
 					if ((rtp_session->rtp_bugs & RTP_BUG_ALWAYS_AUTO_ADJUST)) {
 						switch_rtp_set_flag(rtp_session, SWITCH_RTP_FLAG_AUTOADJ);
 						switch_rtp_set_flag(rtp_session, SWITCH_RTP_FLAG_RTCP_AUTOADJ);
-					} else {
+					} else if (!rtp_session->autoadj_wait_for_advertised_interval || remote_addr_advertised) {
 						switch_rtp_clear_flag(rtp_session, SWITCH_RTP_FLAG_AUTOADJ);
 					}
+
 					if (rtp_session->ice.ice_user) {
 						rtp_session->ice.addr = rtp_session->remote_addr;
 					}
 				}
 			} else {
-				if ((rtp_session->rtp_bugs & RTP_BUG_ALWAYS_AUTO_ADJUST)) {
+				if ((rtp_session->rtp_bugs & RTP_BUG_ALWAYS_AUTO_ADJUST) || (rtp_session->autoadj_wait_for_advertised_interval && !remote_addr_advertised)) {
 					switch_rtp_set_flag(rtp_session, SWITCH_RTP_FLAG_AUTOADJ);
 					switch_rtp_set_flag(rtp_session, SWITCH_RTP_FLAG_RTCP_AUTOADJ);
 				} else {
 					switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG, "Correct %s ip/port confirmed.\n", rtp_type(rtp_session));
 					switch_rtp_clear_flag(rtp_session, SWITCH_RTP_FLAG_AUTOADJ);
 				}
+
 				rtp_session->auto_adj_used = 0;
 			}
 		}

-		if (bytes && !(rtp_session->rtp_bugs & RTP_BUG_ALWAYS_AUTO_ADJUST) && rtp_session->autoadj_window) {
-			if (--rtp_session->autoadj_window == 0) {
+		if (bytes >= 5 && rtp_session->flags[SWITCH_RTP_FLAG_AUTOADJ] && !(rtp_session->rtp_bugs & RTP_BUG_ALWAYS_AUTO_ADJUST) && rtp_session->autoadj_window) {
+			if ((!rtp_session->autoadj_wait_for_advertised_interval || remote_addr_advertised) && --rtp_session->autoadj_window == 0) {
 				switch_rtp_clear_flag(rtp_session, SWITCH_RTP_FLAG_AUTOADJ);
+			} else if (rtp_session->autoadj_wait_for_advertised_interval && !remote_addr_advertised && rtp_session->autoadj_last_ts &&
+							switch_time_now() - rtp_session->autoadj_last_ts >= rtp_session->autoadj_wait_for_advertised_interval) {
+
+				switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_INFO, "Closing auto-adjustment window after [%dms]", rtp_session->autoadj_wait_for_advertised_interval / 1000);
+				switch_rtp_clear_flag(rtp_session, SWITCH_RTP_FLAG_AUTOADJ);
+			}
+		}
+
+		check = !bytes;
+
+		if (rtp_session->flags[SWITCH_RTP_FLAG_FLUSH]) {
+			bytes = do_flush(rtp_session, SWITCH_FALSE, bytes);
+			switch_rtp_clear_flag(rtp_session, SWITCH_RTP_FLAG_FLUSH);
+		}
+
+		if ((!bytes && rtp_session->flags[SWITCH_RTP_FLAG_BREAK]) || (bytes && bytes == 4 && *((int *) &rtp_session->recv_msg) == UINT_MAX)) {
+			switch_rtp_clear_flag(rtp_session, SWITCH_RTP_FLAG_BREAK);
+
+			if (!rtp_session->flags[SWITCH_RTP_FLAG_NOBLOCK] || !rtp_session->flags[SWITCH_RTP_FLAG_USE_TIMER] ||
+				rtp_session->flags[SWITCH_RTP_FLAG_PROXY_MEDIA] || rtp_session->flags[SWITCH_RTP_FLAG_UDPTL] ||
+				(bytes && bytes < 5) || (!bytes && poll_loop)) {
+				bytes = 0;
+				reset_jitter_seq(rtp_session);
+				return_cng_frame();
 			}
 		}

+		if (bytes && bytes < 5) {
+			continue;
+		}
+
+		if (!bytes && poll_loop) {
+			goto recvfrom;
+		}
+
+		if (bytes && rtp_session->last_rtp_hdr.m && rtp_session->last_rtp_hdr.pt != rtp_session->recv_te &&
+			!rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] &&
+			!rtp_session->flags[SWITCH_RTP_FLAG_TEXT] &&
+			!(rtp_session->rtp_bugs & RTP_BUG_IGNORE_MARK_BIT)) {
+			rtp_flush_read_buffer(rtp_session, SWITCH_RTP_FLUSH_ONCE);
+		}
+
+		if (rtp_session->last_rtp_hdr.pt == rtp_session->cng_pt || rtp_session->last_rtp_hdr.pt == 13) {
+			*flags |= SFF_NOT_AUDIO;
+		} else {
+			*flags &= ~SFF_NOT_AUDIO; /* If this flag was already set, make sure to remove it when we get real audio */
+		}
+
 		if (rtp_session->flags[SWITCH_RTP_FLAG_TEXT]) {
 			if (!bytes) {
 				if (rtp_session->flags[SWITCH_RTP_FLAG_USE_TIMER]) {