mgcp: Introduce multiple virtual trunks

A virtual trunk is identified by a virtual domain name.
diff --git a/include/mgcp/mgcp.h b/include/mgcp/mgcp.h
index d5574ed..50b11cc 100644
--- a/include/mgcp/mgcp.h
+++ b/include/mgcp/mgcp.h
@@ -122,6 +122,7 @@
 	struct mgcp_endpoint *endpoints;
 
 	/* Special MGW handling */
+	char *virtual_domain;
 	int target_trunk_start;
 	int vad_enabled;
 
@@ -165,7 +166,7 @@
 	void *data;
 
 	/* trunk handling */
-	struct mgcp_trunk_config trunk;
+	struct llist_head vtrunks;
 	struct llist_head trunks;
 
 	/* only used for start with a static configuration */
diff --git a/include/mgcp/mgcp_internal.h b/include/mgcp/mgcp_internal.h
index d499166..f0faf52 100644
--- a/include/mgcp/mgcp_internal.h
+++ b/include/mgcp/mgcp_internal.h
@@ -154,7 +154,9 @@
 }
 
 struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index);
+struct mgcp_trunk_config *mgcp_vtrunk_alloc(struct mgcp_config *cfg, const char *);
 struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index);
+struct mgcp_trunk_config *mgcp_trunk_domain(struct mgcp_config *cfg, const char *);
 void mgcp_trunk_free(struct mgcp_trunk_config *cfg);
 
 
diff --git a/include/ss7_vty.h b/include/ss7_vty.h
index a0368bd..da599e5 100644
--- a/include/ss7_vty.h
+++ b/include/ss7_vty.h
@@ -28,6 +28,7 @@
 enum ss7_vty_node {
 	MGCP_NODE = _LAST_OSMOVTY_NODE + 1,
 	TRUNK_NODE,
+	VTRUNK_NODE,
 	CELLMGR_NODE,
 	SS7_NODE,
 	LINKSETS_NODE,
diff --git a/src/mgcp/mgcp_protocol.c b/src/mgcp/mgcp_protocol.c
index d583cee..6dc6090 100644
--- a/src/mgcp/mgcp_protocol.c
+++ b/src/mgcp/mgcp_protocol.c
@@ -308,6 +308,27 @@
 	return &tcfg->endpoints[endp];
 }
 
+struct mgcp_endpoint *find_virtual_endpoint(struct mgcp_config *cfg,
+					    const char *endptr, int gw)
+{
+	struct mgcp_trunk_config *tcfg;
+
+	if (gw <= 0)
+		return NULL;
+
+	llist_for_each_entry(tcfg, &cfg->vtrunks, entry) {
+		if (strcmp(&endptr[1], tcfg->virtual_domain) != 0)
+			continue;
+
+		if (gw >= tcfg->number_endpoints)
+			return NULL;
+
+		return &tcfg->endpoints[gw];
+	}
+
+	return NULL;
+}
+
 static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg, const char *mgcp)
 {
 	struct mgcp_endpoint *endp = NULL;
@@ -318,8 +339,7 @@
 		endp = find_e1_endpoint(cfg, mgcp);
 	} else {
 		gw = strtoul(mgcp, &endptr, 16);
-		if (gw > 0 && gw < cfg->trunk.number_endpoints && strcmp(endptr, "@mgw") == 0)
-			endp = &cfg->trunk.endpoints[gw];
+		endp = find_virtual_endpoint(cfg, endptr, gw);
 	}
 
 	if (!endp) {
@@ -908,11 +928,7 @@
 	cfg->net_ports.base_port = RTP_PORT_NET_DEFAULT;
 
 	/* default trunk handling */
-	cfg->trunk.cfg = cfg;
-	cfg->trunk.trunk_nr = 0;
-	cfg->trunk.trunk_type = MGCP_TRUNK_VIRTUAL;
-	trunk_init(&cfg->trunk);
-
+	INIT_LLIST_HEAD(&cfg->vtrunks);
 	INIT_LLIST_HEAD(&cfg->trunks);
 
 	return cfg;
@@ -937,6 +953,32 @@
 	return trunk;
 }
 
+struct mgcp_trunk_config *mgcp_vtrunk_alloc(struct mgcp_config *cfg,
+					    const char *domain)
+{
+	struct mgcp_trunk_config *trunk;
+
+	trunk = talloc_zero(cfg, struct mgcp_trunk_config);
+	if (!trunk) {
+		LOGP(DMGCP, LOGL_ERROR, "Failed to allocate.\n");
+		return NULL;
+	}
+
+	trunk->virtual_domain = talloc_strdup(trunk, domain);
+	if (!trunk->virtual_domain) {
+		LOGP(DMGCP, LOGL_ERROR, "Failed to allocate.\n");
+		talloc_free(trunk);
+		return NULL;
+	}
+
+	trunk->cfg = cfg;
+	trunk->trunk_type = MGCP_TRUNK_VIRTUAL;
+	trunk->trunk_nr = 0;
+	trunk_init(trunk);
+	llist_add_tail(&trunk->entry, &cfg->vtrunks);
+	return trunk;
+}
+
 void mgcp_trunk_free(struct mgcp_trunk_config *cfg)
 {
 	llist_del(&cfg->entry);
@@ -954,6 +996,18 @@
 	return NULL;
 }
 
