Change in ...osmo-sip-connector[master]: Handle SIP re-INVITEs

keith gerrit-no-reply at lists.osmocom.org
Mon Aug 5 17:11:42 UTC 2019


keith has submitted this change and it was merged. ( https://gerrit.osmocom.org/c/osmo-sip-connector/+/14994 )

Change subject: Handle SIP re-INVITEs
......................................................................

Handle SIP re-INVITEs

SIP end points can send periodic re-INVITES. Previous to this commit,
the osmo-sip-connector would send a new call SETUP to the MSC for each
re-INVITE.

Add a function to find if we already handle this call based on the nua handle.
Use this function to detect and respond with an ACK to re-INVITES.

Add a function to extract the media mode from the SDP.
In the case the re-INVITE has a=sendonly (HOLD) respond with a=recvonly

In the case that the re-INVITE changes the media connection ip/port,
forward this to the MNCC side with an MNCC_RTP_CONNECT

Change-Id: I4083ed50d0cf1b302b80354fe0c2b73fc6e14fed
---
M src/call.h
M src/mncc.c
M src/sdp.c
M src/sdp.h
M src/sip.c
5 files changed, 157 insertions(+), 5 deletions(-)

Approvals:
  Jenkins Builder: Verified
  keith: Looks good to me, approved



diff --git a/src/call.h b/src/call.h
index 65d1111..5076c01 100644
--- a/src/call.h
+++ b/src/call.h
@@ -74,6 +74,9 @@
 	 * A DTMF key was entered. Forward it.
 	 */
 	void (*dtmf)(struct call_leg *, int keypad);
+
+	void (*update_rtp)(struct call_leg *);
+
 };
 
 enum sip_cc_state {
diff --git a/src/mncc.c b/src/mncc.c
index ab2bed6..6ee7670 100644
--- a/src/mncc.c
+++ b/src/mncc.c
@@ -198,6 +198,23 @@
 	return true;
 }
 
