Merge commit 'zecke/mgcp-statistics'
diff --git a/openbsc/include/openbsc/bsc_nat.h b/openbsc/include/openbsc/bsc_nat.h
index 1698fa4..6860342 100644
--- a/openbsc/include/openbsc/bsc_nat.h
+++ b/openbsc/include/openbsc/bsc_nat.h
@@ -32,6 +32,7 @@
 #include <osmocom/core/rate_ctr.h>
 #include <osmocom/core/statistics.h>
 #include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/sccp/sccp_types.h>
 
 #include <regex.h>
 
@@ -111,6 +112,9 @@
 	int number_multiplexes;
 	int max_endpoints;
 	int last_endpoint;
+	int next_transaction;
+	uint32_t pending_dlcx_count;
+	struct llist_head pending_dlcx;
 
 	/* track the pending commands for this BSC */
 	struct llist_head cmd_pending;
@@ -270,6 +274,7 @@
 	struct mgcp_config *mgcp_cfg;
 	uint8_t mgcp_msg[4096];
 	int mgcp_length;
+	int mgcp_ipa;
 
 	/* msc things */
 	struct llist_head dests;
@@ -331,6 +336,33 @@
 	struct osmo_timer_list auth_timeout;
 };
 
+struct bsc_nat_call_stats {
+	struct llist_head entry;
+
+	struct sccp_source_reference remote_ref;
+	struct sccp_source_reference src_ref; /* as seen by the MSC */
+
+	/* mgcp options */
+	uint32_t ci;
+	int bts_rtp_port;
+	int net_rtp_port;
+	struct in_addr bts_addr;
+	struct in_addr net_addr;
+
+
+	/* as witnessed by the NAT */
+	uint32_t net_ps;
+	uint32_t net_os;
+	uint32_t bts_pr;
+	uint32_t bts_or;
+	uint32_t bts_expected;
+	uint32_t bts_jitter;
+	int      bts_loss;
+
+	uint32_t trans_id;
+	int msc_endpoint;
+};
+
 struct bsc_nat_reject_cause {
 	int lu_reject_cause;
 	int cm_reject_cause;
@@ -463,6 +495,9 @@
 int bsc_nat_barr_adapt(void *ctx, struct rb_root *rbtree, const struct osmo_config_list *);
 int bsc_nat_barr_find(struct rb_root *root, const char *imsi, int *cm, int *lu);
 
+void bsc_nat_send_mgcp_to_msc(struct bsc_nat *bsc_nat, struct msgb *msg);
+void bsc_nat_handle_mgcp(struct bsc_nat *bsc, struct msgb *msg);
+
 struct ctrl_handle *bsc_nat_controlif_setup(struct bsc_nat *nat, int port);
 void bsc_nat_ctrl_del_pending(struct bsc_cmd_list *pending);
 int bsc_nat_handle_ctrlif_msg(struct bsc_connection *bsc, struct msgb *msg);
diff --git a/openbsc/include/openbsc/mgcp.h b/openbsc/include/openbsc/mgcp.h
index 7a416dd..811dcfd 100644
--- a/openbsc/include/openbsc/mgcp.h
+++ b/openbsc/include/openbsc/mgcp.h
@@ -172,6 +172,7 @@
 void mgcp_free_endp(struct mgcp_endpoint *endp);
 int mgcp_reset_transcoder(struct mgcp_config *cfg);
 void mgcp_format_stats(struct mgcp_endpoint *endp, char *stats, size_t size);
+int mgcp_parse_stats(struct msgb *msg, uint32_t *ps, uint32_t *os, uint32_t *pr, uint32_t *_or, int *loss, uint32_t *jitter);
 
 /*
  * format helper functions
diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c
index 6919a59..dc5e0f9 100644
--- a/openbsc/src/libmgcp/mgcp_protocol.c
+++ b/openbsc/src/libmgcp/mgcp_protocol.c
@@ -1167,9 +1167,37 @@
 				&expected, &ploss);
 	jitter = mgcp_state_calc_jitter(&endp->net_state);
 
-	snprintf(msg, size, "\r\nP: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%d",
+	snprintf(msg, size, "\r\nP: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u",
 			endp->bts_end.packets, endp->bts_end.octets,
 			endp->net_end.packets, endp->net_end.octets,
 			ploss, jitter);
 	msg[size - 1] = '\0';
 }
+
+int mgcp_parse_stats(struct msgb *msg, uint32_t *ps, uint32_t *os,
+		uint32_t *pr, uint32_t *_or, int *loss, uint32_t *jitter)
+{
+	char *line, *save;
+	int rc;
+
+	/* initialize with bad values */
+	*ps = *os = *pr = *_or = *jitter = UINT_MAX;
+	*loss = INT_MAX;
+
+
+	line = strtok_r((char *) msg->l2h, "\r\n", &save);
+	if (!line)
+		return -1;
+
+	/* this can only parse the message that is created above... */
+	for_each_line(line, save) {
+		switch (line[0]) {
+		case 'P':
+			rc = sscanf(line, "P: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u",
+					ps, os, pr, _or, loss, jitter);
+			return rc == 6 ? 0 : -1;
+		}
+	}
+
+	return -1;
+}
diff --git a/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c b/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c
index e5384d1..85f940b 100644
--- a/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c
+++ b/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c
@@ -49,6 +49,7 @@
 #include <openbsc/ipaccess.h>
 #include <openbsc/mgcp.h>
 #include <openbsc/mgcp_internal.h>