+struct mgcp_trunk_config *mgcp_trunk_domain(struct mgcp_config *cfg,
+					    const char *domain)
+{
+	struct mgcp_trunk_config *trunk;
+
+	llist_for_each_entry(trunk, &cfg->vtrunks, entry)
+		if (strcmp(trunk->virtual_domain, domain) == 0)
+			return trunk;
+
+	return NULL;
+}
+
 static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end)
 {
 	if (end->local_alloc == PORT_ALLOC_DYNAMIC) {
diff --git a/src/mgcp/mgcp_vty.c b/src/mgcp/mgcp_vty.c
index 15ea196..33bfe81 100644
--- a/src/mgcp/mgcp_vty.c
+++ b/src/mgcp/mgcp_vty.c
@@ -36,6 +36,7 @@
 /* MGW changes */
 extern void mgcp_write_extra(struct vty *vty, struct mgcp_config *cfg);
 extern void mgcp_write_trunk_extra(struct vty *vty, struct mgcp_trunk_config *cfg);
+extern void mgcp_write_vtrunk_extra(struct vty *vty, struct mgcp_trunk_config *cfg);
 
 static int allocate_endpoints(struct mgcp_trunk_config *tcfg);
 
@@ -43,6 +44,7 @@
 {
 	switch (vty->node) {
 	case TRUNK_NODE:
+	case VTRUNK_NODE:
 		vty->node = MGCP_NODE;
 		break;
 	default:
@@ -88,14 +90,15 @@
 
 struct mgcp_config *g_cfg = NULL;
 
-static struct mgcp_trunk_config *find_trunk(struct mgcp_config *cfg, int nr)
+static struct mgcp_trunk_config *find_trunk(struct mgcp_config *cfg,
+					    const char *type, const char *name)
 {
 	struct mgcp_trunk_config *trunk;
 
-	if (nr == 0)
-		trunk = &cfg->trunk;
+	if (strcmp(type, "virtual") == 0)
+		trunk = mgcp_trunk_domain(cfg, name);
 	else
-		trunk = mgcp_trunk_num(cfg, nr);
+		trunk = mgcp_trunk_num(cfg, atoi(name));
 
 	return trunk;
 }
@@ -109,6 +112,12 @@
 	1,
 };
 
+struct cmd_node vtrunk_node = {
+	VTRUNK_NODE,
+	"%s(vtrunk)#",
+	1,
+};
+
 struct cmd_node trunk_node = {
 	TRUNK_NODE,
 	"%s(trunk)#",
@@ -138,14 +147,6 @@
 			g_cfg->net_ports.range_start, g_cfg->net_ports.range_end, VTY_NEWLINE);
 
 	vty_out(vty, "  rtp ip-dscp %d%s", g_cfg->endp_dscp, VTY_NEWLINE);
-	if (g_cfg->trunk.audio_payload != -1)
-		vty_out(vty, "  sdp audio payload number %d%s",
-			g_cfg->trunk.audio_payload, VTY_NEWLINE);
-	if (g_cfg->trunk.audio_name)
-		vty_out(vty, "  sdp audio payload name %s%s",
-			g_cfg->trunk.audio_name, VTY_NEWLINE);
-	vty_out(vty, "  loop %u%s", !!g_cfg->trunk.audio_loop, VTY_NEWLINE);
-	vty_out(vty, "  number endpoints %u%s", g_cfg->trunk.number_endpoints - 1, VTY_NEWLINE);
 	if (g_cfg->call_agent_addr)
 		vty_out(vty, "  call agent ip %s%s", g_cfg->call_agent_addr, VTY_NEWLINE);
 	if (g_cfg->transcoder_ip)
@@ -159,7 +160,6 @@
 	vty_out(vty, "  transcoder-remote-base %u%s", g_cfg->transcoder_remote_base, VTY_NEWLINE);
 
 	mgcp_write_extra(vty, g_cfg);
-
 	return CMD_SUCCESS;
 }
 
@@ -197,7 +197,8 @@
 {
 	struct mgcp_trunk_config *trunk;
 
-	dump_trunk(vty, &g_cfg->trunk);
+	llist_for_each_entry(trunk, &g_cfg->vtrunks, entry)
+		dump_trunk(vty, trunk);
 
 	llist_for_each_entry(trunk, &g_cfg->trunks, entry)
 		dump_trunk(vty, trunk);
@@ -372,47 +373,21 @@
       "Set the IP_TOS socket attribute on the RTP/RTCP sockets.\n" "The DSCP value.")
 
 
-DEFUN(cfg_mgcp_sdp_payload_number,
-      cfg_mgcp_sdp_payload_number_cmd,
-      "sdp audio payload number <1-255>",
-      "Set the audio codec to use")
-{
-	unsigned int payload = atoi(argv[0]);
-	g_cfg->trunk.audio_payload = payload;
-	return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_sdp_payload_name,
-      cfg_mgcp_sdp_payload_name_cmd,
-      "sdp audio payload name NAME",
-      "Set the audio name to use")
-{
-	bsc_replace_string(g_cfg, &g_cfg->trunk.audio_name, argv[0]);
-	return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_loop,
-      cfg_mgcp_loop_cmd,
-      "loop (0|1)",
-      "Loop the audio")
-{
-	g_cfg->trunk.audio_loop = atoi(argv[0]);
-	return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_number_endp,
-      cfg_mgcp_number_endp_cmd,
+DEFUN(cfg_vtrunk_number_endp,
+      cfg_vtrunk_number_endp_cmd,
       "number endpoints <0-65534>",
       "The number of endpoints to allocate. This is not dynamic.")
 {
-	if (g_cfg->trunk.endpoints) {
+	struct mgcp_trunk_config *trunk = vty->index;
+
+	if (trunk->endpoints) {
 		vty_out(vty, "Can not change size.%s", VTY_NEWLINE);
 		return CMD_WARNING;
 	}
 
 	/* + 1 as we start counting at one */
-	g_cfg->trunk.number_endpoints = atoi(argv[0]) + 1;
-	if (allocate_endpoints(&g_cfg->trunk) != 0) {
+	trunk->number_endpoints = atoi(argv[0]) + 1;
+	if (allocate_endpoints(trunk) != 0) {
 		vty_out(vty, "Can not allocate endpoints.%s", VTY_NEWLINE);
 		return CMD_WARNING;
 	}
@@ -464,6 +439,27 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_mgcp_vtrunk, cfg_mgcp_vtrunk_cmd,
+      "vtrunk NAME",
+      "Configure a Virtual Trunk\n" "Domain Name\n")
+{
+	struct mgcp_trunk_config *trunk;
+
+	trunk = mgcp_trunk_domain(g_cfg, argv[0]);
+	if (!trunk)
+		trunk = mgcp_vtrunk_alloc(g_cfg, argv[0]);
+
+	if (!trunk) {
+		vty_out(vty, "%%Unable to allocate trunk %s.%s",
+			argv[0], VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	vty->node = VTRUNK_NODE;
+	vty->index = trunk;
+	return CMD_SUCCESS;
+}
+
 DEFUN(cfg_mgcp_trunk, cfg_mgcp_trunk_cmd,
       "trunk <1-64>",
       "Configure a SS7 trunk\n" "Trunk Nr\n")
@@ -493,19 +489,39 @@
 	return CMD_SUCCESS;
 }
 
+static void config_write_trunk_common(struct vty *vty,
+				      struct mgcp_trunk_config *tcfg)
+{
+	vty_out(vty, "  sdp audio payload number %d%s",
+		tcfg->audio_payload, VTY_NEWLINE);
+	vty_out(vty, "  sdp audio payload name %s%s",
+		tcfg->audio_name, VTY_NEWLINE);
+	vty_out(vty, "  loop %d%s",
+		tcfg->audio_loop, VTY_NEWLINE);
+}
+
 static int config_write_trunk(struct vty *vty)
 {
 	struct mgcp_trunk_config *trunk;
 
 	llist_for_each_entry(trunk, &g_cfg->trunks, entry) {
 		vty_out(vty, " trunk %d%s", trunk->trunk_nr, VTY_NEWLINE);
-		vty_out(vty, "  sdp audio payload number %d%s",
-			trunk->audio_payload, VTY_NEWLINE);
-		vty_out(vty, "  sdp audio payload name %s%s",
-			trunk->audio_name, VTY_NEWLINE);
-		vty_out(vty, "  loop %d%s",
-			trunk->audio_loop, VTY_NEWLINE);
+		config_write_trunk_common(vty, trunk);
+		mgcp_write_trunk_extra(vty, trunk);
+	}
 
+	return CMD_SUCCESS;
+}
+
+static int config_write_vtrunk(struct vty *vty)
+{
+	struct mgcp_trunk_config *trunk;
+
+	llist_for_each_entry(trunk, &g_cfg->vtrunks, entry) {
+		vty_out(vty, " vtrunk %s%s", trunk->virtual_domain, VTY_NEWLINE);
+		vty_out(vty, "  number endpoints %d%s",
+			trunk->number_endpoints - 1, VTY_NEWLINE);
+		config_write_trunk_common(vty, trunk);
 		mgcp_write_trunk_extra(vty, trunk);
 	}
 
@@ -546,16 +562,21 @@
 	return CMD_SUCCESS;
 }
 
+#define TRUNK_TYPE_STR "Virtual trunk\nE1 trunk\n"
+#define TRUNK_IDENT_STR "Trunk identifier depending on the type\n"
+
 DEFUN(loop_endp,
       loop_endp_cmd,
-      "loop-endpoint <0-64> NAME (0|1)",
-      "Loop a given endpoint\n" "Trunk number\n"
+      "loop-endpoint (virtual|e1) IDENT NAME (0|1)",
+      "Loop a given endpoint\n"
+      TRUNK_TYPE_STR
+      TRUNK_IDENT_STR
       "The name in hex of the endpoint\n" "Disable the loop\n" "Enable the loop\n")
 {
 	struct mgcp_trunk_config *trunk;
 	struct mgcp_endpoint *endp;
 
-	trunk = find_trunk(g_cfg, atoi(argv[0]));
+	trunk = find_trunk(g_cfg, argv[0], argv[1]);
 	if (!trunk) {
 		vty_out(vty, "%%Trunk %d not found in the config.%s",
 			atoi(argv[0]), VTY_NEWLINE);
@@ -568,7 +589,7 @@
 		return CMD_WARNING;
 	}
 
-	int endp_no = strtoul(argv[1], NULL, 16);
+	int endp_no = strtoul(argv[2], NULL, 16);
 	if (endp_no < 1 || endp_no >= trunk->number_endpoints) {
 		vty_out(vty, "Loopback number %s/%d is invalid.%s",
 		argv[1], endp_no, VTY_NEWLINE);
@@ -577,7 +598,7 @@
 
 
 	endp = &trunk->endpoints[endp_no];
-	int loop = atoi(argv[2]);
+	int loop = atoi(argv[3]);
 
 	if (loop)
 		endp->conn_mode = MGCP_CONN_LOOPBACK;
@@ -590,8 +611,10 @@
 
 DEFUN(tap_call,
       tap_call_cmd,
-      "tap-call <0-64> ENDPOINT (bts-in|bts-out|net-in|net-out) A.B.C.D <0-65534>",
-      "Forward data on endpoint to a different system\n" "Trunk number\n"
+      "tap-call (virtual|e1) IDENT ENDPOINT (bts-in|bts-out|net-in|net-out) A.B.C.D <0-65534>",
+      "Forward data on endpoint to a different system\n"
+      TRUNK_TYPE_STR
+      TRUNK_IDENT_STR
       "The endpoint in hex\n"
       "Forward the data coming from the bts\n"
       "Forward the data coming from the bts leaving to the network\n"
@@ -604,10 +627,10 @@
 	struct mgcp_endpoint *endp;
 	int port = 0;
 
-	trunk = find_trunk(g_cfg, atoi(argv[0]));
+	trunk = find_trunk(g_cfg, argv[0], argv[1]);
 	if (!trunk) {
 		vty_out(vty, "%%Trunk %d not found in the config.%s",
-			atoi(argv[0]), VTY_NEWLINE);
+			atoi(argv[1]), VTY_NEWLINE);
 		return CMD_WARNING;
 	}
 
@@ -617,7 +640,7 @@
 		return CMD_WARNING;
 	}
 
-	int endp_no = strtoul(argv[1], NULL, 16);
+	int endp_no = strtoul(argv[2], NULL, 16);
 	if (endp_no < 1 || endp_no >= trunk->number_endpoints) {
 		vty_out(vty, "Endpoint number %s/%d is invalid.%s",
 		argv[1], endp_no, VTY_NEWLINE);
@@ -626,13 +649,13 @@
 
 	endp = &trunk->endpoints[endp_no];
 
-	if (strcmp(argv[2], "bts-in") == 0) {
+	if (strcmp(argv[3], "bts-in") == 0) {
 		port = MGCP_TAP_BTS_IN;
-	} else if (strcmp(argv[2], "bts-out") == 0) {
+	} else if (strcmp(argv[3], "bts-out") == 0) {
 		port = MGCP_TAP_BTS_OUT;
-	} else if (strcmp(argv[2], "net-in") == 0) {
+	} else if (strcmp(argv[3], "net-in") == 0) {
 		port = MGCP_TAP_NET_IN;
-	} else if (strcmp(argv[2], "net-out") == 0) {
+	} else if (strcmp(argv[3], "net-out") == 0) {
 		port = MGCP_TAP_NET_OUT;
 	} else {
 		vty_out(vty, "Unknown mode... tricked vty?%s", VTY_NEWLINE);
@@ -641,24 +664,26 @@
 
 	tap = &endp->taps[port];
 	memset(&tap->forward, 0, sizeof(tap->forward));
-	inet_aton(argv[3], &tap->forward.sin_addr);
-	tap->forward.sin_port = htons(atoi(argv[4]));
+	inet_aton(argv[4], &tap->forward.sin_addr);
+	tap->forward.sin_port = htons(atoi(argv[5]));
 	tap->enabled = 1;
 	return CMD_SUCCESS;
 }
 
 DEFUN(free_endp, free_endp_cmd,
-      "free-endpoint <0-64> NUMBER",
-      "Free the given endpoint\n" "Trunk number\n"
+      "free-endpoint (virtual|e1) IDENT NUMBER",
+      "Free the given endpoint\n"
+      TRUNK_TYPE_STR
+      TRUNK_IDENT_STR
       "Endpoint number in hex.\n")
 {
 	struct mgcp_trunk_config *trunk;
 	struct mgcp_endpoint *endp;
 
-	trunk = find_trunk(g_cfg, atoi(argv[0]));
+	trunk = find_trunk(g_cfg, argv[0], argv[1]);
 	if (!trunk) {
 		vty_out(vty, "%%Trunk %d not found in the config.%s",
-			atoi(argv[0]), VTY_NEWLINE);
+			atoi(argv[1]), VTY_NEWLINE);
 		return CMD_WARNING;
 	}
 
@@ -668,7 +693,7 @@
 		return CMD_WARNING;
 	}
 
-	int endp_no = strtoul(argv[1], NULL, 16);
+	int endp_no = strtoul(argv[2], NULL, 16);
 	if (endp_no < 1 || endp_no >= trunk->number_endpoints) {
 		vty_out(vty, "Endpoint number %s/%d is invalid.%s",
 		argv[1], endp_no, VTY_NEWLINE);
@@ -711,10 +736,16 @@
 	install_element(MGCP_NODE, &cfg_mgcp_transcoder_cmd);
 	install_element(MGCP_NODE, &cfg_mgcp_no_transcoder_cmd);
 	install_element(MGCP_NODE, &cfg_mgcp_transcoder_remote_base_cmd);
-	install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_number_cmd);
-	install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_name_cmd);
-	install_element(MGCP_NODE, &cfg_mgcp_loop_cmd);
-	install_element(MGCP_NODE, &cfg_mgcp_number_endp_cmd);
+
+	install_element(MGCP_NODE, &cfg_mgcp_vtrunk_cmd);
+	install_node(&vtrunk_node, config_write_vtrunk);
+	install_default(VTRUNK_NODE);
+	install_element(VTRUNK_NODE, &ournode_exit_cmd);
+	install_element(VTRUNK_NODE, &ournode_end_cmd);
+	install_element(VTRUNK_NODE, &cfg_vtrunk_number_endp_cmd);
+	install_element(VTRUNK_NODE, &cfg_trunk_payload_number_cmd);
+	install_element(VTRUNK_NODE, &cfg_trunk_payload_name_cmd);
+	install_element(VTRUNK_NODE, &cfg_trunk_loop_cmd);
 
 	install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd);
 	install_node(&trunk_node, config_write_trunk);
@@ -821,9 +852,12 @@
 	g_cfg->last_bts_port = rtp_calculate_port(0, g_cfg->bts_ports.base_port);
 	g_cfg->last_net_port = rtp_calculate_port(0, g_cfg->net_ports.base_port);
 
-	if (configure_endpoints(&g_cfg->trunk) != 0) {
-		LOGP(DMGCP, LOGL_ERROR, "Failed to initialize the virtual trunk.\n");
-		return -1;
+	llist_for_each_entry(trunk, &g_cfg->vtrunks, entry) {
+		if (configure_endpoints(trunk) != 0) {
+			LOGP(DMGCP, LOGL_ERROR,
+			     "Failed to initialize virtual trunk %s.\n", trunk->virtual_domain);
+			return -1;
+		}
 	}
 
 	llist_for_each_entry(trunk, &g_cfg->trunks, entry) {
diff --git a/src/mgcp_ss7.c b/src/mgcp_ss7.c
index 4e2b803..745f312 100644
--- a/src/mgcp_ss7.c
+++ b/src/mgcp_ss7.c
@@ -700,7 +700,7 @@
 static struct mgcp_ss7 *mgcp_ss7_init(struct mgcp_config *cfg)
 {
 	struct mgcp_trunk_config *trunk;
-	int dsp_resource, i;
+	int dsp_resource;
 
 	struct mgcp_ss7 *conf = talloc_zero(NULL, struct mgcp_ss7);
 	if (!conf)
@@ -731,8 +731,8 @@
 
 	/* Now do the init of the trunks */
 	dsp_resource = 0;
-	for (i = 1; i < cfg->trunk.number_endpoints; ++i) {
-		if (configure_trunk(&cfg->trunk, &dsp_resource) != 0) {
+	llist_for_each_entry(trunk, &cfg->vtrunks, entry) {
+		if (configure_trunk(trunk, &dsp_resource) != 0) {
 			talloc_free(conf);
 			return NULL;
 		}
@@ -780,8 +780,9 @@
 	LOGP(DMGCP, LOGL_INFO, "Resetting all endpoints.\n");
 
 	/* free UniPorte and MGCP data */
-	free_trunk(&mgcp->cfg->trunk);
 
+	llist_for_each_entry(trunk, &mgcp->cfg->vtrunks, entry)
+		free_trunk(trunk);
 	llist_for_each_entry(trunk, &mgcp->cfg->trunks, entry)
 		free_trunk(trunk);
 }
diff --git a/src/mgcp_ss7_vty.c b/src/mgcp_ss7_vty.c
index e9c79dc..7020405 100644
--- a/src/mgcp_ss7_vty.c
+++ b/src/mgcp_ss7_vty.c
@@ -46,160 +46,27 @@
 	return CMD_SUCCESS;
 }
 
-DEFUN(cfg_mgcp_vad, cfg_mgcp_vad_cmd,
-      "vad (enabled|disabled)",
-      "Enable the Voice Activity Detection\n"
-      "Enable\n" "Disable\n")
-{
-	if (argv[0][0] == 'e')
-		g_cfg->trunk.vad_enabled = 1;
-	else
-		g_cfg->trunk.vad_enabled = 0;
-	return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_realloc, cfg_mgcp_realloc_cmd,
-      "force-realloc (0|1)",
-      "Force the reallocation of an endpoint\n"
-      "Disable\n" "Enable\n")
-{
-	g_cfg->trunk.force_realloc = atoi(argv[0]);
-	return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_inp_dig_gain, cfg_mgcp_inp_dig_gain_cmd,
-      "input-digital-gain <0-62>",
-      "Static Digital Input Gain\n"
-      "Gain value")
-{
-	g_cfg->trunk.digital_inp_gain = atoi(argv[0]);
-	return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_out_dig_gain, cfg_mgcp_out_dig_gain_cmd,
-      "outut-digital-gain <0-62>",
-      "Static Digital Output Gain\n"
-      "Gain value")
-{
-	g_cfg->trunk.digital_out_gain = atoi(argv[0]);
-	return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_upstr_agc, cfg_mgcp_upstr_agc_cmd,
-      "upstream-automatic-gain (0|1)",
-      "Enable automatic gain control on upstream\n"
-      "Disable\n" "Enabled\n")
-{
-	g_cfg->trunk.upstr_agc_enbl = argv[0][0] == '1';
-	return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgc_upstr_adp, cfg_mgcp_upstr_adp_cmd,
-      "upstream-adaptiton-rate <1-128>",
-      "Set the adaption rate in (dB/sec) * 10\n"
-      "Range\n")
-{
-	g_cfg->trunk.upstr_adp_rate = atoi(argv[0]);
-	return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_upstr_max_gain, cfg_mgcp_upstr_max_gain_cmd,
-      "upstream-max-applied-gain <0-49>",
-      "Maximum applied gain from -31db to 18db\n"
-      "Gain level\n")
-{
-	g_cfg->trunk.upstr_max_gain = atoi(argv[0]);
-	return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_upstr_target, cfg_mgcp_upstr_target_cmd,
-      "upstream-target-level <6-37>",
-      "Set the desired level in db\n"
-      "Desired lievel\n")
-{
-	g_cfg->trunk.upstr_target_lvl = atoi(argv[0]);
-	return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_dwnstr_agc, cfg_mgcp_dwnstr_agc_cmd,
-      "downstream-automatic-gain (0|1)",
-      "Enable automatic gain control on downstream\n"
-      "Disable\n" "Enabled\n")
-{
-	g_cfg->trunk.dwnstr_agc_enbl = argv[0][0] == '1';
-	return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgc_dwnstr_adp, cfg_mgcp_dwnstr_adp_cmd,
-      "downstream-adaptation-rate <1-128>",
-      "Set the adaption rate in (dB/sec) * 10\n"
-      "Range\n")
-{
-	g_cfg->trunk.dwnstr_adp_rate = atoi(argv[0]);
-	return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_dwnstr_max_gain, cfg_mgcp_dwnstr_max_gain_cmd,
-      "downstream-max-applied-gain <0-49>",
-      "Maximum applied gain from -31db to 18db\n"
-      "Gain level\n")
-{
-	g_cfg->trunk.dwnstr_max_gain = atoi(argv[0]);
-	return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_dwnstr_target, cfg_mgcp_dwnstr_target_cmd,
-      "downstream-target-level <6-37>",
-      "Set the desired level in db\n"
-      "Desired lievel\n")
-{
-	g_cfg->trunk.dwnstr_target_lvl = atoi(argv[0]);
-	return CMD_SUCCESS;
-}
-
-DEFUN_DEPRECATED(cfg_mgcp_endp_offset, cfg_mgcp_endp_offset_cmd,
-      "endpoint-offset <-60-60>",
-      "Offset to the CIC map\n" "Value to set\n")
-{
-	vty_out(vty, "%%endpoint-offset is not used anymore.%s", VTY_NEWLINE);
-	return CMD_WARNING;
-}
-
-DEFUN(cfg_mgcp_target_trunk, cfg_mgcp_target_trunk_cmd,
+DEFUN(cfg_vtrunk_target_trunk, cfg_vtrunk_target_trunk_cmd,
       "target-trunk-start <1-24>",
       "Map the virtual trunk to start here\n" "Trunk Nr\n")
 {
-	g_cfg->trunk.target_trunk_start = atoi(argv[0]);
+	struct mgcp_trunk_config *trunk = vty->index;
+	trunk->target_trunk_start = atoi(argv[0]);
 	return CMD_SUCCESS;
 }
 
 #define ENDP_BLOCK_STR "Block the Endpoint/Timeslot for Audio\n"
 
-DEFUN(cfg_mgcp_timeslot_block, cfg_mgcp_timeslot_block_cmd,
-      "block-endpoint <1-65534>",
-       ENDP_BLOCK_STR "Endpoint number\n")
-{
-	int nr = atoi(argv[0]);
-
-	if (g_cfg->trunk.number_endpoints <= nr) {
-		vty_out(vty, "%%Endpoint %d too big. Current size: %d%s",
-			nr, g_cfg->trunk.number_endpoints, VTY_NEWLINE);
-		return CMD_WARNING;
-	}
-
-	g_cfg->trunk.endpoints[nr].blocked = 1;
-	return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_block_defaults, cfg_mgcp_block_defaults_cmd,
+DEFUN(cfg_vtrunk_block_defaults, cfg_vtrunk_block_defaults_cmd,
       "block-defaults",
       "Block the default endpoints 0x0 and 0x1F\n")
 {
 	int i;
+	struct mgcp_trunk_config *trunk = vty->index;
 
-	for (i = 1; i < g_cfg->trunk.number_endpoints; ++i) {
+	for (i = 1; i < trunk->number_endpoints; ++i) {
 		int multiplex, timeslot;
-		struct mgcp_endpoint *endp = &g_cfg->trunk.endpoints[i];
+		struct mgcp_endpoint *endp = &trunk->endpoints[i];
 		mgcp_endpoint_to_timeslot(ENDPOINT_NUMBER(endp), &multiplex, &timeslot);
 
 		if (timeslot == 0x0 || timeslot == 0x1F)
@@ -357,10 +224,23 @@
       ENDP_BLOCK_STR "Endpoint number\n")
 {
 	struct mgcp_trunk_config *trunk = vty->index;
+	int no = atoi(argv[0]);
+
+	if (no <= 0 || no >= trunk->number_endpoints) {
+		vty_out(vty, "%%Endpoint does not fit: %d.%s", no, VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
 	trunk->endpoints[atoi(argv[0])].blocked = 1;
 	return CMD_SUCCESS;
 }
 
+void mgcp_write_extra(struct vty *vty, struct mgcp_config *cfg)
+{
+	vty_out(vty, "  configure-trunks %d%s",
+		cfg->configure_trunks, VTY_NEWLINE);
+}
+
 static void write_blocked_endpoints(struct vty *vty,
 				    struct mgcp_trunk_config *tcfg)
 {
@@ -374,40 +254,7 @@
 	}
 }
 
-void mgcp_write_extra(struct vty *vty, struct mgcp_config *cfg)
-{
-	vty_out(vty, "  configure-trunks %d%s",
-		cfg->configure_trunks, VTY_NEWLINE);
-	vty_out(vty, "  force-realloc %d%s",
-		cfg->trunk.force_realloc, VTY_NEWLINE);
-	vty_out(vty, "  vad %s%s",
-		cfg->trunk.vad_enabled ? "enabled" : "disabled", VTY_NEWLINE);
-	vty_out(vty, "  input-digital-gain %d%s",
-		cfg->trunk.digital_inp_gain, VTY_NEWLINE);
-	vty_out(vty, "  output-digital-gain %d%s",
-		cfg->trunk.digital_out_gain, VTY_NEWLINE);
-	vty_out(vty, "  upstream-automatic-gain %d%s",
-		cfg->trunk.upstr_agc_enbl, VTY_NEWLINE);
-	vty_out(vty, "  upstream-adaptation-rate %d%s",
-		cfg->trunk.upstr_adp_rate, VTY_NEWLINE);
-	vty_out(vty, "  upstream-max-applied-gain %d%s",
-		cfg->trunk.upstr_max_gain, VTY_NEWLINE);
-	vty_out(vty, "  upstream-target-level %d%s",
-		cfg->trunk.upstr_target_lvl, VTY_NEWLINE);
-	vty_out(vty, "  downstream-automatic-gain %d%s",
-		cfg->trunk.dwnstr_agc_enbl, VTY_NEWLINE);
-	vty_out(vty, "  downstream-adaptation-rate %d%s",
-		cfg->trunk.dwnstr_adp_rate, VTY_NEWLINE);
-	vty_out(vty, "  downstream-max-applied-gain %d%s",
-		cfg->trunk.dwnstr_max_gain, VTY_NEWLINE);
-	vty_out(vty, "  downstream-target-level %d%s",
-		cfg->trunk.dwnstr_target_lvl, VTY_NEWLINE);
-	vty_out(vty, "  target-trunk-start %d%s",
-		cfg->trunk.target_trunk_start, VTY_NEWLINE);
-	write_blocked_endpoints(vty, &cfg->trunk);
-}
-
-void mgcp_write_trunk_extra(struct vty *vty, struct mgcp_trunk_config *trunk)
+void write_trunk_extra(struct vty *vty, struct mgcp_trunk_config *trunk)
 {
 	vty_out(vty, "   force-realloc %d%s",
 		trunk->force_realloc, VTY_NEWLINE);
@@ -436,6 +283,18 @@
 	write_blocked_endpoints(vty, trunk);
 }
 
+void mgcp_write_trunk_extra(struct vty *vty, struct mgcp_trunk_config *trunk)
+{
+	write_trunk_extra(vty, trunk);
+}
+
+void mgcp_write_vtrunk_extra(struct vty *vty, struct mgcp_trunk_config *trunk)
+{
+	vty_out(vty, "   target-trunk-start %d%s",
+		trunk->target_trunk_start, VTY_NEWLINE);
+	write_trunk_extra(vty, trunk);
+}
+
 
 void mgcp_mgw_vty_init(void)
 {
@@ -445,22 +304,23 @@
 	mgcp_vty_init();
 
 	install_element(MGCP_NODE, &cfg_mgcp_configure_cmd);
-	install_element(MGCP_NODE, &cfg_mgcp_vad_cmd);
-	install_element(MGCP_NODE, &cfg_mgcp_realloc_cmd);
-	install_element(MGCP_NODE, &cfg_mgcp_inp_dig_gain_cmd);
-	install_element(MGCP_NODE, &cfg_mgcp_out_dig_gain_cmd);
-	install_element(MGCP_NODE, &cfg_mgcp_upstr_agc_cmd);
-	install_element(MGCP_NODE, &cfg_mgcp_upstr_adp_cmd);
-	install_element(MGCP_NODE, &cfg_mgcp_upstr_max_gain_cmd);
-	install_element(MGCP_NODE, &cfg_mgcp_upstr_target_cmd);
-	install_element(MGCP_NODE, &cfg_mgcp_dwnstr_agc_cmd);
-	install_element(MGCP_NODE, &cfg_mgcp_dwnstr_adp_cmd);
-	install_element(MGCP_NODE, &cfg_mgcp_dwnstr_max_gain_cmd);
-	install_element(MGCP_NODE, &cfg_mgcp_dwnstr_target_cmd);
-	install_element(MGCP_NODE, &cfg_mgcp_endp_offset_cmd);
-	install_element(MGCP_NODE, &cfg_mgcp_target_trunk_cmd);
-	install_element(MGCP_NODE, &cfg_mgcp_timeslot_block_cmd);
-	install_element(MGCP_NODE, &cfg_mgcp_block_defaults_cmd);
+
+	install_element(VTRUNK_NODE, &cfg_vtrunk_target_trunk_cmd);
+	install_element(VTRUNK_NODE, &cfg_vtrunk_block_defaults_cmd);
+	install_element(VTRUNK_NODE, &cfg_trunk_vad_cmd);
+	install_element(VTRUNK_NODE, &cfg_trunk_realloc_cmd);
+	install_element(VTRUNK_NODE, &cfg_trunk_inp_dig_gain_cmd);
+	install_element(VTRUNK_NODE, &cfg_trunk_out_dig_gain_cmd);
+	install_element(VTRUNK_NODE, &cfg_trunk_upstr_agc_cmd);
+	install_element(VTRUNK_NODE, &cfg_trunk_upstr_adp_cmd);
+	install_element(VTRUNK_NODE, &cfg_trunk_upstr_max_gain_cmd);
+	install_element(VTRUNK_NODE, &cfg_trunk_upstr_target_cmd);
+	install_element(VTRUNK_NODE, &cfg_trunk_dwnstr_agc_cmd);
+	install_element(VTRUNK_NODE, &cfg_trunk_dwnstr_adp_cmd);
+	install_element(VTRUNK_NODE, &cfg_trunk_dwnstr_max_gain_cmd);
+	install_element(VTRUNK_NODE, &cfg_trunk_dwnstr_target_cmd);
+	install_element(VTRUNK_NODE, &cfg_trunk_endp_offset_cmd);
+	install_element(VTRUNK_NODE, &cfg_trunk_timeslot_block_cmd);
 
 	install_element(TRUNK_NODE, &cfg_trunk_vad_cmd);
 	install_element(TRUNK_NODE, &cfg_trunk_realloc_cmd);