Merge branch 'zecke/features/rtp-bridge' into ciaby/2015-07-28
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index 90f3c80..c167c49 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -120,6 +120,11 @@
 	/* Are we part of a special "silent" call */
 	int silent_call;
 
+	/* MNCC rtp bridge markers */
+	int mncc_rtp_bridge;
+	int mncc_rtp_create_pending;
+	int mncc_rtp_connect_pending;
+
 	/* bsc structures */
 	struct osmo_bsc_sccp_con *sccp_con;
 
diff --git a/openbsc/include/openbsc/mncc.h b/openbsc/include/openbsc/mncc.h
index 94ed685..46ba237 100644
--- a/openbsc/include/openbsc/mncc.h
+++ b/openbsc/include/openbsc/mncc.h
@@ -92,6 +92,9 @@
 #define MNCC_FRAME_RECV		0x0201
 #define MNCC_FRAME_DROP		0x0202
 #define MNCC_LCHAN_MODIFY	0x0203
+#define MNCC_RTP_CREATE		0x0204
+#define MNCC_RTP_CONNECT	0x0205
+#define MNCC_RTP_FREE		0x0206
 
 #define GSM_TCHF_FRAME		0x0300
 #define GSM_TCHF_FRAME_EFR	0x0301
@@ -163,7 +166,7 @@
 	unsigned char	data[0];
 };
 
-#define MNCC_SOCK_VERSION	2
+#define MNCC_SOCK_VERSION	5
 struct gsm_mncc_hello {
 	uint32_t	msg_type;
 	uint32_t	version;
@@ -179,6 +182,15 @@
 	uint32_t	lchan_type_offset;
 };
 
+struct gsm_mncc_rtp {
+	uint32_t	msg_type;
+	uint32_t	callref;
+	uint32_t	ip;
+	uint16_t	port;
+	uint32_t	payload_type;
+	uint32_t	payload_msg_type;
+};
+
 char *get_mncc_name(int value);
 void mncc_set_cause(struct gsm_mncc *data, int loc, int val);
 void cc_tx_to_mncc(struct gsm_network *net, struct msgb *msg);
diff --git a/openbsc/src/libbsc/paging.c b/openbsc/src/libbsc/paging.c
index 8bc10e6..9ae28e0 100644
--- a/openbsc/src/libbsc/paging.c
+++ b/openbsc/src/libbsc/paging.c
@@ -341,8 +341,10 @@
 			break;
 
 		rc = paging_request_bts(bts, subscr, type, cbfn, data);
-		if (rc < 0)
+		if (rc < 0) {
+			paging_request_stop(NULL, subscr, NULL, NULL);
 			return rc;
+		}
 		num_pages += rc;
 	} while (1);
 
@@ -389,28 +391,21 @@
 			 struct gsm_subscriber_connection *conn,
 			 struct msgb *msg)
 {
-	struct gsm_bts *bts = NULL;
+	struct gsm_bts *bts;
 
 	log_set_context(BSC_CTX_SUBSCR, subscr);
 
+	/* Stop this first and dispatch the request */
 	if (_bts)
 		_paging_request_stop(_bts, subscr, conn, msg);
 
-	do {
-		/*
-		 * FIXME: Don't use the lac of the subscriber...
-		 * as it might have magically changed the lac.. use the
-		 * location area of the _bts as reconfiguration of the
-		 * network is probably happening less often.
-		 */
-		bts = gsm_bts_by_lac(subscr->group->net, subscr->lac, bts);
-		if (!bts)
-			break;
-
-		/* Stop paging */
-		if (bts != _bts)
-			_paging_request_stop(bts, subscr, NULL, NULL);
-	} while (1);
+	/* Make sure to cancel this everywhere else */
+	llist_for_each_entry(bts, &subscr->group->net->bts_list, list) {
+		/* Sort of an optimization. */
+		if (bts == _bts)
+			continue;
+		_paging_request_stop(bts, subscr, NULL, NULL);
+	}
 }
 
 void paging_update_buffer_space(struct gsm_bts *bts, uint16_t free_slots)
diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c
index 02ffe58..f71d43c 100644
--- a/openbsc/src/libmsc/gsm_04_08.c
+++ b/openbsc/src/libmsc/gsm_04_08.c
@@ -67,6 +67,8 @@
 void *tall_locop_ctx;
 void *tall_authciphop_ctx;
 
