Merge branch 'zecke/features/osmux-reliability'

Not verified that the audio handling is working. I saw a circuit
set-up of the call though.
diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h
index 1f83659..db22bcf 100644
--- a/openbsc/include/openbsc/mgcp_internal.h
+++ b/openbsc/include/openbsc/mgcp_internal.h
@@ -192,6 +192,8 @@
 		/* Osmux state: disabled, activating, active */
 		enum osmux_state state;
 		/* Allocated Osmux circuit ID for this endpoint */
+		int allocated_cid;
+		/* Used Osmux circuit ID for this endpoint */
 		uint8_t cid;
 		/* handle to batch messages */
 		struct osmux_in_handle *in;
diff --git a/openbsc/include/openbsc/osmux.h b/openbsc/include/openbsc/osmux.h
index 8c01fd0..82b8fa3 100644
--- a/openbsc/include/openbsc/osmux.h
+++ b/openbsc/include/openbsc/osmux.h
@@ -14,6 +14,8 @@
 int osmux_enable_endpoint(struct mgcp_endpoint *endp, int role,
 			  struct in_addr *addr, uint16_t port);
 void osmux_disable_endpoint(struct mgcp_endpoint *endp);
+void osmux_allocate_cid(struct mgcp_endpoint *endp);
+void osmux_release_cid(struct mgcp_endpoint *endp);
 
 int osmux_xfrm_to_rtp(struct mgcp_endpoint *endp, int type, char *buf, int rc);
 int osmux_xfrm_to_osmux(int type, char *buf, int rc, struct mgcp_endpoint *endp);
@@ -22,6 +24,7 @@
 
 int osmux_get_cid(void);
 void osmux_put_cid(uint8_t osmux_cid);
+int osmux_used_cid(void);
 
 enum osmux_state {
 	OSMUX_STATE_DISABLED = 0,
@@ -29,4 +32,10 @@
 	OSMUX_STATE_ENABLED,
 };
 
+enum osmux_usage {
+	OSMUX_USAGE_OFF = 0,
+	OSMUX_USAGE_ON = 1,
+	OSMUX_USAGE_ONLY = 2,
+};
+
 #endif
diff --git a/openbsc/src/libmgcp/mgcp_osmux.c b/openbsc/src/libmgcp/mgcp_osmux.c
index 90b7368..2d39b2c 100644
--- a/openbsc/src/libmgcp/mgcp_osmux.c
+++ b/openbsc/src/libmgcp/mgcp_osmux.c
@@ -492,6 +492,19 @@
 	osmux_handle_put(endp->osmux.in);
 }
 
+void osmux_release_cid(struct mgcp_endpoint *endp)
+{
+	if (endp->osmux.allocated_cid >= 0)
+		osmux_put_cid(endp->osmux.allocated_cid);
+	endp->osmux.allocated_cid = -1;
+}
+
+void osmux_allocate_cid(struct mgcp_endpoint *endp)
+{
+	osmux_release_cid(endp);
+	endp->osmux.allocated_cid = osmux_get_cid();
+}
+
 /* We don't need to send the dummy load for osmux so often as another endpoint
  * may have already punched the hole in the firewall. This approach is simple
  * though.
@@ -532,11 +545,25 @@
 /* bsc-nat allocates/releases the Osmux circuit ID */
 static uint8_t osmux_cid_bitmap[16];
 
