Merge branch 'feature/multiple-msc-connections'
diff --git a/openbsc/include/openbsc/osmo_bsc.h b/openbsc/include/openbsc/osmo_bsc.h
index ffa0b57..0bc39dc 100644
--- a/openbsc/include/openbsc/osmo_bsc.h
+++ b/openbsc/include/openbsc/osmo_bsc.h
@@ -32,13 +32,15 @@
 
 int bsc_queue_for_msc(struct osmo_bsc_sccp_con *conn, struct msgb *msg);
 int bsc_open_connection(struct osmo_bsc_sccp_con *sccp, struct msgb *msg);
-int bsc_create_new_connection(struct gsm_subscriber_connection *conn);
+int bsc_create_new_connection(struct gsm_subscriber_connection *conn,
+			      struct osmo_msc_data *msc);
 int bsc_delete_connection(struct osmo_bsc_sccp_con *sccp);
 
+struct osmo_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn, struct msgb *);
 int bsc_scan_bts_msg(struct gsm_subscriber_connection *conn, struct msgb *msg);
 int bsc_scan_msc_msg(struct gsm_subscriber_connection *conn, struct msgb *msg);
 
-int bsc_handle_udt(struct gsm_network *net, struct bsc_msc_connection *conn, struct msgb *msg, unsigned int length);
+int bsc_handle_udt(struct osmo_msc_data *msc, struct msgb *msg, unsigned int length);
 int bsc_handle_dt1(struct osmo_bsc_sccp_con *conn, struct msgb *msg, unsigned int len);
 
 int bsc_ctrl_cmds_install();
diff --git a/openbsc/include/openbsc/vty.h b/openbsc/include/openbsc/vty.h
index 7d93a16..e3f3182 100644
--- a/openbsc/include/openbsc/vty.h
+++ b/openbsc/include/openbsc/vty.h
@@ -34,6 +34,7 @@
 	TRUNK_NODE,
 	PGROUP_NODE,
 	MNCC_INT_NODE,
+	BSC_NODE,
 };
 
 extern int bsc_vty_is_config_node(struct vty *vty, int node);
diff --git a/openbsc/src/libcommon/common_vty.c b/openbsc/src/libcommon/common_vty.c
index 5958df2..0fd4f43 100644
--- a/openbsc/src/libcommon/common_vty.c
+++ b/openbsc/src/libcommon/common_vty.c
@@ -148,6 +148,7 @@
 	case GBPROXY_NODE:
 	case SGSN_NODE:
 	case NAT_NODE:
+	case BSC_NODE:
 		vty->node = CONFIG_NODE;
 		vty->index = NULL;
 		break;
@@ -195,6 +196,7 @@
 	case PGROUP_NODE:
 	case MSC_NODE:
 	case MNCC_INT_NODE:
+	case BSC_NODE:
 		vty_config_unlock(vty);
 		vty->node = ENABLE_NODE;
 		vty->index = NULL;
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_api.c b/openbsc/src/osmo-bsc/osmo_bsc_api.c
index 04305c2..7acb00a 100644
--- a/openbsc/src/osmo-bsc/osmo_bsc_api.c
+++ b/openbsc/src/osmo-bsc/osmo_bsc_api.c
@@ -89,13 +89,21 @@
 			uint16_t chosen_channel)
 {
 	struct msgb *resp;
+	struct osmo_msc_data *msc;
 	uint16_t network_code;
 	uint16_t country_code;
 
 	LOGP(DMSC, LOGL_INFO, "Tx MSC COMPL L3\n");
 
+	/* find the MSC link we want to use */
+	msc = bsc_find_msc(conn, msg);
+	if (!msc) {
+		LOGP(DMSC, LOGL_ERROR, "Failed to find a MSC for a connection.\n");
+		return -1;
+	}
+
 	/* allocate resource for a new connection */
-	if (bsc_create_new_connection(conn) != 0)
+	if (bsc_create_new_connection(conn, msc) != 0)
 		return BSC_API_CONN_POL_REJECT;
 
 	network_code = get_network_code_for_msc(conn->sccp_con->msc);
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_bssap.c b/openbsc/src/osmo-bsc/osmo_bsc_bssap.c
index f17db32..684a6c1 100644
--- a/openbsc/src/osmo-bsc/osmo_bsc_bssap.c
+++ b/openbsc/src/osmo-bsc/osmo_bsc_bssap.c
@@ -98,7 +98,7 @@
 	return GSM48_CMODE_SPEECH_AMR;
 }
 