+#include <openbsc/control_cmd.h>
 
 #include <osmocom/sccp/sccp.h>
 
@@ -62,6 +63,22 @@
 #include <errno.h>
 #include <unistd.h>
 
+static void send_direct(struct bsc_nat *nat, struct msgb *output)
+{
+	if (osmo_wqueue_enqueue(&nat->mgcp_cfg->gw_fd, output) != 0) {
+		LOGP(DMGCP, LOGL_ERROR, "Failed to queue MGCP msg.\n");
+		msgb_free(output);
+	}
+}
+
+static void mgcp_queue_for_call_agent(struct bsc_nat *nat, struct msgb *output)
+{
+	if (nat->mgcp_ipa)
+		bsc_nat_send_mgcp_to_msc(nat, output);
+	else
+		send_direct(nat, output);
+}
+
 int bsc_mgcp_nr_multiplexes(int max_endpoints)
 {
 	int div = max_endpoints / 32;
@@ -257,17 +274,23 @@
 		return;
 	}
 
-	#warning "The MDCX is not send to the BSC. It should"
+	bsc_write_mgcp(bsc, (uint8_t *) buf, len);
 }
 
-static void bsc_mgcp_send_dlcx(struct bsc_connection *bsc, int endpoint)
+static void bsc_mgcp_send_dlcx(struct bsc_connection *bsc, int endpoint, int trans)
 {
 	char buf[2096];
 	int len;
 
+	/*
+	 * The following is a bit of a spec violation. According to the
+	 * MGCP grammar the transaction id is are upto 9 digits but we
+	 * prefix it with an alpha numeric value so we can easily recognize
+	 * it as a response.
+	 */
 	len = snprintf(buf, sizeof(buf),
-		       "DLCX 26 %x@mgw MGCP 1.0\r\n"
-		       "Z: noanswer\r\n", endpoint);
+		       "DLCX nat-%u %x@mgw MGCP 1.0\r\n",
+			trans, endpoint);
 	if (len < 0) {
 		LOGP(DMGCP, LOGL_ERROR, "snprintf for DLCX failed.\n");
 		return;
@@ -282,18 +305,173 @@
 	con->bsc_endp = -1;
 }
 
