From c7122a7d42dcaea63cad8db5fdede9cdbd638a30 Mon Sep 17 00:00:00 2001
From: Richard Fuchs <rfuchs@sipwise.com>
Date: Mon, 13 Feb 2023 08:26:19 -0500
Subject: [PATCH] MT#56420 suppress port-change for sendonly streams

Don't change to a new port for sendonly streams as this causes problems
with NAT. A device receiving a sendonly SDP with a new port won't send
any RTP to the new port, leading to a closed (non existent) NAT mapping.

Change-Id: I2ea2163eb9f1203226bd781b53f421c790a86f0a
(cherry picked from commit 6d7603e0640407bb7a9231353671aa5bfbb666fe)
---
 daemon/call.c          |   2 +
 t/auto-daemon-tests.pl | 220 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 222 insertions(+)

diff --git a/daemon/call.c b/daemon/call.c
index 5bb41a061..d3b66c6d8 100644
--- a/daemon/call.c
+++ b/daemon/call.c
@@ -999,6 +999,8 @@ static struct endpoint_map *__get_endpoint_map(struct call_media *media, unsigne
 		port_latching = true;
 	else if (MEDIA_ISSET(media, ICE) && (!flags || !flags->no_port_latching))
 		port_latching = true;
+	else if (!MEDIA_ISSET(media, RECV) && (!flags || !flags->no_port_latching))
+		port_latching = true;
 
 	struct endpoint_map *em = NULL;
 	if (port_latching)
diff --git a/t/auto-daemon-tests.pl b/t/auto-daemon-tests.pl
index cff468bb3..d6e6b58f0 100755
--- a/t/auto-daemon-tests.pl
+++ b/t/auto-daemon-tests.pl
@@ -15997,5 +15997,225 @@ SDP
 
 
 
+new_call;
+
+($port_a) = offer('re-invite sendonly port change (control)', { }, <<SDP);
+v=0
+o=- 1545997027 1 IN IP4 198.51.100.1
+s=tester
+c=IN IP4 198.51.100.1
+t=0 0
+m=audio 2000 RTP/AVP 0
+----------------------------
+v=0
+o=- 1545997027 1 IN IP4 198.51.100.1
+s=tester
+c=IN IP4 203.0.113.1
+t=0 0
+m=audio PORT RTP/AVP 0
+a=rtpmap:0 PCMU/8000
+a=sendrecv
+a=rtcp:PORT
+SDP
+
+($port_b) = offer('re-invite sendonly port change (control)', { }, <<SDP);
+v=0
+o=- 1545997027 1 IN IP4 198.51.100.1
+s=tester
+c=IN IP4 198.51.100.1
+t=0 0
+m=audio 2002 RTP/AVP 0
+----------------------------
+v=0
+o=- 1545997027 1 IN IP4 198.51.100.1
+s=tester
+c=IN IP4 203.0.113.1
+t=0 0
+m=audio PORT RTP/AVP 0
+a=rtpmap:0 PCMU/8000
+a=sendrecv
+a=rtcp:PORT
+SDP
+
+isnt($port_a, $port_b, 'port changed');
+
+
+
+new_call;
+
+($port_a) = offer('re-invite sendonly port change', { }, <<SDP);
+v=0
+o=- 1545997027 1 IN IP4 198.51.100.1
+s=tester
+c=IN IP4 198.51.100.1
+t=0 0
+m=audio 2000 RTP/AVP 0
+----------------------------
+v=0
+o=- 1545997027 1 IN IP4 198.51.100.1
+s=tester
+c=IN IP4 203.0.113.1
+t=0 0
+m=audio PORT RTP/AVP 0
+a=rtpmap:0 PCMU/8000
+a=sendrecv
+a=rtcp:PORT
+SDP
+
+($port_ax) = answer('re-invite sendonly port change', { }, <<SDP);
+v=0
+o=- 1545997027 1 IN IP4 198.51.100.3
+s=tester
+t=0 0
+m=audio 2010 RTP/AVP 0
+c=IN IP4 198.51.100.3
+a=sendrecv
+--------------------------------------
+v=0
+o=- 1545997027 1 IN IP4 198.51.100.3
+s=tester
+t=0 0
+m=audio PORT RTP/AVP 0
+c=IN IP4 203.0.113.1
+a=rtpmap:0 PCMU/8000
+a=sendrecv
+a=rtcp:PORT
+SDP
+
+($port_b) = offer('re-invite sendonly port change', { }, <<SDP);
+v=0
+o=- 1545997027 1 IN IP4 198.51.100.1
+s=tester
+c=IN IP4 198.51.100.1
+t=0 0
+m=audio 2002 RTP/AVP 0
+a=sendonly
+----------------------------
+v=0
+o=- 1545997027 1 IN IP4 198.51.100.1
+s=tester
+c=IN IP4 203.0.113.1
+t=0 0
+m=audio PORT RTP/AVP 0
+a=rtpmap:0 PCMU/8000
+a=sendonly
+a=rtcp:PORT
+SDP
+
+is($port_a, $port_b, 'port not changed');
+
+($port_bx) = answer('re-invite sendonly port change', { }, <<SDP);
+v=0
+o=- 1545997027 1 IN IP4 198.51.100.3
+s=tester
+t=0 0
+m=audio 2012 RTP/AVP 0
+c=IN IP4 198.51.100.3
+a=recvonly
+--------------------------------------
+v=0
+o=- 1545997027 1 IN IP4 198.51.100.3
+s=tester
+t=0 0
+m=audio PORT RTP/AVP 0
+c=IN IP4 203.0.113.1
+a=rtpmap:0 PCMU/8000
+a=recvonly
+a=rtcp:PORT
+SDP
+
+isnt($port_ax, $port_bx, 'port changed');
+
+($port_b) = offer('re-invite sendonly port change', { }, <<SDP);
+v=0
+o=- 1545997027 1 IN IP4 198.51.100.1
+s=tester
+c=IN IP4 198.51.100.1
+t=0 0
+m=audio 2000 RTP/AVP 0
+----------------------------
+v=0
+o=- 1545997027 1 IN IP4 198.51.100.1
+s=tester
+c=IN IP4 203.0.113.1
+t=0 0
+m=audio PORT RTP/AVP 0
+a=rtpmap:0 PCMU/8000
+a=sendrecv
+a=rtcp:PORT
+SDP
+
+is($port_a, $port_b, 'original port');
+
+($port_bx) = answer('re-invite sendonly port change', { }, <<SDP);
+v=0
+o=- 1545997027 1 IN IP4 198.51.100.3
+s=tester
+t=0 0
+m=audio 2010 RTP/AVP 0
+c=IN IP4 198.51.100.3
+a=sendrecv
+--------------------------------------
+v=0
+o=- 1545997027 1 IN IP4 198.51.100.3
+s=tester
+t=0 0
+m=audio PORT RTP/AVP 0
+c=IN IP4 203.0.113.1
+a=rtpmap:0 PCMU/8000
+a=sendrecv
+a=rtcp:PORT
+SDP
+
+is($port_ax, $port_bx, 'original port');
+
+reverse_tags();
+
+($port_bx) = offer('re-invite sendonly port change', { }, <<SDP);
+v=0
+o=- 1545997027 1 IN IP4 198.51.100.3
+s=tester
+t=0 0
+m=audio 2012 RTP/AVP 0
+c=IN IP4 198.51.100.3
+a=sendonly
+--------------------------------------
+v=0
+o=- 1545997027 1 IN IP4 198.51.100.3
+s=tester
+t=0 0
+m=audio PORT RTP/AVP 0
+c=IN IP4 203.0.113.1
+a=rtpmap:0 PCMU/8000
+a=sendonly
+a=rtcp:PORT
+SDP
+
+is($port_ax, $port_bx, 'port unchanged');
+
+($port_b) = answer('re-invite sendonly port change', { }, <<SDP);
+v=0
+o=- 1545997027 1 IN IP4 198.51.100.1
+s=tester
+c=IN IP4 198.51.100.1
+t=0 0
+m=audio 2000 RTP/AVP 0
+a=recvonly
+----------------------------
+v=0
+o=- 1545997027 1 IN IP4 198.51.100.1
+s=tester
+c=IN IP4 203.0.113.1
+t=0 0
+m=audio PORT RTP/AVP 0
+a=rtpmap:0 PCMU/8000
+a=recvonly
+a=rtcp:PORT
+SDP
+
+is($port_a, $port_b, 'port unchanged');
+
+
+
 #done_testing;NGCP::Rtpengine::AutoTest::terminate('f00');exit;
 done_testing();