Merge branch 'feature/local-mscs'
diff --git a/openbsc/include/openbsc/osmo_msc_data.h b/openbsc/include/openbsc/osmo_msc_data.h
index 0f3f927..0bda662 100644
--- a/openbsc/include/openbsc/osmo_msc_data.h
+++ b/openbsc/include/openbsc/osmo_msc_data.h
@@ -35,12 +35,20 @@
                 ver : 7;
 };
 
+enum {
+	MSC_CON_TYPE_NORMAL,
+	MSC_CON_TYPE_LOCAL,
+};
+
 struct osmo_msc_data {
 	struct llist_head entry;
 
 	/* Back pointer */
 	struct gsm_network *network;
 
+	int allow_emerg;
+	int type;
+
 	/* Connection data */
 	char *bsc_token;
 	int ping_timeout;
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_filter.c b/openbsc/src/osmo-bsc/osmo_bsc_filter.c
index 200360a..fa7f2d9 100644
--- a/openbsc/src/osmo-bsc/osmo_bsc_filter.c
+++ b/openbsc/src/osmo-bsc/osmo_bsc_filter.c
@@ -106,6 +106,21 @@
 	subscr_put(subscr);
 	return 0;
 }
+
+static int is_cm_service_for_emerg(struct msgb *msg)
+{
+	struct gsm48_service_request *cm;
+	struct gsm48_hdr *gh = msgb_l3(msg);
+
+	if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*cm)) {
+		LOGP(DMSC, LOGL_ERROR, "CM ServiceRequest does not fit.\n");
+		return 0;
+	}
+
+	cm = (struct gsm48_service_request *) &gh->data[0];
+	return cm->cm_service_type == GSM48_CMSERV_EMERGENCY;
+}
+
 struct osmo_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn,
 				   struct msgb *msg)
 {
@@ -115,6 +130,7 @@
 	struct osmo_bsc_data *bsc;
 	struct osmo_msc_data *msc, *pag_msc;
 	struct gsm_subscriber *subscr;
+	int is_emerg = 0;
 
 	bsc = conn->bts->network->bsc_data;
 
@@ -135,13 +151,20 @@
 	 */
 	if (pdisc == GSM48_PDISC_RR && mtype == GSM48_MT_RR_PAG_RESP)
 		goto paging;
-	else
+	else if (pdisc == GSM48_PDISC_MM && mtype == GSM48_MT_MM_CM_SERV_REQ) {
+		is_emerg = is_cm_service_for_emerg(msg);
+		goto round_robin;
+	} else
 		goto round_robin;
 
 round_robin:
 	llist_for_each_entry(msc, &bsc->mscs, entry) {
 		if (!msc->msc_con->is_authenticated)
 			continue;
+		if (!is_emerg && msc->type != MSC_CON_TYPE_NORMAL)
+			continue;
+		if (is_emerg && !msc->allow_emerg)
+			continue;
 
 		/* force round robin by moving it to the end */
 		llist_move_tail(&msc->entry, &bsc->mscs);
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_msc.c b/openbsc/src/osmo-bsc/osmo_bsc_msc.c
index 617a59a..55532db 100644
--- a/openbsc/src/osmo-bsc/osmo_bsc_msc.c
+++ b/openbsc/src/osmo-bsc/osmo_bsc_msc.c
@@ -492,6 +492,7 @@
 	msc_data->rtp_base = 4000;
 
 	msc_data->nr = nr;
+	msc_data->allow_emerg = 1;
 
 	return msc_data;
 }
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_vty.c b/openbsc/src/osmo-bsc/osmo_bsc_vty.c
index 04f9bf4..9f1eb69 100644
--- a/openbsc/src/osmo-bsc/osmo_bsc_vty.c
+++ b/openbsc/src/osmo-bsc/osmo_bsc_vty.c
@@ -114,6 +114,11 @@
 	llist_for_each_entry(dest, &msc->dests, list)
 		vty_out(vty, " dest %s %d %d%s", dest->ip, dest->port,
 			dest->dscp, VTY_NEWLINE);
+
+	vty_out(vty, " type %s%s", msc->type == MSC_CON_TYPE_NORMAL ?
+					"normal" : "local", VTY_NEWLINE);
+	vty_out(vty, " allow-emergency %s%s", msc->allow_emerg ?
+					"allow" : "deny", VTY_NEWLINE);
 }
 
 static int config_write_msc(struct vty *vty)
@@ -332,6 +337,33 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_net_msc_type,
+      cfg_net_msc_type_cmd,
+      "type (normal|local)",
+      "Select the MSC type\n"
+      "Plain GSM MSC\n" "Special MSC for local call routing\n")
+{
+	struct osmo_msc_data *data = osmo_msc_data(vty);
+
+	if (strcmp(argv[0], "normal") == 0)
+		data->type = MSC_CON_TYPE_NORMAL;
+	else if (strcmp(argv[0], "local") == 0)
+		data->type = MSC_CON_TYPE_LOCAL;
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_net_msc_emerg,
+      cfg_net_msc_emerg_cmd,
+      "allow-emergency (allow|deny)",
+      "Allow CM ServiceRequests with type emergency\n"
+      "Allow\n" "Deny\n")
+{
+	struct osmo_msc_data *data = osmo_msc_data(vty);
+	data->allow_emerg = strcmp("allow", argv[0]) == 0;
+	return CMD_SUCCESS;
+}
+
 DEFUN(cfg_net_bsc_mid_call_text,
       cfg_net_bsc_mid_call_text_cmd,
       "mid-call-text .TEXT",
@@ -419,6 +451,8 @@
 	install_element(MSC_NODE, &cfg_net_msc_ping_time_cmd);
 	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_type_cmd);
+	install_element(MSC_NODE, &cfg_net_msc_emerg_cmd);
 
 	install_element_ve(&show_statistics_cmd);
 	install_element_ve(&show_mscs_cmd);