+/**
+ * This code will remember the network side of the audio statistics and
+ * once the internal DLCX response arrives this can be combined with the
+ * the BSC side and forwarded as a trap.
+ */
+static void remember_pending_dlcx(struct sccp_connections *con, uint32_t transaction)
+{
+	struct bsc_nat_call_stats *stats;
+	struct bsc_connection *bsc = con->bsc;
+	struct mgcp_endpoint *endp;
+
+	stats = talloc_zero(bsc, struct bsc_nat_call_stats);
+	if (!stats) {
+		LOGP(DNAT, LOGL_NOTICE,
+			"Failed to allocate statistics for endpoint 0x%x\n",
+			con->msc_endp);
+		return;
+	}
+
+	/* take the endpoint here */
+	endp = &bsc->nat->mgcp_cfg->trunk.endpoints[con->msc_endp];
+
+	stats->remote_ref = con->remote_ref;
+	stats->src_ref = con->patched_ref;
+
+	stats->ci = endp->ci;
+	stats->bts_rtp_port = endp->bts_end.rtp_port;
+	stats->bts_addr = endp->bts_end.addr;
+	stats->net_rtp_port = endp->net_end.rtp_port;
+	stats->net_addr = endp->net_end.addr;
+
+	stats->net_ps = endp->net_end.packets;
+	stats->net_os = endp->net_end.octets;
+	stats->bts_pr = endp->bts_end.packets;
+	stats->bts_or = endp->bts_end.octets;
+	mgcp_state_calc_loss(&endp->bts_state, &endp->bts_end,
+				&stats->bts_expected, &stats->bts_loss);
+	stats->bts_jitter = mgcp_state_calc_jitter(&endp->bts_state);
+
+	stats->trans_id = transaction;
+	stats->msc_endpoint = con->msc_endp;
+
+	/*
+	 * Too many pending requests.. let's remove the first two items.
+	 */
+	if (!llist_empty(&bsc->pending_dlcx) &&
+			bsc->pending_dlcx_count >= bsc->cfg->max_endpoints * 3) {
+		struct bsc_nat_call_stats *tmp;
+		LOGP(DNAT, LOGL_ERROR,
+			"Too many(%d) pending DLCX responses on BSC: %d\n",
+			bsc->pending_dlcx_count, bsc->cfg->nr);
+		bsc->pending_dlcx_count -= 1;
+		tmp = (struct bsc_nat_call_stats *) bsc->pending_dlcx.next;
+		llist_del(&tmp->entry);
+		talloc_free(tmp);
+	}
+
+	bsc->pending_dlcx_count += 1;
+	llist_add_tail(&stats->entry, &bsc->pending_dlcx);
+}
+
 void bsc_mgcp_dlcx(struct sccp_connections *con)
 {
 	/* send a DLCX down the stream */
 	if (con->bsc_endp != -1 && con->bsc->_endpoint_status) {
+		LOGP(DNAT, LOGL_NOTICE,
+			"Endpoint 0x%x was allocated for bsc: %d. Freeing it.\n",
+			con->bsc_endp, con->bsc->cfg->nr);
 		if (con->bsc->_endpoint_status[con->bsc_endp] != 1)
 			LOGP(DNAT, LOGL_ERROR, "Endpoint 0x%x was not in use\n", con->bsc_endp);
+		remember_pending_dlcx(con, con->bsc->next_transaction);
 		con->bsc->_endpoint_status[con->bsc_endp] = 0;
-		bsc_mgcp_send_dlcx(con->bsc, con->bsc_endp);
+		bsc_mgcp_send_dlcx(con->bsc, con->bsc_endp, con->bsc->next_transaction++);
 		bsc_mgcp_free_endpoint(con->bsc->nat, con->msc_endp);
 	}
 
 	bsc_mgcp_init(con);
+
+}
+
+/*
+ * Search for the pending request
+ */
+static void handle_dlcx_response(struct bsc_connection *bsc, struct msgb *msg,
+			int code, const char *transaction)
+{
+	uint32_t trans_id = UINT32_MAX;
+	uint32_t b_ps, b_os, n_pr, n_or, jitter;
+	int loss;
+	struct bsc_nat_call_stats *tmp, *stat = NULL;
+	struct ctrl_cmd *cmd;
+
+	/* parse the transaction identifier */
+	int rc = sscanf(transaction, "nat-%u", &trans_id);
+	if (rc != 1) {
+		LOGP(DNAT, LOGL_ERROR, "Can not parse transaction id: '%s'\n",
+			transaction);
+		return;
+	}
+
+	/* find the answer for the request we made */
+	llist_for_each_entry(tmp, &bsc->pending_dlcx, entry) {
+		if (trans_id != tmp->trans_id)
+			continue;
+
+		stat = tmp;
+		break;
+	}
+
+	if (!stat) {
+		LOGP(DNAT, LOGL_ERROR,
+			"Can not find transaction for: %u\n", trans_id);
+		return;
+	}
+
+	/* attempt to parse the data now */
+	rc = mgcp_parse_stats(msg, &b_ps, &b_os, &n_pr, &n_or, &loss, &jitter);
+	if (rc != 0)
+		LOGP(DNAT, LOGL_ERROR,
+			"Can not parse connection statistics: %d\n", rc);
+
+	/* send a trap now */
+	cmd = ctrl_cmd_create(bsc, CTRL_TYPE_TRAP);
+	if (!cmd) {
+		LOGP(DNAT, LOGL_ERROR,
+			"Creating a ctrl cmd failed.\n");
+		goto free_stat;
+	}
+
+	cmd->id = "0";
+	cmd->variable = talloc_asprintf(cmd, "net.0.bsc.%d.call_stats.v2",
+				bsc->cfg->nr);
+	cmd->reply = talloc_asprintf(cmd,
+			"mg_ip_addr=%s,mg_port=%d,",
+			inet_ntoa(stat->net_addr),
+			stat->net_rtp_port);
+	cmd->reply = talloc_asprintf_append(cmd->reply,
+			"endpoint_ip_addr=%s,endpoint_port=%d,",
+			inet_ntoa(stat->bts_addr),
+			stat->bts_rtp_port);
+	cmd->reply = talloc_asprintf_append(cmd->reply,
+			"nat_pkt_in=%u,nat_pkt_out=%u,"
+			"nat_bytes_in=%u,nat_bytes_out=%u,"
+			"nat_jitter=%u,nat_pkt_lost=%d,",
+			stat->bts_pr, stat->net_ps,
+			stat->bts_or, stat->net_os,
+			stat->bts_jitter, stat->bts_loss);
+	cmd->reply = talloc_asprintf_append(cmd->reply,
+			"bsc_pkt_in=%u,bsc_pkt_out=%u,"
+			"bsc_bytes_in=%u,bsc_bytes_out=%u,"
+			"bsc_jitter=%u,bsc_pkt_lost=%d,",
+			n_pr, b_ps,
+			n_or, b_os,
+			jitter, loss);
+	cmd->reply = talloc_asprintf_append(cmd->reply,
+			"sccp_src_ref=%u,sccp_dst_ref=%u",
+			sccp_src_ref_to_int(&stat->src_ref),
+			sccp_src_ref_to_int(&stat->remote_ref));
+
+	/* send it and be done */
+	ctrl_cmd_send_to_all(bsc->nat->ctrl, cmd);
+	talloc_free(cmd);
+
+free_stat:
+	bsc->pending_dlcx_count -= 1;
+	llist_del(&stat->entry);
+	talloc_free(stat);
 }
 
 