+static int tch_rtp_signal(struct gsm_lchan *lchan, int signal);
+
 static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn);
 static int gsm48_tx_simple(struct gsm_subscriber_connection *conn,
 			   uint8_t pdisc, uint8_t msg_type);
@@ -1463,6 +1465,15 @@
 {
 	struct rtp_socket *old_rs, *new_rs, *other_rs;
 
+	/* Ask the new socket to send to the already known port. */
+	if (new_lchan->conn->mncc_rtp_bridge) {
+		LOGP(DHO, LOGL_DEBUG, "Forwarding RTP\n");
+		rsl_ipacc_mdcx(new_lchan,
+					old_lchan->abis_ip.connect_ip,
+					old_lchan->abis_ip.connect_port, 0);
+		return 0;
+	}
+
 	if (ipacc_rtp_direct) {
 		LOGP(DHO, LOGL_ERROR, "unable to handover in direct RTP mode\n");
 		return 0;
@@ -1507,11 +1518,19 @@
 	return 0;
 }
 
+static void maybe_switch_for_handover(struct gsm_lchan *lchan)
+{
+	struct gsm_lchan *old_lchan;
+	old_lchan = bsc_handover_pending(lchan);
+	if (old_lchan)
+		switch_for_handover(old_lchan, lchan);
+}
+
 /* some other part of the code sends us a signal */
 static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
 				 void *handler_data, void *signal_data)
 {
-	struct gsm_lchan *lchan = signal_data, *old_lchan;
+	struct gsm_lchan *lchan = signal_data;
 	int rc;
 	struct gsm_network *net;
 	struct gsm_trans *trans;
@@ -1519,6 +1538,10 @@
 	if (subsys != SS_ABISIP)
 		return 0;
 
+	/* RTP bridge handling */
+	if (lchan->conn && lchan->conn->mncc_rtp_bridge)
+		return tch_rtp_signal(lchan, signal);
+
 	/* in case we use direct BTS-to-BTS RTP */
 	if (ipacc_rtp_direct)
 		return 0;
@@ -1560,9 +1583,7 @@
 		 * Do we have a handover pending for this new lchan? In that
 		 * case re-route the audio from the old channel to the new one.
 		 */
-		old_lchan = bsc_handover_pending(lchan);
-		if (old_lchan)
-			switch_for_handover(old_lchan, lchan);
+		maybe_switch_for_handover(lchan);
 		break;
 	case S_ABISIP_DLCX_IND:
 		/* the BTS tells us a RTP stream has been disconnected */
@@ -2905,11 +2926,218 @@
 static int _gsm48_lchan_modify(struct gsm_trans *trans, void *arg)
 {
 	struct gsm_mncc *mode = arg;
+	struct gsm_lchan *lchan = trans->conn->lchan;
+
+	/*
+	 * We were forced to make an assignment a lot earlier and
+	 * we should avoid sending another assignment that might
+	 * even lead to a different kind of lchan (TCH/F vs. TCH/H).
+	 * In case of rtp-bridge it is too late to change things
+	 * here.
+	 */
+	if (trans->conn->mncc_rtp_bridge && lchan->tch_mode != GSM48_CMODE_SIGN)
+		return 0;
 
 	return gsm0808_assign_req(trans->conn, mode->lchan_mode,
 		trans->conn->lchan->type != GSM_LCHAN_TCH_H);
 }
 
+static void mncc_recv_rtp(struct gsm_network *net, uint32_t callref,
+		int cmd, uint32_t addr, uint16_t port, uint32_t payload_type,
+		uint32_t payload_msg_type)
+{
+	uint8_t data[sizeof(struct gsm_mncc)];
+	struct gsm_mncc_rtp *rtp;
+
+	memset(&data, 0, sizeof(data));
+	rtp = (struct gsm_mncc_rtp *) &data[0];
+
+	rtp->callref = callref;
+	rtp->msg_type = cmd;
+	rtp->ip = addr;
+	rtp->port = port;
+	rtp->payload_type = payload_type;
+	rtp->payload_msg_type = payload_msg_type;
+	mncc_recvmsg(net, NULL, cmd, (struct gsm_mncc *)data);
+}
+
+static void mncc_recv_rtp_sock(struct gsm_network *net, struct gsm_trans *trans, int cmd)
+{
+	struct gsm_lchan *lchan;
+	int msg_type;
+
+	lchan = trans->conn->lchan;
+	switch (lchan->abis_ip.rtp_payload) {
+	case RTP_PT_GSM_FULL:
+		msg_type = GSM_TCHF_FRAME;
+		break;
+	case RTP_PT_GSM_EFR:
+		msg_type = GSM_TCHF_FRAME_EFR;
+		break;
+	case RTP_PT_GSM_HALF:
+		msg_type = GSM_TCHH_FRAME;
+		break;
+	case RTP_PT_AMR:
+		msg_type = GSM_TCH_FRAME_AMR;
+		break;
+	default:
+		LOGP(DMNCC, LOGL_ERROR, "%s unknown payload type %d\n",
+			gsm_lchan_name(lchan), lchan->abis_ip.rtp_payload);
+		msg_type = 0;
+		break;
+	}
+
+	return mncc_recv_rtp(net, trans->callref, cmd,
+			lchan->abis_ip.bound_ip,
+			lchan->abis_ip.bound_port,
+			lchan->abis_ip.rtp_payload,
+			msg_type);
+}
+
+static void mncc_recv_rtp_err(struct gsm_network *net, uint32_t callref, int cmd)
+{
+	return mncc_recv_rtp(net, callref, cmd, 0, 0, 0, 0);
+}
+
+static int tch_rtp_create(struct gsm_network *net, uint32_t callref)
+{
+	struct gsm_bts *bts;
+	struct gsm_lchan *lchan;
+	struct gsm_trans *trans;
+
+	/* Find callref */
+	trans = trans_find_by_callref(net, callref);
+	if (!trans) {
+		LOGP(DMNCC, LOGL_ERROR, "RTP create for non-existing trans\n");
+		mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE);
+		return -EIO;
+	}
+	log_set_context(BSC_CTX_SUBSCR, trans->subscr);
+	if (!trans->conn) {
+		LOGP(DMNCC, LOGL_NOTICE, "RTP create for trans without conn\n");
+		mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE);
+		return 0;
+	}
+
+	lchan = trans->conn->lchan;
+	bts = lchan->ts->trx->bts;
+	if (!is_ipaccess_bts(bts)) {
+		/*
+		 * I want this to be straight forward and have no audio flow
+		 * through the nitb/osmo-mss system. This currently means that
+		 * this will not work with BS11/Nokia type BTS. We would need
+		 * to have a trau<->rtp bridge for these but still preferable
+		 * in another process.
+		 */
+		LOGP(DMNCC, LOGL_ERROR, "RTP create only works with IP systems\n");
+		mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE);
+		return -EINVAL;
+	}
+
+	trans->conn->mncc_rtp_bridge = 1;
+	/*
+	 * *sigh* we need to pick a codec now. Pick the most generic one
+	 * right now and hope we could fix that later on. This is very
+	 * similiar to the above routine.
+	 * TODO: Use the default codec version...
+	 */
+	if (lchan->tch_mode == GSM48_CMODE_SIGN) {
+		trans->conn->mncc_rtp_create_pending = 1;
+		/* TODO... transport or fix the default type... */
+		return gsm0808_assign_req(trans->conn, GSM48_CMODE_SPEECH_V1,
+				lchan->type != GSM_LCHAN_TCH_H);
+	}
+
+	mncc_recv_rtp_sock(trans->net, trans, MNCC_RTP_CREATE);
+	return 0;
+}
+
+static int tch_rtp_connect(struct gsm_network *net, void *arg)
+{
+	struct gsm_lchan *lchan;
+	struct gsm_trans *trans;
+	struct gsm_mncc_rtp *rtp = arg;
+
+	/* Find callref */
+	trans = trans_find_by_callref(net, rtp->callref);
+	if (!trans) {
+		LOGP(DMNCC, LOGL_ERROR, "RTP connect for non-existing trans\n");
+		mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT);
+		return -EIO;
+	}
+	log_set_context(BSC_CTX_SUBSCR, trans->subscr);
+	if (!trans->conn) {
+		LOGP(DMNCC, LOGL_ERROR, "RTP connect for trans without conn\n");
+		mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT);
+		return 0;
+	}
+
+	lchan = trans->conn->lchan;
+
+	/* TODO: Check if payload_msg_type is compatible with what we have */
+	if (rtp->payload_type != lchan->abis_ip.rtp_payload) {
+		LOGP(DMNCC, LOGL_ERROR, "RTP connect with different RTP payload\n");
+		mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT);
+	}
+
+	/*
+	 * FIXME: payload2 can't be sent with MDCX as the osmo-bts code
+	 * complains about both rtp and rtp payload2 being present in the
+	 * same package!
+	 */
+	trans->conn->mncc_rtp_connect_pending = 1;
+	return rsl_ipacc_mdcx(lchan, rtp->ip, rtp->port, 0);
+}
+
+static int tch_rtp_signal(struct gsm_lchan *lchan, int signal)
+{
+	struct gsm_network *net;
+	struct gsm_trans *tmp, *trans = NULL;
+
+	net = lchan->ts->trx->bts->network;
+	llist_for_each_entry(tmp, &net->trans_list, entry) {
+		if (!tmp->conn)
+			continue;
+		if (tmp->conn->lchan != lchan && tmp->conn->ho_lchan != lchan)
+			continue;
+		trans = tmp;
+		break;
+	}
+
+	if (!trans) {
+		LOGP(DMNCC, LOGL_ERROR, "%s IPA abis signal but no transaction.\n",
+			gsm_lchan_name(lchan));
+		return 0;
+	}
+
+	switch (signal) {
+	case S_ABISIP_CRCX_ACK:
+		if (lchan->conn->mncc_rtp_create_pending) {
+			lchan->conn->mncc_rtp_create_pending = 0;
+			LOGP(DMNCC, LOGL_NOTICE, "%s sending pending RTP create ind.\n",
+				gsm_lchan_name(lchan));
+			mncc_recv_rtp_sock(net, trans, MNCC_RTP_CREATE);
+		}
+		/*
+		 * TODO: this appears to be too early? Why not until after
+		 * the handover detect or the handover complete?
+		 */
+		maybe_switch_for_handover(lchan);
+		break;
+	case S_ABISIP_MDCX_ACK:
+		if (lchan->conn->mncc_rtp_connect_pending) {
+			lchan->conn->mncc_rtp_connect_pending = 0;
+			LOGP(DMNCC, LOGL_NOTICE, "%s sending pending RTP connect ind.\n",
+				gsm_lchan_name(lchan));
+			mncc_recv_rtp_sock(net, trans, MNCC_RTP_CONNECT);
+		}
+		break;
+	}
+
+	return 0;
+}
+
+
 static struct downstate {
 	uint32_t	states;
 	int		type;
@@ -2990,6 +3218,13 @@
 		return tch_recv_mncc(net, data->callref, 0);
 	case MNCC_FRAME_RECV:
 		return tch_recv_mncc(net, data->callref, 1);
+	case MNCC_RTP_CREATE:
+		return tch_rtp_create(net, data->callref);
+	case MNCC_RTP_CONNECT:
+		return tch_rtp_connect(net, arg);
+	case MNCC_RTP_FREE:
+		/* unused right now */
+		return -EIO;
 	case GSM_TCHF_FRAME:
 	case GSM_TCHF_FRAME_EFR:
 	case GSM_TCHH_FRAME:
diff --git a/openbsc/src/libmsc/gsm_subscriber.c b/openbsc/src/libmsc/gsm_subscriber.c
index ff77aa5..ccba360 100644
--- a/openbsc/src/libmsc/gsm_subscriber.c
+++ b/openbsc/src/libmsc/gsm_subscriber.c
@@ -99,6 +99,14 @@
 		return 0;
 	}
 