-static int bssmap_handle_reset_ack(struct gsm_network *net,
+static int bssmap_handle_reset_ack(struct osmo_msc_data *msc,
 				   struct msgb *msg, unsigned int length)
 {
 	LOGP(DMSC, LOGL_NOTICE, "Reset ACK from MSC\n");
@@ -106,7 +106,7 @@
 }
 
 /* GSM 08.08 § 3.2.1.19 */
-static int bssmap_handle_paging(struct gsm_network *net,
+static int bssmap_handle_paging(struct osmo_msc_data *msc,
 				struct msgb *msg, unsigned int payload_length)
 {
 	struct gsm_subscriber *subscr;
@@ -167,7 +167,7 @@
 		LOGP(DMSC, LOGL_ERROR, "eMLPP is not handled\n");
 	}
 
-	subscr = subscr_get_or_create(net, mi_string);
+	subscr = subscr_get_or_create(msc->network, mi_string);
 	if (!subscr) {
 		LOGP(DMSC, LOGL_ERROR, "Failed to allocate a subscriber for %s\n", mi_string);
 		return -1;
@@ -177,7 +177,7 @@
 	subscr->tmsi = tmsi;
 
 	LOGP(DMSC, LOGL_INFO, "Paging request from MSC IMSI: '%s' TMSI: '0x%x/%u' LAC: 0x%x\n", mi_string, tmsi, tmsi, lac);
-	paging_request(net, subscr, chan_needed, NULL, NULL);
+	paging_request(msc->network, subscr, chan_needed, NULL, msc);
 	return 0;
 }
 
@@ -395,7 +395,7 @@
 	return -1;
 }
 
-static int bssmap_rcvmsg_udt(struct gsm_network *net,
+static int bssmap_rcvmsg_udt(struct osmo_msc_data *msc,
 			     struct msgb *msg, unsigned int length)
 {
 	int ret = 0;
@@ -410,11 +410,11 @@
 
 	switch (msg->l4h[0]) {
 	case BSS_MAP_MSG_RESET_ACKNOWLEDGE:
-		ret = bssmap_handle_reset_ack(net, msg, length);
+		ret = bssmap_handle_reset_ack(msc, msg, length);
 		break;
 	case BSS_MAP_MSG_PAGING:
-		if (bsc_grace_allow_new_connection(net))
-			ret = bssmap_handle_paging(net, msg, length);
+		if (bsc_grace_allow_new_connection(msc->network))
+			ret = bssmap_handle_paging(msc, msg, length);
 		break;
 	}
 
@@ -499,8 +499,7 @@
 	return gsm0808_submit_dtap(conn->conn, gsm48, header->link_id, 1);
 }
 
-int bsc_handle_udt(struct gsm_network *network,
-		   struct bsc_msc_connection *conn,
+int bsc_handle_udt(struct osmo_msc_data *msc,
 		   struct msgb *msgb, unsigned int length)
 {
 	struct bssmap_header *bs;
@@ -520,7 +519,7 @@
 	switch (bs->type) {
 	case BSSAP_MSG_BSS_MANAGEMENT:
 		msgb->l4h = &msgb->l3h[sizeof(*bs)];
-		bssmap_rcvmsg_udt(network, msgb, length - sizeof(*bs));
+		bssmap_rcvmsg_udt(msc, msgb, length - sizeof(*bs));
 		break;
 	default:
 		LOGP(DMSC, LOGL_NOTICE, "Unimplemented msg type: %s\n",
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_filter.c b/openbsc/src/osmo-bsc/osmo_bsc_filter.c
index 9d6909c..200360a 100644
--- a/openbsc/src/osmo-bsc/osmo_bsc_filter.c
+++ b/openbsc/src/osmo-bsc/osmo_bsc_filter.c
@@ -53,8 +53,9 @@
 	}
 }
 
-/* we will need to stop the paging request */
-static int handle_page_resp(struct gsm_subscriber_connection *conn, struct msgb *msg)
+/* extract a subscriber from the paging response */
+static struct gsm_subscriber *extract_sub(struct gsm_subscriber_connection *conn,
+					  struct msgb *msg)
 {
 	uint8_t mi_type;
 	char mi_string[GSM48_MI_SIZE];
@@ -64,7 +65,7 @@
 
 	if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*resp)) {
 		LOGP(DMSC, LOGL_ERROR, "PagingResponse too small: %u\n", msgb_l3len(msg));
-		return -1;
+		return NULL;
 	}
 
 	gh = msgb_l3(msg);
@@ -88,6 +89,14 @@
 		break;
 	}
 
+	return subscr;
+}
+
+/* we will need to stop the paging request */
+static int handle_page_resp(struct gsm_subscriber_connection *conn, struct msgb *msg)
+{
+	struct gsm_subscriber *subscr = extract_sub(conn, msg);
+
 	if (!subscr) {
 		LOGP(DMSC, LOGL_ERROR, "Non active subscriber got paged.\n");
 		return -1;
@@ -97,6 +106,79 @@
 	subscr_put(subscr);
 	return 0;
 }
+struct osmo_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn,
+				   struct msgb *msg)
+{
+	struct gsm48_hdr *gh;
+	int8_t pdisc;
+	uint8_t mtype;
+	struct osmo_bsc_data *bsc;
+	struct osmo_msc_data *msc, *pag_msc;
+	struct gsm_subscriber *subscr;
+
+	bsc = conn->bts->network->bsc_data;
+
+	if (msgb_l3len(msg) < sizeof(*gh)) {
+		LOGP(DMSC, LOGL_ERROR, "There is no GSM48 header here.\n");
+		return NULL;
+	}
+
+	gh = msgb_l3(msg);
+	pdisc = gh->proto_discr & 0x0f;
+	mtype = gh->msg_type & 0xbf;
+
+	/*
+	 * We are asked to select a MSC here but they are not equal. We
+	 * want to respond to a paging request on the MSC where we got the
+	 * request from. This is where we need to decide where this connection
+	 * will go.
+	 */
+	if (pdisc == GSM48_PDISC_RR && mtype == GSM48_MT_RR_PAG_RESP)
+		goto paging;
+	else
+		goto round_robin;
+
+round_robin:
+	llist_for_each_entry(msc, &bsc->mscs, entry) {
+		if (!msc->msc_con->is_authenticated)
+			continue;
+
+		/* force round robin by moving it to the end */
+		llist_move_tail(&msc->entry, &bsc->mscs);
+		return msc;
+	}
+
+	return NULL;
+
+paging:
+	subscr = extract_sub(conn, msg);
+
+	if (!subscr) {
+		LOGP(DMSC, LOGL_ERROR, "Got paged but no subscriber found.\n");
+		return NULL;
+	}
+
+	pag_msc = paging_get_data(conn->bts, subscr);
+	subscr_put(subscr);
+
+	llist_for_each_entry(msc, &bsc->mscs, entry) {
+		if (msc != pag_msc)
+			continue;
+
+		/*
+		 * We don't check if the MSC is connected. In case it
+		 * is not the connection will be dropped.
+		 */
+
+		/* force round robin by moving it to the end */
+		llist_move_tail(&msc->entry, &bsc->mscs);
+		return msc;
+	}
+
+	LOGP(DMSC, LOGL_ERROR, "Got paged but no request found.\n");
+	return NULL;
+}
+
 
 /**
  * This is used to scan a message for extra functionality of the BSC. This
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_sccp.c b/openbsc/src/osmo-bsc/osmo_bsc_sccp.c
index cc4eedf..37eb1b7 100644
--- a/openbsc/src/osmo-bsc/osmo_bsc_sccp.c
+++ b/openbsc/src/osmo-bsc/osmo_bsc_sccp.c
@@ -161,7 +161,7 @@
 static int msc_sccp_read(struct msgb *msgb, unsigned int length, void *data)
 {
 	struct osmo_msc_data *msc = (struct osmo_msc_data *) msgb->cb[0];
-	return bsc_handle_udt(msc->network, msc->msc_con, msgb, length);
+	return bsc_handle_udt(msc, msgb, length);
 }
 
 int bsc_queue_for_msc(struct osmo_bsc_sccp_con *conn, struct msgb *msg)
@@ -187,22 +187,19 @@
 	return 0;
 }
 
-int bsc_create_new_connection(struct gsm_subscriber_connection *conn)
+int bsc_create_new_connection(struct gsm_subscriber_connection *conn,
+			      struct osmo_msc_data *msc)
 {
 	struct gsm_network *net;
-	struct osmo_msc_data *msc;
 	struct osmo_bsc_sccp_con *bsc_con;
 	struct sccp_connection *sccp;
 
 	net = conn->bts->network;
-	msc = osmo_msc_data_find(net, 0);
-	if (!msc) {
-		LOGP(DMSC, LOGL_ERROR, "Failed to select a MSC.\n");
-		return -1;
-	}
 
+	/* This should not trigger */
 	if (!msc->msc_con->is_authenticated) {
-		LOGP(DMSC, LOGL_ERROR, "Not connected to a MSC. Not forwarding data.\n");
+		LOGP(DMSC, LOGL_ERROR,
+		     "How did this happen? MSC is not connected. Dropping.\n");
 		return -1;
 	}
 
@@ -272,8 +269,10 @@
 {
 	struct osmo_bsc_sccp_con *con, *tmp;
 
-	llist_for_each_entry_safe(con, tmp, &active_connections, entry)
-		bsc_sccp_force_free(con);
+	llist_for_each_entry_safe(con, tmp, &active_connections, entry) {
+		if (con->msc->msc_con == msc_con)
+			bsc_sccp_force_free(con);
+	}
 }
 
 static int handle_msc_signal(unsigned int subsys, unsigned int signal,
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_vty.c b/openbsc/src/osmo-bsc/osmo_bsc_vty.c
index 26f9229..04f9bf4 100644
--- a/openbsc/src/osmo-bsc/osmo_bsc_vty.c
+++ b/openbsc/src/osmo-bsc/osmo_bsc_vty.c
@@ -39,6 +39,12 @@
 	return osmo_msc_data_find(bsc_gsmnet, (int) vty->index);
 }
 
+static struct cmd_node bsc_node = {
+	BSC_NODE,
+	"%s(bsc)#",
+	1,
+};
+
 static struct cmd_node msc_node = {
 	MSC_NODE,
 	"%s(config-msc)# ",
@@ -46,9 +52,9 @@
 };
 
 DEFUN(cfg_net_msc, cfg_net_msc_cmd,
-      "msc", "Configure MSC details")
+      "msc [<0-1000>]", "Configure MSC details\n" "MSC connection to configure\n")
 {
-	int index = 0;
+	int index = argc == 1 ? atoi(argv[0]) : 0;
 	struct osmo_msc_data *msc;
 
 	msc = osmo_msc_data_alloc(bsc_gsmnet, index);
@@ -62,11 +68,18 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_net_bsc, cfg_net_bsc_cmd,
+      "bsc", "Configure BSC\n")
+{
+	vty->node = BSC_NODE;
+	return CMD_SUCCESS;
+}
+
 static void write_msc(struct vty *vty, struct osmo_msc_data *msc)
 {
 	struct bsc_msc_dest *dest;
 
-	vty_out(vty, "msc%s", VTY_NEWLINE);
+	vty_out(vty, "msc %d%s", msc->nr, VTY_NEWLINE);
 	if (msc->bsc_token)
 		vty_out(vty, " token %s%s", msc->bsc_token, VTY_NEWLINE);
 	if (msc->core_ncc != -1)
@@ -111,6 +124,14 @@
 	llist_for_each_entry(msc, &bsc->mscs, entry)
 		write_msc(vty, msc);
 
+	return CMD_SUCCESS;
+}
+
+static int config_write_bsc(struct vty *vty)
+{
+	struct osmo_bsc_data *bsc = osmo_bsc_data(vty);
+
+	vty_out(vty, "bsc%s", VTY_NEWLINE);
 	if (bsc->mid_call_txt)
 		vty_out(vty, " mid-call-text %s%s", bsc->mid_call_txt, VTY_NEWLINE);
 	vty_out(vty, " mid-call-timeout %d%s", bsc->mid_call_timeout, VTY_NEWLINE);
@@ -296,8 +317,23 @@
 	return CMD_SUCCESS;
 }
 