@@ -314,7 +492,8 @@
 	if (con)
 		return con;
 
-	LOGP(DMGCP, LOGL_ERROR, "Failed to find the connection.\n");
+	LOGP(DMGCP, LOGL_ERROR,
+		"Failed to find the connection for endpoint: 0x%x\n", endpoint);
 	return NULL;
 }
 
@@ -419,7 +598,7 @@
 				ENDPOINT_NUMBER(endp));
 		} else {
 			if (con->bsc == bsc) {
-				bsc_mgcp_send_dlcx(bsc, con->bsc_endp);
+				bsc_mgcp_send_dlcx(bsc, con->bsc_endp, con->bsc->next_transaction++);
 			} else {
 				LOGP(DMGCP, LOGL_ERROR,
 					"Endpoint belongs to a different BSC\n");
@@ -436,6 +615,9 @@
  * this transaction and if it belongs to the BSC. Then we will
  * need to patch the content to point to the local network and we
  * need to update the I: that was assigned by the BSS.
+ *
+ * Only responses to CRCX and DLCX should arrive here. The DLCX
+ * needs to be handled specially to combine the two statistics.
  */
 void bsc_mgcp_forward(struct bsc_connection *bsc, struct msgb *msg)
 {
@@ -472,6 +654,11 @@
 		break;
 	}
 
+	if (!bsc_endp && strncmp("nat-", transaction_id, 4) == 0) {
+		handle_dlcx_response(bsc, msg, code, transaction_id);
+		return;
+	}
+
 	if (!bsc_endp) {
 		LOGP(DMGCP, LOGL_ERROR, "Could not find active endpoint: %s for msg: '%s'\n",
 		     transaction_id, (const char *) msg->l2h);
@@ -502,16 +689,16 @@
 		return;
 	}
 
-	if (osmo_wqueue_enqueue(&bsc->nat->mgcp_cfg->gw_fd, output) != 0) {
-		LOGP(DMGCP, LOGL_ERROR, "Failed to queue MGCP msg.\n");
-		msgb_free(output);
-	}
+	mgcp_queue_for_call_agent(bsc->nat, output);
 }
 
 int bsc_mgcp_parse_response(const char *str, int *code, char transaction[60])
 {
+	int rc;
 	/* we want to parse two strings */
-	return sscanf(str, "%3d %59s\n", code, transaction) != 2;
+	rc = sscanf(str, "%3d %59s\n", code, transaction) != 2;
+	transaction[59] = '\0';
+	return rc;
 }
 
 uint32_t bsc_mgcp_extract_ci(const char *str)
@@ -651,6 +838,38 @@
 	return output;
 }
 