+	/*
+	 * Stop paging on all other BTS. E.g. if this is
+	 * the first timeout on a BTS then the others will
+	 * timeout soon as well. Let's just stop everything
+	 * and forget we wanted to page.
+	 */
+	paging_request_stop(NULL, subscr, NULL, NULL);
+
 	/* Inform parts of the system we don't know */
 	sig_data.subscr = subscr;
 	sig_data.bts	= conn ? conn->bts : NULL;
@@ -111,15 +119,6 @@
 		&sig_data
 	);
 
-	/*
-	 * Stop paging on all other BTS. E.g. if this is
-	 * the first timeout on a BTS then the others will
-	 * timeout soon as well. Let's just stop everything
-	 * and forget we wanted to page.
-	 */
-	paging_request_stop(NULL, subscr, NULL, NULL);
-	subscr->is_paging = 0;
-
 	llist_for_each_entry_safe(request, tmp, &subscr->requests, entry) {
 		llist_del(&request->entry);
 		request->cbfn(hooknum, event, msg, data, request->param);
@@ -127,6 +126,7 @@
 	}
 
 	/* balanced with the moment we start paging */
+	subscr->is_paging = 0;
 	subscr_put(subscr);
 	return 0;
 }
@@ -368,6 +368,7 @@
 		LOGP(DMM, LOGL_DEBUG, "Not expiring subscriber %s (ID %llu)\n",
 			subscr_name(s), id);
 		subscr_update_expire_lu(s, conn->bts);