-DEFUN(cfg_net_msc_mid_call_text,
-      cfg_net_msc_mid_call_text_cmd,
+DEFUN(cfg_net_msc_welcome_ussd,
+      cfg_net_msc_welcome_ussd_cmd,
+      "bsc-welcome-text .TEXT",
+      "Set the USSD notification to be sent.\n" "Text to be sent\n")
+{
+	struct osmo_msc_data *data = osmo_msc_data(vty);
+	char *str = argv_concat(argv, argc, 0);
+	if (!str)
+		return CMD_WARNING;
+
+	bsc_replace_string(osmo_bsc_data(vty), &data->ussd_welcome_txt, str);
+	talloc_free(str);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_net_bsc_mid_call_text,
+      cfg_net_bsc_mid_call_text_cmd,
       "mid-call-text .TEXT",
       "Set the USSD notifcation to be send.\n" "Text to be sent\n")
 {
@@ -311,8 +347,8 @@
 	return CMD_SUCCESS;
 }
 
-DEFUN(cfg_net_msc_mid_call_timeout,
-      cfg_net_msc_mid_call_timeout_cmd,
+DEFUN(cfg_net_bsc_mid_call_timeout,
+      cfg_net_bsc_mid_call_timeout_cmd,
       "mid-call-timeout NR",
       "Switch from Grace to Off in NR seconds.\n" "Timeout in seconds\n")
 {
@@ -321,21 +357,6 @@
 	return CMD_SUCCESS;
 }
 
-DEFUN(cfg_net_msc_welcome_ussd,
-      cfg_net_msc_welcome_ussd_cmd,
-      "bsc-welcome-text .TEXT",
-      "Set the USSD notification to be sent.\n" "Text to be sent\n")
-{
-	struct osmo_msc_data *data = osmo_msc_data(vty);
-	char *str = argv_concat(argv, argc, 0);
-	if (!str)
-		return CMD_WARNING;
-
-	bsc_replace_string(osmo_bsc_data(vty), &data->ussd_welcome_txt, str);
-	talloc_free(str);
-	return CMD_SUCCESS;
-}
-
 DEFUN(cfg_net_rf_socket,
       cfg_net_rf_socket_cmd,
       "bsc-rf-socket PATH",
@@ -376,6 +397,16 @@
 int bsc_vty_init_extra(void)
 {
 	install_element(CONFIG_NODE, &cfg_net_msc_cmd);
+	install_element(CONFIG_NODE, &cfg_net_bsc_cmd);
+
+	install_node(&bsc_node, config_write_bsc);
+	install_default(BSC_NODE);
+	install_element(BSC_NODE, &cfg_net_bsc_mid_call_text_cmd);
+	install_element(BSC_NODE, &cfg_net_bsc_mid_call_timeout_cmd);
+	install_element(BSC_NODE, &cfg_net_rf_socket_cmd);
+
+
+
 	install_node(&msc_node, config_write_msc);
 	install_default(MSC_NODE);
 	install_element(MSC_NODE, &cfg_net_bsc_token_cmd);
@@ -389,10 +420,6 @@
 	install_element(MSC_NODE, &cfg_net_msc_pong_time_cmd);
 	install_element(MSC_NODE, &cfg_net_msc_welcome_ussd_cmd);
 
-	install_element(MSC_NODE, &cfg_net_msc_mid_call_text_cmd);
-	install_element(MSC_NODE, &cfg_net_msc_mid_call_timeout_cmd);
-	install_element(MSC_NODE, &cfg_net_rf_socket_cmd);
-
 	install_element_ve(&show_statistics_cmd);
 	install_element_ve(&show_mscs_cmd);