+/*
+ * This comes from the MSC and we will now parse it. The caller needs
+ * to free the msgb.
+ */
+void bsc_nat_handle_mgcp(struct bsc_nat *nat, struct msgb *msg)
+{
+	struct msgb *resp;
+
+	if (!nat->mgcp_ipa) {
+		LOGP(DMGCP, LOGL_ERROR, "MGCP message not allowed on IPA.\n");
+		return;
+	}
+
+	if (msgb_l2len(msg) > sizeof(nat->mgcp_msg) - 1) {
+		LOGP(DMGCP, LOGL_ERROR, "MGCP msg too big for handling.\n");
+		return;
+	}
+
+	memcpy(nat->mgcp_msg, msg->l2h, msgb_l2len(msg));
+	nat->mgcp_length = msgb_l2len(msg);
+	nat->mgcp_msg[nat->mgcp_length] = '\0';
+
+	/* now handle the message */
+	resp = mgcp_handle_message(nat->mgcp_cfg, msg);
+
+	/* we do have a direct answer... e.g. AUEP */
+	if (resp)
+		mgcp_queue_for_call_agent(nat, resp);
+
+	return;
+}
+
 static int mgcp_do_read(struct osmo_fd *fd)
 {
 	struct bsc_nat *nat;
@@ -680,12 +899,8 @@
 	msgb_free(msg);
 
 	/* we do have a direct answer... e.g. AUEP */
-	if (resp) {
-		if (osmo_wqueue_enqueue(&nat->mgcp_cfg->gw_fd, resp) != 0) {
-			LOGP(DMGCP, LOGL_ERROR, "Failed to enqueue msg.\n");
-			msgb_free(resp);
-		}
-	}
+	if (resp)
+		mgcp_queue_for_call_agent(nat, resp);
 
 	return 0;
 }
@@ -704,21 +919,10 @@
 	return rc;
 }
 
-int bsc_mgcp_nat_init(struct bsc_nat *nat)
+static int init_mgcp_socket(struct bsc_nat *nat, struct mgcp_config *cfg)
 {
-	int on;
 	struct sockaddr_in addr;
-	struct mgcp_config *cfg = nat->mgcp_cfg;
-
-	if (!cfg->call_agent_addr) {
-		LOGP(DMGCP, LOGL_ERROR, "The BSC nat requires the call agent ip to be set.\n");
-		return -1;
-	}
-
-	if (cfg->bts_ip) {
-		LOGP(DMGCP, LOGL_ERROR, "Do not set the BTS ip for the nat.\n");
-		return -1;
-	}
+	int on;
 
 	cfg->gw_fd.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
 	if (cfg->gw_fd.bfd.fd < 0) {
@@ -764,6 +968,31 @@
 		return -1;
 	}
 
+	return 0;
+}
+
+int bsc_mgcp_nat_init(struct bsc_nat *nat)
+{
+	struct mgcp_config *cfg = nat->mgcp_cfg;
+
+	if (!cfg->call_agent_addr) {
+		LOGP(DMGCP, LOGL_ERROR, "The BSC nat requires the call agent ip to be set.\n");
+		return -1;
+	}
+
+	if (cfg->bts_ip) {
+		LOGP(DMGCP, LOGL_ERROR, "Do not set the BTS ip for the nat.\n");
+		return -1;
+	}
+
+	/* initialize the MGCP socket */
+	if (!nat->mgcp_ipa) {
+		int rc =  init_mgcp_socket(nat, cfg);
+		if (rc != 0)
+			return rc;
+	}
+
+
 	/* some more MGCP config handling */
 	cfg->data = nat;
 	cfg->policy_cb = bsc_mgcp_policy_cb;
diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat.c b/openbsc/src/osmo-bsc_nat/bsc_nat.c
index 792d33c..e70f549 100644
--- a/openbsc/src/osmo-bsc_nat/bsc_nat.c
+++ b/openbsc/src/osmo-bsc_nat/bsc_nat.c
@@ -333,6 +333,12 @@
 	bsc_write_mgcp(bsc, mgcp_reset, sizeof mgcp_reset - 1);
 }
 