+		subscr_put(s);
 		return;
 	}
 
diff --git a/openbsc/src/libmsc/mncc.c b/openbsc/src/libmsc/mncc.c
index 73db5f0..2767faa 100644
--- a/openbsc/src/libmsc/mncc.c
+++ b/openbsc/src/libmsc/mncc.c
@@ -83,6 +83,9 @@
 	{"MNCC_FRAME_RECV",	0x0201},
 	{"MNCC_FRAME_DROP",	0x0202},
 	{"MNCC_LCHAN_MODIFY",	0x0203},
+	{"MNCC_RTP_CREATE",	0x0204},
+	{"MNCC_RTP_CONNECT",	0x0205},
+	{"MNCC_RTP_FREE",	0x0206},
 
 	{"GSM_TCHF_FRAME",	0x0300},
 	{"GSM_TCHF_FRAME_EFR",	0x0301},
diff --git a/openbsc/src/libmsc/silent_call.c b/openbsc/src/libmsc/silent_call.c
index 010c2b4..e9ece18 100644
--- a/openbsc/src/libmsc/silent_call.c
+++ b/openbsc/src/libmsc/silent_call.c
@@ -118,11 +118,10 @@
 /* initiate a silent call with a given subscriber */
 int gsm_silent_call_start(struct gsm_subscriber *subscr, void *data, int type)
 {
-	int rc;
+	struct subscr_request *req;
 
-	rc = paging_request(subscr->group->net, subscr, type,
-			    paging_cb_silent, data);
-	return rc;
+	req = subscr_request_channel(subscr, type, paging_cb_silent, data);
+	return req != NULL;
 }
 
 /* end a silent call with a given subscriber */