+int osmux_used_cid(void)
+{
+	int i, j, used = 0;
+
+	for (i = 0; i < sizeof(osmux_cid_bitmap); i++) {
+		for (j = 0; j < 8; j++) {
+			if (osmux_cid_bitmap[i] & (1 << j))
+				used += 1;
+		}
+	}
+
+	return used;
+}
+
 int osmux_get_cid(void)
 {
 	int i, j;
 
-	for (i = 0; i < sizeof(osmux_cid_bitmap) / 8; i++) {
+	for (i = 0; i < sizeof(osmux_cid_bitmap); i++) {
 		for (j = 0; j < 8; j++) {
 			if (osmux_cid_bitmap[i] & (1 << j))
 				continue;
diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c
index ab98be1..42ce8bb 100644
--- a/openbsc/src/libmgcp/mgcp_protocol.c
+++ b/openbsc/src/libmgcp/mgcp_protocol.c
@@ -818,6 +818,10 @@
 	if (osmux_cid >= 0) {
 		endp->osmux.cid = osmux_cid;
 		endp->osmux.state = OSMUX_STATE_ACTIVATING;
+	} else if(endp->cfg->osmux == OSMUX_USAGE_ONLY) {
+		LOGP(DMGCP, LOGL_ERROR,
+			"Osmux only and no osmux offered on 0x%x\n", ENDPOINT_NUMBER(endp));
+		goto error2;
 	}
 
 	endp->allocated = 1;
@@ -1310,6 +1314,7 @@
 		return -1;
 
 	for (i = 0; i < tcfg->number_endpoints; ++i) {
+		tcfg->endpoints[i].osmux.allocated_cid = -1;
 		tcfg->endpoints[i].ci = CI_UNUSED;
 		tcfg->endpoints[i].cfg = tcfg->cfg;
 		tcfg->endpoints[i].tcfg = tcfg;
@@ -1350,6 +1355,9 @@
 	if (endp->osmux.state == OSMUX_STATE_ENABLED)
 		osmux_disable_endpoint(endp);
 
+	/* release the circuit ID if it had been allocated */
+	osmux_release_cid(endp);
+
 	memset(&endp->taps, 0, sizeof(endp->taps));
 }
 
diff --git a/openbsc/src/libmgcp/mgcp_vty.c b/openbsc/src/libmgcp/mgcp_vty.c
index fc2f818..c478b0a 100644
--- a/openbsc/src/libmgcp/mgcp_vty.c
+++ b/openbsc/src/libmgcp/mgcp_vty.c
@@ -139,8 +139,19 @@
 	if (g_cfg->bts_force_ptime > 0)
 		vty_out(vty, "  rtp force-ptime %d%s", g_cfg->bts_force_ptime, VTY_NEWLINE);
 	vty_out(vty, "  transcoder-remote-base %u%s", g_cfg->transcoder_remote_base, VTY_NEWLINE);
-	vty_out(vty, "  osmux %s%s",
-		g_cfg->osmux == 1 ? "on" : "off", VTY_NEWLINE);
+
+	switch (g_cfg->osmux) {
+	case OSMUX_USAGE_ON:
+		vty_out(vty, "  osmux on%s", VTY_NEWLINE);
+		break;
+	case OSMUX_USAGE_ONLY:
+		vty_out(vty, "  osmux only%s", VTY_NEWLINE);
+		break;
+	case OSMUX_USAGE_OFF:
+	default:
+		vty_out(vty, "  osmux off%s", VTY_NEWLINE);
+		break;
+	}
 	if (g_cfg->osmux) {
 		vty_out(vty, "  osmux batch-factor %d%s",
 			g_cfg->osmux_batch, VTY_NEWLINE);
@@ -226,6 +237,9 @@
 	llist_for_each_entry(trunk, &g_cfg->trunks, entry)
 		dump_trunk(vty, trunk, show_stats);
 
+	if (g_cfg->osmux)
+		vty_out(vty, "Osmux used CID: %d%s", osmux_used_cid(), VTY_NEWLINE);
+
 	return CMD_SUCCESS;
 }
 
@@ -1246,18 +1260,24 @@
 #define OSMUX_STR "RTP multiplexing\n"
 DEFUN(cfg_mgcp_osmux,
       cfg_mgcp_osmux_cmd,
-      "osmux (on|off)",
-       OSMUX_STR "Enable OSMUX\n" "Disable OSMUX\n")
+      "osmux (on|off|only)",
+       OSMUX_STR "Enable OSMUX\n" "Disable OSMUX\n" "Only use OSMUX\n")
 {
-	if (strcmp(argv[0], "on") == 0) {
-		g_cfg->osmux = 1;
-		if (g_cfg->trunk.audio_loop) {
-			vty_out(vty, "Cannot use `loop' with `osmux'.%s",
-				VTY_NEWLINE);
-			return CMD_WARNING;
-		}
-	} else if (strcmp(argv[0], "off") == 0)
-		g_cfg->osmux = 0;
+	if (strcmp(argv[0], "off") == 0) {
+		g_cfg->osmux = OSMUX_USAGE_OFF;
+		return CMD_SUCCESS;
+	}
+
+	if (strcmp(argv[0], "on") == 0)
+		g_cfg->osmux = OSMUX_USAGE_ON;
+	else if (strcmp(argv[0], "only") == 0)
+		g_cfg->osmux = OSMUX_USAGE_ONLY;
+
+	if (g_cfg->trunk.audio_loop) {
+		vty_out(vty, "Cannot use `loop' with `osmux'.%s",
+			VTY_NEWLINE);
+		return CMD_WARNING;
+	}
 
 	return CMD_SUCCESS;
 }
diff --git a/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c b/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c
index f19cb4c..9fd9967 100644
--- a/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c
+++ b/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c
@@ -499,6 +499,15 @@
 	return NULL;
 }
 
+static int nat_osmux_only(struct mgcp_config *mgcp_cfg, struct bsc_config *bsc_cfg)
+{
+	if (mgcp_cfg->osmux == OSMUX_USAGE_ONLY)
+		return 1;
+	if (bsc_cfg->osmux == OSMUX_USAGE_ONLY)
+		return 1;
+	return 0;
+}
+
 static int bsc_mgcp_policy_cb(struct mgcp_trunk_config *tcfg, int endpoint, int state, const char *transaction_id)
 {
 	struct bsc_nat *nat;
@@ -506,7 +515,6 @@
 	struct nat_sccp_connection *sccp;
 	struct mgcp_endpoint *mgcp_endp;
 	struct msgb *bsc_msg;
-	int osmux_cid = -1;
 
 	nat = tcfg->cfg->data;
 	bsc_endp = &nat->bsc_endpoints[endpoint];
@@ -544,14 +552,23 @@
 	}
 
 	/* Allocate a Osmux circuit ID */
-	if (state == MGCP_ENDP_CRCX &&
-	    nat->mgcp_cfg->osmux && sccp->bsc->cfg->osmux)
-		osmux_cid = osmux_get_cid();
+	if (state == MGCP_ENDP_CRCX) {
+		if (nat->mgcp_cfg->osmux && sccp->bsc->cfg->osmux) {
+			osmux_allocate_cid(mgcp_endp);
+			if (mgcp_endp->osmux.allocated_cid < 0 &&
+				nat_osmux_only(nat->mgcp_cfg, sccp->bsc->cfg)) {
+				LOGP(DMGCP, LOGL_ERROR,
+					"Rejecting usage of endpoint\n");
+				return MGCP_POLICY_REJECT;
+			}
+		}
+	}
 
 	/* we need to generate a new and patched message */
 	bsc_msg = bsc_mgcp_rewrite((char *) nat->mgcp_msg, nat->mgcp_length,
 				   sccp->bsc_endp, mgcp_bts_src_addr(mgcp_endp),
-				   mgcp_endp->bts_end.local_port, osmux_cid,
+				   mgcp_endp->bts_end.local_port,
+				   mgcp_endp->osmux.allocated_cid,
 				   &mgcp_endp->net_end.codec.payload_type,
 				   nat->sdp_ensure_amr_mode_set);
 	if (!bsc_msg) {
@@ -571,10 +588,10 @@
 		/* Annotate the allocated Osmux CID until the bsc confirms that
 		 * it agrees to use Osmux for this voice flow.
 		 */
-		if (osmux_cid >= 0 &&
+		if (mgcp_endp->osmux.allocated_cid >= 0 &&
 		    mgcp_endp->osmux.state != OSMUX_STATE_ENABLED) {
 			mgcp_endp->osmux.state = OSMUX_STATE_ACTIVATING;
-			mgcp_endp->osmux.cid = osmux_cid;
+			mgcp_endp->osmux.cid = mgcp_endp->osmux.allocated_cid;
 		}
 
 		socklen_t len = sizeof(sock);
@@ -596,7 +613,7 @@
 
 		/* libmgcp clears the MGCP endpoint for us */
 		if (mgcp_endp->osmux.state == OSMUX_STATE_ENABLED)
-			osmux_put_cid(mgcp_endp->osmux.cid);
+			osmux_release_cid(mgcp_endp);
 
 		return MGCP_POLICY_CONT;
 	} else {
@@ -665,8 +682,7 @@
 	     osmux_cid);
 	return;
 err:
-	osmux_put_cid(endp->osmux.cid);
-	endp->osmux.cid = -1;
+	osmux_release_cid(endp);
 	endp->osmux.state = OSMUX_STATE_DISABLED;
 }
 
@@ -734,6 +750,16 @@
 	if (endp->osmux.state == OSMUX_STATE_ACTIVATING)
 		bsc_mgcp_osmux_confirm(endp, (const char *) msg->l2h);
 
+	/* If we require osmux and it is disabled.. fail */
+	if (nat_osmux_only(bsc->nat->mgcp_cfg, bsc->cfg) &&
+		endp->osmux.state == OSMUX_STATE_DISABLED) {
+		LOGP(DMGCP, LOGL_ERROR,
+			"Failed to activate osmux endpoint 0x%x\n",
+			ENDPOINT_NUMBER(endp));
+		free_chan_downstream(endp, bsc_endp, bsc);
+		return;
+	}
+
 	/* free some stuff */
 	talloc_free(bsc_endp->transaction_id);
 	bsc_endp->transaction_id = NULL;
@@ -792,7 +818,7 @@
 	int slen;
 	int ret;
 	char buf[40];
-	char osmux_extension[strlen("X-Osmux: 255")];
+	char osmux_extension[strlen("\nX-Osmux: 255") + 1];
 
 	buf[0] = buf[39] = '\0';
 	ret = sscanf(tok, "%*s %s", buf);
@@ -803,7 +829,7 @@
 	}
 
 	if (osmux_cid >= 0)
-		sprintf(osmux_extension, "\nX-Osmux: %u", osmux_cid);
+		sprintf(osmux_extension, "\nX-Osmux: %u", osmux_cid & 0xff);
 	else
 		osmux_extension[0] = '\0';
 
diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c b/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c
index 981168c..cd8293c 100644
--- a/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c
+++ b/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c
@@ -162,8 +162,14 @@
 	if (bsc->paging_group != -1)
 		vty_out(vty, "  paging group %d%s", bsc->paging_group, VTY_NEWLINE);
 	vty_out(vty, "  paging forbidden %d%s", bsc->forbid_paging, VTY_NEWLINE);
-	if (bsc->osmux)
+	switch (bsc->osmux) {
+	case OSMUX_USAGE_ON:
 		vty_out(vty, "  osmux on%s", VTY_NEWLINE);
+		break;
+	case OSMUX_USAGE_ONLY:
+		vty_out(vty, "  osmux only%s", VTY_NEWLINE);
+		break;
+	}
 }
 
 static int config_write_bsc(struct vty *vty)