+void bsc_nat_send_mgcp_to_msc(struct bsc_nat *nat, struct msgb *msg)
+{
+	ipaccess_prepend_header(msg, IPAC_PROTO_MGCP_OLD);
+	queue_for_msc(nat->msc_con, msg);
+}
+
 /*
  * Below is the handling of messages coming
  * from the MSC and need to be forwarded to
@@ -820,8 +826,11 @@
 			initialize_msc_if_needed(msc_con);
 		else if (msg->l2h[0] == IPAC_MSGT_ID_GET)
 			send_id_get_response(msc_con);
-	} else if (hh->proto == IPAC_PROTO_SCCP)
+	} else if (hh->proto == IPAC_PROTO_SCCP) {
 		forward_sccp_to_bts(msc_con, msg);
+	} else if (hh->proto == IPAC_PROTO_MGCP_OLD) {
+		bsc_nat_handle_mgcp(nat, msg);
+	}
 
 	msgb_free(msg);
 	return 0;
diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c b/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c
index 0c56a8c..2e6d9a3 100644
--- a/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c
+++ b/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c
@@ -132,6 +132,7 @@
 	con->nat = nat;
 	osmo_wqueue_init(&con->write_queue, 100);
 	INIT_LLIST_HEAD(&con->cmd_pending);
+	INIT_LLIST_HEAD(&con->pending_dlcx);
 	return con;
 }
 
diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c b/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c
index 7bbc890..e5993b4 100644
--- a/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c
+++ b/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c
@@ -143,6 +143,8 @@
 		write_acc_lst(vty, lst);
 	llist_for_each_entry(pgroup, &_nat->paging_groups, entry)
 		write_pgroup_lst(vty, pgroup);
+	if (_nat->mgcp_ipa)
+		vty_out(vty, " mgcp-through-msc-ipa%s", VTY_NEWLINE);
 
 	return CMD_SUCCESS;
 }
@@ -204,10 +206,11 @@
 
 	llist_for_each_entry(con, &_nat->bsc_connections, list_entry) {
 		getpeername(con->write_queue.bfd.fd, (struct sockaddr *) &sock, &len);
-		vty_out(vty, "BSC nr: %d auth: %d fd: %d peername: %s%s",
+		vty_out(vty, "BSC nr: %d auth: %d fd: %d peername: %s pending-stats: %u%s",
 			con->cfg ? con->cfg->nr : -1,
 			con->authenticated, con->write_queue.bfd.fd,
-			inet_ntoa(sock.sin_addr), VTY_NEWLINE);
+			inet_ntoa(sock.sin_addr), con->pending_dlcx_count,
+			VTY_NEWLINE);
 	}
 
 	return CMD_SUCCESS;
@@ -657,6 +660,20 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_nat_mgcp_ipa,
+      cfg_nat_mgcp_ipa_cmd,
+      "mgcp-through-msc-ipa",
+      "This needs to be set at start. Handle MGCP messages through "
+      "the IPA protocol and not through the UDP socket.\n")
+{
+	if (_nat->mgcp_cfg->data)
+		vty_out(vty,
+			"%%the setting will not be applied right now.%s",
+			VTY_NEWLINE);
+	_nat->mgcp_ipa = 1;
+	return CMD_SUCCESS;
+}
+
 /* per BSC configuration */
 DEFUN(cfg_bsc, cfg_bsc_cmd, "bsc BSC_NR",
       "BSC configuration\n" "Identifier of the BSC\n")
@@ -1086,6 +1103,7 @@
 	install_element(NAT_NODE, &cfg_nat_ussd_query_cmd);
 	install_element(NAT_NODE, &cfg_nat_ussd_token_cmd);
 	install_element(NAT_NODE, &cfg_nat_ussd_local_cmd);
