aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c')
-rw-r--r--openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c291
1 files changed, 260 insertions, 31 deletions
diff --git a/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c b/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c
index e5384d1a7..85f940b49 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 @@ static void bsc_mgcp_send_mdcx(struct bsc_connection *bsc, int port, struct mgcp
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 @@ void bsc_mgcp_init(struct sccp_connections *con)
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 @@ struct sccp_connections *bsc_mgcp_find_con(struct bsc_nat *nat, int endpoint)
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 @@ static void free_chan_downstream(struct mgcp_endpoint *endp, struct bsc_endpoint
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 @@ static void free_chan_downstream(struct mgcp_endpoint *endp, struct bsc_endpoint
* 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 @@ void bsc_mgcp_forward(struct bsc_connection *bsc, struct msgb *msg)
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 @@ void bsc_mgcp_forward(struct bsc_connection *bsc, struct msgb *msg)
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 @@ copy:
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 @@ static int mgcp_do_read(struct osmo_fd *fd)
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 @@ static int mgcp_do_write(struct osmo_fd *bfd, struct msgb *msg)
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 @@ int bsc_mgcp_nat_init(struct bsc_nat *nat)
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;