+static void update_rtp(struct call_leg *_leg) {
+
+	struct mncc_call_leg *leg;
+
+	LOGP(DMNCC, LOGL_DEBUG, "UPDATE RTP with LEG Type (%u)\n", _leg->type);
+
+	if (_leg->type == CALL_TYPE_MNCC) {
+		leg = (struct mncc_call_leg *) _leg;
+		struct call_leg *other = call_leg_other(&leg->base);
+		send_rtp_connect(leg, other);
+	} else {
+		leg = (struct mncc_call_leg *) call_leg_other(_leg);
+		send_rtp_connect(leg, _leg);
+	}
+}
+
+
 /* CONNECT call-back for MNCC call leg */
 static void mncc_call_leg_connect(struct call_leg *_leg)
 {
@@ -482,6 +499,7 @@
 	leg->base.connect_call = mncc_call_leg_connect;
 	leg->base.ring_call = mncc_call_leg_ring;
 	leg->base.release_call = mncc_call_leg_release;
+	leg->base.update_rtp = update_rtp;
 	leg->callref = data->callref;
 	leg->conn = conn;
 	leg->state = MNCC_CC_INITIAL;
@@ -788,6 +806,7 @@
 	leg->base.ring_call = mncc_call_leg_ring;
 	leg->base.release_call = mncc_call_leg_release;
 	leg->base.call = call;
+	leg->base.update_rtp = update_rtp;
 
 	leg->callref = call->id;
 
diff --git a/src/sdp.c b/src/sdp.c
index 9bb55d4..52f7e25 100644
--- a/src/sdp.c
+++ b/src/sdp.c
@@ -33,6 +33,45 @@
 #include <string.h>
 
 /*
+ * Check if the media mode attribute exists in SDP, in this
+ * case update the passed pointer with the media mode
+ */
+bool sdp_get_sdp_mode(const sip_t *sip, sdp_mode_t *mode) {
+
+	const char *sdp_data;
+	sdp_parser_t *parser;
+	sdp_session_t *sdp;
+
+	if (!sip->sip_payload || !sip->sip_payload->pl_data) {
+		LOGP(DSIP, LOGL_ERROR, "No SDP file\n");
+		return false;
+	}
+
+	sdp_data = sip->sip_payload->pl_data;
+	parser = sdp_parse(NULL, sdp_data, strlen(sdp_data), sdp_f_mode_0000);
+	if (!parser) {
+		LOGP(DSIP, LOGL_ERROR, "Failed to parse SDP\n");
+		return false;
+	}
+
+	sdp = sdp_session(parser);
+	if (!sdp) {
+		LOGP(DSIP, LOGL_ERROR, "No sdp session\n");
+		sdp_parser_free(parser);
+		return false;
+	}
+
+	if (!sdp->sdp_media || !sdp->sdp_media->m_mode) {
+		sdp_parser_free(parser);
+		return sdp_sendrecv;
+	}
+
+	sdp_parser_free(parser);
+	*mode = sdp->sdp_media->m_mode;
+	return true;
+}
+
+/*
  * We want to decide on the audio codec later but we need to see
  * if it is even including some of the supported ones.
  */
diff --git a/src/sdp.h b/src/sdp.h
index 72ff6b7..8e4e314 100644
--- a/src/sdp.h
+++ b/src/sdp.h
@@ -8,6 +8,7 @@
 struct sip_call_leg;
 struct call_leg;
 
+bool sdp_get_sdp_mode(const sip_t *sip, sdp_mode_t *mode);
 bool sdp_screen_sdp(const sip_t *sip);
 bool sdp_extract_sdp(struct sip_call_leg *leg, const sip_t *sip, bool any_codec);
 
diff --git a/src/sip.c b/src/sip.c
index 21401c6..be0d24a 100644
--- a/src/sip.c
+++ b/src/sip.c
@@ -41,6 +41,27 @@
 static void sip_connect_call(struct call_leg *_leg);
 static void sip_dtmf_call(struct call_leg *_leg, int keypad);
 
+/* Find a SIP Call leg by given nua_handle */
+static struct sip_call_leg *sip_find_leg(nua_handle_t *nh)
+{
+	struct call *call;
+
+	llist_for_each_entry(call, &g_call_list, entry) {
+		if (call->initial && call->initial->type == CALL_TYPE_SIP) {
+			struct sip_call_leg *leg = (struct sip_call_leg *) call->initial;
+			if (leg->nua_handle == nh)
+				return leg;
+		}
+		if (call->remote && call->remote->type == CALL_TYPE_SIP) {
+			struct sip_call_leg *leg = (struct sip_call_leg *) call->remote;
+			if (leg->nua_handle == nh)
+				return leg;
+		}
+	}
+
+	return NULL;
+}
+
 static void call_progress(struct sip_call_leg *leg, const sip_t *sip, int status)
 {
 	struct call_leg *other = call_leg_other(&leg->base);
@@ -149,6 +170,57 @@
 			talloc_strdup(leg, to));
 }
 
