mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-sip-connector.git
				synced 2025-11-03 21:53:38 +00:00 
			
		
		
		
	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
This commit is contained in:
		@@ -74,6 +74,9 @@ struct call_leg {
 | 
			
		||||
	 * A DTMF key was entered. Forward it.
 | 
			
		||||
	 */
 | 
			
		||||
	void (*dtmf)(struct call_leg *, int keypad);
 | 
			
		||||
 | 
			
		||||
	void (*update_rtp)(struct call_leg *);
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum sip_cc_state {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								src/mncc.c
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								src/mncc.c
									
									
									
									
									
								
							@@ -198,6 +198,23 @@ static bool send_rtp_connect(struct mncc_call_leg *leg, struct call_leg *other)
 | 
			
		||||
	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 @@ static void check_setup(struct mncc_connection *conn, const char *buf, int rc)
 | 
			
		||||
	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 @@ int mncc_create_remote_leg(struct mncc_connection *conn, struct call *call)
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										39
									
								
								src/sdp.c
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								src/sdp.c
									
									
									
									
									
								
							@@ -32,6 +32,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.
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										100
									
								
								src/sip.c
									
									
									
									
									
								
							
							
						
						
									
										100
									
								
								src/sip.c
									
									
									
									
									
								
							@@ -41,6 +41,27 @@ static void sip_ring_call(struct call_leg *_leg);
 | 
			
		||||
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 @@ static void new_call(struct sip_agent *agent, nua_handle_t *nh,
 | 
			
		||||
			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 @@ void nua_callback(nua_event_t event, int status, char const *phrase, nua_t *nua,
 | 
			
		||||
 | 
			
		||||
		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 @@ void nua_callback(nua_event_t event, int status, char const *phrase, nua_t *nua,
 | 
			
		||||
				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 @@ void nua_callback(nua_event_t event, int status, char const *phrase, nua_t *nua,
 | 
			
		||||
		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;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user