diff --git a/openbsc/src/libmsc/smpp_openbsc.c b/openbsc/src/libmsc/smpp_openbsc.c
index 057a9d0..a2fa0f4 100644
--- a/openbsc/src/libmsc/smpp_openbsc.c
+++ b/openbsc/src/libmsc/smpp_openbsc.c
@@ -110,6 +110,7 @@
 			/* ERROR: we cannot have both! */
 			LOGP(DLSMS, LOGL_ERROR, "SMPP Cannot have payload in "
 				"TLV _and_ in the header\n");
+			subscr_put(dest);
 			return ESME_ROPTPARNOTALLWD;
 		}
 		sms_msg = t->value.octet;
@@ -120,6 +121,7 @@
 	} else {
 		LOGP(DLSMS, LOGL_ERROR,
 			"SMPP neither message payload nor valid sm_length.\n");
+		subscr_put(dest);
 		return ESME_RINVPARLEN;
 	}
 
diff --git a/openbsc/src/libmsc/vty_interface_layer3.c b/openbsc/src/libmsc/vty_interface_layer3.c
index 71fff93..f49c53a 100644
--- a/openbsc/src/libmsc/vty_interface_layer3.c
+++ b/openbsc/src/libmsc/vty_interface_layer3.c
@@ -58,8 +58,10 @@
 static void subscr_dump_full_vty(struct vty *vty, struct gsm_subscriber *subscr)
 {
 	int rc;
+	int reqs;
 	struct gsm_auth_info ainfo;
 	struct gsm_auth_tuple atuple;
+	struct llist_head *entry;
 	char expire_time[200];
 
 	vty_out(vty, "    ID: %llu, Authorized: %d%s", subscr->id,
@@ -107,8 +109,12 @@
 			"%a, %d %b %Y %T %z", localtime(&subscr->expire_lu));
 	expire_time[sizeof(expire_time) - 1] = '\0';
 	vty_out(vty, "    Expiration Time: %s%s", expire_time, VTY_NEWLINE);
-	vty_out(vty, "    Paging: %s paging%s",
-		subscr->is_paging ? "is" : "not", VTY_NEWLINE);
+
+	reqs = 0;
+	llist_for_each(entry, &subscr->requests)
+		reqs += 1;
+	vty_out(vty, "    Paging: %s paging Requests: %d%s",
+		subscr->is_paging ? "is" : "not", reqs, VTY_NEWLINE);
 	vty_out(vty, "    Use count: %u%s", subscr->use_count, VTY_NEWLINE);
 }