+	install_element(NAT_NODE, &cfg_nat_mgcp_ipa_cmd);
 
 	/* access-list */
 	install_element(NAT_NODE, &cfg_lst_imsi_allow_cmd);
diff --git a/openbsc/tests/bsc-nat/Makefile.am b/openbsc/tests/bsc-nat/Makefile.am
index e284851..9b71d09 100644
--- a/openbsc/tests/bsc-nat/Makefile.am
+++ b/openbsc/tests/bsc-nat/Makefile.am
@@ -14,6 +14,7 @@
 			$(top_srcdir)/src/osmo-bsc_nat/bsc_nat_rewrite.c \
 			$(top_srcdir)/src/osmo-bsc_nat/bsc_mgcp_utils.c
 bsc_nat_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \
+			$(top_srcdir)/src/libctrl/libctrl.a \
 			$(top_srcdir)/src/libmgcp/libmgcp.a \
 			$(top_srcdir)/src/libtrau/libtrau.a \
 			$(top_srcdir)/src/libcommon/libcommon.a \
diff --git a/openbsc/tests/bsc-nat/bsc_nat_test.c b/openbsc/tests/bsc-nat/bsc_nat_test.c
index 66b4ff5..c2931f9 100644
--- a/openbsc/tests/bsc-nat/bsc_nat_test.c
+++ b/openbsc/tests/bsc-nat/bsc_nat_test.c
@@ -514,6 +514,7 @@
 					       33);
 	nat->mgcp_cfg = mgcp_config_alloc();
 	nat->mgcp_cfg->trunk.number_endpoints = 64;
+	mgcp_endpoints_allocate(&nat->mgcp_cfg->trunk);
 
 	bsc = bsc_connection_alloc(nat);
 	bsc->cfg = bsc_config_alloc(nat, "foo");
@@ -1242,3 +1243,9 @@
 	printf("Testing execution completed.\n");
 	return 0;
 }
+
+/* stub */
+void bsc_nat_send_mgcp_to_msc(struct bsc_nat *nat, struct msgb *msg)
+{
+	abort();
+}
diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c
index 4dfd2ab..5565e73 100644
--- a/openbsc/tests/mgcp/mgcp_test.c
+++ b/openbsc/tests/mgcp/mgcp_test.c
@@ -323,6 +323,30 @@
 	}
 }
 
+static void test_mgcp_stats(void)
+{
+	printf("Testing stat parsing\n");
+
+	uint32_t bps, bos, pr, _or, jitter;
+	struct msgb *msg;
+	int loss;
+	int rc;
+
+	msg = create_msg(DLCX_RET);
+	rc = mgcp_parse_stats(msg, &bps, &bos, &pr, &_or, &loss, &jitter);
+	printf("Parsing result: %d\n", rc);
+	if (bps != 0 || bos != 0 || pr != 0 ||  _or != 0 || loss != 0 || jitter != 0)
+		printf("FAIL: Parsing failed1.\n");
+	msgb_free(msg);
+
+	msg = create_msg("250 7 OK\r\nP: PS=10, OS=20, PR=30, OR=40, PL=-3, JI=40\r\n");
+	rc = mgcp_parse_stats(msg, &bps, &bos, &pr, &_or, &loss, &jitter);
+	printf("Parsing result: %d\n", rc);
+	if (bps != 10 || bos != 20 || pr != 30 || _or != 40 || loss != -3 || jitter != 40)
+		printf("FAIL: Parsing failed2.\n");
+	msgb_free(msg);
+}
+
 int main(int argc, char **argv)
 {
 	osmo_init_logging(&log_info);
@@ -331,6 +355,7 @@
 	test_retransmission();
 	test_packet_loss_calc();
 	test_rqnt_cb();
+	test_mgcp_stats();
 
 	printf("Done\n");
 	return EXIT_SUCCESS;
diff --git a/openbsc/tests/mgcp/mgcp_test.ok b/openbsc/tests/mgcp/mgcp_test.ok
index 224bba8..8711e38 100644
--- a/openbsc/tests/mgcp/mgcp_test.ok
+++ b/openbsc/tests/mgcp/mgcp_test.ok
@@ -25,4 +25,7 @@
 Testing DLCX
 Re-transmitting DLCX
 Testing packet loss calculation.
+Testing stat parsing
+Parsing result: 0
+Parsing result: 0
 Done