@@ -1124,18 +1130,20 @@
 #define OSMUX_STR "RTP multiplexing\n"
 DEFUN(cfg_bsc_osmux,
       cfg_bsc_osmux_cmd,
-      "osmux (on|off)",
-       OSMUX_STR "Enable OSMUX\n" "Disable OSMUX\n")
+      "osmux (on|off|only)",
+       OSMUX_STR "Enable OSMUX\n" "Disable OSMUX\n" "Only OSMUX\n")
 {
 	struct bsc_config *conf = vty->index;
 	int old = conf->osmux;
 
 	if (strcmp(argv[0], "on") == 0)
-		conf->osmux = 1;
+		conf->osmux = OSMUX_USAGE_ON;
 	else if (strcmp(argv[0], "off") == 0)
-		conf->osmux = 0;
+		conf->osmux = OSMUX_USAGE_OFF;
+	else if (strcmp(argv[0], "only") == 0)
+		conf->osmux = OSMUX_USAGE_ONLY;
 
-	if (old == 0 && conf->osmux == 1 && !conf->nat->mgcp_cfg->osmux_init) {
+	if (old == 0 && conf->osmux > 0 && !conf->nat->mgcp_cfg->osmux_init) {
 		LOGP(DMGCP, LOGL_NOTICE, "Setting up OSMUX socket\n");
 		if (osmux_init(OSMUX_ROLE_BSC_NAT, conf->nat->mgcp_cfg) < 0) {
 			LOGP(DMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
@@ -1143,7 +1151,7 @@
 				VTY_NEWLINE);
 			return CMD_WARNING;
 		}
-	} else if (old == 1 && conf->osmux == 0) {
+	} else if (old > 0 && conf->osmux == 0) {
 		LOGP(DMGCP, LOGL_NOTICE, "Disabling OSMUX socket\n");
 		/* Don't stop the socket, we may already have ongoing voice
 		 * flows already using Osmux. This just switch indicates that
diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c
index b2cb938..ec86c55 100644
--- a/openbsc/tests/mgcp/mgcp_test.c
+++ b/openbsc/tests/mgcp/mgcp_test.c
@@ -1175,6 +1175,31 @@
 	talloc_free(cfg);
 }
 
+static void test_osmux_cid(void)
+{
+	int id, i;
+
+	OSMO_ASSERT(osmux_used_cid() == 0);
+	id = osmux_get_cid();
+	OSMO_ASSERT(id == 0);
+	OSMO_ASSERT(osmux_used_cid() == 1);
+	osmux_put_cid(id);
+	OSMO_ASSERT(osmux_used_cid() == 0);
+
+	for (i = 0; i < 128; ++i) {
+		id = osmux_get_cid();
+		OSMO_ASSERT(id == i);
+		OSMO_ASSERT(osmux_used_cid() == i + 1);
+	}
+
+	id = osmux_get_cid();
+	OSMO_ASSERT(id == -1);
+
+	for (i = 0; i < 256; ++i)
+		osmux_put_cid(i);
+	OSMO_ASSERT(osmux_used_cid() == 0);
+}
+
 int main(int argc, char **argv)
 {
 	osmo_init_logging(&log_info);
@@ -1193,6 +1218,7 @@
 	test_multilple_codec();
 	test_no_cycle();
 	test_no_name();
+	test_osmux_cid();
 
 	printf("Done\n");
 	return EXIT_SUCCESS;