<p>keith <strong>merged</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/osmo-sip-connector/+/14994">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Jenkins Builder: Verified
  keith: Looks good to me, approved

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Handle SIP re-INVITEs<br><br>SIP end points can send periodic re-INVITES. Previous to this commit,<br>the osmo-sip-connector would send a new call SETUP to the MSC for each<br>re-INVITE.<br><br>Add a function to find if we already handle this call based on the nua handle.<br>Use this function to detect and respond with an ACK to re-INVITES.<br><br>Add a function to extract the media mode from the SDP.<br>In the case the re-INVITE has a=sendonly (HOLD) respond with a=recvonly<br><br>In the case that the re-INVITE changes the media connection ip/port,<br>forward this to the MNCC side with an MNCC_RTP_CONNECT<br><br>Change-Id: I4083ed50d0cf1b302b80354fe0c2b73fc6e14fed<br>---<br>M src/call.h<br>M src/mncc.c<br>M src/sdp.c<br>M src/sdp.h<br>M src/sip.c<br>5 files changed, 157 insertions(+), 5 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/src/call.h b/src/call.h</span><br><span>index 65d1111..5076c01 100644</span><br><span>--- a/src/call.h</span><br><span>+++ b/src/call.h</span><br><span>@@ -74,6 +74,9 @@</span><br><span>     * A DTMF key was entered. Forward it.</span><br><span>        */</span><br><span>  void (*dtmf)(struct call_leg *, int keypad);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        void (*update_rtp)(struct call_leg *);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> };</span><br><span> </span><br><span> enum sip_cc_state {</span><br><span>diff --git a/src/mncc.c b/src/mncc.c</span><br><span>index ab2bed6..6ee7670 100644</span><br><span>--- a/src/mncc.c</span><br><span>+++ b/src/mncc.c</span><br><span>@@ -198,6 +198,23 @@</span><br><span>        return true;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void update_rtp(struct call_leg *_leg) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      struct mncc_call_leg *leg;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  LOGP(DMNCC, LOGL_DEBUG, "UPDATE RTP with LEG Type (%u)\n", _leg->type);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (_leg->type == CALL_TYPE_MNCC) {</span><br><span style="color: hsl(120, 100%, 40%);">+                leg = (struct mncc_call_leg *) _leg;</span><br><span style="color: hsl(120, 100%, 40%);">+          struct call_leg *other = call_leg_other(&leg->base);</span><br><span style="color: hsl(120, 100%, 40%);">+           send_rtp_connect(leg, other);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              leg = (struct mncc_call_leg *) call_leg_other(_leg);</span><br><span style="color: hsl(120, 100%, 40%);">+          send_rtp_connect(leg, _leg);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* CONNECT call-back for MNCC call leg */</span><br><span> static void mncc_call_leg_connect(struct call_leg *_leg)</span><br><span> {</span><br><span>@@ -482,6 +499,7 @@</span><br><span>         leg->base.connect_call = mncc_call_leg_connect;</span><br><span>   leg->base.ring_call = mncc_call_leg_ring;</span><br><span>         leg->base.release_call = mncc_call_leg_release;</span><br><span style="color: hsl(120, 100%, 40%);">+    leg->base.update_rtp = update_rtp;</span><br><span>        leg->callref = data->callref;</span><br><span>  leg->conn = conn;</span><br><span>         leg->state = MNCC_CC_INITIAL;</span><br><span>@@ -788,6 +806,7 @@</span><br><span>       leg->base.ring_call = mncc_call_leg_ring;</span><br><span>         leg->base.release_call = mncc_call_leg_release;</span><br><span>   leg->base.call = call;</span><br><span style="color: hsl(120, 100%, 40%);">+     leg->base.update_rtp = update_rtp;</span><br><span> </span><br><span>    leg->callref = call->id;</span><br><span> </span><br><span>diff --git a/src/sdp.c b/src/sdp.c</span><br><span>index 9bb55d4..52f7e25 100644</span><br><span>--- a/src/sdp.c</span><br><span>+++ b/src/sdp.c</span><br><span>@@ -33,6 +33,45 @@</span><br><span> #include <string.h></span><br><span> </span><br><span> /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Check if the media mode attribute exists in SDP, in this</span><br><span style="color: hsl(120, 100%, 40%);">+ * case update the passed pointer with the media mode</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+bool sdp_get_sdp_mode(const sip_t *sip, sdp_mode_t *mode) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       const char *sdp_data;</span><br><span style="color: hsl(120, 100%, 40%);">+ sdp_parser_t *parser;</span><br><span style="color: hsl(120, 100%, 40%);">+ sdp_session_t *sdp;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!sip->sip_payload || !sip->sip_payload->pl_data) {</span><br><span style="color: hsl(120, 100%, 40%);">+               LOGP(DSIP, LOGL_ERROR, "No SDP file\n");</span><br><span style="color: hsl(120, 100%, 40%);">+            return false;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   sdp_data = sip->sip_payload->pl_data;</span><br><span style="color: hsl(120, 100%, 40%);">+   parser = sdp_parse(NULL, sdp_data, strlen(sdp_data), sdp_f_mode_0000);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!parser) {</span><br><span style="color: hsl(120, 100%, 40%);">+                LOGP(DSIP, LOGL_ERROR, "Failed to parse SDP\n");</span><br><span style="color: hsl(120, 100%, 40%);">+            return false;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   sdp = sdp_session(parser);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!sdp) {</span><br><span style="color: hsl(120, 100%, 40%);">+           LOGP(DSIP, LOGL_ERROR, "No sdp session\n");</span><br><span style="color: hsl(120, 100%, 40%);">+         sdp_parser_free(parser);</span><br><span style="color: hsl(120, 100%, 40%);">+              return false;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!sdp->sdp_media || !sdp->sdp_media->m_mode) {</span><br><span style="color: hsl(120, 100%, 40%);">+            sdp_parser_free(parser);</span><br><span style="color: hsl(120, 100%, 40%);">+              return sdp_sendrecv;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   sdp_parser_free(parser);</span><br><span style="color: hsl(120, 100%, 40%);">+      *mode = sdp->sdp_media->m_mode;</span><br><span style="color: hsl(120, 100%, 40%);">+ return true;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span>  * We want to decide on the audio codec later but we need to see</span><br><span>  * if it is even including some of the supported ones.</span><br><span>  */</span><br><span>diff --git a/src/sdp.h b/src/sdp.h</span><br><span>index 72ff6b7..8e4e314 100644</span><br><span>--- a/src/sdp.h</span><br><span>+++ b/src/sdp.h</span><br><span>@@ -8,6 +8,7 @@</span><br><span> struct sip_call_leg;</span><br><span> struct call_leg;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+bool sdp_get_sdp_mode(const sip_t *sip, sdp_mode_t *mode);</span><br><span> bool sdp_screen_sdp(const sip_t *sip);</span><br><span> bool sdp_extract_sdp(struct sip_call_leg *leg, const sip_t *sip, bool any_codec);</span><br><span> </span><br><span>diff --git a/src/sip.c b/src/sip.c</span><br><span>index 21401c6..be0d24a 100644</span><br><span>--- a/src/sip.c</span><br><span>+++ b/src/sip.c</span><br><span>@@ -41,6 +41,27 @@</span><br><span> static void sip_connect_call(struct call_leg *_leg);</span><br><span> static void sip_dtmf_call(struct call_leg *_leg, int keypad);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* Find a SIP Call leg by given nua_handle */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct sip_call_leg *sip_find_leg(nua_handle_t *nh)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct call *call;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  llist_for_each_entry(call, &g_call_list, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+         if (call->initial && call->initial->type == CALL_TYPE_SIP) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 struct sip_call_leg *leg = (struct sip_call_leg *) call->initial;</span><br><span style="color: hsl(120, 100%, 40%);">+                  if (leg->nua_handle == nh)</span><br><span style="color: hsl(120, 100%, 40%);">+                         return leg;</span><br><span style="color: hsl(120, 100%, 40%);">+           }</span><br><span style="color: hsl(120, 100%, 40%);">+             if (call->remote && call->remote->type == CALL_TYPE_SIP) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   struct sip_call_leg *leg = (struct sip_call_leg *) call->remote;</span><br><span style="color: hsl(120, 100%, 40%);">+                   if (leg->nua_handle == nh)</span><br><span style="color: hsl(120, 100%, 40%);">+                         return leg;</span><br><span style="color: hsl(120, 100%, 40%);">+           }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static void call_progress(struct sip_call_leg *leg, const sip_t *sip, int status)</span><br><span> {</span><br><span>  struct call_leg *other = call_leg_other(&leg->base);</span><br><span>@@ -149,6 +170,57 @@</span><br><span>                   talloc_strdup(leg, to));</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void sip_handle_reinvite(struct sip_call_leg *leg, nua_handle_t *nh, const sip_t *sip) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  char *sdp;</span><br><span style="color: hsl(120, 100%, 40%);">+    sdp_mode_t mode = sdp_sendrecv;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     LOGP(DSIP, LOGL_NOTICE, "re-INVITE for call %s\n", sip->sip_call_id->i_id);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ struct call_leg *other = call_leg_other(&leg->base);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!sdp_get_sdp_mode(sip, &mode)) {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* re-INVITE with no SDP.</span><br><span style="color: hsl(120, 100%, 40%);">+              * We should respond with SDP reflecting current session</span><br><span style="color: hsl(120, 100%, 40%);">+               */</span><br><span style="color: hsl(120, 100%, 40%);">+           sdp = sdp_create_file(leg, other, sdp_sendrecv);</span><br><span style="color: hsl(120, 100%, 40%);">+              nua_respond(nh, SIP_200_OK,</span><br><span style="color: hsl(120, 100%, 40%);">+                       NUTAG_MEDIA_ENABLE(0),</span><br><span style="color: hsl(120, 100%, 40%);">+                        SIPTAG_CONTENT_TYPE_STR("application/sdp"),</span><br><span style="color: hsl(120, 100%, 40%);">+                         SIPTAG_PAYLOAD_STR(sdp),</span><br><span style="color: hsl(120, 100%, 40%);">+                      TAG_END());</span><br><span style="color: hsl(120, 100%, 40%);">+               talloc_free(sdp);</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (mode == sdp_sendonly) {</span><br><span style="color: hsl(120, 100%, 40%);">+           /* SIP side places call on HOLD */</span><br><span style="color: hsl(120, 100%, 40%);">+            sdp = sdp_create_file(leg, other, sdp_recvonly);</span><br><span style="color: hsl(120, 100%, 40%);">+              /* TODO: Tell core network to stop sending RTP ? */</span><br><span style="color: hsl(120, 100%, 40%);">+   } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* SIP re-INVITE may want to change media, IP, port */</span><br><span style="color: hsl(120, 100%, 40%);">+                if (!sdp_extract_sdp(leg, sip, true)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       LOGP(DSIP, LOGL_ERROR, "leg(%p) no audio, releasing\n", leg);</span><br><span style="color: hsl(120, 100%, 40%);">+                       nua_respond(nh, SIP_406_NOT_ACCEPTABLE, TAG_END());</span><br><span style="color: hsl(120, 100%, 40%);">+                   nua_handle_destroy(nh);</span><br><span style="color: hsl(120, 100%, 40%);">+                       call_leg_release(&leg->base);</span><br><span style="color: hsl(120, 100%, 40%);">+                  return;</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+             if (other->update_rtp)</span><br><span style="color: hsl(120, 100%, 40%);">+                     other->update_rtp(leg->base.call->remote);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+         sdp = sdp_create_file(leg, other, sdp_sendrecv);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   LOGP(DSIP, LOGL_DEBUG, "Sending 200 response to re-INVITE for mode(%u)\n", mode);</span><br><span style="color: hsl(120, 100%, 40%);">+   nua_respond(nh, SIP_200_OK,</span><br><span style="color: hsl(120, 100%, 40%);">+               NUTAG_MEDIA_ENABLE(0),</span><br><span style="color: hsl(120, 100%, 40%);">+                SIPTAG_CONTENT_TYPE_STR("application/sdp"),</span><br><span style="color: hsl(120, 100%, 40%);">+                 SIPTAG_PAYLOAD_STR(sdp),</span><br><span style="color: hsl(120, 100%, 40%);">+              TAG_END());</span><br><span style="color: hsl(120, 100%, 40%);">+       talloc_free(sdp);</span><br><span style="color: hsl(120, 100%, 40%);">+     return;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* Sofia SIP definitions come with error code numbers and strings, this</span><br><span>  * map allows us to reuse the existing definitions.</span><br><span>  * The map is in priority order. The first matching entry found</span><br><span>@@ -235,8 +307,13 @@</span><br><span> </span><br><span>                 if (status == 180 || status == 183)</span><br><span>                  call_progress(leg, sip, status);</span><br><span style="color: hsl(0, 100%, 40%);">-                else if (status == 200)</span><br><span style="color: hsl(0, 100%, 40%);">-                 call_connect(leg, sip);</span><br><span style="color: hsl(120, 100%, 40%);">+               else if (status == 200) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     struct sip_call_leg *leg = sip_find_leg(nh);</span><br><span style="color: hsl(120, 100%, 40%);">+                  if (leg)</span><br><span style="color: hsl(120, 100%, 40%);">+                              nua_ack(leg->nua_handle, TAG_END());</span><br><span style="color: hsl(120, 100%, 40%);">+                       else</span><br><span style="color: hsl(120, 100%, 40%);">+                          call_connect(leg, sip);</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span>            else if (status >= 300) {</span><br><span>                         struct call_leg *other = call_leg_other(&leg->base);</span><br><span> </span><br><span>@@ -251,6 +328,14 @@</span><br><span>                               other->release_call(other);</span><br><span>                       }</span><br><span>            }</span><br><span style="color: hsl(120, 100%, 40%);">+     } else if (event == nua_i_ack) {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* SDP comes back to us in 200 ACK after we</span><br><span style="color: hsl(120, 100%, 40%);">+            * respond to the re-INVITE query. */</span><br><span style="color: hsl(120, 100%, 40%);">+         if (sip->sip_payload && sip->sip_payload->pl_data) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 struct sip_call_leg *leg = sip_find_leg(nh);</span><br><span style="color: hsl(120, 100%, 40%);">+                  if (leg)</span><br><span style="color: hsl(120, 100%, 40%);">+                              sip_handle_reinvite(leg, nh, sip);</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span>    } else if (event == nua_r_bye || event == nua_r_cancel) {</span><br><span>            /* our bye or hang up is answered */</span><br><span>                 struct sip_call_leg *leg = (struct sip_call_leg *) hmagic;</span><br><span>@@ -270,10 +355,15 @@</span><br><span>           if (other)</span><br><span>                   other->release_call(other);</span><br><span>       } else if (event == nua_i_invite) {</span><br><span style="color: hsl(0, 100%, 40%);">-             /* new incoming leg */</span><br><span style="color: hsl(120, 100%, 40%);">+                /* new incoming leg or re-INVITE */</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-         if (status == 100)</span><br><span style="color: hsl(0, 100%, 40%);">-                      new_call((struct sip_agent *) magic, nh, sip);</span><br><span style="color: hsl(120, 100%, 40%);">+                if (status == 100) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  struct sip_call_leg *leg = sip_find_leg(nh);</span><br><span style="color: hsl(120, 100%, 40%);">+                  if (leg)</span><br><span style="color: hsl(120, 100%, 40%);">+                              sip_handle_reinvite(leg, nh, sip);</span><br><span style="color: hsl(120, 100%, 40%);">+                    else</span><br><span style="color: hsl(120, 100%, 40%);">+                          new_call((struct sip_agent *) magic, nh, sip);</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span>    } else if (event == nua_i_cancel) {</span><br><span>          struct sip_call_leg *leg;</span><br><span>            struct call_leg *other;</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-sip-connector/+/14994">change 14994</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/osmo-sip-connector/+/14994"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmo-sip-connector </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I4083ed50d0cf1b302b80354fe0c2b73fc6e14fed </div>
<div style="display:none"> Gerrit-Change-Number: 14994 </div>
<div style="display:none"> Gerrit-PatchSet: 9 </div>
<div style="display:none"> Gerrit-Owner: keith <keith@rhizomatica.org> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: keith <keith@rhizomatica.org> </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@gnumonks.org> </div>
<div style="display:none"> Gerrit-Reviewer: neels <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>