+static void sip_handle_reinvite(struct sip_call_leg *leg, nua_handle_t *nh, const sip_t *sip) {
+
+	char *sdp;
+	sdp_mode_t mode = sdp_sendrecv;
+
+	LOGP(DSIP, LOGL_NOTICE, "re-INVITE for call %s\n", sip->sip_call_id->i_id);
+
+	struct call_leg *other = call_leg_other(&leg->base);
+	if (!sdp_get_sdp_mode(sip, &mode)) {
+		/* re-INVITE with no SDP.
+		 * We should respond with SDP reflecting current session
+		 */
+		sdp = sdp_create_file(leg, other, sdp_sendrecv);
+		nua_respond(nh, SIP_200_OK,
+			    NUTAG_MEDIA_ENABLE(0),
+			    SIPTAG_CONTENT_TYPE_STR("application/sdp"),
+			    SIPTAG_PAYLOAD_STR(sdp),
+			    TAG_END());
+		talloc_free(sdp);
+		return;
+	}
+
+	if (mode == sdp_sendonly) {
+		/* SIP side places call on HOLD */
+		sdp = sdp_create_file(leg, other, sdp_recvonly);
+		/* TODO: Tell core network to stop sending RTP ? */
+	} else {
+		/* SIP re-INVITE may want to change media, IP, port */
+		if (!sdp_extract_sdp(leg, sip, true)) {
+			LOGP(DSIP, LOGL_ERROR, "leg(%p) no audio, releasing\n", leg);
+			nua_respond(nh, SIP_406_NOT_ACCEPTABLE, TAG_END());
+			nua_handle_destroy(nh);
+			call_leg_release(&leg->base);
+			return;
+		}
+		if (other->update_rtp)
+			other->update_rtp(leg->base.call->remote);
+
+		sdp = sdp_create_file(leg, other, sdp_sendrecv);
+	}
+
+	LOGP(DSIP, LOGL_DEBUG, "Sending 200 response to re-INVITE for mode(%u)\n", mode);
+	nua_respond(nh, SIP_200_OK,
+		    NUTAG_MEDIA_ENABLE(0),
+		    SIPTAG_CONTENT_TYPE_STR("application/sdp"),
+		    SIPTAG_PAYLOAD_STR(sdp),
+		    TAG_END());
+	talloc_free(sdp);
+	return;
+}
+
 /* Sofia SIP definitions come with error code numbers and strings, this
  * map allows us to reuse the existing definitions.
  * The map is in priority order. The first matching entry found
@@ -235,8 +307,13 @@
 
 		if (status == 180 || status == 183)
 			call_progress(leg, sip, status);
-		else if (status == 200)
-			call_connect(leg, sip);
+		else if (status == 200) {
+			struct sip_call_leg *leg = sip_find_leg(nh);
+			if (leg)
+				nua_ack(leg->nua_handle, TAG_END());
+			else
+				call_connect(leg, sip);
+		}
 		else if (status >= 300) {
 			struct call_leg *other = call_leg_other(&leg->base);
 
@@ -251,6 +328,14 @@
 				other->release_call(other);
 			}
 		}
+	} else if (event == nua_i_ack) {
+		/* SDP comes back to us in 200 ACK after we
+		 * respond to the re-INVITE query. */
+		if (sip->sip_payload && sip->sip_payload->pl_data) {
+			struct sip_call_leg *leg = sip_find_leg(nh);
+			if (leg)
+				sip_handle_reinvite(leg, nh, sip);
+		}
 	} else if (event == nua_r_bye || event == nua_r_cancel) {
 		/* our bye or hang up is answered */
 		struct sip_call_leg *leg = (struct sip_call_leg *) hmagic;
@@ -270,10 +355,15 @@
 		if (other)
 			other->release_call(other);
 	} else if (event == nua_i_invite) {
-		/* new incoming leg */
+		/* new incoming leg or re-INVITE */
 
-		if (status == 100)
-			new_call((struct sip_agent *) magic, nh, sip);
+		if (status == 100) {
+			struct sip_call_leg *leg = sip_find_leg(nh);
+			if (leg)
+				sip_handle_reinvite(leg, nh, sip);
+			else
+				new_call((struct sip_agent *) magic, nh, sip);
+		}
 	} else if (event == nua_i_cancel) {
 		struct sip_call_leg *leg;
 		struct call_leg *other;

-- 
To view, visit https://gerrit.osmocom.org/c/osmo-sip-connector/+/14994
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-sip-connector
Gerrit-Branch: master
Gerrit-Change-Id: I4083ed50d0cf1b302b80354fe0c2b73fc6e14fed
Gerrit-Change-Number: 14994
Gerrit-PatchSet: 9
Gerrit-Owner: keith <keith at rhizomatica.org>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: keith <keith at rhizomatica.org>
Gerrit-Reviewer: laforge <laforge at gnumonks.org>
Gerrit-Reviewer: neels <nhofmeyr at sysmocom.de>
Gerrit-Reviewer: pespin <pespin at sysmocom.de>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20190805/2d55856d/attachment.html>


More information about the gerrit-log mailing list