send + receive SDP via MNCC

 CC               MNCC
 | ---ALERTING--> | add SDP to MNCC msg
 | <--ALERTING--- | store remote side SDP
 | <--SETUP-RESP- | store remote side SDP
 | --SETUP-CNF--> | add SDP to MNCC msg
 | -RTP-CREATE--> | use codec_filter, add SDP
 | <-RTP-CONNECT- | store remote SDP

Related: SYS#5066
Change-Id: Ie0668c0e079ec69da1532b52d00621efe114fc2c
diff --git a/src/libmsc/gsm_04_08_cc.c b/src/libmsc/gsm_04_08_cc.c
index 63b83d0..328f1cf 100644
--- a/src/libmsc/gsm_04_08_cc.c
+++ b/src/libmsc/gsm_04_08_cc.c
@@ -515,6 +515,10 @@
 	unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
 	struct tlv_parsed tp;
 	struct gsm_mncc setup;
+	struct osmo_sockaddr_str *rtp_cn_local;
+	struct sdp_msg *sdp;
+	struct msc_a *msc_a = trans->msc_a;
+	int rc;
 
 	gsm48_start_guard_timer(trans);
 
@@ -624,6 +628,27 @@
 
 	rate_ctr_inc(rate_ctr_group_get_ctr(trans->net->msc_ctrs, MSC_CTR_CALL_MO_SETUP));
 
+	/* Insert the CN side RTP port now available into SDP and compose SDP string */
+	rtp_cn_local = call_leg_local_ip(msc_a->cc.call_leg, RTP_TO_CN);
+	if (!osmo_sockaddr_str_is_nonzero(rtp_cn_local)) {
+		LOG_TRANS(trans, LOGL_ERROR, "Cannot compose SDP for MNCC_SETUP_IND: no RTP set up for the CN side\n");
+		/* FIXME: re-add below two lines as soon as CN CRCX is done first
+		 * (see Change-Id Ie433db1ba0c46d4b97538a969233c155cefac21c).
+		 * keeping it non-fatal for the moment, for tests to continue succeeding. */
+		//trans_free(trans);
+		//return -EINVAL;
+	}
+
+	codec_filter_set_local_rtp(&trans->cc.codecs, rtp_cn_local);
+	codec_filter_run(&trans->cc.codecs);
+	sdp = trans->cc.codecs.result.audio_codecs.count ? &trans->cc.codecs.result : NULL;
+	rc = sdp_msg_to_sdp_str_buf(setup.sdp, sizeof(setup.sdp), sdp);
+	if (rc >= sizeof(setup.sdp)) {
+		LOG_TRANS(trans, LOGL_ERROR, "MNCC_SETUP_IND: SDP too long (%d > %zu bytes)\n", rc, sizeof(setup.sdp));
+		trans_free(trans);
+		return -EINVAL;
+	}
+
 	/* indicate setup to MNCC */
 	mncc_recvmsg(trans->net, trans, MNCC_SETUP_IND, &setup);
 
@@ -843,6 +868,7 @@
 	unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
 	struct tlv_parsed tp;
 	struct gsm_mncc alerting;
+	int rc;
 
 	gsm48_stop_cc_timer(trans);
 	gsm48_start_cc_timer(trans, 0x301, GSM48_T301);
@@ -872,6 +898,16 @@
 
 	new_cc_state(trans, GSM_CSTATE_CALL_RECEIVED);
 
+	codec_filter_run(&trans->cc.codecs);
+	LOG_TRANS(trans, LOGL_DEBUG, "codecs: %s\n", codec_filter_to_str(&trans->cc.codecs));
+	rc = sdp_msg_to_sdp_str_buf(alerting.sdp, sizeof(alerting.sdp), &trans->cc.codecs.result);
+	if (rc >= sizeof(alerting.sdp)) {
+		LOG_TRANS(trans, LOGL_ERROR, "MNCC_ALERT_IND: SDP too long (%d > %zu bytes)\n",
+			  rc, sizeof(alerting.sdp));
+		trans_free(trans);
+		return -EINVAL;
+	}
+
 	return mncc_recvmsg(trans->net, trans, MNCC_ALERT_IND,
 			    &alerting);
 }
@@ -896,6 +932,19 @@
 
 	new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED);
 
+	if (alerting->sdp[0]) {
+		struct call_leg *cl = trans->msc_a->cc.call_leg;
+		struct rtp_stream *rtp_cn = cl ? cl->rtp[RTP_TO_CN] : NULL;
+		codec_filter_set_remote(&trans->cc.codecs, alerting->sdp);
+		codec_filter_run(&trans->cc.codecs);
+		LOG_TRANS(trans, LOGL_DEBUG, "%s codecs: %s\n",
+			  get_mncc_name(alerting->msg_type), codec_filter_to_str(&trans->cc.codecs));
+		if (rtp_cn) {
+			rtp_stream_set_remote_addr_and_codecs(rtp_cn, &trans->cc.codecs.remote);
+			rtp_stream_commit(rtp_cn);
+		}
+	}
+
 	return trans_tx_gsm48(trans, msg);
 }
 
@@ -942,6 +991,20 @@
 
 	new_cc_state(trans, GSM_CSTATE_CONNECT_IND);
 
+	/* Received an MNCC_SETUP_RSP with the remote leg's SDP information. Apply codec choice. */
+	if (connect->sdp[0]) {
+		struct call_leg *cl = trans->msc_a->cc.call_leg;
+		struct rtp_stream *rtp_cn = cl ? cl->rtp[RTP_TO_CN] : NULL;
+		sdp_msg_from_sdp_str(&trans->cc.codecs.remote, connect->sdp);
+		LOG_TRANS(trans, LOGL_DEBUG, "%s codecs: %s\n",
+			  get_mncc_name(connect->msg_type),
+			  codec_filter_to_str(&trans->cc.codecs));
+		if (rtp_cn) {
+			rtp_stream_set_remote_addr_and_codecs(rtp_cn, &trans->cc.codecs.remote);
+			rtp_stream_commit(rtp_cn);
+		}
+	}
+
 	return trans_tx_gsm48(trans, msg);
 }
 
@@ -984,6 +1047,8 @@
 	new_cc_state(trans, GSM_CSTATE_CONNECT_REQUEST);
 	rate_ctr_inc(rate_ctr_group_get_ctr(trans->net->msc_ctrs, MSC_CTR_CALL_MT_CONNECT));
 
+	codec_filter_run(&trans->cc.codecs);
+	sdp_msg_to_sdp_str_buf(connect.sdp, sizeof(connect.sdp), &trans->cc.codecs.result);
 	return mncc_recvmsg(trans->net, trans, MNCC_SETUP_CNF, &connect);
 }
 
@@ -1790,7 +1855,7 @@
 	}
 
 	return mncc_recv_rtp(net, trans, trans->callref, MNCC_RTP_CREATE, rtp_cn_local,
-			     codec->payload_type, mncc_payload_msg_type, NULL);
+			     codec->payload_type, mncc_payload_msg_type, &trans->cc.codecs.result);
 }
 
 static int tch_rtp_connect(struct gsm_network *net, const struct gsm_mncc_rtp *rtp)
@@ -1798,7 +1863,6 @@
 	struct gsm_trans *trans;
 	struct call_leg *cl;
 	struct rtp_stream *rtps;
-	struct osmo_sockaddr_str rtp_addr;
 	char ipbuf[INET6_ADDRSTRLEN];
 
 	/* FIXME: in *rtp we should get the codec information of the remote
@@ -1838,12 +1902,26 @@
 		return -EINVAL;
 	}
 
-	if (osmo_sockaddr_str_from_sockaddr(&rtp_addr, &rtp->addr) < 0) {
-		LOG_TRANS_CAT(trans, DMNCC, LOGL_ERROR, "RTP connect with invalid IP addr\n");
-		mncc_recv_rtp_err(net, trans, rtp->callref, MNCC_RTP_CONNECT);
-		return -EINVAL;
+	if (rtp->sdp[0]) {
+		sdp_msg_from_sdp_str(&trans->cc.codecs.remote, rtp->sdp);
+		LOG_TRANS(trans, LOGL_DEBUG, "%s contained SDP %s\n",
+			  get_mncc_name(rtp->msg_type),
+			  sdp_msg_to_str(&trans->cc.codecs.remote));
 	}
-	rtp_stream_set_remote_addr(rtps, &rtp_addr);
+
+	rtp_stream_set_remote_addr_and_codecs(rtps, &trans->cc.codecs.remote);
+
+	if (!osmo_sockaddr_str_is_nonzero(&rtps->remote)) {
+		/* Didn't get an IP address from SDP. Try legacy MNCC IP address */
+		struct osmo_sockaddr_str rtp_addr;
+		if (osmo_sockaddr_str_from_sockaddr(&rtp_addr, &rtp->addr) < 0) {
+			LOG_TRANS_CAT(trans, DMNCC, LOGL_ERROR, "RTP connect with invalid IP addr\n");
+			mncc_recv_rtp_err(net, trans, rtp->callref, MNCC_RTP_CONNECT);
+			return -EINVAL;
+		}
+		rtp_stream_set_remote_addr(rtps, &rtp_addr);
+	}
+
 	rtp_stream_commit(rtps);
 	return 0;
 }
@@ -2025,6 +2103,19 @@
 			return -ENOMEM;
 		}
 
+		/* Remember remote SDP, if any */
+		if (data->sdp[0]) {
+			if (sdp_msg_from_sdp_str(&trans->cc.codecs.remote, data->sdp)) {
+				LOG_TRANS(trans, LOGL_ERROR, "Failed to parse incoming SDP: %s\n",
+					  osmo_quote_str(data->sdp, -1));
+				vlr_subscr_put(vsub, __func__);
+				mncc_release_ind(net, NULL, data->callref,
+						 GSM48_CAUSE_LOC_PRN_S_LU,
+						 GSM48_CC_CAUSE_NORMAL_UNSPEC);
+				return -EINVAL;
+			}
+		}
+
 		/* If subscriber has no conn */
 		if (!msc_a) {
 			/* This condition will return before the common logging of the received MNCC message below, so