diff options
Diffstat (limited to 'src/osmo-bsc_nat')
-rw-r--r-- | src/osmo-bsc_nat/Makefile.am | 59 | ||||
-rw-r--r-- | src/osmo-bsc_nat/bsc_filter.c | 218 | ||||
-rw-r--r-- | src/osmo-bsc_nat/bsc_mgcp_utils.c | 1152 | ||||
-rw-r--r-- | src/osmo-bsc_nat/bsc_nat.c | 1736 | ||||
-rw-r--r-- | src/osmo-bsc_nat/bsc_nat_ctrl.c | 524 | ||||
-rw-r--r-- | src/osmo-bsc_nat/bsc_nat_filter.c | 119 | ||||
-rw-r--r-- | src/osmo-bsc_nat/bsc_nat_rewrite.c | 714 | ||||
-rw-r--r-- | src/osmo-bsc_nat/bsc_nat_rewrite_trie.c | 259 | ||||
-rw-r--r-- | src/osmo-bsc_nat/bsc_nat_utils.c | 535 | ||||
-rw-r--r-- | src/osmo-bsc_nat/bsc_nat_vty.c | 1336 | ||||
-rw-r--r-- | src/osmo-bsc_nat/bsc_sccp.c | 247 | ||||
-rw-r--r-- | src/osmo-bsc_nat/bsc_ussd.c | 453 |
12 files changed, 0 insertions, 7352 deletions
diff --git a/src/osmo-bsc_nat/Makefile.am b/src/osmo-bsc_nat/Makefile.am deleted file mode 100644 index b7c13ad3e..000000000 --- a/src/osmo-bsc_nat/Makefile.am +++ /dev/null @@ -1,59 +0,0 @@ -AM_CPPFLAGS = \ - $(all_includes) \ - -I$(top_srcdir)/include \ - -I$(top_builddir) \ - $(NULL) - -AM_CFLAGS = \ - -Wall \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) \ - $(LIBOSMOCTRL_CFLAGS) \ - $(LIBOSMOSCCP_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) \ - $(LIBOSMONETIF_CFLAGS) \ - $(LIBCRYPTO_CFLAGS) \ - $(COVERAGE_CFLAGS) \ - $(NULL) - -AM_LDFLAGS = \ - $(COVERAGE_LDFLAGS) \ - $(NULL) - -bin_PROGRAMS = \ - osmo-bsc_nat \ - $(NULL) - -osmo_bsc_nat_SOURCES = \ - bsc_filter.c \ - bsc_mgcp_utils.c \ - bsc_nat.c \ - bsc_nat_utils.c \ - bsc_nat_vty.c \ - bsc_sccp.c \ - bsc_ussd.c \ - bsc_nat_ctrl.c \ - bsc_nat_rewrite.c \ - bsc_nat_rewrite_trie.c \ - bsc_nat_filter.c \ - $(NULL) - -osmo_bsc_nat_LDADD = \ - $(top_builddir)/src/libmgcp/libmgcp.a \ - $(top_builddir)/src/libfilter/libfilter.a \ - $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libcommon-cs/libcommon-cs.a \ - $(top_builddir)/src/libtrau/libtrau.a \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(LIBOSMOSCCP_LIBS) \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOGSM_LIBS) \ - $(LIBOSMOVTY_LIBS) \ - $(LIBOSMOCTRL_LIBS) \ - $(LIBOSMOABIS_LIBS) \ - $(LIBOSMONETIF_LIBS) \ - $(LIBOSMOSIGTRAN_LIBS) \ - $(LIBCRYPTO_LIBS) \ - -lrt \ - $(NULL) diff --git a/src/osmo-bsc_nat/bsc_filter.c b/src/osmo-bsc_nat/bsc_filter.c deleted file mode 100644 index 6a9e99fb8..000000000 --- a/src/osmo-bsc_nat/bsc_filter.c +++ /dev/null @@ -1,218 +0,0 @@ -/* BSC Multiplexer/NAT */ - -/* - * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org> - * (C) 2010 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <openbsc/bsc_nat.h> -#include <openbsc/bsc_nat_sccp.h> -#include <openbsc/ipaccess.h> -#include <openbsc/debug.h> - -#include <osmocom/core/talloc.h> -#include <osmocom/gsm/protocol/gsm_08_08.h> - -#include <osmocom/sccp/sccp.h> - -/* - * The idea is to have a simple struct describing a IPA packet with - * SCCP SSN and the GSM 08.08 payload and decide. We will both have - * a white and a blacklist of packets we want to handle. - * - * TODO: Implement a "NOT" in the filter language. - */ - -#define ALLOW_ANY -1 - -#define FILTER_TO_BSC 1 -#define FILTER_TO_MSC 2 -#define FILTER_TO_BOTH 3 - - -struct bsc_pkt_filter { - int ipa_proto; - int dest_ssn; - int bssap; - int gsm; - int filter_dir; -}; - -static struct bsc_pkt_filter black_list[] = { - /* filter reset messages to the MSC */ - { IPAC_PROTO_SCCP, SCCP_SSN_BSSAP, 0, BSS_MAP_MSG_RESET, FILTER_TO_MSC }, - - /* filter reset ack messages to the BSC */ - { IPAC_PROTO_SCCP, SCCP_SSN_BSSAP, 0, BSS_MAP_MSG_RESET_ACKNOWLEDGE, FILTER_TO_BSC }, - - /* filter ip access */ - { IPAC_PROTO_IPACCESS, ALLOW_ANY, ALLOW_ANY, ALLOW_ANY, FILTER_TO_MSC }, -}; - -static struct bsc_pkt_filter white_list[] = { - /* allow IPAC_PROTO_SCCP messages to both sides */ - { IPAC_PROTO_SCCP, ALLOW_ANY, ALLOW_ANY, ALLOW_ANY, FILTER_TO_BOTH }, - - /* allow MGCP messages to both sides */ - { IPAC_PROTO_MGCP_OLD, ALLOW_ANY, ALLOW_ANY, ALLOW_ANY, FILTER_TO_BOTH }, -}; - -struct bsc_nat_parsed *bsc_nat_parse(struct msgb *msg) -{ - struct sccp_parse_result result; - struct bsc_nat_parsed *parsed; - struct ipaccess_head *hh; - - /* quick fail */ - if (msg->len < 4) - return NULL; - - parsed = talloc_zero(msg, struct bsc_nat_parsed); - if (!parsed) - return NULL; - - /* more init */ - parsed->ipa_proto = parsed->called_ssn = parsed->calling_ssn = -1; - parsed->sccp_type = parsed->bssap = parsed->gsm_type = -1; - - /* start parsing */ - hh = (struct ipaccess_head *) msg->data; - parsed->ipa_proto = hh->proto; - - msg->l2h = &hh->data[0]; - - /* do a size check on the input */ - if (ntohs(hh->len) != msgb_l2len(msg)) { - LOGP(DLINP, LOGL_ERROR, "Wrong input length?\n"); - talloc_free(parsed); - return NULL; - } - - /* analyze sccp down here */ - if (parsed->ipa_proto == IPAC_PROTO_SCCP) { - memset(&result, 0, sizeof(result)); - if (sccp_parse_header(msg, &result) != 0) { - talloc_free(parsed); - return 0; - } - - if (msg->l3h && msgb_l3len(msg) < 3) { - LOGP(DNAT, LOGL_ERROR, "Not enough space or GSM payload\n"); - talloc_free(parsed); - return 0; - } - - parsed->sccp_type = sccp_determine_msg_type(msg); - parsed->src_local_ref = result.source_local_reference; - parsed->dest_local_ref = result.destination_local_reference; - if (parsed->dest_local_ref) - parsed->original_dest_ref = *parsed->dest_local_ref; - parsed->called_ssn = result.called.ssn; - parsed->calling_ssn = result.calling.ssn; - - /* in case of connection confirm we have no payload */ - if (msg->l3h) { - parsed->bssap = msg->l3h[0]; - parsed->gsm_type = msg->l3h[2]; - } - } - - return parsed; -} - -int bsc_nat_filter_ipa(int dir, struct msgb *msg, struct bsc_nat_parsed *parsed) -{ - int i; - - /* go through the blacklist now */ - for (i = 0; i < ARRAY_SIZE(black_list); ++i) { - /* ignore the rule? */ - if (black_list[i].filter_dir != FILTER_TO_BOTH - && black_list[i].filter_dir != dir) - continue; - - /* the proto is not blacklisted */ - if (black_list[i].ipa_proto != ALLOW_ANY - && black_list[i].ipa_proto != parsed->ipa_proto) - continue; - - if (parsed->ipa_proto == IPAC_PROTO_SCCP) { - /* the SSN is not blacklisted */ - if (black_list[i].dest_ssn != ALLOW_ANY - && black_list[i].dest_ssn != parsed->called_ssn) - continue; - - /* bssap */ - if (black_list[i].bssap != ALLOW_ANY - && black_list[i].bssap != parsed->bssap) - continue; - - /* gsm */ - if (black_list[i].gsm != ALLOW_ANY - && black_list[i].gsm != parsed->gsm_type) - continue; - - /* blacklisted */ - LOGP(DNAT, LOGL_INFO, "Blacklisted with rule %d\n", i); - return 1; - } else { - /* blacklisted, we have no content sniffing yet */ - LOGP(DNAT, LOGL_INFO, "Blacklisted with rule %d\n", i); - return 1; - } - } - - /* go through the whitelust now */ - for (i = 0; i < ARRAY_SIZE(white_list); ++i) { - /* ignore the rule? */ - if (white_list[i].filter_dir != FILTER_TO_BOTH - && white_list[i].filter_dir != dir) - continue; - - /* the proto is not whitelisted */ - if (white_list[i].ipa_proto != ALLOW_ANY - && white_list[i].ipa_proto != parsed->ipa_proto) - continue; - - if (parsed->ipa_proto == IPAC_PROTO_SCCP) { - /* the SSN is not whitelisted */ - if (white_list[i].dest_ssn != ALLOW_ANY - && white_list[i].dest_ssn != parsed->called_ssn) - continue; - - /* bssap */ - if (white_list[i].bssap != ALLOW_ANY - && white_list[i].bssap != parsed->bssap) - continue; - - /* gsm */ - if (white_list[i].gsm != ALLOW_ANY - && white_list[i].gsm != parsed->gsm_type) - continue; - - /* whitelisted */ - LOGP(DNAT, LOGL_INFO, "Whitelisted with rule %d\n", i); - return 0; - } else { - /* whitelisted */ - return 0; - } - } - - return 1; -} diff --git a/src/osmo-bsc_nat/bsc_mgcp_utils.c b/src/osmo-bsc_nat/bsc_mgcp_utils.c deleted file mode 100644 index 48847865c..000000000 --- a/src/osmo-bsc_nat/bsc_mgcp_utils.c +++ /dev/null @@ -1,1152 +0,0 @@ -/** - * This file contains helper routines for MGCP Gateway handling. - * - * The first thing to remember is that each BSC has its own namespace/range - * of endpoints. Whenever a BSSMAP ASSIGNMENT REQUEST is received this code - * will be called to select an endpoint on the BSC. The mapping from original - * multiplex/timeslot to BSC multiplex'/timeslot' will be stored. - * - * The second part is to take messages on the public MGCP GW interface - * and forward them to the right BSC. This requires the MSC to first - * assign the timeslot. This assumption has been true so far. We are using - * the policy_cb of the MGCP protocol code to decide if the request should - * be immediately answered or delayed. An extension "Z: noanswer" is used - * to request the BSC to not respond. This is saving some bytes of bandwidth - * and as we are using TCP to forward the message we know it will arrive. - * The mgcp_do_read method reads these messages and hands them to the protocol - * parsing code which will call the mentioned policy_cb. The bsc_mgcp_forward - * method is used on the way back from the BSC to the network. - * - * The third part is to patch messages forwarded to the BSC. This includes - * the endpoint number, the ports to be used inside the SDP file and maybe - * some other bits. - * - */ -/* - * (C) 2010-2012 by Holger Hans Peter Freyther <zecke@selfish.org> - * (C) 2010-2012 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <openbsc/bsc_nat.h> -#include <openbsc/bsc_nat_callstats.h> -#include <openbsc/bsc_nat_sccp.h> -#include <openbsc/gsm_data.h> -#include <openbsc/debug.h> -#include <openbsc/ipaccess.h> -#include <openbsc/mgcp.h> -#include <openbsc/mgcp_internal.h> -#include <openbsc/osmux.h> - -#include <osmocom/ctrl/control_cmd.h> - -#include <osmocom/sccp/sccp.h> - -#include <osmocom/core/talloc.h> -#include <osmocom/gsm/gsm0808.h> -#include <osmocom/gsm/protocol/gsm_08_08.h> - -#include <netinet/in.h> -#include <arpa/inet.h> - -#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; - - if ((max_endpoints % 32) != 0) - div += 1; - - return div; -} - -static int bsc_init_endps_if_needed(struct bsc_connection *con) -{ - int multiplexes; - - /* we have done that */ - if (con->_endpoint_status) - return 0; - - /* we have no config... */ - if (!con->cfg) - return -1; - - multiplexes = bsc_mgcp_nr_multiplexes(con->cfg->max_endpoints); - con->number_multiplexes = multiplexes; - con->max_endpoints = con->cfg->max_endpoints; - con->_endpoint_status = talloc_zero_array(con, char, 32 * multiplexes + 1); - return con->_endpoint_status == NULL; -} - -static int bsc_assign_endpoint(struct bsc_connection *bsc, struct nat_sccp_connection *con) -{ - int multiplex; - int timeslot; - const int number_endpoints = bsc->max_endpoints; - int i; - - mgcp_endpoint_to_timeslot(bsc->last_endpoint, &multiplex, ×lot); - timeslot += 1; - - for (i = 0; i < number_endpoints; ++i) { - int endpoint; - - /* Wrap around timeslots */ - if (timeslot == 0) - timeslot = 1; - - if (timeslot == 0x1f) { - timeslot = 1; - multiplex += 1; - } - - /* Wrap around the multiplex */ - if (multiplex >= bsc->number_multiplexes) - multiplex = 0; - - endpoint = mgcp_timeslot_to_endpoint(multiplex, timeslot); - - /* Now check if we are allowed to assign this one */ - if (endpoint >= bsc->max_endpoints) { - multiplex = 0; - timeslot = 1; - endpoint = mgcp_timeslot_to_endpoint(multiplex, timeslot); - } - - - if (bsc->_endpoint_status[endpoint] == 0) { - bsc->_endpoint_status[endpoint] = 1; - con->bsc_endp = endpoint; - bsc->last_endpoint = endpoint; - return 0; - } - - timeslot += 1; - } - - return -1; -} - -static uint16_t create_cic(int endpoint) -{ - int timeslot, multiplex; - - mgcp_endpoint_to_timeslot(endpoint, &multiplex, ×lot); - return (multiplex << 5) | (timeslot & 0x1f); -} - -int bsc_mgcp_assign_patch(struct nat_sccp_connection *con, struct msgb *msg) -{ - struct nat_sccp_connection *mcon; - struct tlv_parsed tp; - uint16_t cic; - uint8_t timeslot; - uint8_t multiplex; - unsigned int endp; - - if (!msg->l3h) { - LOGP(DNAT, LOGL_ERROR, "Assignment message should have l3h pointer.\n"); - return -1; - } - - if (msgb_l3len(msg) < 3) { - LOGP(DNAT, LOGL_ERROR, "Assignment message has not enough space for GSM0808.\n"); - return -1; - } - - tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 3, msgb_l3len(msg) - 3, 0, 0); - if (!TLVP_PRESENT(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)) { - LOGP(DNAT, LOGL_ERROR, "Circuit identity code not found in assignment message.\n"); - return -1; - } - - cic = ntohs(tlvp_val16_unal(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)); - timeslot = cic & 0x1f; - multiplex = (cic & ~0x1f) >> 5; - - - endp = mgcp_timeslot_to_endpoint(multiplex, timeslot); - - if (endp >= con->bsc->nat->mgcp_cfg->trunk.number_endpoints) { - LOGP(DNAT, LOGL_ERROR, - "MSC attempted to assign bad endpoint 0x%x\n", - endp); - return -1; - } - - /* find stale connections using that endpoint */ - llist_for_each_entry(mcon, &con->bsc->nat->sccp_connections, list_entry) { - if (mcon->msc_endp == endp) { - LOGP(DNAT, LOGL_ERROR, - "Endpoint %d was assigned to 0x%x and now 0x%x\n", - endp, - sccp_src_ref_to_int(&mcon->patched_ref), - sccp_src_ref_to_int(&con->patched_ref)); - bsc_mgcp_dlcx(mcon); - } - } - - con->msc_endp = endp; - if (bsc_init_endps_if_needed(con->bsc) != 0) - return -1; - if (bsc_assign_endpoint(con->bsc, con) != 0) - return -1; - - /* - * now patch the message for the new CIC... - * still assumed to be one multiplex only - */ - cic = htons(create_cic(con->bsc_endp)); - memcpy((uint8_t *) TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE), - &cic, sizeof(cic)); - - return 0; -} - -static void bsc_mgcp_free_endpoint(struct bsc_nat *nat, int i) -{ - if (nat->bsc_endpoints[i].transaction_id) { - talloc_free(nat->bsc_endpoints[i].transaction_id); - nat->bsc_endpoints[i].transaction_id = NULL; - } - - nat->bsc_endpoints[i].transaction_state = 0; - nat->bsc_endpoints[i].bsc = NULL; -} - -void bsc_mgcp_free_endpoints(struct bsc_nat *nat) -{ - int i; - - for (i = 1; i < nat->mgcp_cfg->trunk.number_endpoints; ++i){ - bsc_mgcp_free_endpoint(nat, i); - mgcp_release_endp(&nat->mgcp_cfg->trunk.endpoints[i]); - } -} - -/* send a MDCX where we do not want a response */ -static void bsc_mgcp_send_mdcx(struct bsc_connection *bsc, int port, struct mgcp_endpoint *endp) -{ - char buf[2096]; - int len; - - len = snprintf(buf, sizeof(buf), - "MDCX 23 %x@mgw MGCP 1.0\r\n" - "Z: noanswer\r\n" - "\r\n" - "c=IN IP4 %s\r\n" - "m=audio %d RTP/AVP 255\r\n", - port, mgcp_bts_src_addr(endp), - endp->bts_end.local_port); - if (len < 0) { - LOGP(DMGCP, LOGL_ERROR, "snprintf for MDCX failed.\n"); - return; - } - - bsc_write_mgcp(bsc, (uint8_t *) buf, len); -} - -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 nat-%u %x@mgw MGCP 1.0\r\n", - trans, endpoint); - if (len < 0) { - LOGP(DMGCP, LOGL_ERROR, "snprintf for DLCX failed.\n"); - return; - } - - bsc_write_mgcp(bsc, (uint8_t *) buf, len); -} - -void bsc_mgcp_init(struct nat_sccp_connection *con) -{ - con->msc_endp = -1; - 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 nat_sccp_connection *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 nat_sccp_connection *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, 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); -} - - -struct nat_sccp_connection *bsc_mgcp_find_con(struct bsc_nat *nat, int endpoint) -{ - struct nat_sccp_connection *con = NULL; - struct nat_sccp_connection *sccp; - - llist_for_each_entry(sccp, &nat->sccp_connections, list_entry) { - if (sccp->msc_endp == -1) - continue; - if (sccp->msc_endp != endpoint) - continue; - - con = sccp; - } - - if (con) - return con; - - LOGP(DMGCP, LOGL_ERROR, - "Failed to find the connection for endpoint: 0x%x\n", endpoint); - 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; - struct bsc_endpoint *bsc_endp; - struct nat_sccp_connection *sccp; - struct mgcp_endpoint *mgcp_endp; - struct msgb *bsc_msg; - - nat = tcfg->cfg->data; - bsc_endp = &nat->bsc_endpoints[endpoint]; - mgcp_endp = &nat->mgcp_cfg->trunk.endpoints[endpoint]; - - if (bsc_endp->transaction_id) { - LOGP(DMGCP, LOGL_ERROR, "Endpoint 0x%x had pending transaction: '%s'\n", - endpoint, bsc_endp->transaction_id); - talloc_free(bsc_endp->transaction_id); - bsc_endp->transaction_id = NULL; - bsc_endp->transaction_state = 0; - } - bsc_endp->bsc = NULL; - - sccp = bsc_mgcp_find_con(nat, endpoint); - - if (!sccp) { - LOGP(DMGCP, LOGL_ERROR, "Did not find BSC for change on endpoint: 0x%x state: %d\n", endpoint, state); - - switch (state) { - case MGCP_ENDP_CRCX: - return MGCP_POLICY_REJECT; - break; - case MGCP_ENDP_DLCX: - return MGCP_POLICY_CONT; - break; - case MGCP_ENDP_MDCX: - return MGCP_POLICY_CONT; - break; - default: - LOGP(DMGCP, LOGL_FATAL, "Unhandled state: %d\n", state); - return MGCP_POLICY_CONT; - break; - } - } - - /* Allocate a Osmux circuit ID */ - 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, - mgcp_endp->osmux.allocated_cid, - &mgcp_endp->net_end.codec.payload_type, - nat->sdp_ensure_amr_mode_set); - if (!bsc_msg) { - LOGP(DMGCP, LOGL_ERROR, "Failed to patch the msg.\n"); - return MGCP_POLICY_CONT; - } - - - bsc_endp->transaction_id = talloc_strdup(nat, transaction_id); - bsc_endp->transaction_state = state; - bsc_endp->bsc = sccp->bsc; - - /* we need to update some bits */ - if (state == MGCP_ENDP_CRCX) { - struct sockaddr_in sock; - - /* Annotate the allocated Osmux CID until the bsc confirms that - * it agrees to use Osmux for this voice flow. - */ - if (mgcp_endp->osmux.allocated_cid >= 0 && - mgcp_endp->osmux.state != OSMUX_STATE_ENABLED) { - mgcp_endp->osmux.state = OSMUX_STATE_NEGOTIATING; - mgcp_endp->osmux.cid = mgcp_endp->osmux.allocated_cid; - } - - socklen_t len = sizeof(sock); - if (getpeername(sccp->bsc->write_queue.bfd.fd, (struct sockaddr *) &sock, &len) != 0) { - LOGP(DMGCP, LOGL_ERROR, "Can not get the peername...%d/%s\n", - errno, strerror(errno)); - } else { - mgcp_endp->bts_end.addr = sock.sin_addr; - } - - /* send the message and a fake MDCX to force sending of a dummy packet */ - bsc_write(sccp->bsc, bsc_msg, IPAC_PROTO_MGCP_OLD); - bsc_mgcp_send_mdcx(sccp->bsc, sccp->bsc_endp, mgcp_endp); - return MGCP_POLICY_DEFER; - } else if (state == MGCP_ENDP_DLCX) { - /* we will free the endpoint now and send a DLCX to the BSC */ - msgb_free(bsc_msg); - bsc_mgcp_dlcx(sccp); - - /* libmgcp clears the MGCP endpoint for us */ - if (mgcp_endp->osmux.state == OSMUX_STATE_ENABLED) - osmux_release_cid(mgcp_endp); - - return MGCP_POLICY_CONT; - } else { - bsc_write(sccp->bsc, bsc_msg, IPAC_PROTO_MGCP_OLD); - return MGCP_POLICY_DEFER; - } -} - -/* - * We do have a failure, free data downstream.. - */ -static void free_chan_downstream(struct mgcp_endpoint *endp, struct bsc_endpoint *bsc_endp, - struct bsc_connection *bsc) -{ - LOGP(DMGCP, LOGL_ERROR, "No CI, freeing endpoint 0x%x in state %d\n", - ENDPOINT_NUMBER(endp), bsc_endp->transaction_state); - - /* if a CRCX failed... send a DLCX down the stream */ - if (bsc_endp->transaction_state == MGCP_ENDP_CRCX) { - struct nat_sccp_connection *con; - con = bsc_mgcp_find_con(bsc->nat, ENDPOINT_NUMBER(endp)); - if (!con) { - LOGP(DMGCP, LOGL_ERROR, - "No SCCP connection for endp 0x%x\n", - ENDPOINT_NUMBER(endp)); - } else { - if (con->bsc == bsc) { - bsc_mgcp_send_dlcx(bsc, con->bsc_endp, con->bsc->next_transaction++); - } else { - LOGP(DMGCP, LOGL_ERROR, - "Endpoint belongs to a different BSC\n"); - } - } - } - - bsc_mgcp_free_endpoint(bsc->nat, ENDPOINT_NUMBER(endp)); - mgcp_release_endp(endp); -} - -static void bsc_mgcp_osmux_confirm(struct mgcp_endpoint *endp, const char *str) -{ - unsigned int osmux_cid; - char *res; - - res = strstr(str, "X-Osmux: "); - if (!res) { - LOGP(DMGCP, LOGL_INFO, - "BSC doesn't want to use Osmux, failing back to RTP\n"); - goto err; - } - - if (sscanf(res, "X-Osmux: %u", &osmux_cid) != 1) { - LOGP(DMGCP, LOGL_ERROR, "Failed to parse Osmux CID '%s'\n", - str); - goto err; - } - - if (endp->osmux.cid != osmux_cid) { - LOGP(DMGCP, LOGL_ERROR, - "BSC sent us wrong CID %u, we expected %u", - osmux_cid, endp->osmux.cid); - goto err; - } - - LOGP(DMGCP, LOGL_NOTICE, "bsc accepted to use Osmux (cid=%u)\n", - osmux_cid); - endp->osmux.state = OSMUX_STATE_ACTIVATING; - return; -err: - osmux_release_cid(endp); - endp->osmux.state = OSMUX_STATE_DISABLED; -} - -/* - * We have received a msg from the BSC. We will see if we know - * 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) -{ - struct msgb *output; - struct bsc_endpoint *bsc_endp = NULL; - struct mgcp_endpoint *endp = NULL; - int i, code; - char transaction_id[60]; - - /* Some assumption that our buffer is big enough.. and null terminate */ - if (msgb_l2len(msg) > 2000) { - LOGP(DMGCP, LOGL_ERROR, "MGCP message too long.\n"); - return; - } - - msg->l2h[msgb_l2len(msg)] = '\0'; - - if (bsc_mgcp_parse_response((const char *) msg->l2h, &code, transaction_id) != 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to parse response code.\n"); - return; - } - - for (i = 1; i < bsc->nat->mgcp_cfg->trunk.number_endpoints; ++i) { - if (bsc->nat->bsc_endpoints[i].bsc != bsc) - continue; - /* no one listening? a bug? */ - if (!bsc->nat->bsc_endpoints[i].transaction_id) - continue; - if (strcmp(transaction_id, bsc->nat->bsc_endpoints[i].transaction_id) != 0) - continue; - - endp = &bsc->nat->mgcp_cfg->trunk.endpoints[i]; - bsc_endp = &bsc->nat->bsc_endpoints[i]; - 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); - return; - } - - endp->ci = bsc_mgcp_extract_ci((const char *) msg->l2h); - if (endp->ci == CI_UNUSED) { - free_chan_downstream(endp, bsc_endp, bsc); - return; - } - - if (endp->osmux.state == OSMUX_STATE_NEGOTIATING) - 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; - bsc_endp->transaction_state = 0; - - /* - * rewrite the information. In case the endpoint was deleted - * there should be nothing for us to rewrite so putting endp->rtp_port - * with the value of 0 should be no problem. - */ - output = bsc_mgcp_rewrite((char * ) msg->l2h, msgb_l2len(msg), -1, - mgcp_net_src_addr(endp), - endp->net_end.local_port, -1, - &endp->bts_end.codec.payload_type, - bsc->nat->sdp_ensure_amr_mode_set); - if (!output) { - LOGP(DMGCP, LOGL_ERROR, "Failed to rewrite MGCP msg.\n"); - return; - } - - 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 */ - rc = sscanf(str, "%3d %59s\n", code, transaction) != 2; - transaction[59] = '\0'; - return rc; -} - -uint32_t bsc_mgcp_extract_ci(const char *str) -{ - unsigned int ci; - char *res = strstr(str, "I: "); - if (!res) { - LOGP(DMGCP, LOGL_ERROR, "No CI in msg '%s'\n", str); - return CI_UNUSED; - } - - if (sscanf(res, "I: %u", &ci) != 1) { - LOGP(DMGCP, LOGL_ERROR, "Failed to parse CI in msg '%s'\n", str); - return CI_UNUSED; - } - - return ci; -} - -/** - * Create a new MGCPCommand based on the input and endpoint from a message - */ -static void patch_mgcp(struct msgb *output, const char *op, const char *tok, - int endp, int len, int cr, int osmux_cid) -{ - int slen; - int ret; - char buf[40]; - char osmux_extension[strlen("\nX-Osmux: 255") + 1]; - - buf[0] = buf[39] = '\0'; - ret = sscanf(tok, "%*s %s", buf); - if (ret != 1) { - LOGP(DMGCP, LOGL_ERROR, - "Failed to find Endpoint in: %s\n", tok); - return; - } - - if (osmux_cid >= 0) - sprintf(osmux_extension, "\nX-Osmux: %u", osmux_cid & 0xff); - else - osmux_extension[0] = '\0'; - - slen = sprintf((char *) output->l3h, "%s %s %x@mgw MGCP 1.0%s%s", - op, buf, endp, osmux_extension, cr ? "\r\n" : "\n"); - output->l3h = msgb_put(output, slen); -} - -/* we need to replace some strings... */ -struct msgb *bsc_mgcp_rewrite(char *input, int length, int endpoint, - const char *ip, int port, int osmux_cid, - int *first_payload_type, int ensure_mode_set) -{ - static const char crcx_str[] = "CRCX "; - static const char dlcx_str[] = "DLCX "; - static const char mdcx_str[] = "MDCX "; - - static const char ip_str[] = "c=IN IP4 "; - static const char aud_str[] = "m=audio "; - static const char fmt_str[] = "a=fmtp:"; - - char buf[128]; - char *running, *token; - struct msgb *output; - - /* keep state to add the a=fmtp line */ - int found_fmtp = 0; - int payload = -1; - int cr = 1; - - if (length > 4096 - 256) { - LOGP(DMGCP, LOGL_ERROR, "Input is too long.\n"); - return NULL; - } - - output = msgb_alloc_headroom(4096, 128, "MGCP rewritten"); - if (!output) { - LOGP(DMGCP, LOGL_ERROR, "Failed to allocate new MGCP msg.\n"); - return NULL; - } - - running = input; - output->l2h = output->data; - output->l3h = output->l2h; - for (token = strsep(&running, "\n"); running; token = strsep(&running, "\n")) { - int len = strlen(token); - cr = len > 0 && token[len - 1] == '\r'; - - if (strncmp(crcx_str, token, (sizeof crcx_str) - 1) == 0) { - patch_mgcp(output, "CRCX", token, endpoint, len, cr, osmux_cid); - } else if (strncmp(dlcx_str, token, (sizeof dlcx_str) - 1) == 0) { - patch_mgcp(output, "DLCX", token, endpoint, len, cr, -1); - } else if (strncmp(mdcx_str, token, (sizeof mdcx_str) - 1) == 0) { - patch_mgcp(output, "MDCX", token, endpoint, len, cr, -1); - } else if (strncmp(ip_str, token, (sizeof ip_str) - 1) == 0) { - output->l3h = msgb_put(output, strlen(ip_str)); - memcpy(output->l3h, ip_str, strlen(ip_str)); - output->l3h = msgb_put(output, strlen(ip)); - memcpy(output->l3h, ip, strlen(ip)); - - if (cr) { - output->l3h = msgb_put(output, 2); - output->l3h[0] = '\r'; - output->l3h[1] = '\n'; - } else { - output->l3h = msgb_put(output, 1); - output->l3h[0] = '\n'; - } - } else if (strncmp(aud_str, token, (sizeof aud_str) - 1) == 0) { - int offset; - if (sscanf(token, "m=audio %*d RTP/AVP %n%d", &offset, &payload) != 1) { - LOGP(DMGCP, LOGL_ERROR, "Could not parsed audio line.\n"); - msgb_free(output); - return NULL; - } - - snprintf(buf, sizeof(buf)-1, "m=audio %d RTP/AVP %s\n", - port, &token[offset]); - buf[sizeof(buf)-1] = '\0'; - - output->l3h = msgb_put(output, strlen(buf)); - memcpy(output->l3h, buf, strlen(buf)); - } else if (strncmp(fmt_str, token, (sizeof fmt_str) - 1) == 0) { - found_fmtp = 1; - goto copy; - } else { -copy: - output->l3h = msgb_put(output, len + 1); - memcpy(output->l3h, token, len); - output->l3h[len] = '\n'; - } - } - - /* - * the above code made sure that we have 128 bytes lefts. So we can - * safely append another line. - */ - if (ensure_mode_set && !found_fmtp && payload != -1) { - snprintf(buf, sizeof(buf) - 1, "a=fmtp:%d mode-set=2%s", - payload, cr ? "\r\n" : "\n"); - buf[sizeof(buf) - 1] = '\0'; - output->l3h = msgb_put(output, strlen(buf)); - memcpy(output->l3h, buf, strlen(buf)); - } - - if (payload != -1 && first_payload_type) - *first_payload_type = payload; - - 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; - struct msgb *msg, *resp; - int rc; - - nat = fd->data; - - rc = read(fd->fd, nat->mgcp_msg, sizeof(nat->mgcp_msg) - 1); - if (rc <= 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to read errno: %d\n", errno); - return -1; - } - - nat->mgcp_msg[rc] = '\0'; - nat->mgcp_length = rc; - - msg = msgb_alloc(sizeof(nat->mgcp_msg), "MGCP GW Read"); - if (!msg) { - LOGP(DMGCP, LOGL_ERROR, "Failed to create buffer.\n"); - return -1; - } - - msg->l2h = msgb_put(msg, rc); - memcpy(msg->l2h, nat->mgcp_msg, msgb_l2len(msg)); - resp = mgcp_handle_message(nat->mgcp_cfg, msg); - msgb_free(msg); - - /* we do have a direct answer... e.g. AUEP */ - if (resp) - mgcp_queue_for_call_agent(nat, resp); - - return 0; -} - -static int mgcp_do_write(struct osmo_fd *bfd, struct msgb *msg) -{ - int rc; - - rc = write(bfd->fd, msg->data, msg->len); - - if (rc != msg->len) { - LOGP(DMGCP, LOGL_ERROR, "Failed to write msg to MGCP CallAgent.\n"); - return -1; - } - - return rc; -} - -static int init_mgcp_socket(struct bsc_nat *nat, struct mgcp_config *cfg) -{ - struct sockaddr_in addr; - int on; - - cfg->gw_fd.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0); - if (cfg->gw_fd.bfd.fd < 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to create MGCP socket. errno: %d\n", errno); - return -1; - } - - on = 1; - setsockopt(cfg->gw_fd.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(cfg->source_port); - inet_aton(cfg->source_addr, &addr.sin_addr); - - if (bind(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to bind on %s:%d errno: %d\n", - cfg->source_addr, cfg->source_port, errno); - close(cfg->gw_fd.bfd.fd); - cfg->gw_fd.bfd.fd = -1; - return -1; - } - - addr.sin_port = htons(2727); - inet_aton(cfg->call_agent_addr, &addr.sin_addr); - if (connect(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to connect to: '%s'. errno: %d\n", - cfg->call_agent_addr, errno); - close(cfg->gw_fd.bfd.fd); - cfg->gw_fd.bfd.fd = -1; - return -1; - } - - osmo_wqueue_init(&cfg->gw_fd, 10); - cfg->gw_fd.bfd.when = BSC_FD_READ; - cfg->gw_fd.bfd.data = nat; - cfg->gw_fd.read_cb = mgcp_do_read; - cfg->gw_fd.write_cb = mgcp_do_write; - - if (osmo_fd_register(&cfg->gw_fd.bfd) != 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to register MGCP fd.\n"); - close(cfg->gw_fd.bfd.fd); - cfg->gw_fd.bfd.fd = -1; - 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; - cfg->trunk.force_realloc = 1; - - if (cfg->bts_ip) - talloc_free(cfg->bts_ip); - cfg->bts_ip = ""; - - nat->bsc_endpoints = talloc_zero_array(nat, - struct bsc_endpoint, - cfg->trunk.number_endpoints + 1); - if (!nat->bsc_endpoints) { - LOGP(DMGCP, LOGL_ERROR, "Failed to allocate nat endpoints\n"); - close(cfg->gw_fd.bfd.fd); - cfg->gw_fd.bfd.fd = -1; - return -1; - } - - if (mgcp_reset_transcoder(cfg) < 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to send packet to the transcoder.\n"); - talloc_free(nat->bsc_endpoints); - nat->bsc_endpoints = NULL; - close(cfg->gw_fd.bfd.fd); - cfg->gw_fd.bfd.fd = -1; - return -1; - } - - return 0; -} - -void bsc_mgcp_clear_endpoints_for(struct bsc_connection *bsc) -{ - struct rate_ctr *ctr = NULL; - int i; - - if (bsc->cfg) - ctr = &bsc->cfg->stats.ctrg->ctr[BCFG_CTR_DROPPED_CALLS]; - - for (i = 1; i < bsc->nat->mgcp_cfg->trunk.number_endpoints; ++i) { - struct bsc_endpoint *bsc_endp = &bsc->nat->bsc_endpoints[i]; - - if (bsc_endp->bsc != bsc) - continue; - - if (ctr) - rate_ctr_inc(ctr); - - bsc_mgcp_free_endpoint(bsc->nat, i); - mgcp_release_endp(&bsc->nat->mgcp_cfg->trunk.endpoints[i]); - } -} diff --git a/src/osmo-bsc_nat/bsc_nat.c b/src/osmo-bsc_nat/bsc_nat.c deleted file mode 100644 index daa066d05..000000000 --- a/src/osmo-bsc_nat/bsc_nat.c +++ /dev/null @@ -1,1736 +0,0 @@ -/* BSC Multiplexer/NAT */ - -/* - * (C) 2010-2013 by Holger Hans Peter Freyther <zecke@selfish.org> - * (C) 2010-2013 by On-Waves - * (C) 2009 by Harald Welte <laforge@gnumonks.org> - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <arpa/inet.h> - -#include <errno.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <time.h> -#include <unistd.h> -#include <fcntl.h> -#include <libgen.h> - -#define _GNU_SOURCE -#include <getopt.h> - -#include <openbsc/debug.h> -#include <openbsc/bsc_msc.h> -#include <openbsc/bsc_nat.h> -#include <openbsc/bsc_nat_sccp.h> -#include <openbsc/bsc_msg_filter.h> -#include <openbsc/ipaccess.h> -#include <openbsc/abis_nm.h> -#include <openbsc/socket.h> -#include <openbsc/vty.h> - -#include <osmocom/ctrl/control_cmd.h> -#include <osmocom/ctrl/control_if.h> -#include <osmocom/ctrl/ports.h> -#include <osmocom/ctrl/control_vty.h> - -#include <osmocom/crypt/auth.h> - -#include <osmocom/core/application.h> -#include <osmocom/core/talloc.h> -#include <osmocom/core/stats.h> - -#include <osmocom/gsm/tlv.h> -#include <osmocom/gsm/gsm0808.h> -#include <osmocom/gsm/protocol/gsm_08_08.h> - -#include <osmocom/vty/telnet_interface.h> -#include <osmocom/vty/vty.h> -#include <osmocom/vty/logging.h> -#include <osmocom/vty/stats.h> -#include <osmocom/vty/ports.h> - -#include <osmocom/sccp/sccp.h> - -#include <osmocom/abis/ipa.h> - -#include <openssl/rand.h> - -#include "../../bscconfig.h" - -#define SCCP_CLOSE_TIME 20 -#define SCCP_CLOSE_TIME_TIMEOUT 19 - -static const char *config_file = "bsc-nat.cfg"; -static struct in_addr local_addr; -static struct osmo_fd bsc_listen; -static const char *msc_ip = NULL; -static struct osmo_timer_list sccp_close; -static int daemonize = 0; - -const char *openbsc_copyright = - "Copyright (C) 2010 Holger Hans Peter Freyther and On-Waves\r\n" - "License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n" - "This is free software: you are free to change and redistribute it.\r\n" - "There is NO WARRANTY, to the extent permitted by law.\r\n"; - -static struct bsc_nat *nat; -static void bsc_send_data(struct bsc_connection *bsc, const uint8_t *data, unsigned int length, int); -static void msc_send_reset(struct bsc_msc_connection *con); -static void bsc_stat_reject(int filter, struct bsc_connection *bsc, int normal); - -struct bsc_config *bsc_config_num(struct bsc_nat *nat, int num) -{ - struct bsc_config *conf; - - llist_for_each_entry(conf, &nat->bsc_configs, entry) - if (conf->nr == num) - return conf; - - return NULL; -} - -static void queue_for_msc(struct bsc_msc_connection *con, struct msgb *msg) -{ - if (!con) { - LOGP(DLINP, LOGL_ERROR, "No MSC Connection assigned. Check your code.\n"); - msgb_free(msg); - return; - } - - - if (osmo_wqueue_enqueue(&con->write_queue, msg) != 0) { - LOGP(DLINP, LOGL_ERROR, "Failed to enqueue the write.\n"); - msgb_free(msg); - } -} - -static void send_reset_ack(struct bsc_connection *bsc) -{ - static const uint8_t gsm_reset_ack[] = { - 0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01, - 0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x03, - 0x00, 0x01, 0x31, - }; - - bsc_send_data(bsc, gsm_reset_ack, sizeof(gsm_reset_ack), IPAC_PROTO_SCCP); -} - -static void send_ping(struct bsc_connection *bsc) -{ - static const uint8_t id_ping[] = { - IPAC_MSGT_PING, - }; - - bsc_send_data(bsc, id_ping, sizeof(id_ping), IPAC_PROTO_IPACCESS); -} - -static void send_pong(struct bsc_connection *bsc) -{ - static const uint8_t id_pong[] = { - IPAC_MSGT_PONG, - }; - - bsc_send_data(bsc, id_pong, sizeof(id_pong), IPAC_PROTO_IPACCESS); -} - -static void bsc_pong_timeout(void *_bsc) -{ - struct bsc_connection *bsc = _bsc; - - LOGP(DNAT, LOGL_ERROR, "BSC Nr: %d PONG timeout.\n", bsc->cfg->nr); - bsc_close_connection(bsc); -} - -static void bsc_ping_timeout(void *_bsc) -{ - struct bsc_connection *bsc = _bsc; - - if (bsc->nat->ping_timeout < 0) - return; - - send_ping(bsc); - - /* send another ping in 20 seconds */ - osmo_timer_schedule(&bsc->ping_timeout, bsc->nat->ping_timeout, 0); - - /* also start a pong timer */ - osmo_timer_schedule(&bsc->pong_timeout, bsc->nat->pong_timeout, 0); -} - -static void start_ping_pong(struct bsc_connection *bsc) -{ - osmo_timer_setup(&bsc->pong_timeout, bsc_pong_timeout, bsc); - osmo_timer_setup(&bsc->ping_timeout, bsc_ping_timeout, bsc); - - bsc_ping_timeout(bsc); -} - -static void send_id_ack(struct bsc_connection *bsc) -{ - static const uint8_t id_ack[] = { - IPAC_MSGT_ID_ACK - }; - - bsc_send_data(bsc, id_ack, sizeof(id_ack), IPAC_PROTO_IPACCESS); -} - -static void send_id_req(struct bsc_nat *nat, struct bsc_connection *bsc) -{ - static const uint8_t s_id_req[] = { - IPAC_MSGT_ID_GET, - 0x01, IPAC_IDTAG_UNIT, - 0x01, IPAC_IDTAG_MACADDR, - 0x01, IPAC_IDTAG_LOCATION1, - 0x01, IPAC_IDTAG_LOCATION2, - 0x01, IPAC_IDTAG_EQUIPVERS, - 0x01, IPAC_IDTAG_SWVERSION, - 0x01, IPAC_IDTAG_UNITNAME, - 0x01, IPAC_IDTAG_SERNR, - }; - - uint8_t *mrand; - uint8_t id_req[sizeof(s_id_req) + (2+16)]; - uint8_t *buf = &id_req[sizeof(s_id_req)]; - - /* copy the static data */ - memcpy(id_req, s_id_req, sizeof(s_id_req)); - - /* put the RAND with length, tag, value */ - buf = v_put(buf, 0x11); - buf = v_put(buf, 0x23); - mrand = bsc->last_rand; - - if (RAND_bytes(mrand, 16) != 1) - goto failed_random; - - memcpy(buf, mrand, 16); - buf += 16; - - bsc_send_data(bsc, id_req, sizeof(id_req), IPAC_PROTO_IPACCESS); - return; - -failed_random: - /* the timeout will trigger and close this connection */ - LOGP(DNAT, LOGL_ERROR, "Failed to read from urandom.\n"); - return; -} - -static struct msgb *nat_create_rlsd(struct nat_sccp_connection *conn) -{ - struct sccp_connection_released *rel; - struct msgb *msg; - - msg = msgb_alloc_headroom(4096, 128, "rlsd"); - if (!msg) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate released.\n"); - return NULL; - } - - msg->l2h = msgb_put(msg, sizeof(*rel)); - rel = (struct sccp_connection_released *) msg->l2h; - rel->type = SCCP_MSG_TYPE_RLSD; - rel->release_cause = SCCP_RELEASE_CAUSE_SCCP_FAILURE; - rel->destination_local_reference = conn->remote_ref; - rel->source_local_reference = conn->patched_ref; - - return msg; -} - -static void nat_send_rlsd_ussd(struct bsc_nat *nat, struct nat_sccp_connection *conn) -{ - struct msgb *msg; - - if (!nat->ussd_con) - return; - - msg = nat_create_rlsd(conn); - if (!msg) - return; - - bsc_do_write(&nat->ussd_con->queue, msg, IPAC_PROTO_SCCP); -} - -static void nat_send_rlsd_msc(struct nat_sccp_connection *conn) -{ - struct msgb *msg; - - msg = nat_create_rlsd(conn); - if (!msg) - return; - - ipa_prepend_header(msg, IPAC_PROTO_SCCP); - queue_for_msc(conn->msc_con, msg); -} - -static void nat_send_rlsd_bsc(struct nat_sccp_connection *conn) -{ - struct msgb *msg; - struct sccp_connection_released *rel; - - msg = msgb_alloc_headroom(4096, 128, "rlsd"); - if (!msg) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate clear command.\n"); - return; - } - - msg->l2h = msgb_put(msg, sizeof(*rel)); - rel = (struct sccp_connection_released *) msg->l2h; - rel->type = SCCP_MSG_TYPE_RLSD; - rel->release_cause = SCCP_RELEASE_CAUSE_SCCP_FAILURE; - rel->destination_local_reference = conn->real_ref; - rel->source_local_reference = conn->remote_ref; - - bsc_write(conn->bsc, msg, IPAC_PROTO_SCCP); -} - -static struct msgb *nat_creat_clrc(struct nat_sccp_connection *conn, uint8_t cause) -{ - struct msgb *msg; - struct msgb *sccp; - - msg = gsm0808_create_clear_command(cause); - if (!msg) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate clear command.\n"); - return NULL; - } - - sccp = sccp_create_dt1(&conn->real_ref, msg->data, msg->len); - if (!sccp) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate SCCP msg.\n"); - msgb_free(msg); - return NULL; - } - - msgb_free(msg); - return sccp; -} - -static int nat_send_clrc_bsc(struct nat_sccp_connection *conn) -{ - struct msgb *sccp; - - sccp = nat_creat_clrc(conn, 0x20); - if (!sccp) - return -1; - return bsc_write(conn->bsc, sccp, IPAC_PROTO_SCCP); -} - -static void nat_send_rlc(struct bsc_msc_connection *msc_con, - struct sccp_source_reference *src, - struct sccp_source_reference *dst) -{ - struct sccp_connection_release_complete *rlc; - struct msgb *msg; - - msg = msgb_alloc_headroom(4096, 128, "rlc"); - if (!msg) { - LOGP(DNAT, LOGL_ERROR, "Failed to sccp rlc.\n"); - return; - } - - msg->l2h = msgb_put(msg, sizeof(*rlc)); - rlc = (struct sccp_connection_release_complete *) msg->l2h; - rlc->type = SCCP_MSG_TYPE_RLC; - rlc->destination_local_reference = *dst; - rlc->source_local_reference = *src; - - ipa_prepend_header(msg, IPAC_PROTO_SCCP); - - queue_for_msc(msc_con, msg); -} - -static void send_mgcp_reset(struct bsc_connection *bsc) -{ - static const uint8_t mgcp_reset[] = { - "RSIP 1 13@mgw MGCP 1.0\r\n" - }; - - bsc_write_mgcp(bsc, mgcp_reset, sizeof mgcp_reset - 1); -} - -void bsc_nat_send_mgcp_to_msc(struct bsc_nat *nat, struct msgb *msg) -{ - ipa_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 - * a real BSC. - */ -static void initialize_msc_if_needed(struct bsc_msc_connection *msc_con) -{ - if (msc_con->first_contact) - return; - - msc_con->first_contact = 1; - msc_send_reset(msc_con); -} - -static void send_id_get_response(struct bsc_msc_connection *msc_con) -{ - struct msgb *msg = bsc_msc_id_get_resp(0, nat->token, NULL, 0); - if (!msg) - return; - - ipa_prepend_header(msg, IPAC_PROTO_IPACCESS); - queue_for_msc(msc_con, msg); -} - -/* - * Currently we are lacking refcounting so we need to copy each message. - */ -static void bsc_send_data(struct bsc_connection *bsc, const uint8_t *data, unsigned int length, int proto) -{ - struct msgb *msg; - - if (length > 4096 - 128) { - LOGP(DLINP, LOGL_ERROR, "Can not send message of that size.\n"); - return; - } - - msg = msgb_alloc_headroom(4096, 128, "to-bsc"); - if (!msg) { - LOGP(DLINP, LOGL_ERROR, "Failed to allocate memory for BSC msg.\n"); - return; - } - - msg->l2h = msgb_put(msg, length); - memcpy(msg->data, data, length); - - bsc_write(bsc, msg, proto); -} - -/* - * Update the release statistics - */ -static void bsc_stat_reject(int filter, struct bsc_connection *bsc, int normal) -{ - if (!bsc->cfg) { - LOGP(DNAT, LOGL_ERROR, "BSC is not authenticated."); - return; - } - - if (filter >= 0) { - LOGP(DNAT, LOGL_ERROR, "Connection was not rejected"); - return; - } - - if (filter == -1) - rate_ctr_inc(&bsc->cfg->stats.ctrg->ctr[BCFG_CTR_ILL_PACKET]); - else if (normal) - rate_ctr_inc(&bsc->cfg->stats.ctrg->ctr[BCFG_CTR_REJECTED_MSG]); - else - rate_ctr_inc(&bsc->cfg->stats.ctrg->ctr[BCFG_CTR_REJECTED_CR]); -} - -/* - * Release an established connection. We will have to release it to the BSC - * and to the network and we do it the following way. - * 1.) Give up on the MSC side - * 1.1) Send a RLSD message, it is a bit non standard but should work, we - * ignore the RLC... we might complain about it. Other options would - * be to send a Release Request, handle the Release Complete.. - * 1.2) Mark the data structure to be con_local and wait for 2nd - * - * 2.) Give up on the BSC side - * 2.1) Depending on the con type reject the service, or just close it - */ -static void bsc_send_con_release(struct bsc_connection *bsc, - struct nat_sccp_connection *con, - struct bsc_filter_reject_cause *cause) -{ - struct msgb *rlsd; - /* 1. release the network */ - rlsd = sccp_create_rlsd(&con->patched_ref, &con->remote_ref, - SCCP_RELEASE_CAUSE_END_USER_ORIGINATED); - if (!rlsd) - LOGP(DNAT, LOGL_ERROR, "Failed to create RLSD message.\n"); - else { - ipa_prepend_header(rlsd, IPAC_PROTO_SCCP); - queue_for_msc(con->msc_con, rlsd); - } - con->con_local = NAT_CON_END_LOCAL; - con->msc_con = NULL; - - /* 2. release the BSC side */ - if (con->filter_state.con_type == FLT_CON_TYPE_LU) { - struct msgb *payload, *udt; - payload = gsm48_create_loc_upd_rej(cause->lu_reject_cause); - - if (payload) { - gsm0808_prepend_dtap_header(payload, 0); - udt = sccp_create_dt1(&con->real_ref, payload->data, payload->len); - if (udt) - bsc_write(bsc, udt, IPAC_PROTO_SCCP); - else - LOGP(DNAT, LOGL_ERROR, "Failed to create DT1\n"); - - msgb_free(payload); - } else { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate LU Reject.\n"); - } - } - - nat_send_clrc_bsc(con); - - rlsd = sccp_create_rlsd(&con->remote_ref, &con->real_ref, - SCCP_RELEASE_CAUSE_END_USER_ORIGINATED); - if (!rlsd) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate RLSD for the BSC.\n"); - sccp_connection_destroy(con); - return; - } - - con->filter_state.con_type = FLT_CON_TYPE_LOCAL_REJECT; - bsc_write(bsc, rlsd, IPAC_PROTO_SCCP); -} - -static void bsc_send_con_refuse(struct bsc_connection *bsc, - struct bsc_nat_parsed *parsed, int con_type, - struct bsc_filter_reject_cause *cause) -{ - struct msgb *payload; - struct msgb *refuse; - - if (con_type == FLT_CON_TYPE_LU) - payload = gsm48_create_loc_upd_rej(cause->lu_reject_cause); - else if (con_type == FLT_CON_TYPE_CM_SERV_REQ || con_type == FLT_CON_TYPE_SSA) - payload = gsm48_create_mm_serv_rej(cause->cm_reject_cause); - else { - LOGP(DNAT, LOGL_ERROR, "Unknown connection type: %d\n", con_type); - payload = NULL; - } - - /* - * Some BSCs do not handle the payload inside a SCCP CREF msg - * so we will need to: - * 1.) Allocate a local connection and mark it as local.. - * 2.) queue data for downstream.. and the RLC should delete everything - */ - if (payload) { - struct msgb *cc, *udt, *clear, *rlsd; - struct nat_sccp_connection *con; - con = create_sccp_src_ref(bsc, parsed); - if (!con) - goto send_refuse; - - /* declare it local and assign a unique remote_ref */ - con->filter_state.con_type = FLT_CON_TYPE_LOCAL_REJECT; - con->con_local = NAT_CON_END_LOCAL; - con->has_remote_ref = 1; - con->remote_ref = con->patched_ref; - - /* 1. create a confirmation */ - cc = sccp_create_cc(&con->remote_ref, &con->real_ref); - if (!cc) - goto send_refuse; - - /* 2. create the DT1 */ - gsm0808_prepend_dtap_header(payload, 0); - udt = sccp_create_dt1(&con->real_ref, payload->data, payload->len); - if (!udt) { - msgb_free(cc); - goto send_refuse; - } - - /* 3. send a Clear Command */ - clear = nat_creat_clrc(con, 0x20); - if (!clear) { - msgb_free(cc); - msgb_free(udt); - goto send_refuse; - } - - /* 4. send a RLSD */ - rlsd = sccp_create_rlsd(&con->remote_ref, &con->real_ref, - SCCP_RELEASE_CAUSE_END_USER_ORIGINATED); - if (!rlsd) { - msgb_free(cc); - msgb_free(udt); - msgb_free(clear); - goto send_refuse; - } - - bsc_write(bsc, cc, IPAC_PROTO_SCCP); - bsc_write(bsc, udt, IPAC_PROTO_SCCP); - bsc_write(bsc, clear, IPAC_PROTO_SCCP); - bsc_write(bsc, rlsd, IPAC_PROTO_SCCP); - msgb_free(payload); - return; - } - - -send_refuse: - if (payload) - msgb_free(payload); - - refuse = sccp_create_refuse(parsed->src_local_ref, - SCCP_REFUSAL_SCCP_FAILURE, NULL, 0); - if (!refuse) { - LOGP(DNAT, LOGL_ERROR, - "Creating refuse msg failed for SCCP 0x%x on BSC Nr: %d.\n", - sccp_src_ref_to_int(parsed->src_local_ref), bsc->cfg->nr); - return; - } - - bsc_write(bsc, refuse, IPAC_PROTO_SCCP); -} - -static void bsc_nat_send_paging(struct bsc_connection *bsc, struct msgb *msg) -{ - if (bsc->cfg->forbid_paging) { - LOGP(DNAT, LOGL_DEBUG, "Paging forbidden for BTS: %d\n", bsc->cfg->nr); - return; - } - - bsc_send_data(bsc, msg->l2h, msgb_l2len(msg), IPAC_PROTO_SCCP); -} - -static void bsc_nat_handle_paging(struct bsc_nat *nat, struct msgb *msg) -{ - struct bsc_connection *bsc; - const uint8_t *paging_start; - int paging_length, i, ret; - - ret = bsc_nat_find_paging(msg, &paging_start, &paging_length); - if (ret != 0) { - LOGP(DNAT, LOGL_ERROR, "Could not parse paging message: %d\n", ret); - return; - } - - /* This is quite expensive now */ - for (i = 0; i < paging_length; i += 2) { - unsigned int _lac = ntohs(*(unsigned int *) &paging_start[i]); - unsigned int paged = 0; - llist_for_each_entry(bsc, &nat->bsc_connections, list_entry) { - if (!bsc->cfg) - continue; - if (!bsc->authenticated) - continue; - if (!bsc_config_handles_lac(bsc->cfg, _lac)) - continue; - bsc_nat_send_paging(bsc, msg); - paged += 1; - } - - /* highlight a possible config issue */ - if (paged == 0) - LOGP(DNAT, LOGL_ERROR, "No BSC for LAC %d/0x%d\n", _lac, _lac); - - } -} - - -/* - * Update the auth status. This can be either a CIPHER MODE COMMAND or - * a CM Serivce Accept. Maybe also LU Accept or such in the future. - */ -static void update_con_authorize(struct nat_sccp_connection *con, - struct bsc_nat_parsed *parsed, - struct msgb *msg) -{ - if (!con) - return; - if (con->authorized) - return; - - if (parsed->bssap == BSSAP_MSG_BSS_MANAGEMENT && - parsed->gsm_type == BSS_MAP_MSG_CIPHER_MODE_CMD) { - con->authorized = 1; - } else if (parsed->bssap == BSSAP_MSG_DTAP) { - uint8_t msg_type, proto; - uint32_t len; - struct gsm48_hdr *hdr48; - hdr48 = bsc_unpack_dtap(parsed, msg, &len); - if (!hdr48) - return; - - proto = gsm48_hdr_pdisc(hdr48); - msg_type = gsm48_hdr_msg_type(hdr48); - if (proto == GSM48_PDISC_MM && - msg_type == GSM48_MT_MM_CM_SERV_ACC) - con->authorized = 1; - } -} - -static int forward_sccp_to_bts(struct bsc_msc_connection *msc_con, struct msgb *msg) -{ - struct nat_sccp_connection *con = NULL; - struct bsc_connection *bsc; - struct bsc_nat_parsed *parsed; - int proto; - - /* filter, drop, patch the message? */ - parsed = bsc_nat_parse(msg); - if (!parsed) { - LOGP(DNAT, LOGL_ERROR, "Can not parse msg from BSC.\n"); - return -1; - } - - if (bsc_nat_filter_ipa(DIR_BSC, msg, parsed)) - goto exit; - - proto = parsed->ipa_proto; - - /* Route and modify the SCCP packet */ - if (proto == IPAC_PROTO_SCCP) { - switch (parsed->sccp_type) { - case SCCP_MSG_TYPE_UDT: - /* forward UDT messages to every BSC */ - goto send_to_all; - break; - case SCCP_MSG_TYPE_RLSD: - case SCCP_MSG_TYPE_CREF: - case SCCP_MSG_TYPE_DT1: - case SCCP_MSG_TYPE_IT: - con = patch_sccp_src_ref_to_bsc(msg, parsed, nat); - if (parsed->gsm_type == BSS_MAP_MSG_ASSIGMENT_RQST) { - osmo_counter_inc(nat->stats.sccp.calls); - - if (con) { - struct rate_ctr_group *ctrg; - ctrg = con->bsc->cfg->stats.ctrg; - rate_ctr_inc(&ctrg->ctr[BCFG_CTR_SCCP_CALLS]); - if (bsc_mgcp_assign_patch(con, msg) != 0) - LOGP(DNAT, LOGL_ERROR, "Failed to assign...\n"); - } else - LOGP(DNAT, LOGL_ERROR, "Assignment command but no BSC.\n"); - } else if (con && con->con_local == NAT_CON_END_USSD && - parsed->gsm_type == BSS_MAP_MSG_CLEAR_CMD) { - LOGP(DNAT, LOGL_NOTICE, "Clear Command for USSD Connection. Ignoring.\n"); - con = NULL; - } - break; - case SCCP_MSG_TYPE_CC: - con = patch_sccp_src_ref_to_bsc(msg, parsed, nat); - if (!con || update_sccp_src_ref(con, parsed) != 0) - goto exit; - break; - case SCCP_MSG_TYPE_RLC: - LOGP(DNAT, LOGL_ERROR, "Unexpected release complete from MSC.\n"); - goto exit; - break; - case SCCP_MSG_TYPE_CR: - /* MSC never opens a SCCP connection, fall through */ - default: - goto exit; - } - - if (!con && parsed->sccp_type == SCCP_MSG_TYPE_RLSD) { - LOGP(DNAT, LOGL_NOTICE, "Sending fake RLC on RLSD message to network.\n"); - /* Exchange src/dest for the reply */ - nat_send_rlc(msc_con, &parsed->original_dest_ref, - parsed->src_local_ref); - } else if (!con) - LOGP(DNAT, LOGL_ERROR, "Unknown connection for msg type: 0x%x from the MSC.\n", parsed->sccp_type); - } - - if (!con) { - talloc_free(parsed); - return -1; - } - if (!con->bsc->authenticated) { - talloc_free(parsed); - LOGP(DNAT, LOGL_ERROR, "Selected BSC not authenticated.\n"); - return -1; - } - - update_con_authorize(con, parsed, msg); - talloc_free(parsed); - - bsc_send_data(con->bsc, msg->l2h, msgb_l2len(msg), proto); - return 0; - -send_to_all: - /* - * Filter Paging from the network. We do not want to send a PAGING - * Command to every BSC in our network. We will analys the PAGING - * message and then send it to the authenticated messages... - */ - if (parsed->ipa_proto == IPAC_PROTO_SCCP && parsed->gsm_type == BSS_MAP_MSG_PAGING) { - bsc_nat_handle_paging(nat, msg); - goto exit; - } - /* currently send this to every BSC connected */ - llist_for_each_entry(bsc, &nat->bsc_connections, list_entry) { - if (!bsc->authenticated) - continue; - - bsc_send_data(bsc, msg->l2h, msgb_l2len(msg), parsed->ipa_proto); - } - -exit: - talloc_free(parsed); - return 0; -} - -static void msc_connection_was_lost(struct bsc_msc_connection *con) -{ - struct bsc_connection *bsc, *tmp; - - LOGP(DMSC, LOGL_ERROR, "Closing all connections downstream.\n"); - llist_for_each_entry_safe(bsc, tmp, &nat->bsc_connections, list_entry) - bsc_close_connection(bsc); - - bsc_mgcp_free_endpoints(nat); - bsc_msc_schedule_connect(con); -} - -static void msc_connection_connected(struct bsc_msc_connection *con) -{ - osmo_counter_inc(nat->stats.msc.reconn); -} - -static void msc_send_reset(struct bsc_msc_connection *msc_con) -{ - static const uint8_t reset[] = { - 0x00, 0x12, 0xfd, - 0x09, 0x00, 0x03, 0x05, 0x07, 0x02, 0x42, 0xfe, - 0x02, 0x42, 0xfe, 0x06, 0x00, 0x04, 0x30, 0x04, - 0x01, 0x20 - }; - - struct msgb *msg; - - msg = msgb_alloc_headroom(4096, 128, "08.08 reset"); - if (!msg) { - LOGP(DMSC, LOGL_ERROR, "Failed to allocate reset msg.\n"); - return; - } - - msg->l2h = msgb_put(msg, sizeof(reset)); - memcpy(msg->l2h, reset, msgb_l2len(msg)); - - queue_for_msc(msc_con, msg); - - LOGP(DMSC, LOGL_NOTICE, "Scheduled GSM0808 reset msg for the MSC.\n"); -} - -static int ipaccess_msc_read_cb(struct osmo_fd *bfd) -{ - struct bsc_msc_connection *msc_con; - struct msgb *msg = NULL; - struct ipaccess_head *hh; - int ret; - - msc_con = (struct bsc_msc_connection *) bfd->data; - - ret = ipa_msg_recv_buffered(bfd->fd, &msg, &msc_con->pending_msg); - if (ret <= 0) { - if (ret == -EAGAIN) - return 0; - if (ret == 0) - LOGP(DNAT, LOGL_FATAL, - "The connection the MSC(%s) was lost, exiting\n", - msc_con->name); - else - LOGP(DNAT, LOGL_ERROR, - "Failed to parse ip access message on %s: %d\n", - msc_con->name, ret); - - bsc_msc_lost(msc_con); - return -1; - } - - LOGP(DNAT, LOGL_DEBUG, - "MSG from MSC(%s): %s proto: %d\n", msc_con->name, - osmo_hexdump(msg->data, msg->len), msg->l2h[0]); - - /* handle base message handling */ - hh = (struct ipaccess_head *) msg->data; - - /* initialize the networking. This includes sending a GSM08.08 message */ - if (hh->proto == IPAC_PROTO_IPACCESS) { - ipa_ccm_rcvmsg_base(msg, bfd); - if (msg->l2h[0] == IPAC_MSGT_ID_ACK) - 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) { - 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; -} - -static int ipaccess_msc_write_cb(struct osmo_fd *bfd, struct msgb *msg) -{ - int rc; - rc = write(bfd->fd, msg->data, msg->len); - - if (rc != msg->len) { - LOGP(DNAT, LOGL_ERROR, "Failed to write MSG to MSC.\n"); - return -1; - } - - return rc; -} - -/* - * Below is the handling of messages coming - * from the BSC and need to be forwarded to - * a real BSC. - */ - -/* - * Remove the connection from the connections list, - * remove it from the patching of SCCP header lists - * as well. Maybe in the future even close connection.. - */ -void bsc_close_connection(struct bsc_connection *connection) -{ - struct nat_sccp_connection *sccp_patch, *tmp; - struct bsc_cmd_list *cmd_entry, *cmd_tmp; - struct rate_ctr *ctr = NULL; - - /* stop the timeout timer */ - osmo_timer_del(&connection->id_timeout); - osmo_timer_del(&connection->ping_timeout); - osmo_timer_del(&connection->pong_timeout); - - if (connection->cfg) - ctr = &connection->cfg->stats.ctrg->ctr[BCFG_CTR_DROPPED_SCCP]; - - /* remove all SCCP connections */ - llist_for_each_entry_safe(sccp_patch, tmp, &nat->sccp_connections, list_entry) { - if (sccp_patch->bsc != connection) - continue; - - if (ctr) - rate_ctr_inc(ctr); - if (sccp_patch->has_remote_ref) { - if (sccp_patch->con_local == NAT_CON_END_MSC) - nat_send_rlsd_msc(sccp_patch); - else if (sccp_patch->con_local == NAT_CON_END_USSD) - nat_send_rlsd_ussd(nat, sccp_patch); - } - - sccp_connection_destroy(sccp_patch); - } - - /* Reply to all outstanding commands */ - llist_for_each_entry_safe(cmd_entry, cmd_tmp, &connection->cmd_pending, list_entry) { - cmd_entry->cmd->type = CTRL_TYPE_ERROR; - cmd_entry->cmd->reply = "BSC closed the connection"; - ctrl_cmd_send(&cmd_entry->ccon->write_queue, cmd_entry->cmd); - bsc_nat_ctrl_del_pending(cmd_entry); - } - - /* close endpoints allocated by this BSC */ - bsc_mgcp_clear_endpoints_for(connection); - - osmo_fd_unregister(&connection->write_queue.bfd); - close(connection->write_queue.bfd.fd); - osmo_wqueue_clear(&connection->write_queue); - llist_del(&connection->list_entry); - - if (connection->pending_msg) { - LOGP(DNAT, LOGL_ERROR, "Dropping partial message on connection %d.\n", - connection->cfg ? connection->cfg->nr : -1); - msgb_free(connection->pending_msg); - connection->pending_msg = NULL; - } - - talloc_free(connection); -} - -static void bsc_maybe_close(struct bsc_connection *bsc) -{ - struct nat_sccp_connection *sccp; - if (!bsc->nat->blocked) - return; - - /* are there any connections left */ - llist_for_each_entry(sccp, &bsc->nat->sccp_connections, list_entry) - if (sccp->bsc == bsc) - return; - - /* nothing left, close the BSC */ - LOGP(DNAT, LOGL_NOTICE, "Cleaning up BSC %d in blocking mode.\n", - bsc->cfg ? bsc->cfg->nr : -1); - bsc_close_connection(bsc); -} - -static void ipaccess_close_bsc(void *data) -{ - struct sockaddr_in sock; - socklen_t len = sizeof(sock); - struct bsc_connection *conn = data; - - - getpeername(conn->write_queue.bfd.fd, (struct sockaddr *) &sock, &len); - LOGP(DNAT, LOGL_ERROR, "BSC on %s didn't respond to identity request. Closing.\n", - inet_ntoa(sock.sin_addr)); - bsc_close_connection(conn); -} - -static int verify_key(struct bsc_connection *conn, struct bsc_config *conf, const uint8_t *key, const int keylen) -{ - struct osmo_auth_vector vec; - - struct osmo_sub_auth_data auth = { - .type = OSMO_AUTH_TYPE_GSM, - .algo = OSMO_AUTH_ALG_MILENAGE, - }; - - /* expect a specific keylen */ - if (keylen != 8) { - LOGP(DNAT, LOGL_ERROR, "Key length is wrong: %d for bsc nr %d\n", - keylen, conf->nr); - return 0; - } - - memcpy(auth.u.umts.opc, conf->key, 16); - memcpy(auth.u.umts.k, conf->key, 16); - memset(auth.u.umts.amf, 0, 2); - auth.u.umts.sqn = 0; - - memset(&vec, 0, sizeof(vec)); - osmo_auth_gen_vec(&vec, &auth, conn->last_rand); - - if (vec.res_len != 8) { - LOGP(DNAT, LOGL_ERROR, "Res length is wrong: %d for bsc nr %d\n", - vec.res_len, conf->nr); - return 0; - } - - return osmo_constant_time_cmp(vec.res, key, 8) == 0; -} - -static void ipaccess_auth_bsc(struct tlv_parsed *tvp, struct bsc_connection *bsc) -{ - struct bsc_config *conf; - const char *token = (const char *) TLVP_VAL(tvp, IPAC_IDTAG_UNITNAME); - int len = TLVP_LEN(tvp, IPAC_IDTAG_UNITNAME); - const uint8_t *xres = TLVP_VAL(tvp, 0x24); - const int xlen = TLVP_LEN(tvp, 0x24); - - if (bsc->cfg) { - LOGP(DNAT, LOGL_ERROR, "Reauth on fd %d bsc nr %d\n", - bsc->write_queue.bfd.fd, bsc->cfg->nr); - return; - } - - if (len <= 0) { - LOGP(DNAT, LOGL_ERROR, "Token with length zero on fd: %d\n", - bsc->write_queue.bfd.fd); - return; - } - - if (token[len - 1] != '\0') { - LOGP(DNAT, LOGL_ERROR, "Token not null terminated on fd: %d\n", - bsc->write_queue.bfd.fd); - return; - } - - /* - * New systems have fixed the structure of the message but - * we need to support old ones too. - */ - if (len >= 2 && token[len - 2] == '\0') - len -= 1; - - conf = bsc_config_by_token(bsc->nat, token, len); - if (!conf) { - LOGP(DNAT, LOGL_ERROR, - "No bsc found for token '%s' len %d on fd: %d.\n", token, - bsc->write_queue.bfd.fd, len); - bsc_close_connection(bsc); - return; - } - - /* We have set a key and expect it to be present */ - if (conf->key_present && !verify_key(bsc, conf, xres, xlen - 1)) { - LOGP(DNAT, LOGL_ERROR, - "Wrong key for bsc nr %d fd: %d.\n", conf->nr, - bsc->write_queue.bfd.fd); - bsc_close_connection(bsc); - return; - } - - rate_ctr_inc(&conf->stats.ctrg->ctr[BCFG_CTR_NET_RECONN]); - bsc->authenticated = 1; - bsc->cfg = conf; - osmo_timer_del(&bsc->id_timeout); - LOGP(DNAT, LOGL_NOTICE, "Authenticated bsc nr: %d on fd %d\n", - conf->nr, bsc->write_queue.bfd.fd); - start_ping_pong(bsc); -} - -static void handle_con_stats(struct nat_sccp_connection *con) -{ - struct rate_ctr_group *ctrg; - int id = bsc_conn_type_to_ctr(con); - - if (id == -1) - return; - - if (!con->bsc || !con->bsc->cfg) - return; - - ctrg = con->bsc->cfg->stats.ctrg; - rate_ctr_inc(&ctrg->ctr[id]); -} - -static int forward_sccp_to_msc(struct bsc_connection *bsc, struct msgb *msg) -{ - int con_filter = 0; - char *imsi = NULL; - struct bsc_msc_connection *con_msc = NULL; - struct bsc_connection *con_bsc = NULL; - int con_type; - struct bsc_nat_parsed *parsed; - struct bsc_filter_reject_cause cause; - - /* Parse and filter messages */ - parsed = bsc_nat_parse(msg); - if (!parsed) { - LOGP(DNAT, LOGL_ERROR, "Can not parse msg from BSC.\n"); - msgb_free(msg); - return -1; - } - - if (bsc_nat_filter_ipa(DIR_MSC, msg, parsed)) - goto exit; - - /* - * check authentication after filtering to not reject auth - * responses coming from the BSC. We have to make sure that - * nothing from the exit path will forward things to the MSC - */ - if (!bsc->authenticated) { - LOGP(DNAT, LOGL_ERROR, "BSC is not authenticated.\n"); - msgb_free(msg); - return -1; - } - - - /* modify the SCCP entries */ - if (parsed->ipa_proto == IPAC_PROTO_SCCP) { - int filter; - struct nat_sccp_connection *con; - switch (parsed->sccp_type) { - case SCCP_MSG_TYPE_CR: - memset(&cause, 0, sizeof(cause)); - filter = bsc_nat_filter_sccp_cr(bsc, msg, parsed, - &con_type, &imsi, &cause); - if (filter < 0) { - if (imsi) - bsc_nat_inform_reject(bsc, imsi); - bsc_stat_reject(filter, bsc, 0); - goto exit3; - } - - if (!create_sccp_src_ref(bsc, parsed)) - goto exit2; - con = patch_sccp_src_ref_to_msc(msg, parsed, bsc); - OSMO_ASSERT(con); - con->msc_con = bsc->nat->msc_con; - con_msc = con->msc_con; - con->filter_state.con_type = con_type; - con->filter_state.imsi_checked = filter; - bsc_nat_extract_lac(bsc, con, parsed, msg); - if (imsi) - con->filter_state.imsi = talloc_steal(con, imsi); - imsi = NULL; - con_bsc = con->bsc; - handle_con_stats(con); - break; - case SCCP_MSG_TYPE_RLSD: - case SCCP_MSG_TYPE_CREF: - case SCCP_MSG_TYPE_DT1: - case SCCP_MSG_TYPE_CC: - case SCCP_MSG_TYPE_IT: - con = patch_sccp_src_ref_to_msc(msg, parsed, bsc); - if (con) { - /* only filter non local connections */ - if (!con->con_local) { - memset(&cause, 0, sizeof(cause)); - filter = bsc_nat_filter_dt(bsc, msg, - con, parsed, &cause); - if (filter < 0) { - if (con->filter_state.imsi) - bsc_nat_inform_reject(bsc, - con->filter_state.imsi); - bsc_stat_reject(filter, bsc, 1); - bsc_send_con_release(bsc, con, &cause); - con = NULL; - goto exit2; - } - - /* hand data to a side channel */ - if (bsc_ussd_check(con, parsed, msg) == 1) - con->con_local = NAT_CON_END_USSD; - - /* - * Optionally rewrite setup message. This can - * replace the msg and the parsed structure becomes - * invalid. - */ - msg = bsc_nat_rewrite_msg(bsc->nat, msg, parsed, - con->filter_state.imsi); - talloc_free(parsed); - parsed = NULL; - } else if (con->con_local == NAT_CON_END_USSD) { - bsc_ussd_check(con, parsed, msg); - } - - con_bsc = con->bsc; - con_msc = con->msc_con; - con_filter = con->con_local; - } - - break; - case SCCP_MSG_TYPE_RLC: - con = patch_sccp_src_ref_to_msc(msg, parsed, bsc); - if (con) { - con_bsc = con->bsc; - con_msc = con->msc_con; - con_filter = con->con_local; - } - remove_sccp_src_ref(bsc, msg, parsed); - bsc_maybe_close(bsc); - break; - case SCCP_MSG_TYPE_UDT: - /* simply forward everything */ - con = NULL; - break; - default: - LOGP(DNAT, LOGL_ERROR, "Not forwarding to msc sccp type: 0x%x\n", parsed->sccp_type); - con = NULL; - goto exit2; - break; - } - } else if (parsed->ipa_proto == IPAC_PROTO_MGCP_OLD) { - bsc_mgcp_forward(bsc, msg); - goto exit2; - } else { - LOGP(DNAT, LOGL_ERROR, "Not forwarding unknown stream id: 0x%x\n", parsed->ipa_proto); - goto exit2; - } - - if (con_msc && con_bsc != bsc) { - LOGP(DNAT, LOGL_ERROR, "The connection belongs to a different BTS: input: %d con: %d\n", - bsc->cfg->nr, con_bsc->cfg->nr); - goto exit2; - } - - /* do not forward messages to the MSC */ - if (con_filter) - goto exit2; - - if (!con_msc) { - LOGP(DNAT, LOGL_ERROR, "Not forwarding data bsc_nr: %d ipa: %d type: 0x%x\n", - bsc->cfg->nr, - parsed ? parsed->ipa_proto : -1, - parsed ? parsed->sccp_type : -1); - goto exit2; - } - - /* send the non-filtered but maybe modified msg */ - queue_for_msc(con_msc, msg); - if (parsed) - talloc_free(parsed); - return 0; - -exit: - /* if we filter out the reset send an ack to the BSC */ - if (parsed->bssap == 0 && parsed->gsm_type == BSS_MAP_MSG_RESET) { - send_reset_ack(bsc); - send_reset_ack(bsc); - } else if (parsed->ipa_proto == IPAC_PROTO_IPACCESS) { - /* do we know who is handling this? */ - if (msg->l2h[0] == IPAC_MSGT_ID_RESP && msgb_l2len(msg) > 2) { - struct tlv_parsed tvp; - int ret; - ret = ipa_ccm_idtag_parse_off(&tvp, - (unsigned char *) msg->l2h + 2, - msgb_l2len(msg) - 2, 0); - if (ret < 0) { - LOGP(DNAT, LOGL_ERROR, "ignoring IPA response " - "message with malformed TLVs\n"); - return ret; - } - if (TLVP_PRESENT(&tvp, IPAC_IDTAG_UNITNAME)) - ipaccess_auth_bsc(&tvp, bsc); - } - - goto exit2; - } - -exit2: - if (imsi) - talloc_free(imsi); - talloc_free(parsed); - msgb_free(msg); - return -1; - -exit3: - /* send a SCCP Connection Refused */ - if (imsi) - talloc_free(imsi); - bsc_send_con_refuse(bsc, parsed, con_type, &cause); - talloc_free(parsed); - msgb_free(msg); - return -1; -} - -static int ipaccess_bsc_read_cb(struct osmo_fd *bfd) -{ - struct bsc_connection *bsc = bfd->data; - struct msgb *msg = NULL; - struct ipaccess_head *hh; - struct ipaccess_head_ext *hh_ext; - int ret; - - ret = ipa_msg_recv_buffered(bfd->fd, &msg, &bsc->pending_msg); - if (ret <= 0) { - if (ret == -EAGAIN) - return 0; - if (ret == 0) - LOGP(DNAT, LOGL_ERROR, - "The connection to the BSC Nr: %d was lost. Cleaning it\n", - bsc->cfg ? bsc->cfg->nr : -1); - else - LOGP(DNAT, LOGL_ERROR, - "Stream error on BSC Nr: %d. Failed to parse ip access message: %d (%s)\n", - bsc->cfg ? bsc->cfg->nr : -1, ret, strerror(-ret)); - - bsc_close_connection(bsc); - return -1; - } - - - LOGP(DNAT, LOGL_DEBUG, "MSG from BSC: %s proto: %d\n", osmo_hexdump(msg->data, msg->len), msg->l2h[0]); - - /* Handle messages from the BSC */ - hh = (struct ipaccess_head *) msg->data; - - /* stop the pong timeout */ - if (hh->proto == IPAC_PROTO_IPACCESS) { - if (msg->l2h[0] == IPAC_MSGT_PONG) { - osmo_timer_del(&bsc->pong_timeout); - msgb_free(msg); - return 0; - } else if (msg->l2h[0] == IPAC_MSGT_PING) { - send_pong(bsc); - msgb_free(msg); - return 0; - } - /* Message contains the ipaccess_head_ext header, investigate further */ - } else if (hh->proto == IPAC_PROTO_OSMO && - msg->len > sizeof(*hh) + sizeof(*hh_ext)) { - - hh_ext = (struct ipaccess_head_ext *) hh->data; - /* l2h is where the actual command data is expected */ - msg->l2h = hh_ext->data; - - if (hh_ext->proto == IPAC_PROTO_EXT_CTRL) - return bsc_nat_handle_ctrlif_msg(bsc, msg); - } - - /* FIXME: Currently no PONG is sent to the BSC */ - /* FIXME: Currently no ID ACK is sent to the BSC */ - forward_sccp_to_msc(bsc, msg); - - return 0; -} - -static int ipaccess_listen_bsc_cb(struct osmo_fd *bfd, unsigned int what) -{ - struct bsc_connection *bsc; - int fd, rc, on; - struct sockaddr_in sa; - socklen_t sa_len = sizeof(sa); - - if (!(what & BSC_FD_READ)) - return 0; - - fd = accept(bfd->fd, (struct sockaddr *) &sa, &sa_len); - if (fd < 0) { - perror("accept"); - return fd; - } - - /* count the reconnect */ - osmo_counter_inc(nat->stats.bsc.reconn); - - /* - * if we are not connected to a msc... just close the socket - */ - if (!bsc_nat_msc_is_connected(nat)) { - LOGP(DNAT, LOGL_NOTICE, "Disconnecting BSC due lack of MSC connection.\n"); - close(fd); - return 0; - } - - if (nat->blocked) { - LOGP(DNAT, LOGL_NOTICE, "Disconnecting BSC due NAT being blocked.\n"); - close(fd); - return 0; - } - - on = 1; - rc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); - if (rc != 0) - LOGP(DNAT, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno)); - - rc = setsockopt(fd, IPPROTO_IP, IP_TOS, - &nat->bsc_ip_dscp, sizeof(nat->bsc_ip_dscp)); - if (rc != 0) - LOGP(DNAT, LOGL_ERROR, "Failed to set IP_TOS: %s\n", strerror(errno)); - - /* todo... do something with the connection */ - /* todo... use GNUtls to see if we want to trust this as a BTS */ - - /* - * - */ - bsc = bsc_connection_alloc(nat); - if (!bsc) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate BSC struct.\n"); - close(fd); - return -1; - } - - bsc->write_queue.bfd.data = bsc; - bsc->write_queue.bfd.fd = fd; - bsc->write_queue.read_cb = ipaccess_bsc_read_cb; - bsc->write_queue.write_cb = bsc_write_cb; - bsc->write_queue.bfd.when = BSC_FD_READ; - if (osmo_fd_register(&bsc->write_queue.bfd) < 0) { - LOGP(DNAT, LOGL_ERROR, "Failed to register BSC fd.\n"); - close(fd); - talloc_free(bsc); - return -2; - } - - LOGP(DNAT, LOGL_NOTICE, "BSC connection on %d with IP: %s\n", - fd, inet_ntoa(sa.sin_addr)); - - llist_add(&bsc->list_entry, &nat->bsc_connections); - bsc->last_id = 0; - - send_id_ack(bsc); - send_id_req(nat, bsc); - send_mgcp_reset(bsc); - - /* - * start the hangup timer - */ - osmo_timer_setup(&bsc->id_timeout, ipaccess_close_bsc, bsc); - osmo_timer_schedule(&bsc->id_timeout, nat->auth_timeout, 0); - return 0; -} - -static void print_usage() -{ - printf("Usage: bsc_nat\n"); -} - -static void print_help() -{ - printf(" Some useful help...\n"); - printf(" -h --help this text\n"); - printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM enable debugging\n"); - printf(" -D --daemonize Fork the process into a background daemon\n"); - printf(" -s --disable-color\n"); - printf(" -c --config-file filename The config file to use.\n"); - printf(" -m --msc=IP. The address of the MSC.\n"); - printf(" -l --local=IP. The local address of this BSC.\n"); -} - -static void handle_options(int argc, char **argv) -{ - while (1) { - int option_index = 0, c; - static struct option long_options[] = { - {"help", 0, 0, 'h'}, - {"debug", 1, 0, 'd'}, - {"daemonize", 0, 0, 'D'}, - {"config-file", 1, 0, 'c'}, - {"disable-color", 0, 0, 's'}, - {"timestamp", 0, 0, 'T'}, - {"msc", 1, 0, 'm'}, - {"local", 1, 0, 'l'}, - {0, 0, 0, 0} - }; - - c = getopt_long(argc, argv, "hd:sTPc:m:l:D", - long_options, &option_index); - if (c == -1) - break; - - switch (c) { - case 'h': - print_usage(); - print_help(); - exit(0); - case 's': - log_set_use_color(osmo_stderr_target, 0); - break; - case 'd': - log_parse_category_mask(osmo_stderr_target, optarg); - break; - case 'D': - daemonize = 1; - break; - case 'c': - config_file = optarg; - break; - case 'T': - log_set_print_timestamp(osmo_stderr_target, 1); - break; - case 'm': - msc_ip = optarg; - break; - case 'l': - inet_aton(optarg, &local_addr); - break; - default: - /* ignore */ - break; - } - } -} - -static void signal_handler(int signal) -{ - switch (signal) { - case SIGABRT: - /* in case of abort, we want to obtain a talloc report - * and then return to the caller, who will abort the process */ - case SIGUSR1: - talloc_report_full(tall_bsc_ctx, stderr); - break; - default: - break; - } -} - -static void sccp_close_unconfirmed(void *_data) -{ - int destroyed = 0; - struct bsc_connection *bsc, *bsc_tmp; - struct nat_sccp_connection *conn, *tmp1; - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - - llist_for_each_entry_safe(conn, tmp1, &nat->sccp_connections, list_entry) { - if (conn->has_remote_ref) - continue; - - int diff = (now.tv_sec - conn->creation_time.tv_sec) / 60; - if (diff < SCCP_CLOSE_TIME_TIMEOUT) - continue; - - LOGP(DNAT, LOGL_ERROR, - "SCCP connection 0x%x/0x%x was never confirmed on bsc nr. %d\n", - sccp_src_ref_to_int(&conn->real_ref), - sccp_src_ref_to_int(&conn->patched_ref), - conn->bsc->cfg->nr); - sccp_connection_destroy(conn); - destroyed = 1; - } - - if (!destroyed) - goto out; - - /* now close out any BSC */ - llist_for_each_entry_safe(bsc, bsc_tmp, &nat->bsc_connections, list_entry) - bsc_maybe_close(bsc); - -out: - osmo_timer_schedule(&sccp_close, SCCP_CLOSE_TIME, 0); -} - -extern void *tall_ctr_ctx; -static void talloc_init_ctx() -{ - tall_bsc_ctx = talloc_named_const(NULL, 0, "nat"); - msgb_talloc_ctx_init(tall_bsc_ctx, 0); - tall_ctr_ctx = talloc_named_const(tall_bsc_ctx, 0, "counter"); -} - -extern int bsc_vty_go_parent(struct vty *vty); - -static struct vty_app_info vty_info = { - .name = "OsmoBSCNAT", - .version = PACKAGE_VERSION, - .go_parent_cb = bsc_vty_go_parent, - .is_config_node = bsc_vty_is_config_node, -}; - - -int main(int argc, char **argv) -{ - int rc; - - talloc_init_ctx(); - - osmo_init_logging(&log_info); - - nat = bsc_nat_alloc(); - if (!nat) { - fprintf(stderr, "Failed to allocate the BSC nat.\n"); - return -4; - } - - nat->mgcp_cfg = mgcp_config_alloc(); - if (!nat->mgcp_cfg) { - fprintf(stderr, "Failed to allocate MGCP cfg.\n"); - return -5; - } - - /* We need to add mode-set for amr codecs */ - nat->sdp_ensure_amr_mode_set = 1; - - vty_info.copyright = openbsc_copyright; - vty_init(&vty_info); - logging_vty_add_cmds(NULL); - osmo_stats_vty_add_cmds(&log_info); - bsc_nat_vty_init(nat); - ctrl_vty_init(tall_bsc_ctx); - - - /* parse options */ - local_addr.s_addr = INADDR_ANY; - handle_options(argc, argv); - - nat->include_base = dirname(talloc_strdup(tall_bsc_ctx, config_file)); - - rate_ctr_init(tall_bsc_ctx); - osmo_stats_init(tall_bsc_ctx); - - /* init vty and parse */ - if (mgcp_parse_config(config_file, nat->mgcp_cfg, MGCP_BSC_NAT) < 0) { - fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); - return -3; - } - - /* start telnet after reading config for vty_get_bind_addr() */ - if (telnet_init_dynif(tall_bsc_ctx, NULL, vty_get_bind_addr(), - OSMO_VTY_PORT_BSC_NAT)) { - fprintf(stderr, "Creating VTY telnet line failed\n"); - return -5; - } - - /* over rule the VTY config for MSC IP */ - if (msc_ip) - bsc_nat_set_msc_ip(nat, msc_ip); - - /* seed the PRNG */ - srand(time(NULL)); - - LOGP(DNAT, LOGL_NOTICE, "BSCs configured from %s\n", nat->resolved_path); - - /* - * Setup the MGCP code.. - */ - if (bsc_mgcp_nat_init(nat) != 0) - return -4; - - /* connect to the MSC */ - nat->msc_con = bsc_msc_create(nat, &nat->dests); - if (!nat->msc_con) { - fprintf(stderr, "Creating a bsc_msc_connection failed.\n"); - exit(1); - } - - /* start control interface after reading config for - * ctrl_vty_get_bind_addr() */ - nat->ctrl = bsc_nat_controlif_setup(nat, ctrl_vty_get_bind_addr(), - OSMO_CTRL_PORT_BSC_NAT); - if (!nat->ctrl) { - fprintf(stderr, "Creating the control interface failed.\n"); - exit(1); - } - - nat->msc_con->name = "main MSC"; - nat->msc_con->connection_loss = msc_connection_was_lost; - nat->msc_con->connected = msc_connection_connected; - nat->msc_con->write_queue.read_cb = ipaccess_msc_read_cb; - nat->msc_con->write_queue.write_cb = ipaccess_msc_write_cb;; - nat->msc_con->write_queue.bfd.data = nat->msc_con; - bsc_msc_connect(nat->msc_con); - - /* wait for the BSC */ - rc = make_sock(&bsc_listen, IPPROTO_TCP, ntohl(local_addr.s_addr), - 5000, 0, ipaccess_listen_bsc_cb, nat); - if (rc != 0) { - fprintf(stderr, "Failed to listen for BSC.\n"); - exit(1); - } - - rc = bsc_ussd_init(nat); - if (rc != 0) { - LOGP(DNAT, LOGL_ERROR, "Failed to bind the USSD socket.\n"); - exit(1); - } - - signal(SIGABRT, &signal_handler); - signal(SIGUSR1, &signal_handler); - osmo_init_ignore_signals(); - - if (daemonize) { - rc = osmo_daemonize(); - if (rc < 0) { - perror("Error during daemonize"); - exit(1); - } - } - - /* recycle timer */ - sccp_set_log_area(DSCCP); - osmo_timer_setup(&sccp_close, sccp_close_unconfirmed, NULL); - osmo_timer_schedule(&sccp_close, SCCP_CLOSE_TIME, 0); - - while (1) { - osmo_select_main(0); - } - - return 0; -} - -/* Close all connections handed out to the USSD module */ -int bsc_ussd_close_connections(struct bsc_nat *nat) -{ - struct nat_sccp_connection *con; - llist_for_each_entry(con, &nat->sccp_connections, list_entry) { - if (con->con_local != NAT_CON_END_USSD) - continue; - if (!con->bsc) - continue; - - nat_send_clrc_bsc(con); - nat_send_rlsd_bsc(con); - } - - return 0; -} diff --git a/src/osmo-bsc_nat/bsc_nat_ctrl.c b/src/osmo-bsc_nat/bsc_nat_ctrl.c deleted file mode 100644 index 128ea6518..000000000 --- a/src/osmo-bsc_nat/bsc_nat_ctrl.c +++ /dev/null @@ -1,524 +0,0 @@ -/* - * (C) 2011-2012 by Holger Hans Peter Freyther - * (C) 2011-2012 by On-Waves - * (C) 2011 by Daniel Willmann - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <osmocom/core/talloc.h> - -#include <osmocom/ctrl/control_cmd.h> -#include <osmocom/ctrl/control_if.h> -#include <osmocom/ctrl/ports.h> - -#include <osmocom/vty/misc.h> - -#include <openbsc/ctrl.h> -#include <openbsc/bsc_nat.h> -#include <openbsc/bsc_msg_filter.h> -#include <openbsc/vty.h> -#include <openbsc/gsm_data.h> - -#include <unistd.h> -#include <string.h> -#include <errno.h> - - -#define NAT_MAX_CTRL_ID 65535 - -static struct bsc_nat *g_nat; - -static int bsc_id_unused(int id, struct bsc_connection *bsc) -{ - struct bsc_cmd_list *pending; - - llist_for_each_entry(pending, &bsc->cmd_pending, list_entry) { - if (pending->nat_id == id) - return 0; - } - return 1; -} - -static int get_next_free_bsc_id(struct bsc_connection *bsc) -{ - int new_id, overflow = 0; - - new_id = bsc->last_id; - - do { - new_id++; - if (new_id == NAT_MAX_CTRL_ID) { - new_id = 1; - overflow++; - } - - if (bsc_id_unused(new_id, bsc)) { - bsc->last_id = new_id; - return new_id; - } - } while (overflow != 2); - - return -1; -} - -void bsc_nat_ctrl_del_pending(struct bsc_cmd_list *pending) -{ - llist_del(&pending->list_entry); - osmo_timer_del(&pending->timeout); - talloc_free(pending->cmd); - talloc_free(pending); -} - -static struct bsc_cmd_list *bsc_get_pending(struct bsc_connection *bsc, char *id_str) -{ - struct bsc_cmd_list *cmd_entry; - int id = atoi(id_str); - if (id == 0) - return NULL; - - llist_for_each_entry(cmd_entry, &bsc->cmd_pending, list_entry) { - if (cmd_entry->nat_id == id) { - return cmd_entry; - } - } - return NULL; -} - -int bsc_nat_handle_ctrlif_msg(struct bsc_connection *bsc, struct msgb *msg) -{ - struct ctrl_cmd *cmd; - struct bsc_cmd_list *pending; - char *var, *id; - - cmd = ctrl_cmd_parse(bsc, msg); - msgb_free(msg); - - if (!cmd) { - cmd = talloc_zero(bsc, struct ctrl_cmd); - if (!cmd) { - LOGP(DNAT, LOGL_ERROR, "OOM!\n"); - return -ENOMEM; - } - cmd->type = CTRL_TYPE_ERROR; - cmd->id = "err"; - cmd->reply = "Failed to parse command."; - goto err; - } - - if (bsc->cfg && !llist_empty(&bsc->cfg->lac_list)) { - if (cmd->variable) { - var = talloc_asprintf(cmd, "net.0.bsc.%i.%s", bsc->cfg->nr, - cmd->variable); - if (!var) { - cmd->type = CTRL_TYPE_ERROR; - cmd->reply = "OOM"; - goto err; - } - talloc_free(cmd->variable); - cmd->variable = var; - } - - /* We have to handle TRAPs before matching pending */ - if (cmd->type == CTRL_TYPE_TRAP) { - ctrl_cmd_send_to_all(bsc->nat->ctrl, cmd); - talloc_free(cmd); - return 0; - } - - /* Find the pending command */ - pending = bsc_get_pending(bsc, cmd->id); - if (pending) { - id = talloc_strdup(cmd, pending->cmd->id); - if (!id) { - cmd->type = CTRL_TYPE_ERROR; - cmd->reply = "OOM"; - goto err; - } - cmd->id = id; - ctrl_cmd_send(&pending->ccon->write_queue, cmd); - bsc_nat_ctrl_del_pending(pending); - } else { - /* We need to handle TRAPS here */ - if ((cmd->type != CTRL_TYPE_ERROR) && - (cmd->type != CTRL_TYPE_TRAP)) { - LOGP(DNAT, LOGL_NOTICE, "Got control message " - "from BSC without pending entry\n"); - cmd->type = CTRL_TYPE_ERROR; - cmd->reply = "No request outstanding"; - goto err; - } - } - } - talloc_free(cmd); - return 0; -err: - ctrl_cmd_send(&bsc->write_queue, cmd); - talloc_free(cmd); - return 0; -} - -static void pending_timeout_cb(void *data) -{ - struct bsc_cmd_list *pending = data; - LOGP(DNAT, LOGL_ERROR, "Command timed out\n"); - pending->cmd->type = CTRL_TYPE_ERROR; - pending->cmd->reply = "Command timed out"; - ctrl_cmd_send(&pending->ccon->write_queue, pending->cmd); - - bsc_nat_ctrl_del_pending(pending); -} - -static void ctrl_conn_closed_cb(struct ctrl_connection *connection) -{ - struct bsc_connection *bsc; - struct bsc_cmd_list *pending, *tmp; - - llist_for_each_entry(bsc, &g_nat->bsc_connections, list_entry) { - llist_for_each_entry_safe(pending, tmp, &bsc->cmd_pending, list_entry) { - if (pending->ccon == connection) - bsc_nat_ctrl_del_pending(pending); - } - } -} - -static int extract_bsc_nr_variable(char *variable, unsigned int *nr, char **bsc_variable) -{ - char *nr_str, *tmp, *saveptr = NULL; - - tmp = strtok_r(variable, ".", &saveptr); - tmp = strtok_r(NULL, ".", &saveptr); - tmp = strtok_r(NULL, ".", &saveptr); - nr_str = strtok_r(NULL, ".", &saveptr); - if (!nr_str) - return 0; - *nr = atoi(nr_str); - - tmp = strtok_r(NULL, "\0", &saveptr); - if (!tmp) - return 0; - - *bsc_variable = tmp; - return 1; -} - -static int forward_to_bsc(struct ctrl_cmd *cmd) -{ - int ret = CTRL_CMD_HANDLED; - struct ctrl_cmd *bsc_cmd = NULL; - struct bsc_connection *bsc; - struct bsc_cmd_list *pending; - unsigned int nr; - char *bsc_variable; - - /* Skip over the beginning (bsc.) */ - if (!extract_bsc_nr_variable(cmd->variable, &nr, &bsc_variable)) { - cmd->reply = "command incomplete"; - goto err; - } - - - llist_for_each_entry(bsc, &g_nat->bsc_connections, list_entry) { - if (!bsc->cfg) - continue; - if (!bsc->authenticated) - continue; - if (bsc->cfg->nr == nr) { - /* Add pending command to list */ - pending = talloc_zero(bsc, struct bsc_cmd_list); - if (!pending) { - cmd->reply = "OOM"; - goto err; - } - - pending->nat_id = get_next_free_bsc_id(bsc); - if (pending->nat_id < 0) { - cmd->reply = "No free ID found"; - goto err; - } - - bsc_cmd = ctrl_cmd_cpy(bsc, cmd); - if (!bsc_cmd) { - cmd->reply = "Could not forward command"; - goto err; - } - - talloc_free(bsc_cmd->id); - bsc_cmd->id = talloc_asprintf(bsc_cmd, "%i", pending->nat_id); - if (!bsc_cmd->id) { - cmd->reply = "OOM"; - goto err; - } - - talloc_free(bsc_cmd->variable); - bsc_cmd->variable = talloc_strdup(bsc_cmd, bsc_variable); - if (!bsc_cmd->variable) { - cmd->reply = "OOM"; - goto err; - } - - if (ctrl_cmd_send(&bsc->write_queue, bsc_cmd)) { - cmd->reply = "Sending failed"; - goto err; - } - pending->ccon = cmd->ccon; - pending->ccon->closed_cb = ctrl_conn_closed_cb; - pending->cmd = cmd; - - /* Setup the timeout */ - osmo_timer_setup(&pending->timeout, pending_timeout_cb, - pending); - /* TODO: Make timeout configurable */ - osmo_timer_schedule(&pending->timeout, 10, 0); - llist_add_tail(&pending->list_entry, &bsc->cmd_pending); - - goto done; - } - } - /* We end up here if there's no bsc to handle our LAC */ - cmd->reply = "no BSC with this nr"; -err: - ret = CTRL_CMD_ERROR; -done: - talloc_free(bsc_cmd); - return ret; - -} - - -CTRL_CMD_DEFINE(fwd_cmd, "net 0 bsc *"); -static int get_fwd_cmd(struct ctrl_cmd *cmd, void *data) -{ - return forward_to_bsc(cmd); -} - -static int set_fwd_cmd(struct ctrl_cmd *cmd, void *data) -{ - return forward_to_bsc(cmd); -} - -static int verify_fwd_cmd(struct ctrl_cmd *cmd, const char *value, void *data) -{ - return 0; -} - -static int extract_bsc_cfg_variable(struct ctrl_cmd *cmd, struct bsc_config **cfg, - char **bsc_variable) -{ - unsigned int nr; - - if (!extract_bsc_nr_variable(cmd->variable, &nr, bsc_variable)) { - cmd->reply = "command incomplete"; - return 0; - } - - *cfg = bsc_config_num(g_nat, nr); - if (!*cfg) { - cmd->reply = "Unknown BSC"; - return 0; - } - - return 1; -} - -CTRL_CMD_DEFINE(net_cfg_cmd, "net 0 bsc_cfg *"); -static int get_net_cfg_cmd(struct ctrl_cmd *cmd, void *data) -{ - char *bsc_variable; - struct bsc_config *bsc_cfg; - - if (!extract_bsc_cfg_variable(cmd, &bsc_cfg, &bsc_variable)) - return CTRL_CMD_ERROR; - - if (strcmp(bsc_variable, "access-list-name") == 0) { - cmd->reply = talloc_asprintf(cmd, "%s", - bsc_cfg->acc_lst_name ? bsc_cfg->acc_lst_name : ""); - return CTRL_CMD_REPLY; - } - - cmd->reply = "unknown command"; - return CTRL_CMD_ERROR; -} - -static int set_net_cfg_cmd(struct ctrl_cmd *cmd, void *data) -{ - char *bsc_variable; - struct bsc_config *bsc_cfg; - - if (!extract_bsc_cfg_variable(cmd, &bsc_cfg, &bsc_variable)) - return CTRL_CMD_ERROR; - - if (strcmp(bsc_variable, "access-list-name") == 0) { - osmo_talloc_replace_string(bsc_cfg, &bsc_cfg->acc_lst_name, cmd->value); - cmd->reply = talloc_asprintf(cmd, "%s", - bsc_cfg->acc_lst_name ? bsc_cfg->acc_lst_name : ""); - return CTRL_CMD_REPLY; - } else if (strcmp(bsc_variable, "no-access-list-name") == 0) { - talloc_free(bsc_cfg->acc_lst_name); - bsc_cfg->acc_lst_name = NULL; - cmd->reply = ""; - return CTRL_CMD_REPLY; - } - - cmd->reply = "unknown command"; - return CTRL_CMD_ERROR; -} - -static int verify_net_cfg_cmd(struct ctrl_cmd *cmd, const char *value, void *data) -{ - return 0; -} - -CTRL_CMD_DEFINE(net_cfg_acc_cmd, "net 0 add allow access-list *"); -static const char *extract_acc_name(const char *var) -{ - char *str; - - str = strstr(var, "net.0.add.allow.access-list."); - if (!str) - return NULL; - str += strlen("net.0.add.allow.access-list."); - if (strlen(str) == 0) - return NULL; - return str; -} - -static int get_net_cfg_acc_cmd(struct ctrl_cmd *cmd, void *data) -{ - cmd->reply = "Append only"; - return CTRL_CMD_ERROR; -} - -static int set_net_cfg_acc_cmd(struct ctrl_cmd *cmd, void *data) -{ - const char *access_name = extract_acc_name(cmd->variable); - struct bsc_msg_acc_lst *acc; - struct bsc_msg_acc_lst_entry *entry; - const char *value = cmd->value; - int rc; - - /* Should have been caught by verify_net_cfg_acc_cmd */ - acc = bsc_msg_acc_lst_find(&g_nat->access_lists, access_name); - if (!acc) { - cmd->reply = "Access list not found"; - return CTRL_CMD_ERROR; - } - - entry = bsc_msg_acc_lst_entry_create(acc); - if (!entry) { - cmd->reply = "OOM"; - return CTRL_CMD_ERROR; - } - - rc = gsm_parse_reg(acc, &entry->imsi_allow_re, &entry->imsi_allow, 1, &value); - if (rc != 0) { - cmd->reply = "Failed to compile expression"; - return CTRL_CMD_ERROR; - } - - cmd->reply = "IMSI allow added to access list"; - return CTRL_CMD_REPLY; -} - -static int verify_net_cfg_acc_cmd(struct ctrl_cmd *cmd, const char *value, void *data) -{ - const char *access_name = extract_acc_name(cmd->variable); - struct bsc_msg_acc_lst *acc = bsc_msg_acc_lst_find(&g_nat->access_lists, access_name); - - if (!acc) { - cmd->reply = "Access list not known"; - return -1; - } - - return 0; -} - -CTRL_CMD_DEFINE_WO_NOVRF(net_save_cmd, "net 0 save-configuration"); - -static int set_net_save_cmd(struct ctrl_cmd *cmd, void *data) -{ - int rc = osmo_vty_save_config_file(); - cmd->reply = talloc_asprintf(cmd, "%d", rc); - if (!cmd->reply) { - cmd->reply = "OOM"; - return CTRL_CMD_ERROR; - } - - return CTRL_CMD_REPLY; -} - -struct ctrl_handle *bsc_nat_controlif_setup(struct bsc_nat *nat, - const char *bind_addr, int port) -{ - struct ctrl_handle *ctrl; - int rc; - - - ctrl = bsc_controlif_setup(NULL, bind_addr, OSMO_CTRL_PORT_BSC_NAT); - if (!ctrl) { - fprintf(stderr, "Failed to initialize the control interface. Exiting.\n"); - return NULL; - } - - rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_fwd_cmd); - if (rc) { - fprintf(stderr, "Failed to install the control command. Exiting.\n"); - goto error; - } - rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_cfg_cmd); - if (rc) { - fprintf(stderr, "Failed to install the net cfg command. Exiting.\n"); - goto error; - } - rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_cfg_acc_cmd); - if (rc) { - fprintf(stderr, "Failed to install the net acc command. Exiting.\n"); - goto error; - } - rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_save_cmd); - if (rc) { - fprintf(stderr, "Failed to install the net save command. Exiting.\n"); - goto error; - } - - g_nat = nat; - return ctrl; - -error: - osmo_fd_unregister(&ctrl->listen_fd); - close(ctrl->listen_fd.fd); - talloc_free(ctrl); - return NULL; -} - -void bsc_nat_inform_reject(struct bsc_connection *conn, const char *imsi) -{ - struct ctrl_cmd *cmd; - - cmd = ctrl_cmd_create(conn, CTRL_TYPE_TRAP); - if (!cmd) { - LOGP(DCTRL, LOGL_ERROR, "Failed to create TRAP command.\n"); - return; - } - - cmd->id = "0"; - cmd->variable = talloc_asprintf(cmd, "net.0.bsc.%d.notification-rejection-v1", - conn->cfg->nr); - cmd->reply = talloc_asprintf(cmd, "imsi=%s", imsi); - - ctrl_cmd_send_to_all(conn->cfg->nat->ctrl, cmd); - talloc_free(cmd); -} diff --git a/src/osmo-bsc_nat/bsc_nat_filter.c b/src/osmo-bsc_nat/bsc_nat_filter.c deleted file mode 100644 index e73529097..000000000 --- a/src/osmo-bsc_nat/bsc_nat_filter.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * (C) 2010-2015 by Holger Hans Peter Freyther <zecke@selfish.org> - * (C) 2010-2012 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <openbsc/bsc_nat.h> -#include <openbsc/bsc_nat_sccp.h> -#include <openbsc/bsc_msg_filter.h> -#include <openbsc/debug.h> - -#include <osmocom/gsm/gsm0808.h> - -#include <osmocom/gsm/protocol/gsm_08_08.h> -#include <osmocom/gsm/protocol/gsm_04_11.h> - -#include <osmocom/sccp/sccp.h> - -/* Filter out CR data... */ -int bsc_nat_filter_sccp_cr(struct bsc_connection *bsc, struct msgb *msg, - struct bsc_nat_parsed *parsed, int *con_type, - char **imsi, struct bsc_filter_reject_cause *cause) -{ - struct bsc_filter_request req; - struct tlv_parsed tp; - struct gsm48_hdr *hdr48; - int hdr48_len; - int len; - - *con_type = FLT_CON_TYPE_NONE; - cause->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; - cause->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; - *imsi = NULL; - - if (parsed->gsm_type != BSS_MAP_MSG_COMPLETE_LAYER_3) { - LOGP(DNAT, LOGL_ERROR, - "Rejecting CR message due wrong GSM Type %d\n", parsed->gsm_type); - return -1; - } - - /* the parsed has had some basic l3 length check */ - len = msg->l3h[1]; - if (msgb_l3len(msg) - 3 < len) { - LOGP(DNAT, LOGL_ERROR, - "The CR Data has not enough space...\n"); - return -1; - } - - msg->l4h = &msg->l3h[3]; - len -= 1; - - tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h, len, 0, 0); - - if (!TLVP_PRESENT(&tp, GSM0808_IE_LAYER_3_INFORMATION)) { - LOGP(DNAT, LOGL_ERROR, "CR Data does not contain layer3 information.\n"); - return -1; - } - - hdr48_len = TLVP_LEN(&tp, GSM0808_IE_LAYER_3_INFORMATION); - - if (hdr48_len < sizeof(*hdr48)) { - LOGP(DNAT, LOGL_ERROR, "GSM48 header does not fit.\n"); - return -1; - } - - hdr48 = (struct gsm48_hdr *) TLVP_VAL(&tp, GSM0808_IE_LAYER_3_INFORMATION); - req.ctx = bsc; - req.black_list = &bsc->nat->imsi_black_list; - req.access_lists = &bsc->nat->access_lists; - req.local_lst_name = bsc->cfg->acc_lst_name; - req.global_lst_name = bsc->nat->acc_lst_name; - req.bsc_nr = bsc->cfg->nr; - return bsc_msg_filter_initial(hdr48, hdr48_len, &req, con_type, imsi, cause); -} - -int bsc_nat_filter_dt(struct bsc_connection *bsc, struct msgb *msg, - struct nat_sccp_connection *con, struct bsc_nat_parsed *parsed, - struct bsc_filter_reject_cause *cause) -{ - uint32_t len; - struct gsm48_hdr *hdr48; - struct bsc_filter_request req; - - cause->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; - cause->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; - - if (con->filter_state.imsi_checked) - return 0; - - /* only care about DTAP messages */ - if (parsed->bssap != BSSAP_MSG_DTAP) - return 0; - - hdr48 = bsc_unpack_dtap(parsed, msg, &len); - if (!hdr48) - return -1; - - req.ctx = con; - req.black_list = &bsc->nat->imsi_black_list; - req.access_lists = &bsc->nat->access_lists; - req.local_lst_name = bsc->cfg->acc_lst_name; - req.global_lst_name = bsc->nat->acc_lst_name; - req.bsc_nr = bsc->cfg->nr; - return bsc_msg_filter_data(hdr48, len, &req, &con->filter_state, cause); -} diff --git a/src/osmo-bsc_nat/bsc_nat_rewrite.c b/src/osmo-bsc_nat/bsc_nat_rewrite.c deleted file mode 100644 index e7c387c22..000000000 --- a/src/osmo-bsc_nat/bsc_nat_rewrite.c +++ /dev/null @@ -1,714 +0,0 @@ -/* - * Message rewriting functionality - */ -/* - * (C) 2010-2013 by Holger Hans Peter Freyther <zecke@selfish.org> - * (C) 2010-2013 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <openbsc/bsc_nat.h> -#include <openbsc/bsc_nat_sccp.h> -#include <openbsc/bsc_msc.h> -#include <openbsc/gsm_data.h> -#include <openbsc/debug.h> -#include <openbsc/ipaccess.h> -#include <openbsc/nat_rewrite_trie.h> - -#include <osmocom/core/linuxlist.h> -#include <osmocom/core/talloc.h> -#include <osmocom/core/utils.h> -#include <osmocom/gsm/gsm0808.h> -#include <osmocom/gsm/ipa.h> - -#include <osmocom/gsm/protocol/gsm_08_08.h> -#include <osmocom/gsm/protocol/gsm_04_11.h> - -#include <osmocom/sccp/sccp.h> - -static char *trie_lookup(struct nat_rewrite *trie, const char *number, - regoff_t off, void *ctx) -{ - struct nat_rewrite_rule *rule; - - if (!trie) { - LOGP(DCC, LOGL_ERROR, - "Asked to do a table lookup but no table.\n"); - return NULL; - } - - rule = nat_rewrite_lookup(trie, number); - if (!rule) { - LOGP(DCC, LOGL_DEBUG, - "Couldn't find a prefix rule for %s\n", number); - return NULL; - } - - return talloc_asprintf(ctx, "%s%s", rule->rewrite, &number[off]); -} - -static char *match_and_rewrite_number(void *ctx, const char *number, - const char *imsi, struct llist_head *list, - struct nat_rewrite *trie) -{ - struct bsc_nat_num_rewr_entry *entry; - char *new_number = NULL; - - /* need to find a replacement and then fix it */ - llist_for_each_entry(entry, list, list) { - regmatch_t matches[2]; - - /* check the IMSI match */ - if (regexec(&entry->msisdn_reg, imsi, 0, NULL, 0) != 0) - continue; - - /* this regexp matches... */ - if (regexec(&entry->num_reg, number, 2, matches, 0) == 0 - && matches[1].rm_eo != -1) { - if (entry->is_prefix_lookup) - new_number = trie_lookup(trie, number, - matches[1].rm_so, ctx); - else - new_number = talloc_asprintf(ctx, "%s%s", - entry->replace, - &number[matches[1].rm_so]); - } - - if (new_number) - break; - } - - return new_number; -} - -static char *rewrite_isdn_number(struct bsc_nat *nat, struct llist_head *rewr_list, - void *ctx, const char *imsi, - struct gsm_mncc_number *called) -{ - char int_number[sizeof(called->number) + 2]; - char *number = called->number; - - if (llist_empty(&nat->num_rewr)) { - LOGP(DCC, LOGL_DEBUG, "Rewrite rules empty.\n"); - return NULL; - } - - /* only ISDN plan */ - if (called->plan != 1) { - LOGP(DCC, LOGL_DEBUG, "Called plan is not 1 it was %d\n", - called->plan); - return NULL; - } - - /* international, prepend */ - if (called->type == 1) { - int_number[0] = '+'; - memcpy(&int_number[1], number, strlen(number) + 1); - number = int_number; - } - - return match_and_rewrite_number(ctx, number, - imsi, rewr_list, nat->num_rewr_trie); -} - -static void update_called_number(struct gsm_mncc_number *called, - const char *chosen_number) -{ - if (strncmp(chosen_number, "00", 2) == 0) { - called->type = 1; - osmo_strlcpy(called->number, chosen_number + 2, - sizeof(called->number)); - } else { - /* rewrite international to unknown */ - if (called->type == 1) - called->type = 0; - osmo_strlcpy(called->number, chosen_number, - sizeof(called->number)); - } -} - -/** - * Rewrite non global numbers... according to rules based on the IMSI - */ -static struct msgb *rewrite_setup(struct bsc_nat *nat, struct msgb *msg, - struct bsc_nat_parsed *parsed, const char *imsi, - struct gsm48_hdr *hdr48, const uint32_t len) -{ - struct tlv_parsed tp; - unsigned int payload_len; - struct gsm_mncc_number called; - struct msgb *out; - char *new_number_pre = NULL, *new_number_post = NULL, *chosen_number; - uint8_t *outptr; - const uint8_t *msgptr; - int sec_len; - - /* decode and rewrite the message */ - payload_len = len - sizeof(*hdr48); - tlv_parse(&tp, &gsm48_att_tlvdef, hdr48->data, payload_len, 0, 0); - - /* no number, well let us ignore it */ - if (!TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) - return NULL; - - memset(&called, 0, sizeof(called)); - gsm48_decode_called(&called, - TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) - 1); - - /* check if it looks international and stop */ - LOGP(DCC, LOGL_DEBUG, - "Pre-Rewrite for IMSI(%s) Plan(%d) Type(%d) Number(%s)\n", - imsi, called.plan, called.type, called.number); - new_number_pre = rewrite_isdn_number(nat, &nat->num_rewr, msg, imsi, &called); - - if (!new_number_pre) { - LOGP(DCC, LOGL_DEBUG, "No IMSI(%s) match found, returning message.\n", - imsi); - return NULL; - } - - if (strlen(new_number_pre) > sizeof(called.number)) { - LOGP(DCC, LOGL_ERROR, "Number %s is too long for structure.\n", - new_number_pre); - talloc_free(new_number_pre); - return NULL; - } - update_called_number(&called, new_number_pre); - - /* another run through the re-write engine with other rules */ - LOGP(DCC, LOGL_DEBUG, - "Post-Rewrite for IMSI(%s) Plan(%d) Type(%d) Number(%s)\n", - imsi, called.plan, called.type, called.number); - new_number_post = rewrite_isdn_number(nat, &nat->num_rewr_post, msg, - imsi, &called); - chosen_number = new_number_post ? new_number_post : new_number_pre; - - - if (strlen(chosen_number) > sizeof(called.number)) { - LOGP(DCC, LOGL_ERROR, "Number %s is too long for structure.\n", - chosen_number); - talloc_free(new_number_pre); - talloc_free(new_number_post); - return NULL; - } - - /* - * Need to create a new message now based on the old onew - * with a new number. We can sadly not patch this in place - * so we will need to regenerate it. - */ - - out = msgb_alloc_headroom(4096, 128, "changed-setup"); - if (!out) { - LOGP(DCC, LOGL_ERROR, "Failed to allocate.\n"); - talloc_free(new_number_pre); - talloc_free(new_number_post); - return NULL; - } - - /* copy the header */ - outptr = msgb_put(out, sizeof(*hdr48)); - memcpy(outptr, hdr48, sizeof(*hdr48)); - - /* copy everything up to the number */ - sec_len = TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) - 2 - &hdr48->data[0]; - outptr = msgb_put(out, sec_len); - memcpy(outptr, &hdr48->data[0], sec_len); - - /* create the new number */ - update_called_number(&called, chosen_number); - LOGP(DCC, LOGL_DEBUG, - "Chosen number for IMSI(%s) is Plan(%d) Type(%d) Number(%s)\n", - imsi, called.plan, called.type, called.number); - gsm48_encode_called(out, &called); - - /* copy thre rest */ - msgptr = TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) + - TLVP_LEN(&tp, GSM48_IE_CALLED_BCD); - sec_len = payload_len - (msgptr - &hdr48->data[0]); - outptr = msgb_put(out, sec_len); - memcpy(outptr, msgptr, sec_len); - - talloc_free(new_number_pre); - talloc_free(new_number_post); - return out; -} - -/** - * Find a new SMSC address, returns an allocated string that needs to be - * freed or is NULL. - */ -static char *find_new_smsc(struct bsc_nat *nat, void *ctx, const char *imsi, - const char *smsc_addr, const char *dest_nr) -{ - struct bsc_nat_num_rewr_entry *entry; - char *new_number = NULL; - uint8_t dest_match = llist_empty(&nat->tpdest_match); - - /* We will find a new number now */ - llist_for_each_entry(entry, &nat->smsc_rewr, list) { - regmatch_t matches[2]; - - /* check the IMSI match */ - if (regexec(&entry->msisdn_reg, imsi, 0, NULL, 0) != 0) - continue; - - /* this regexp matches... */ - if (regexec(&entry->num_reg, smsc_addr, 2, matches, 0) == 0 && - matches[1].rm_eo != -1) - new_number = talloc_asprintf(ctx, "%s%s", - entry->replace, - &smsc_addr[matches[1].rm_so]); - if (new_number) - break; - } - - if (!new_number) - return NULL; - - /* - * now match the number against another list - */ - llist_for_each_entry(entry, &nat->tpdest_match, list) { - /* check the IMSI match */ - if (regexec(&entry->msisdn_reg, imsi, 0, NULL, 0) != 0) - continue; - - if (regexec(&entry->num_reg, dest_nr, 0, NULL, 0) == 0) { - dest_match = 1; - break; - } - } - - if (!dest_match) { - talloc_free(new_number); - return NULL; - } - - return new_number; -} - -/** - * Clear the TP-SRR from the TPDU header - */ -static uint8_t sms_new_tpdu_hdr(struct bsc_nat *nat, const char *imsi, - const char *dest_nr, uint8_t hdr) -{ - struct bsc_nat_num_rewr_entry *entry; - - /* We will find a new number now */ - llist_for_each_entry(entry, &nat->sms_clear_tp_srr, list) { - /* check the IMSI match */ - if (regexec(&entry->msisdn_reg, imsi, 0, NULL, 0) != 0) - continue; - if (regexec(&entry->num_reg, dest_nr, 0, NULL, 0) != 0) - continue; - - /* matched phone number and imsi */ - return hdr & ~0x20; - } - - return hdr; -} - -/** - * Check if we need to rewrite the number. For this SMS. - */ -static char *sms_new_dest_nr(struct bsc_nat *nat, void *ctx, - const char *imsi, const char *dest_nr) -{ - return match_and_rewrite_number(ctx, dest_nr, imsi, - &nat->sms_num_rewr, NULL); -} - -/** - * This is a helper for GSM 04.11 8.2.5.2 Destination address element - */ -void sms_encode_addr_element(struct msgb *out, const char *new_number, - int format, int tp_data) -{ - uint8_t new_addr_len; - uint8_t new_addr[26]; - - /* - * Copy the new number. We let libosmocore encode it, then set - * the extension followed after the length. Depending on if - * we want to write RP we will let the TLV code add the - * length for us or we need to use strlen... This is not very clear - * as of 03.40 and 04.11. - */ - new_addr_len = gsm48_encode_bcd_number(new_addr, ARRAY_SIZE(new_addr), - 1, new_number); - new_addr[1] = format; - if (tp_data) { - uint8_t *data = msgb_put(out, new_addr_len); - memcpy(data, new_addr, new_addr_len); - data[0] = strlen(new_number); - } else { - msgb_lv_put(out, new_addr_len - 1, new_addr + 1); - } -} - -static struct msgb *sms_create_new(uint8_t type, uint8_t ref, - struct gsm48_hdr *old_hdr48, - const uint8_t *orig_addr_ptr, - int orig_addr_len, const char *new_number, - const uint8_t *data_ptr, int data_len, - uint8_t tpdu_first_byte, - const int old_dest_len, const char *new_dest_nr) -{ - struct gsm48_hdr *new_hdr48; - struct msgb *out; - - /* - * We need to re-create the patched structure. This is why we have - * saved the above pointers. - */ - out = msgb_alloc_headroom(4096, 128, "changed-smsc"); - if (!out) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n"); - return NULL; - } - - out->l2h = out->data; - msgb_v_put(out, GSM411_MT_RP_DATA_MO); - msgb_v_put(out, ref); - msgb_lv_put(out, orig_addr_len, orig_addr_ptr); - - sms_encode_addr_element(out, new_number, 0x91, 0); - - - /* Patch the TPDU from here on */ - - /** - * Do we need to put a new TP-Destination-Address (TP-DA) here or - * can we copy the old thing? For the TP-DA we need to find out the - * new size. - */ - if (new_dest_nr) { - uint8_t *data, *new_size; - - /* reserve the size and write the header */ - new_size = msgb_put(out, 1); - out->l3h = new_size + 1; - msgb_v_put(out, tpdu_first_byte); - msgb_v_put(out, data_ptr[1]); - - /* encode the new number and put it */ - if (strncmp(new_dest_nr, "00", 2) == 0) - sms_encode_addr_element(out, new_dest_nr + 2, 0x91, 1); - else - sms_encode_addr_element(out, new_dest_nr, 0x81, 1); - - /* Copy the rest after the TP-DS */ - data = msgb_put(out, data_len - 2 - 1 - old_dest_len); - memcpy(data, &data_ptr[2 + 1 + old_dest_len], data_len - 2 - 1 - old_dest_len); - - /* fill in the new size */ - new_size[0] = msgb_l3len(out); - } else { - msgb_v_put(out, data_len); - msgb_tv_fixed_put(out, tpdu_first_byte, data_len - 1, &data_ptr[1]); - } - - /* prepend GSM 04.08 header */ - new_hdr48 = (struct gsm48_hdr *) msgb_push(out, sizeof(*new_hdr48) + 1); - memcpy(new_hdr48, old_hdr48, sizeof(*old_hdr48)); - new_hdr48->data[0] = msgb_l2len(out); - - return out; -} - -/** - * Parse the SMS and check if it needs to be rewritten - */ -static struct msgb *rewrite_sms(struct bsc_nat *nat, struct msgb *msg, - struct bsc_nat_parsed *parsed, const char *imsi, - struct gsm48_hdr *hdr48, const uint32_t len) -{ - unsigned int payload_len; - unsigned int cp_len; - - uint8_t ref; - uint8_t orig_addr_len, *orig_addr_ptr; - uint8_t dest_addr_len, *dest_addr_ptr; - uint8_t data_len, *data_ptr; - char smsc_addr[30]; - - - uint8_t dest_len, orig_dest_len; - char _dest_nr[30]; - char *dest_nr; - char *new_dest_nr; - - char *new_number = NULL; - uint8_t tpdu_hdr; - struct msgb *out; - - payload_len = len - sizeof(*hdr48); - if (payload_len < 1) { - LOGP(DNAT, LOGL_ERROR, "SMS too short for things. %d\n", payload_len); - return NULL; - } - - cp_len = hdr48->data[0]; - if (payload_len + 1 < cp_len) { - LOGP(DNAT, LOGL_ERROR, "SMS RPDU can not fit in: %d %d\n", cp_len, payload_len); - return NULL; - } - - if (hdr48->data[1] != GSM411_MT_RP_DATA_MO) - return NULL; - - if (cp_len < 5) { - LOGP(DNAT, LOGL_ERROR, "RD-DATA can not fit in the CP len: %d\n", cp_len); - return NULL; - } - - /* RP */ - ref = hdr48->data[2]; - orig_addr_len = hdr48->data[3]; - orig_addr_ptr = &hdr48->data[4]; - - /* the +1 is for checking if the following element has some space */ - if (cp_len < 3 + orig_addr_len + 1) { - LOGP(DNAT, LOGL_ERROR, "RP-Originator addr does not fit: %d\n", orig_addr_len); - return NULL; - } - - dest_addr_len = hdr48->data[3 + orig_addr_len + 1]; - dest_addr_ptr = &hdr48->data[3 + orig_addr_len + 2]; - - if (cp_len < 3 + orig_addr_len + 1 + dest_addr_len + 1) { - LOGP(DNAT, LOGL_ERROR, "RP-Destination addr does not fit: %d\n", dest_addr_len); - return NULL; - } - gsm48_decode_bcd_number(smsc_addr, ARRAY_SIZE(smsc_addr), dest_addr_ptr - 1, 1); - - data_len = hdr48->data[3 + orig_addr_len + 1 + dest_addr_len + 1]; - data_ptr = &hdr48->data[3 + orig_addr_len + 1 + dest_addr_len + 2]; - - if (cp_len < 3 + orig_addr_len + 1 + dest_addr_len + 1 + data_len) { - LOGP(DNAT, LOGL_ERROR, "RP-Data does not fit: %d\n", data_len); - return NULL; - } - - if (data_len < 3) { - LOGP(DNAT, LOGL_ERROR, "SMS-SUBMIT is too short.\n"); - return NULL; - } - - /* TP-PDU starts here */ - if ((data_ptr[0] & 0x03) != GSM340_SMS_SUBMIT_MS2SC) - return NULL; - - /* - * look into the phone number. The length is in semi-octets, we will - * need to add the byte for the number type as well. - */ - orig_dest_len = data_ptr[2]; - dest_len = ((orig_dest_len + 1) / 2) + 1; - if (data_len < dest_len + 3 || dest_len < 2) { - LOGP(DNAT, LOGL_ERROR, "SMS-SUBMIT can not have TP-DestAddr.\n"); - return NULL; - } - - if ((data_ptr[3] & 0x80) == 0) { - LOGP(DNAT, LOGL_ERROR, "TP-DestAddr has extension. Not handled.\n"); - return NULL; - } - - if ((data_ptr[3] & 0x0F) == 0) { - LOGP(DNAT, LOGL_ERROR, "TP-DestAddr is of unknown type.\n"); - return NULL; - } - - /** - * Besides of what I think I read in GSM 03.40 and 04.11 the TP-DA - * contains the semi-octets as length (strlen), change it to the - * the number of bytes, but then change it back. - */ - data_ptr[2] = dest_len; - gsm48_decode_bcd_number(_dest_nr + 2, ARRAY_SIZE(_dest_nr) - 2, - &data_ptr[2], 1); - data_ptr[2] = orig_dest_len; - if ((data_ptr[3] & 0x70) == 0x10) { - _dest_nr[0] = _dest_nr[1] = '0'; - dest_nr = &_dest_nr[0]; - } else { - dest_nr = &_dest_nr[2]; - } - - /** - * Call functions to rewrite the data - */ - tpdu_hdr = sms_new_tpdu_hdr(nat, imsi, dest_nr, data_ptr[0]); - new_number = find_new_smsc(nat, msg, imsi, smsc_addr, dest_nr); - new_dest_nr = sms_new_dest_nr(nat, msg, imsi, dest_nr); - - if (tpdu_hdr == data_ptr[0] && !new_number && !new_dest_nr) - return NULL; - - out = sms_create_new(GSM411_MT_RP_DATA_MO, ref, hdr48, - orig_addr_ptr, orig_addr_len, - new_number ? new_number : smsc_addr, - data_ptr, data_len, tpdu_hdr, - dest_len, new_dest_nr); - talloc_free(new_number); - talloc_free(new_dest_nr); - return out; -} - -struct msgb *bsc_nat_rewrite_msg(struct bsc_nat *nat, struct msgb *msg, struct bsc_nat_parsed *parsed, const char *imsi) -{ - struct gsm48_hdr *hdr48; - uint32_t len; - uint8_t msg_type, proto; - struct msgb *new_msg = NULL, *sccp; - uint8_t link_id; - - if (!imsi || strlen(imsi) < 5) - return msg; - - /* only care about DTAP messages */ - if (parsed->bssap != BSSAP_MSG_DTAP) - return msg; - if (!parsed->dest_local_ref) - return msg; - - hdr48 = bsc_unpack_dtap(parsed, msg, &len); - if (!hdr48) - return msg; - - link_id = msg->l3h[1]; - proto = gsm48_hdr_pdisc(hdr48); - msg_type = gsm48_hdr_msg_type(hdr48); - - if (proto == GSM48_PDISC_CC && msg_type == GSM48_MT_CC_SETUP) - new_msg = rewrite_setup(nat, msg, parsed, imsi, hdr48, len); - else if (proto == GSM48_PDISC_SMS && msg_type == GSM411_MT_CP_DATA) - new_msg = rewrite_sms(nat, msg, parsed, imsi, hdr48, len); - - if (!new_msg) - return msg; - - /* wrap with DTAP, SCCP, then IPA. TODO: Stop copying */ - gsm0808_prepend_dtap_header(new_msg, link_id); - sccp = sccp_create_dt1(parsed->dest_local_ref, new_msg->data, new_msg->len); - talloc_free(new_msg); - - if (!sccp) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n"); - return msg; - } - - ipa_prepend_header(sccp, IPAC_PROTO_SCCP); - - /* the parsed hangs off from msg but it needs to survive */ - talloc_steal(sccp, parsed); - msgb_free(msg); - return sccp; -} - -static void num_rewr_free_data(struct bsc_nat_num_rewr_entry *entry) -{ - regfree(&entry->msisdn_reg); - regfree(&entry->num_reg); - talloc_free(entry->replace); -} - -void bsc_nat_num_rewr_entry_adapt(void *ctx, struct llist_head *head, - const struct osmo_config_list *list) -{ - struct bsc_nat_num_rewr_entry *entry, *tmp; - struct osmo_config_entry *cfg_entry; - - /* free the old data */ - llist_for_each_entry_safe(entry, tmp, head, list) { - num_rewr_free_data(entry); - llist_del(&entry->list); - talloc_free(entry); - } - - - if (!list) - return; - - llist_for_each_entry(cfg_entry, &list->entry, list) { - char *regexp; - if (cfg_entry->text[0] == '+') { - LOGP(DNAT, LOGL_ERROR, - "Plus is not allowed in the number\n"); - continue; - } - - entry = talloc_zero(ctx, struct bsc_nat_num_rewr_entry); - if (!entry) { - LOGP(DNAT, LOGL_ERROR, - "Allocation of the num_rewr entry failed.\n"); - continue; - } - - entry->replace = talloc_strdup(entry, cfg_entry->text); - if (!entry->replace) { - LOGP(DNAT, LOGL_ERROR, - "Failed to copy the replacement text.\n"); - talloc_free(entry); - continue; - } - - if (strcmp("prefix_lookup", entry->replace) == 0) - entry->is_prefix_lookup = 1; - - /* we will now build a regexp string */ - if (cfg_entry->mcc[0] == '^') { - regexp = talloc_strdup(entry, cfg_entry->mcc); - } else { - regexp = talloc_asprintf(entry, "^%s%s", - cfg_entry->mcc[0] == '*' ? - "[0-9][0-9][0-9]" : cfg_entry->mcc, - cfg_entry->mnc[0] == '*' ? - "[0-9][0-9]" : cfg_entry->mnc); - } - - if (!regexp) { - LOGP(DNAT, LOGL_ERROR, "Failed to create a regexp string.\n"); - talloc_free(entry); - continue; - } - - if (regcomp(&entry->msisdn_reg, regexp, 0) != 0) { - LOGP(DNAT, LOGL_ERROR, - "Failed to compile regexp '%s'\n", regexp); - talloc_free(regexp); - talloc_free(entry); - continue; - } - - talloc_free(regexp); - if (regcomp(&entry->num_reg, cfg_entry->option, REG_EXTENDED) != 0) { - LOGP(DNAT, LOGL_ERROR, - "Failed to compile regexp '%s'\n", cfg_entry->option); - regfree(&entry->msisdn_reg); - talloc_free(entry); - continue; - } - - /* we have copied the number */ - llist_add_tail(&entry->list, head); - } -} diff --git a/src/osmo-bsc_nat/bsc_nat_rewrite_trie.c b/src/osmo-bsc_nat/bsc_nat_rewrite_trie.c deleted file mode 100644 index 633fa87d0..000000000 --- a/src/osmo-bsc_nat/bsc_nat_rewrite_trie.c +++ /dev/null @@ -1,259 +0,0 @@ -/* Handling for loading a re-write file/database */ -/* - * (C) 2013 by On-Waves - * (C) 2013 by Holger Hans Peter Freyther <zecke@selfish.org> - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <openbsc/nat_rewrite_trie.h> -#include <openbsc/debug.h> -#include <openbsc/vty.h> - -#include <osmocom/core/talloc.h> -#include <osmocom/core/utils.h> - -#include <assert.h> -#include <stdio.h> -#include <string.h> -#include <ctype.h> - -#define CHECK_IS_DIGIT_OR_FAIL(prefix, pos) \ - if (!isdigit(prefix[pos]) && prefix[pos] != '+') { \ - LOGP(DNAT, LOGL_ERROR, \ - "Prefix(%s) contains non ascii text at(%d=%c)\n", \ - prefix, pos, prefix[pos]); \ - goto fail; \ - } -#define TO_INT(c) \ - ((c) == '+' ? 10 : ((c - '0') % 10)) - -static void insert_rewrite_node(struct nat_rewrite_rule *rule, struct nat_rewrite *root) -{ - struct nat_rewrite_rule *new = &root->rule; - - const int len = strlen(rule->prefix); - int i; - - if (len <= 0) { - LOGP(DNAT, LOGL_ERROR, "An empty prefix does not make sense.\n"); - goto fail; - } - - for (i = 0; i < len - 1; ++i) { - int pos; - - /* check if the input is valid */ - CHECK_IS_DIGIT_OR_FAIL(rule->prefix, i); - - /* check if the next node is already valid */ - pos = TO_INT(rule->prefix[i]); - if (!new->rules[pos]) { - new->rules[pos] = talloc_zero(root, struct nat_rewrite_rule); - if (!new->rules[pos]) { - LOGP(DNAT, LOGL_ERROR, - "Failed to allocate memory.\n"); - goto fail; - } - - new->rules[pos]->empty = 1; - } - - /* we continue here */ - new = new->rules[pos]; - } - - /* new now points to the place where we want to add it */ - int pos; - - /* check if the input is valid */ - CHECK_IS_DIGIT_OR_FAIL(rule->prefix, (len - 1)); - - /* check if the next node is already valid */ - pos = TO_INT(rule->prefix[len - 1]); - if (!new->rules[pos]) - new->rules[pos] = rule; - else if (new->rules[pos]->empty) { - /* copy over entries */ - new->rules[pos]->empty = 0; - memcpy(new->rules[pos]->prefix, rule->prefix, sizeof(rule->prefix)); - memcpy(new->rules[pos]->rewrite, rule->rewrite, sizeof(rule->rewrite)); - talloc_free(rule); - } else { - LOGP(DNAT, LOGL_ERROR, - "Prefix(%s) is already installed\n", rule->prefix); - goto fail; - } - - root->prefixes += 1; - return; - -fail: - talloc_free(rule); - return; -} - -static void handle_line(struct nat_rewrite *rewrite, char *line) -{ - char *split; - struct nat_rewrite_rule *rule; - size_t size_prefix, size_end, len; - - - /* Find the ',' in the line */ - len = strlen(line); - split = strstr(line, ","); - if (!split) { - LOGP(DNAT, LOGL_ERROR, "Line doesn't contain ','\n"); - return; - } - - /* Check if there is space for the rewrite rule */ - size_prefix = split - line; - if (len - size_prefix <= 2) { - LOGP(DNAT, LOGL_ERROR, "No rewrite available.\n"); - return; - } - - /* Continue after the ',' to the end */ - split = &line[size_prefix + 1]; - size_end = strlen(split) - 1; - - /* Check if both strings can fit into the static array */ - if (size_prefix > sizeof(rule->prefix) - 1) { - LOGP(DNAT, LOGL_ERROR, - "Prefix is too long with %zu\n", size_prefix); - return; - } - - if (size_end > sizeof(rule->rewrite) - 1) { - LOGP(DNAT, LOGL_ERROR, - "Rewrite is too long with %zu on %s\n", - size_end, &line[size_prefix + 1]); - return; - } - - /* Now create the entry and insert it into the trie */ - rule = talloc_zero(rewrite, struct nat_rewrite_rule); - if (!rule) { - LOGP(DNAT, LOGL_ERROR, "Can not allocate memory\n"); - return; - } - - memcpy(rule->prefix, line, size_prefix); - assert(size_prefix < sizeof(rule->prefix)); - rule->prefix[size_prefix] = '\0'; - - memcpy(rule->rewrite, split, size_end); - assert(size_end < sizeof(rule->rewrite)); - rule->rewrite[size_end] = '\0'; - - /* now insert and balance the tree */ - insert_rewrite_node(rule, rewrite); -} - -struct nat_rewrite *nat_rewrite_parse(void *ctx, const char *filename) -{ - FILE *file; - char *line = NULL; - size_t n = 0; - struct nat_rewrite *res; - - file = fopen(filename, "r"); - if (!file) - return NULL; - - res = talloc_zero(ctx, struct nat_rewrite); - if (!res) { - fclose(file); - return NULL; - } - - /* mark the root as empty */ - res->rule.empty = 1; - - while (getline(&line, &n, file) != -1) { - handle_line(res, line); - } - - free(line); - fclose(file); - return res; -} - -/** - * Simple find that tries to do a longest match... - */ -struct nat_rewrite_rule *nat_rewrite_lookup(struct nat_rewrite *rewrite, - const char *prefix) -{ - struct nat_rewrite_rule *rule = &rewrite->rule; - struct nat_rewrite_rule *last = NULL; - const int len = OSMO_MIN(strlen(prefix), (sizeof(rule->prefix) - 1)); - int i; - - for (i = 0; rule && i < len; ++i) { - int pos; - - CHECK_IS_DIGIT_OR_FAIL(prefix, i); - pos = TO_INT(prefix[i]); - - rule = rule->rules[pos]; - if (rule && !rule->empty) - last = rule; - } - - return last; - -fail: - return NULL; -} - -static void nat_rewrite_dump_rec(struct nat_rewrite_rule *rule) -{ - int i; - if (!rule->empty) - printf("%s,%s\n", rule->prefix, rule->rewrite); - - for (i = 0; i < ARRAY_SIZE(rule->rules); ++i) { - if (!rule->rules[i]) - continue; - nat_rewrite_dump_rec(rule->rules[i]); - } -} - -void nat_rewrite_dump(struct nat_rewrite *rewrite) -{ - nat_rewrite_dump_rec(&rewrite->rule); -} - -static void nat_rewrite_dump_rec_vty(struct vty *vty, struct nat_rewrite_rule *rule) -{ - int i; - if (!rule->empty) - vty_out(vty, "%s,%s%s", rule->prefix, rule->rewrite, VTY_NEWLINE); - - for (i = 0; i < ARRAY_SIZE(rule->rules); ++i) { - if (!rule->rules[i]) - continue; - nat_rewrite_dump_rec_vty(vty, rule->rules[i]); - } -} - -void nat_rewrite_dump_vty(struct vty *vty, struct nat_rewrite *rewrite) -{ - nat_rewrite_dump_rec_vty(vty, &rewrite->rule); -} diff --git a/src/osmo-bsc_nat/bsc_nat_utils.c b/src/osmo-bsc_nat/bsc_nat_utils.c deleted file mode 100644 index c12b29f1f..000000000 --- a/src/osmo-bsc_nat/bsc_nat_utils.c +++ /dev/null @@ -1,535 +0,0 @@ - -/* BSC Multiplexer/NAT Utilities */ - -/* - * (C) 2010-2011 by Holger Hans Peter Freyther <zecke@selfish.org> - * (C) 2010-2011 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <openbsc/bsc_nat.h> -#include <openbsc/bsc_nat_sccp.h> -#include <openbsc/bsc_msg_filter.h> -#include <openbsc/bsc_msc.h> -#include <openbsc/gsm_data.h> -#include <openbsc/debug.h> -#include <openbsc/ipaccess.h> -#include <openbsc/vty.h> - -#include <osmocom/core/linuxlist.h> -#include <osmocom/core/talloc.h> -#include <osmocom/core/stats.h> -#include <osmocom/gsm/gsm0808.h> -#include <osmocom/gsm/ipa.h> - -#include <osmocom/gsm/protocol/gsm_08_08.h> -#include <osmocom/gsm/protocol/gsm_04_11.h> - -#include <osmocom/sccp/sccp.h> - -#include <netinet/in.h> -#include <arpa/inet.h> -#include <unistd.h> - -static const struct rate_ctr_desc bsc_cfg_ctr_description[] = { - [BCFG_CTR_SCCP_CONN] = { "sccp.conn", "SCCP Connections "}, - [BCFG_CTR_SCCP_CALLS] = { "sccp.calls", "SCCP Assignment Commands "}, - [BCFG_CTR_NET_RECONN] = { "net.reconnects", "Network reconnects "}, - [BCFG_CTR_DROPPED_SCCP] = { "dropped.sccp", "Dropped SCCP connections."}, - [BCFG_CTR_DROPPED_CALLS] = { "dropped.calls", "Dropped active calls. "}, - [BCFG_CTR_REJECTED_CR] = { "rejected.cr", "Rejected CR due filter "}, - [BCFG_CTR_REJECTED_MSG] = { "rejected.msg", "Rejected MSG due filter "}, - [BCFG_CTR_ILL_PACKET] = { "rejected.ill", "Rejected due parse error "}, - [BCFG_CTR_CON_TYPE_LU] = { "conn.lu", "Conn Location Update "}, - [BCFG_CTR_CON_CMSERV_RQ] = { "conn.rq", "Conn CM Service Req "}, - [BCFG_CTR_CON_PAG_RESP] = { "conn.pag", "Conn Paging Response "}, - [BCFG_CTR_CON_SSA] = { "conn.ssa", "Conn USSD "}, - [BCFG_CTR_CON_OTHER] = { "conn.other", "Conn Other "}, -}; - -static const struct rate_ctr_group_desc bsc_cfg_ctrg_desc = { - .group_name_prefix = "nat.bsc", - .group_description = "NAT BSC Statistics", - .num_ctr = ARRAY_SIZE(bsc_cfg_ctr_description), - .ctr_desc = bsc_cfg_ctr_description, - .class_id = OSMO_STATS_CLASS_PEER, -}; - -struct bsc_nat *bsc_nat_alloc(void) -{ - struct bsc_nat *nat = talloc_zero(tall_bsc_ctx, struct bsc_nat); - if (!nat) - return NULL; - - nat->main_dest = talloc_zero(nat, struct bsc_msc_dest); - if (!nat->main_dest) { - talloc_free(nat); - return NULL; - } - - INIT_LLIST_HEAD(&nat->sccp_connections); - INIT_LLIST_HEAD(&nat->bsc_connections); - INIT_LLIST_HEAD(&nat->paging_groups); - INIT_LLIST_HEAD(&nat->bsc_configs); - INIT_LLIST_HEAD(&nat->access_lists); - INIT_LLIST_HEAD(&nat->dests); - INIT_LLIST_HEAD(&nat->num_rewr); - INIT_LLIST_HEAD(&nat->num_rewr_post); - INIT_LLIST_HEAD(&nat->smsc_rewr); - INIT_LLIST_HEAD(&nat->tpdest_match); - INIT_LLIST_HEAD(&nat->sms_clear_tp_srr); - INIT_LLIST_HEAD(&nat->sms_num_rewr); - - nat->stats.sccp.conn = osmo_counter_alloc("nat.sccp.conn"); - nat->stats.sccp.calls = osmo_counter_alloc("nat.sccp.calls"); - nat->stats.bsc.reconn = osmo_counter_alloc("nat.bsc.conn"); - nat->stats.bsc.auth_fail = osmo_counter_alloc("nat.bsc.auth_fail"); - nat->stats.msc.reconn = osmo_counter_alloc("nat.msc.conn"); - nat->stats.ussd.reconn = osmo_counter_alloc("nat.ussd.conn"); - nat->auth_timeout = 2; - nat->ping_timeout = 20; - nat->pong_timeout = 5; - - llist_add(&nat->main_dest->list, &nat->dests); - nat->main_dest->ip = talloc_strdup(nat, "127.0.0.1"); - nat->main_dest->port = 5000; - - return nat; -} - -void bsc_nat_free(struct bsc_nat *nat) -{ - struct bsc_config *cfg, *tmp; - struct bsc_msg_acc_lst *lst, *tmp_lst; - - llist_for_each_entry_safe(cfg, tmp, &nat->bsc_configs, entry) - bsc_config_free(cfg); - llist_for_each_entry_safe(lst, tmp_lst, &nat->access_lists, list) - bsc_msg_acc_lst_delete(lst); - - bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, NULL); - bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr_post, NULL); - bsc_nat_num_rewr_entry_adapt(nat, &nat->sms_clear_tp_srr, NULL); - bsc_nat_num_rewr_entry_adapt(nat, &nat->sms_num_rewr, NULL); - bsc_nat_num_rewr_entry_adapt(nat, &nat->tpdest_match, NULL); - - osmo_counter_free(nat->stats.sccp.conn); - osmo_counter_free(nat->stats.sccp.calls); - osmo_counter_free(nat->stats.bsc.reconn); - osmo_counter_free(nat->stats.bsc.auth_fail); - osmo_counter_free(nat->stats.msc.reconn); - osmo_counter_free(nat->stats.ussd.reconn); - talloc_free(nat->mgcp_cfg); - talloc_free(nat); -} - -void bsc_nat_set_msc_ip(struct bsc_nat *nat, const char *ip) -{ - osmo_talloc_replace_string(nat, &nat->main_dest->ip, ip); -} - -struct bsc_connection *bsc_connection_alloc(struct bsc_nat *nat) -{ - struct bsc_connection *con = talloc_zero(nat, struct bsc_connection); - if (!con) - return NULL; - - con->nat = nat; - osmo_wqueue_init(&con->write_queue, 100); - INIT_LLIST_HEAD(&con->cmd_pending); - INIT_LLIST_HEAD(&con->pending_dlcx); - return con; -} - -struct bsc_config *bsc_config_alloc(struct bsc_nat *nat, const char *token, - unsigned int number) -{ - struct bsc_config *conf = talloc_zero(nat, struct bsc_config); - if (!conf) - return NULL; - - conf->token = talloc_strdup(conf, token); - conf->nr = number; - conf->nat = nat; - conf->max_endpoints = 32; - conf->paging_group = PAGIN_GROUP_UNASSIGNED; - - INIT_LLIST_HEAD(&conf->lac_list); - - llist_add_tail(&conf->entry, &nat->bsc_configs); - ++nat->num_bsc; - - conf->stats.ctrg = rate_ctr_group_alloc(conf, &bsc_cfg_ctrg_desc, conf->nr); - if (!conf->stats.ctrg) { - llist_del(&conf->entry); - talloc_free(conf); - return NULL; - } - - return conf; -} - -struct bsc_config *bsc_config_by_token(struct bsc_nat *nat, const char *token, int len) -{ - struct bsc_config *conf; - - llist_for_each_entry(conf, &nat->bsc_configs, entry) { - /* - * Add the '\0' of the token for the memcmp, the IPA messages - * for some reason added null termination. - */ - const int token_len = strlen(conf->token) + 1; - - if (token_len == len && memcmp(conf->token, token, token_len) == 0) - return conf; - } - - return NULL; -} - -void bsc_config_free(struct bsc_config *cfg) -{ - llist_del(&cfg->entry); - rate_ctr_group_free(cfg->stats.ctrg); - cfg->nat->num_bsc--; - OSMO_ASSERT(cfg->nat->num_bsc >= 0) - talloc_free(cfg); -} - -static void _add_lac(void *ctx, struct llist_head *list, int _lac) -{ - struct bsc_lac_entry *lac; - - llist_for_each_entry(lac, list, entry) - if (lac->lac == _lac) - return; - - lac = talloc_zero(ctx, struct bsc_lac_entry); - if (!lac) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n"); - return; - } - - lac->lac = _lac; - llist_add_tail(&lac->entry, list); -} - -static void _del_lac(struct llist_head *list, int _lac) -{ - struct bsc_lac_entry *lac; - - llist_for_each_entry(lac, list, entry) - if (lac->lac == _lac) { - llist_del(&lac->entry); - talloc_free(lac); - return; - } -} - -void bsc_config_add_lac(struct bsc_config *cfg, int _lac) -{ - _add_lac(cfg, &cfg->lac_list, _lac); -} - -void bsc_config_del_lac(struct bsc_config *cfg, int _lac) -{ - _del_lac(&cfg->lac_list, _lac); -} - -struct bsc_nat_paging_group *bsc_nat_paging_group_create(struct bsc_nat *nat, int group) -{ - struct bsc_nat_paging_group *pgroup; - - pgroup = talloc_zero(nat, struct bsc_nat_paging_group); - if (!pgroup) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate a paging group.\n"); - return NULL; - } - - pgroup->nr = group; - INIT_LLIST_HEAD(&pgroup->lists); - llist_add_tail(&pgroup->entry, &nat->paging_groups); - return pgroup; -} - -void bsc_nat_paging_group_delete(struct bsc_nat_paging_group *pgroup) -{ - llist_del(&pgroup->entry); - talloc_free(pgroup); -} - -struct bsc_nat_paging_group *bsc_nat_paging_group_num(struct bsc_nat *nat, int group) -{ - struct bsc_nat_paging_group *pgroup; - - llist_for_each_entry(pgroup, &nat->paging_groups, entry) - if (pgroup->nr == group) - return pgroup; - - return NULL; -} - -void bsc_nat_paging_group_add_lac(struct bsc_nat_paging_group *pgroup, int lac) -{ - _add_lac(pgroup, &pgroup->lists, lac); -} - -void bsc_nat_paging_group_del_lac(struct bsc_nat_paging_group *pgroup, int lac) -{ - _del_lac(&pgroup->lists, lac); -} - -int bsc_config_handles_lac(struct bsc_config *cfg, int lac_nr) -{ - struct bsc_nat_paging_group *pgroup; - struct bsc_lac_entry *entry; - - llist_for_each_entry(entry, &cfg->lac_list, entry) - if (entry->lac == lac_nr) - return 1; - - /* now lookup the paging group */ - pgroup = bsc_nat_paging_group_num(cfg->nat, cfg->paging_group); - if (!pgroup) - return 0; - - llist_for_each_entry(entry, &pgroup->lists, entry) - if (entry->lac == lac_nr) - return 1; - - return 0; -} - -void sccp_connection_destroy(struct nat_sccp_connection *conn) -{ - LOGP(DNAT, LOGL_DEBUG, "Destroy 0x%x <-> 0x%x mapping for con %p\n", - sccp_src_ref_to_int(&conn->real_ref), - sccp_src_ref_to_int(&conn->patched_ref), conn->bsc); - bsc_mgcp_dlcx(conn); - llist_del(&conn->list_entry); - talloc_free(conn); -} - - -int bsc_nat_find_paging(struct msgb *msg, - const uint8_t **out_data, int *out_leng) -{ - int data_length; - const uint8_t *data; - struct tlv_parsed tp; - - if (!msg->l3h || msgb_l3len(msg) < 3) { - LOGP(DNAT, LOGL_ERROR, "Paging message is too short.\n"); - return -1; - } - - tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 3, msgb_l3len(msg) - 3, 0, 0); - if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST)) { - LOGP(DNAT, LOGL_ERROR, "No CellIdentifier List inside paging msg.\n"); - return -2; - } - - data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST); - data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST); - - /* No need to try a different BSS */ - if (data[0] == CELL_IDENT_BSS) { - return -3; - } else if (data[0] != CELL_IDENT_LAC) { - LOGP(DNAT, LOGL_ERROR, "Unhandled cell ident discrminator: %d\n", data[0]); - return -4; - } - - *out_data = &data[1]; - *out_leng = data_length - 1; - return 0; -} - -int bsc_write_mgcp(struct bsc_connection *bsc, const uint8_t *data, unsigned int length) -{ - struct msgb *msg; - - if (length > 4096 - 128) { - LOGP(DLINP, LOGL_ERROR, "Can not send message of that size.\n"); - return -1; - } - - msg = msgb_alloc_headroom(4096, 128, "to-bsc"); - if (!msg) { - LOGP(DLINP, LOGL_ERROR, "Failed to allocate memory for BSC msg.\n"); - return -1; - } - - /* copy the data */ - msg->l3h = msgb_put(msg, length); - memcpy(msg->l3h, data, length); - - return bsc_write(bsc, msg, IPAC_PROTO_MGCP_OLD); -} - -int bsc_write(struct bsc_connection *bsc, struct msgb *msg, int proto) -{ - return bsc_do_write(&bsc->write_queue, msg, proto); -} - -int bsc_do_write(struct osmo_wqueue *queue, struct msgb *msg, int proto) -{ - /* prepend the header */ - ipa_prepend_header(msg, proto); - return bsc_write_msg(queue, msg); -} - -int bsc_write_msg(struct osmo_wqueue *queue, struct msgb *msg) -{ - if (osmo_wqueue_enqueue(queue, msg) != 0) { - LOGP(DLINP, LOGL_ERROR, "Failed to enqueue the write.\n"); - msgb_free(msg); - return -1; - } - - return 0; -} - -struct gsm48_hdr *bsc_unpack_dtap(struct bsc_nat_parsed *parsed, - struct msgb *msg, uint32_t *len) -{ - /* gsm_type is actually the size of the dtap */ - *len = parsed->gsm_type; - if (*len < msgb_l3len(msg) - 3) { - LOGP(DNAT, LOGL_ERROR, "Not enough space for DTAP.\n"); - return NULL; - } - - if (msgb_l3len(msg) - 3 < msg->l3h[2]) { - LOGP(DNAT, LOGL_ERROR, - "GSM48 payload does not fit: %d %d\n", - msg->l3h[2], msgb_l3len(msg) - 3); - return NULL; - } - - msg->l4h = &msg->l3h[3]; - return (struct gsm48_hdr *) msg->l4h; -} - -static const char *con_types [] = { - [FLT_CON_TYPE_NONE] = "n/a", - [FLT_CON_TYPE_LU] = "Location Update", - [FLT_CON_TYPE_CM_SERV_REQ] = "CM Serv Req", - [FLT_CON_TYPE_PAG_RESP] = "Paging Response", - [FLT_CON_TYPE_SSA] = "Supplementar Service Activation", - [FLT_CON_TYPE_LOCAL_REJECT] = "Local Reject", - [FLT_CON_TYPE_OTHER] = "Other", -}; - -const char *bsc_con_type_to_string(int type) -{ - return con_types[type]; -} - -int bsc_nat_msc_is_connected(struct bsc_nat *nat) -{ - return nat->msc_con->is_connected; -} - -static const int con_to_ctr[] = { - [FLT_CON_TYPE_NONE] = -1, - [FLT_CON_TYPE_LU] = BCFG_CTR_CON_TYPE_LU, - [FLT_CON_TYPE_CM_SERV_REQ] = BCFG_CTR_CON_CMSERV_RQ, - [FLT_CON_TYPE_PAG_RESP] = BCFG_CTR_CON_PAG_RESP, - [FLT_CON_TYPE_SSA] = BCFG_CTR_CON_SSA, - [FLT_CON_TYPE_LOCAL_REJECT] = -1, - [FLT_CON_TYPE_OTHER] = BCFG_CTR_CON_OTHER, -}; - -int bsc_conn_type_to_ctr(struct nat_sccp_connection *conn) -{ - return con_to_ctr[conn->filter_state.con_type]; -} - -int bsc_write_cb(struct osmo_fd *bfd, struct msgb *msg) -{ - int rc; - - rc = write(bfd->fd, msg->data, msg->len); - if (rc != msg->len) - LOGP(DNAT, LOGL_ERROR, "Failed to write message to the BSC.\n"); - - return rc; -} - -static void extract_lac(const uint8_t *data, uint16_t *lac, uint16_t *ci) -{ - memcpy(lac, &data[0], sizeof(*lac)); - memcpy(ci, &data[2], sizeof(*ci)); - - *lac = ntohs(*lac); - *ci = ntohs(*ci); -} - -int bsc_nat_extract_lac(struct bsc_connection *bsc, - struct nat_sccp_connection *con, - struct bsc_nat_parsed *parsed, struct msgb *msg) -{ - int data_length; - const uint8_t *data; - struct tlv_parsed tp; - uint16_t lac, ci; - - if (parsed->gsm_type != BSS_MAP_MSG_COMPLETE_LAYER_3) { - LOGP(DNAT, LOGL_ERROR, "Can only extract LAC from Complete Layer3\n"); - return -1; - } - - if (!msg->l3h || msgb_l3len(msg) < 3) { - LOGP(DNAT, LOGL_ERROR, "Complete Layer3 mssage is too short.\n"); - return -1; - } - - tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 3, msgb_l3len(msg) - 3, 0, 0); - if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER)) { - LOGP(DNAT, LOGL_ERROR, "No CellIdentifier List inside paging msg.\n"); - return -2; - } - - data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER); - data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER); - - /* Attemt to get the LAC/CI from it */ - if (data[0] == CELL_IDENT_WHOLE_GLOBAL) { - if (data_length != 8) { - LOGP(DNAT, LOGL_ERROR, - "Ident too short: %d\n", data_length); - return -3; - } - extract_lac(&data[1 + 3], &lac, &ci); - } else if (data[0] == CELL_IDENT_LAC_AND_CI) { - if (data_length != 5) { - LOGP(DNAT, LOGL_ERROR, - "Ident too short: %d\n", data_length); - return -3; - } - extract_lac(&data[1], &lac, &ci); - } else { - LOGP(DNAT, LOGL_ERROR, - "Unhandled cell identifier: %d\n", data[0]); - return -1; - } - - con->lac = lac; - con->ci = ci; - return 0; -} diff --git a/src/osmo-bsc_nat/bsc_nat_vty.c b/src/osmo-bsc_nat/bsc_nat_vty.c deleted file mode 100644 index a11ae151e..000000000 --- a/src/osmo-bsc_nat/bsc_nat_vty.c +++ /dev/null @@ -1,1336 +0,0 @@ -/* OpenBSC NAT interface to quagga VTY */ -/* (C) 2010-2015 by Holger Hans Peter Freyther - * (C) 2010-2015 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <openbsc/vty.h> -#include <openbsc/gsm_data.h> -#include <openbsc/bsc_nat.h> -#include <openbsc/bsc_nat_sccp.h> -#include <openbsc/bsc_msg_filter.h> -#include <openbsc/bsc_msc.h> -#include <openbsc/gsm_04_08.h> -#include <openbsc/mgcp.h> -#include <openbsc/vty.h> -#include <openbsc/nat_rewrite_trie.h> - -#include <osmocom/core/talloc.h> -#include <osmocom/core/rate_ctr.h> -#include <osmocom/core/utils.h> -#include <osmocom/vty/logging.h> -#include <osmocom/vty/misc.h> -#include <openbsc/osmux.h> - -#include <osmocom/sccp/sccp.h> - -#include <stdlib.h> -#include <stdbool.h> - -static struct bsc_nat *_nat; - - -#define BSC_STR "Information about BSCs\n" -#define MGCP_STR "MGCP related status\n" -#define PAGING_STR "Paging\n" -#define SMSC_REWRITE "SMSC Rewriting\n" - -static struct cmd_node nat_node = { - NAT_NODE, - "%s(config-nat)# ", - 1, -}; - -static struct cmd_node bsc_node = { - NAT_BSC_NODE, - "%s(config-nat-bsc)# ", - 1, -}; - -static struct cmd_node pgroup_node = { - PGROUP_NODE, - "%s(config-nat-paging-group)# ", - 1, -}; - -static int config_write_pgroup(struct vty *vty) -{ - return CMD_SUCCESS; -} - -static void dump_lac(struct vty *vty, struct llist_head *head) -{ - struct bsc_lac_entry *lac; - llist_for_each_entry(lac, head, entry) - vty_out(vty, " location_area_code %u%s", lac->lac, VTY_NEWLINE); -} - - -static void write_pgroup_lst(struct vty *vty, struct bsc_nat_paging_group *pgroup) -{ - vty_out(vty, " paging-group %d%s", pgroup->nr, VTY_NEWLINE); - dump_lac(vty, &pgroup->lists); -} - -static int config_write_nat(struct vty *vty) -{ - struct bsc_msg_acc_lst *lst; - struct bsc_nat_paging_group *pgroup; - - vty_out(vty, "nat%s", VTY_NEWLINE); - vty_out(vty, " msc ip %s%s", _nat->main_dest->ip, VTY_NEWLINE); - vty_out(vty, " msc port %d%s", _nat->main_dest->port, VTY_NEWLINE); - vty_out(vty, " timeout auth %d%s", _nat->auth_timeout, VTY_NEWLINE); - vty_out(vty, " timeout ping %d%s", _nat->ping_timeout, VTY_NEWLINE); - vty_out(vty, " timeout pong %d%s", _nat->pong_timeout, VTY_NEWLINE); - if (_nat->include_file) - vty_out(vty, " bscs-config-file %s%s", _nat->include_file, VTY_NEWLINE); - if (_nat->token) - vty_out(vty, " token %s%s", _nat->token, VTY_NEWLINE); - vty_out(vty, " ip-dscp %d%s", _nat->bsc_ip_dscp, VTY_NEWLINE); - if (_nat->acc_lst_name) - vty_out(vty, " access-list-name %s%s", _nat->acc_lst_name, VTY_NEWLINE); - if (_nat->imsi_black_list_fn) - vty_out(vty, " imsi-black-list-file-name %s%s", - _nat->imsi_black_list_fn, VTY_NEWLINE); - if (_nat->ussd_lst_name) - vty_out(vty, " ussd-list-name %s%s", _nat->ussd_lst_name, VTY_NEWLINE); - if (_nat->ussd_query) - vty_out(vty, " ussd-query %s%s", _nat->ussd_query, VTY_NEWLINE); - if (_nat->ussd_token) - vty_out(vty, " ussd-token %s%s", _nat->ussd_token, VTY_NEWLINE); - if (_nat->ussd_local) - vty_out(vty, " ussd-local-ip %s%s", _nat->ussd_local, VTY_NEWLINE); - - if (_nat->num_rewr_name) - vty_out(vty, " number-rewrite %s%s", _nat->num_rewr_name, VTY_NEWLINE); - if (_nat->num_rewr_post_name) - vty_out(vty, " number-rewrite-post %s%s", - _nat->num_rewr_post_name, VTY_NEWLINE); - - if (_nat->smsc_rewr_name) - vty_out(vty, " rewrite-smsc addr %s%s", - _nat->smsc_rewr_name, VTY_NEWLINE); - if (_nat->tpdest_match_name) - vty_out(vty, " rewrite-smsc tp-dest-match %s%s", - _nat->tpdest_match_name, VTY_NEWLINE); - if (_nat->sms_clear_tp_srr_name) - vty_out(vty, " sms-clear-tp-srr %s%s", - _nat->sms_clear_tp_srr_name, VTY_NEWLINE); - if (_nat->sms_num_rewr_name) - vty_out(vty, " sms-number-rewrite %s%s", - _nat->sms_num_rewr_name, VTY_NEWLINE); - if (_nat->num_rewr_trie_name) - vty_out(vty, " prefix-tree %s%s", - _nat->num_rewr_trie_name, VTY_NEWLINE); - - llist_for_each_entry(lst, &_nat->access_lists, list) - bsc_msg_acc_lst_write(vty, lst); - llist_for_each_entry(pgroup, &_nat->paging_groups, entry) - write_pgroup_lst(vty, pgroup); - if (_nat->mgcp_ipa) - vty_out(vty, " use-msc-ipa-for-mgcp%s", VTY_NEWLINE); - vty_out(vty, " %ssdp-ensure-amr-mode-set%s", - _nat->sdp_ensure_amr_mode_set ? "" : "no ", VTY_NEWLINE); - - return CMD_SUCCESS; -} - -static void config_write_bsc_single(struct vty *vty, struct bsc_config *bsc) -{ - vty_out(vty, " bsc %u%s", bsc->nr, VTY_NEWLINE); - vty_out(vty, " token %s%s", bsc->token, VTY_NEWLINE); - if (bsc->key_present) - vty_out(vty, " auth-key %s%s", osmo_hexdump(bsc->key, 16), VTY_NEWLINE); - dump_lac(vty, &bsc->lac_list); - if (bsc->description) - vty_out(vty, " description %s%s", bsc->description, VTY_NEWLINE); - if (bsc->acc_lst_name) - vty_out(vty, " access-list-name %s%s", bsc->acc_lst_name, VTY_NEWLINE); - vty_out(vty, " max-endpoints %d%s", bsc->max_endpoints, VTY_NEWLINE); - 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); - 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) -{ - struct bsc_config *bsc; - - llist_for_each_entry(bsc, &_nat->bsc_configs, entry) - config_write_bsc_single(vty, bsc); - return CMD_SUCCESS; -} - -DEFUN(show_bscs, show_bscs_cmd, "show bscs-config", - SHOW_STR "Show configured BSCs\n" - "Both from included file and vty\n") -{ - vty_out(vty, "BSCs configuration loaded from %s:%s", _nat->resolved_path, - VTY_NEWLINE); - return config_write_bsc(vty); -} - -DEFUN(show_sccp, show_sccp_cmd, "show sccp connections", - SHOW_STR "Display information about SCCP\n" - "All active connections\n") -{ - struct nat_sccp_connection *con; - vty_out(vty, "Listing all open SCCP connections%s", VTY_NEWLINE); - - llist_for_each_entry(con, &_nat->sccp_connections, list_entry) { - vty_out(vty, "For BSC Nr: %d BSC ref: 0x%x; MUX ref: 0x%x; Network has ref: %d ref: 0x%x MSC/BSC mux: 0x%x/0x%x type: %s%s", - con->bsc->cfg ? con->bsc->cfg->nr : -1, - sccp_src_ref_to_int(&con->real_ref), - sccp_src_ref_to_int(&con->patched_ref), - con->has_remote_ref, - sccp_src_ref_to_int(&con->remote_ref), - con->msc_endp, con->bsc_endp, - bsc_con_type_to_string(con->filter_state.con_type), - VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -DEFUN(show_nat_bsc, show_nat_bsc_cmd, "show nat num-bscs-configured", - SHOW_STR "Display NAT configuration details\n" - "BSCs-related\n") -{ - vty_out(vty, "%d BSCs configured%s", _nat->num_bsc, VTY_NEWLINE); - return CMD_SUCCESS; -} - -DEFUN(show_bsc, show_bsc_cmd, "show bsc connections", - SHOW_STR BSC_STR - "All active connections\n") -{ - struct bsc_connection *con; - struct sockaddr_in sock; - socklen_t len = sizeof(sock); - - 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 pending-stats: %u%s", - con->cfg ? con->cfg->nr : -1, - con->authenticated, con->write_queue.bfd.fd, - inet_ntoa(sock.sin_addr), con->pending_dlcx_count, - VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -DEFUN(show_bsc_mgcp, show_bsc_mgcp_cmd, "show bsc mgcp NR", - SHOW_STR BSC_STR MGCP_STR "Identifier of the BSC\n") -{ - struct bsc_connection *con; - int nr = atoi(argv[0]); - int i, j, endp; - - llist_for_each_entry(con, &_nat->bsc_connections, list_entry) { - int max; - if (!con->cfg) - continue; - if (con->cfg->nr != nr) - continue; - - /* this bsc has no audio endpoints yet */ - if (!con->_endpoint_status) - continue; - - vty_out(vty, "MGCP Status for %d%s", con->cfg->nr, VTY_NEWLINE); - max = bsc_mgcp_nr_multiplexes(con->max_endpoints); - for (i = 0; i < max; ++i) { - for (j = 1; j < 32; ++j) { - endp = mgcp_timeslot_to_endpoint(i, j); - vty_out(vty, " Endpoint 0x%x %s%s", endp, - con->_endpoint_status[endp] == 0 - ? "free" : "allocated", - VTY_NEWLINE); - } - } - break; - } - - return CMD_SUCCESS; -} - -DEFUN(show_bsc_cfg, show_bsc_cfg_cmd, "show bsc config", - SHOW_STR BSC_STR "Configuration of BSCs\n") -{ - struct bsc_config *conf; - llist_for_each_entry(conf, &_nat->bsc_configs, entry) { - vty_out(vty, "BSC token: '%s' nr: %u%s", - conf->token, conf->nr, VTY_NEWLINE); - if (conf->acc_lst_name) - vty_out(vty, " access-list: %s%s", - conf->acc_lst_name, VTY_NEWLINE); - vty_out(vty, " paging forbidden: %d%s", - conf->forbid_paging, VTY_NEWLINE); - if (conf->description) - vty_out(vty, " description: %s%s", conf->description, VTY_NEWLINE); - else - vty_out(vty, " No description.%s", VTY_NEWLINE); - - } - - return CMD_SUCCESS; -} - -static void dump_stat_total(struct vty *vty, struct bsc_nat *nat) -{ - vty_out(vty, "NAT statistics%s", VTY_NEWLINE); - vty_out(vty, " SCCP Connections %lu total, %lu calls%s", - osmo_counter_get(nat->stats.sccp.conn), - osmo_counter_get(nat->stats.sccp.calls), VTY_NEWLINE); - vty_out(vty, " MSC Connections %lu%s", - osmo_counter_get(nat->stats.msc.reconn), VTY_NEWLINE); - vty_out(vty, " MSC Connected: %d%s", - bsc_nat_msc_is_connected(nat), VTY_NEWLINE); - vty_out(vty, " BSC Connections %lu total, %lu auth failed.%s", - osmo_counter_get(nat->stats.bsc.reconn), - osmo_counter_get(nat->stats.bsc.auth_fail), VTY_NEWLINE); -} - -static void dump_stat_bsc(struct vty *vty, struct bsc_config *conf) -{ - int connected = 0; - struct bsc_connection *con; - - vty_out(vty, " BSC nr: %d%s", - conf->nr, VTY_NEWLINE); - vty_out_rate_ctr_group(vty, " ", conf->stats.ctrg); - - llist_for_each_entry(con, &conf->nat->bsc_connections, list_entry) { - if (con->cfg != conf) - continue; - connected = 1; - break; - } - - vty_out(vty, " Connected: %d%s", connected, VTY_NEWLINE); -} - -DEFUN(show_stats, - show_stats_cmd, - "show statistics [NR]", - SHOW_STR "Display network statistics\n" - "Number of the BSC\n") -{ - struct bsc_config *conf; - - int nr = -1; - - if (argc == 1) - nr = atoi(argv[0]); - - dump_stat_total(vty, _nat); - llist_for_each_entry(conf, &_nat->bsc_configs, entry) { - if (argc == 1 && nr != conf->nr) - continue; - dump_stat_bsc(vty, conf); - } - - return CMD_SUCCESS; -} - -DEFUN(show_stats_lac, - show_stats_lac_cmd, - "show statistics-by-lac <0-65535>", - SHOW_STR "Display network statistics by lac\n" - "The lac of the BSC\n") -{ - int lac; - struct bsc_config *conf; - - lac = atoi(argv[0]); - - dump_stat_total(vty, _nat); - llist_for_each_entry(conf, &_nat->bsc_configs, entry) { - if (!bsc_config_handles_lac(conf, lac)) - continue; - dump_stat_bsc(vty, conf); - } - - return CMD_SUCCESS; -} - -DEFUN(show_msc, - show_msc_cmd, - "show msc connection", - SHOW_STR "MSC related information\n" - "Status of the A-link connection\n") -{ - if (!_nat->msc_con) { - vty_out(vty, "The MSC is not yet configured.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - vty_out(vty, "MSC is connected: %d%s", - bsc_nat_msc_is_connected(_nat), VTY_NEWLINE); - return CMD_SUCCESS; -} - -DEFUN(close_bsc, - close_bsc_cmd, - "close bsc connection BSC_NR", - "Close\n" "A-link\n" "Connection\n" "Identifier of the BSC\n") -{ - struct bsc_connection *bsc; - int bsc_nr = atoi(argv[0]); - - llist_for_each_entry(bsc, &_nat->bsc_connections, list_entry) { - if (!bsc->cfg || bsc->cfg->nr != bsc_nr) - continue; - bsc_close_connection(bsc); - break; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_nat, cfg_nat_cmd, "nat", "Configure the NAT") -{ - vty->index = _nat; - vty->node = NAT_NODE; - - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_msc_ip, - cfg_nat_msc_ip_cmd, - "msc ip A.B.C.D", - "MSC related configuration\n" - "Configure the IP address\n" IP_STR) -{ - bsc_nat_set_msc_ip(_nat, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_msc_port, - cfg_nat_msc_port_cmd, - "msc port <1-65500>", - "MSC related configuration\n" - "Configure the port\n" - "Port number\n") -{ - _nat->main_dest->port = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_auth_time, - cfg_nat_auth_time_cmd, - "timeout auth <1-256>", - "Timeout configuration\n" - "Authentication timeout\n" - "Timeout in seconds\n") -{ - _nat->auth_timeout = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_ping_time, - cfg_nat_ping_time_cmd, - "timeout ping NR", - "Timeout configuration\n" - "Time between two pings\n" - "Timeout in seconds\n") -{ - _nat->ping_timeout = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_pong_time, - cfg_nat_pong_time_cmd, - "timeout pong NR", - "Timeout configuration\n" - "Waiting for pong timeout\n" - "Timeout in seconds\n") -{ - _nat->pong_timeout = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_token, cfg_nat_token_cmd, - "token TOKEN", - "Authentication token configuration\n" - "Token of the BSC, currently transferred in cleartext\n") -{ - osmo_talloc_replace_string(_nat, &_nat->token, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_bsc_ip_dscp, cfg_nat_bsc_ip_dscp_cmd, - "ip-dscp <0-255>", - "Set the IP DSCP for the BSCs to use\n" "Set the IP_TOS attribute") -{ - _nat->bsc_ip_dscp = atoi(argv[0]); - return CMD_SUCCESS; -} - -ALIAS_DEPRECATED(cfg_nat_bsc_ip_dscp, cfg_nat_bsc_ip_tos_cmd, - "ip-tos <0-255>", - "Use ip-dscp in the future.\n" "Set the DSCP\n") - - -DEFUN(cfg_nat_acc_lst_name, - cfg_nat_acc_lst_name_cmd, - "access-list-name NAME", - "Set the name of the access list to use.\n" - "The name of the to be used access list.") -{ - osmo_talloc_replace_string(_nat, &_nat->acc_lst_name, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_include, - cfg_nat_include_cmd, - "bscs-config-file NAME", - "Set the filename of the BSC configuration to include.\n" - "The filename to be included.") -{ - char *path; - int rc; - struct bsc_config *cf1, *cf2; - struct bsc_connection *con1, *con2; - - if ('/' == argv[0][0]) - osmo_talloc_replace_string(_nat, &_nat->resolved_path, argv[0]); - else { - path = talloc_asprintf(_nat, "%s/%s", _nat->include_base, - argv[0]); - osmo_talloc_replace_string(_nat, &_nat->resolved_path, path); - talloc_free(path); - } - - llist_for_each_entry_safe(cf1, cf2, &_nat->bsc_configs, entry) { - cf1->remove = true; - cf1->token_updated = false; - } - - rc = vty_read_config_file(_nat->resolved_path, NULL); - if (rc < 0) { - vty_out(vty, "Failed to parse the config file %s: %s%s", - _nat->resolved_path, strerror(-rc), VTY_NEWLINE); - return CMD_WARNING; - } - - osmo_talloc_replace_string(_nat, &_nat->include_file, argv[0]); - - llist_for_each_entry_safe(con1, con2, &_nat->bsc_connections, - list_entry) { - if (con1->cfg) - if (con1->cfg->token_updated || con1->cfg->remove) - bsc_close_connection(con1); - } - - llist_for_each_entry_safe(cf1, cf2, &_nat->bsc_configs, entry) { - if (cf1->remove) - bsc_config_free(cf1); - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_no_acc_lst_name, - cfg_nat_no_acc_lst_name_cmd, - "no access-list-name", - NO_STR "Remove the access list from the NAT.\n") -{ - if (_nat->acc_lst_name) { - talloc_free(_nat->acc_lst_name); - _nat->acc_lst_name = NULL; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_imsi_black_list_fn, - cfg_nat_imsi_black_list_fn_cmd, - "imsi-black-list-file-name NAME", - "IMSI black listing\n" "Filename IMSI and reject-cause\n") -{ - - osmo_talloc_replace_string(_nat, &_nat->imsi_black_list_fn, argv[0]); - if (_nat->imsi_black_list_fn) { - int rc; - struct osmo_config_list *rewr = NULL; - rewr = osmo_config_list_parse(_nat, _nat->imsi_black_list_fn); - rc = bsc_filter_barr_adapt(_nat, &_nat->imsi_black_list, rewr); - if (rc != 0) { - vty_out(vty, "%%There was an error parsing the list." - " Please see the error log.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; - } - - bsc_filter_barr_adapt(_nat, &_nat->imsi_black_list, NULL); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_no_imsi_black_list_fn, - cfg_nat_no_imsi_black_list_fn_cmd, - "no imsi-black-list-file-name", - NO_STR "Remove the imsi-black-list\n") -{ - talloc_free(_nat->imsi_black_list_fn); - _nat->imsi_black_list_fn = NULL; - bsc_filter_barr_adapt(_nat, &_nat->imsi_black_list, NULL); - return CMD_SUCCESS; -} - -static int replace_rules(struct bsc_nat *nat, char **name, - struct llist_head *head, const char *file) -{ - struct osmo_config_list *rewr = NULL; - - osmo_talloc_replace_string(nat, name, file); - if (*name) { - rewr = osmo_config_list_parse(nat, *name); - bsc_nat_num_rewr_entry_adapt(nat, head, rewr); - talloc_free(rewr); - return CMD_SUCCESS; - } else { - bsc_nat_num_rewr_entry_adapt(nat, head, NULL); - return CMD_SUCCESS; - } -} - -DEFUN(cfg_nat_number_rewrite, - cfg_nat_number_rewrite_cmd, - "number-rewrite FILENAME", - "Set the file with rewriting rules.\n" "Filename") -{ - return replace_rules(_nat, &_nat->num_rewr_name, - &_nat->num_rewr, argv[0]); -} - -DEFUN(cfg_nat_no_number_rewrite, - cfg_nat_no_number_rewrite_cmd, - "no number-rewrite", - NO_STR "Set the file with rewriting rules.\n") -{ - talloc_free(_nat->num_rewr_name); - _nat->num_rewr_name = NULL; - - bsc_nat_num_rewr_entry_adapt(NULL, &_nat->num_rewr, NULL); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_number_rewrite_post, - cfg_nat_number_rewrite_post_cmd, - "number-rewrite-post FILENAME", - "Set the file with post-routing rewriting rules.\n" "Filename") -{ - return replace_rules(_nat, &_nat->num_rewr_post_name, - &_nat->num_rewr_post, argv[0]); -} - -DEFUN(cfg_nat_no_number_rewrite_post, - cfg_nat_no_number_rewrite_post_cmd, - "no number-rewrite-post", - NO_STR "Set the file with post-routing rewriting rules.\n") -{ - talloc_free(_nat->num_rewr_post_name); - _nat->num_rewr_post_name = NULL; - - bsc_nat_num_rewr_entry_adapt(NULL, &_nat->num_rewr_post, NULL); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_smsc_addr, - cfg_nat_smsc_addr_cmd, - "rewrite-smsc addr FILENAME", - SMSC_REWRITE - "The SMSC Address to match and replace in RP-DATA\n" - "File with rules for the SMSC Address replacing\n") -{ - return replace_rules(_nat, &_nat->smsc_rewr_name, - &_nat->smsc_rewr, argv[0]); -} - -DEFUN(cfg_nat_smsc_tpdest, - cfg_nat_smsc_tpdest_cmd, - "rewrite-smsc tp-dest-match FILENAME", - SMSC_REWRITE - "Match TP-Destination of a SMS.\n" - "File with rules for matching MSISDN and TP-DEST\n") -{ - return replace_rules(_nat, &_nat->tpdest_match_name, - &_nat->tpdest_match, argv[0]); -} - -DEFUN(cfg_nat_sms_clear_tpsrr, - cfg_nat_sms_clear_tpsrr_cmd, - "sms-clear-tp-srr FILENAME", - "SMS TPDU Sender Report Request clearing\n" - "Files with rules for matching MSISDN\n") -{ - return replace_rules(_nat, &_nat->sms_clear_tp_srr_name, - &_nat->sms_clear_tp_srr, argv[0]); -} - -DEFUN(cfg_nat_no_sms_clear_tpsrr, - cfg_nat_no_sms_clear_tpsrr_cmd, - "no sms-clear-tp-srr", - NO_STR - "SMS TPDU Sender Report Request clearing\n") -{ - talloc_free(_nat->sms_clear_tp_srr_name); - _nat->sms_clear_tp_srr_name = NULL; - - bsc_nat_num_rewr_entry_adapt(NULL, &_nat->sms_clear_tp_srr, NULL); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_sms_number_rewrite, - cfg_nat_sms_number_rewrite_cmd, - "sms-number-rewrite FILENAME", - "SMS TP-DA Number rewriting\n" - "Files with rules for matching MSISDN\n") -{ - return replace_rules(_nat, &_nat->sms_num_rewr_name, - &_nat->sms_num_rewr, argv[0]); -} - -DEFUN(cfg_nat_no_sms_number_rewrite, - cfg_nat_no_sms_number_rewrite_cmd, - "no sms-number-rewrite", - NO_STR "Disable SMS TP-DA rewriting\n") -{ - talloc_free(_nat->sms_num_rewr_name); - _nat->sms_num_rewr_name = NULL; - - bsc_nat_num_rewr_entry_adapt(NULL, &_nat->sms_num_rewr, NULL); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_prefix_trie, - cfg_nat_prefix_trie_cmd, - "prefix-tree FILENAME", - "Prefix tree for number rewriting\n" "File to load\n") -{ - /* give up the old data */ - talloc_free(_nat->num_rewr_trie); - _nat->num_rewr_trie = NULL; - - /* replace the file name */ - osmo_talloc_replace_string(_nat, &_nat->num_rewr_trie_name, argv[0]); - if (!_nat->num_rewr_trie_name) { - vty_out(vty, "%% prefix-tree no filename is present.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - _nat->num_rewr_trie = nat_rewrite_parse(_nat, _nat->num_rewr_trie_name); - if (!_nat->num_rewr_trie) { - vty_out(vty, "%% prefix-tree parsing has failed.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - vty_out(vty, "%% prefix-tree loaded %zu rules.%s", - _nat->num_rewr_trie->prefixes, VTY_NEWLINE); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_no_prefix_trie, cfg_nat_no_prefix_trie_cmd, - "no prefix-tree", - NO_STR "Prefix tree for number rewriting\n") -{ - talloc_free(_nat->num_rewr_trie); - _nat->num_rewr_trie = NULL; - talloc_free(_nat->num_rewr_trie_name); - _nat->num_rewr_trie_name = NULL; - - return CMD_SUCCESS; -} - -DEFUN(show_prefix_tree, show_prefix_tree_cmd, - "show prefix-tree", - SHOW_STR "Prefix tree for number rewriting\n") -{ - if (!_nat->num_rewr_trie) { - vty_out(vty, "%% there is now prefix tree loaded.%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - nat_rewrite_dump_vty(vty, _nat->num_rewr_trie); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_ussd_lst_name, - cfg_nat_ussd_lst_name_cmd, - "ussd-list-name NAME", - "Set the name of the access list to check for IMSIs for USSD message\n" - "The name of the access list for HLR USSD handling") -{ - osmo_talloc_replace_string(_nat, &_nat->ussd_lst_name, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_ussd_query, - cfg_nat_ussd_query_cmd, - "ussd-query REGEXP", - "Set the USSD query to match with the ussd-list-name\n" - "The query to match") -{ - if (gsm_parse_reg(_nat, &_nat->ussd_query_re, &_nat->ussd_query, argc, argv) != 0) - return CMD_WARNING; - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_ussd_token, - cfg_nat_ussd_token_cmd, - "ussd-token TOKEN", - "Set the token used to identify the USSD module\n" "Secret key\n") -{ - osmo_talloc_replace_string(_nat, &_nat->ussd_token, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_ussd_local, - cfg_nat_ussd_local_cmd, - "ussd-local-ip A.B.C.D", - "Set the IP to listen for the USSD Provider\n" "IP Address\n") -{ - osmo_talloc_replace_string(_nat, &_nat->ussd_local, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_use_ipa_for_mgcp, - cfg_nat_use_ipa_for_mgcp_cmd, - "use-msc-ipa-for-mgcp", - "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; -} - -DEFUN(cfg_nat_sdp_amr_mode_set, - cfg_nat_sdp_amr_mode_set_cmd, - "sdp-ensure-amr-mode-set", - "Ensure that SDP records include a mode-set\n") -{ - _nat->sdp_ensure_amr_mode_set = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_no_sdp_amr_mode_set, - cfg_nat_no_sdp_amr_mode_set_cmd, - "no sdp-ensure-amr-mode-set", - NO_STR "Ensure that SDP records include a mode-set\n") -{ - _nat->sdp_ensure_amr_mode_set = 0; - return CMD_SUCCESS; -} - -/* per BSC configuration */ -DEFUN(cfg_bsc, cfg_bsc_cmd, "bsc BSC_NR", - "BSC configuration\n" "Identifier of the BSC\n") -{ - int bsc_nr = atoi(argv[0]); - struct bsc_config *bsc = bsc_config_num(_nat, bsc_nr); - - /* allocate a new one */ - if (!bsc) - bsc = bsc_config_alloc(_nat, "unknown", bsc_nr); - - if (!bsc) - return CMD_WARNING; - - bsc->remove = false; - vty->index = bsc; - vty->node = NAT_BSC_NODE; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_token, cfg_bsc_token_cmd, "token TOKEN", - "Authentication token configuration\n" - "Token of the BSC, currently transferred in cleartext\n") -{ - struct bsc_config *conf = vty->index; - - if (strncmp(conf->token, argv[0], 128) != 0) - conf->token_updated = true; - - osmo_talloc_replace_string(conf, &conf->token, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_auth_key, cfg_bsc_auth_key_cmd, - "auth-key KEY", - "Authentication (secret) key configuration\n" - "Non null security key\n") -{ - struct bsc_config *conf = vty->index; - - memset(conf->key, 0, sizeof(conf->key)); - osmo_hexparse(argv[0], conf->key, sizeof(conf->key)); - conf->key_present = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_no_auth_key, cfg_bsc_no_auth_key_cmd, - "no auth-key", - NO_STR "Authentication (secret) key configuration\n") -{ - struct bsc_config *conf = vty->index; - - memset(conf->key, 0, sizeof(conf->key)); - conf->key_present = 0; - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_lac, cfg_bsc_lac_cmd, "location_area_code <0-65535>", - "Add the Location Area Code (LAC) of this BSC\n" "LAC\n") -{ - struct bsc_config *tmp; - struct bsc_config *conf = vty->index; - - int lac = atoi(argv[0]); - - if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) { - vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s", - lac, VTY_NEWLINE); - return CMD_WARNING; - } - - /* verify that the LACs are unique */ - llist_for_each_entry(tmp, &_nat->bsc_configs, entry) { - if (bsc_config_handles_lac(tmp, lac)) { - if (tmp->nr != conf->nr) { - vty_out(vty, "%% LAC %d is already used.%s", lac, - VTY_NEWLINE); - return CMD_ERR_INCOMPLETE; - } - } - } - - bsc_config_add_lac(conf, lac); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_no_lac, cfg_bsc_no_lac_cmd, - "no location_area_code <0-65535>", - NO_STR "Remove the Location Area Code (LAC) of this BSC\n" "LAC\n") -{ - int lac = atoi(argv[0]); - struct bsc_config *conf = vty->index; - - bsc_config_del_lac(conf, lac); - return CMD_SUCCESS; -} - -DEFUN(show_bar_lst, - show_bar_lst_cmd, - "show imsi-black-list", - SHOW_STR "IMSIs barred from the network\n") -{ - struct rb_node *node; - - vty_out(vty, "IMSIs barred from the network:%s", VTY_NEWLINE); - - for (node = rb_first(&_nat->imsi_black_list); node; node = rb_next(node)) { - struct bsc_filter_barr_entry *entry; - entry = rb_entry(node, struct bsc_filter_barr_entry, node); - - vty_out(vty, " IMSI(%s) CM-Reject-Cause(%d) LU-Reject-Cause(%d)%s", - entry->imsi, entry->cm_reject_cause, entry->lu_reject_cause, - VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - - -DEFUN(cfg_bsc_acc_lst_name, - cfg_bsc_acc_lst_name_cmd, - "access-list-name NAME", - "Set the name of the access list to use.\n" - "The name of the to be used access list.") -{ - struct bsc_config *conf = vty->index; - - osmo_talloc_replace_string(conf, &conf->acc_lst_name, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_no_acc_lst_name, - cfg_bsc_no_acc_lst_name_cmd, - "no access-list-name", - NO_STR "Do not use an access-list for the BSC.\n") -{ - struct bsc_config *conf = vty->index; - - if (conf->acc_lst_name) { - talloc_free(conf->acc_lst_name); - conf->acc_lst_name = NULL; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_max_endps, cfg_bsc_max_endps_cmd, - "max-endpoints <1-1024>", - "Highest endpoint to use (exclusively)\n" "Number of ports\n") -{ - struct bsc_config *conf = vty->index; - - conf->max_endpoints = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_paging, - cfg_bsc_paging_cmd, - "paging forbidden (0|1)", - PAGING_STR "Forbid sending PAGING REQUESTS to the BSC.\n" - "Do not forbid\n" "Forbid\n") -{ - struct bsc_config *conf = vty->index; - - if (strcmp("1", argv[0]) == 0) - conf->forbid_paging = 1; - else - conf->forbid_paging = 0; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_desc, - cfg_bsc_desc_cmd, - "description DESC", - "Provide a description for the given BSC.\n" "Description\n") -{ - struct bsc_config *conf = vty->index; - - osmo_talloc_replace_string(conf, &conf->description, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_paging_grp, - cfg_bsc_paging_grp_cmd, - "paging group <0-1000>", - PAGING_STR "Use a paging group\n" "Paging Group to use\n") -{ - struct bsc_config *conf = vty->index; - conf->paging_group = atoi(argv[0]); - return CMD_SUCCESS; -} - -ALIAS_DEPRECATED(cfg_bsc_paging_grp, cfg_bsc_old_grp_cmd, - "paging-group <0-1000>", - "Use a paging group\n" "Paging Group to use\n") - -DEFUN(cfg_bsc_no_paging_grp, - cfg_bsc_no_paging_grp_cmd, - "no paging group", - NO_STR PAGING_STR "Disable the usage of a paging group.\n") -{ - struct bsc_config *conf = vty->index; - conf->paging_group = PAGIN_GROUP_UNASSIGNED; - return CMD_SUCCESS; -} - -DEFUN(test_regex, test_regex_cmd, - "test regex PATTERN STRING", - "Test utilities\n" - "Regexp testing\n" "The regexp pattern\n" - "The string to match\n") -{ - regex_t reg; - char *str = NULL; - - memset(®, 0, sizeof(reg)); - if (gsm_parse_reg(_nat, ®, &str, 1, argv) != 0) - return CMD_WARNING; - - vty_out(vty, "String matches allow pattern: %d%s", - regexec(®, argv[1], 0, NULL, 0) == 0, VTY_NEWLINE); - - talloc_free(str); - regfree(®); - return CMD_SUCCESS; -} - -DEFUN(set_last_endp, set_last_endp_cmd, - "set bsc last-used-endpoint <0-9999999999> <0-1024>", - "Set a value\n" "Operate on a BSC\n" - "Last used endpoint for an assignment\n" "BSC configuration number\n" - "Endpoint number used\n") -{ - struct bsc_connection *con; - int nr = atoi(argv[0]); - int endp = atoi(argv[1]); - - - llist_for_each_entry(con, &_nat->bsc_connections, list_entry) { - if (!con->cfg) - continue; - if (con->cfg->nr != nr) - continue; - - con->last_endpoint = endp; - vty_out(vty, "Updated the last endpoint for %d to %d.%s", - con->cfg->nr, con->last_endpoint, VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -DEFUN(block_new_conn, block_new_conn_cmd, - "nat-block (block|unblock)", - "Block the NAT for new connections\n" - "Block\n" "Unblock\n") -{ - _nat->blocked = argv[0][0] == 'b'; - vty_out(vty, "%%Going to %s the NAT.%s", - _nat->blocked ? "block" : "unblock", VTY_NEWLINE); - return CMD_SUCCESS; -} - -/* paging group */ -DEFUN(cfg_nat_pgroup, cfg_nat_pgroup_cmd, - "paging-group <0-1000>", - "Create a Paging Group\n" "Number of the Group\n") -{ - int group = atoi(argv[0]); - struct bsc_nat_paging_group *pgroup; - pgroup = bsc_nat_paging_group_num(_nat, group); - if (!pgroup) - pgroup = bsc_nat_paging_group_create(_nat, group); - if (!pgroup) { - vty_out(vty, "Failed to create the group.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - vty->index = pgroup; - vty->node = PGROUP_NODE; - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_no_pgroup, cfg_nat_no_pgroup_cmd, - "no paging-group <0-1000>", - NO_STR "Delete paging-group\n" "Paging-group number\n") -{ - int group = atoi(argv[0]); - struct bsc_nat_paging_group *pgroup; - pgroup = bsc_nat_paging_group_num(_nat, group); - if (!pgroup) { - vty_out(vty, "No such paging group %d.%s", group, VTY_NEWLINE); - return CMD_WARNING; - } - - bsc_nat_paging_group_delete(pgroup); - return CMD_SUCCESS; -} - -DEFUN(cfg_pgroup_lac, cfg_pgroup_lac_cmd, - "location_area_code <0-65535>", - "Add the Location Area Code (LAC)\n" "LAC\n") -{ - struct bsc_nat_paging_group *pgroup = vty->index; - - int lac = atoi(argv[0]); - if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) { - vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s", - lac, VTY_NEWLINE); - return CMD_WARNING; - } - - bsc_nat_paging_group_add_lac(pgroup, lac); - return CMD_SUCCESS; -} - -DEFUN(cfg_pgroup_no_lac, cfg_pgroup_no_lac_cmd, - "no location_area_code <0-65535>", - NO_STR "Remove the Location Area Code (LAC)\n" "LAC\n") -{ - int lac = atoi(argv[0]); - struct bsc_nat_paging_group *pgroup = vty->index; - - bsc_nat_paging_group_del_lac(pgroup, lac); - return CMD_SUCCESS; -} - -DEFUN(show_ussd_connection, - show_ussd_connection_cmd, - "show ussd-connection", - SHOW_STR "USSD connection related information\n") -{ - vty_out(vty, "The USSD side channel provider is %sconnected and %sauthorized.%s", - _nat->ussd_con ? "" : "not ", - _nat->ussd_con && _nat->ussd_con->authorized? "" : "not ", - VTY_NEWLINE); - return CMD_SUCCESS; -} - -#define OSMUX_STR "RTP multiplexing\n" -DEFUN(cfg_bsc_osmux, - cfg_bsc_osmux_cmd, - "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 = OSMUX_USAGE_ON; - else if (strcmp(argv[0], "off") == 0) - conf->osmux = OSMUX_USAGE_OFF; - else if (strcmp(argv[0], "only") == 0) - conf->osmux = OSMUX_USAGE_ONLY; - - 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"); - vty_out(vty, "%% failed to create Osmux socket%s", - VTY_NEWLINE); - return CMD_WARNING; - } - } 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 - * new upcoming flows should use RTP. - */ - } - - return CMD_SUCCESS; -} - -int bsc_nat_vty_init(struct bsc_nat *nat) -{ - _nat = nat; - - /* show commands */ - install_element_ve(&show_sccp_cmd); - install_element_ve(&show_bsc_cmd); - install_element_ve(&show_nat_bsc_cmd); - install_element_ve(&show_bsc_cfg_cmd); - install_element_ve(&show_stats_cmd); - install_element_ve(&show_stats_lac_cmd); - install_element_ve(&close_bsc_cmd); - install_element_ve(&show_msc_cmd); - install_element_ve(&test_regex_cmd); - install_element_ve(&show_bsc_mgcp_cmd); - install_element_ve(&show_bscs_cmd); - install_element_ve(&show_bar_lst_cmd); - install_element_ve(&show_prefix_tree_cmd); - install_element_ve(&show_ussd_connection_cmd); - - install_element(ENABLE_NODE, &set_last_endp_cmd); - install_element(ENABLE_NODE, &block_new_conn_cmd); - - /* nat group */ - install_element(CONFIG_NODE, &cfg_nat_cmd); - install_node(&nat_node, config_write_nat); - vty_install_default(NAT_NODE); - install_element(NAT_NODE, &cfg_nat_msc_ip_cmd); - install_element(NAT_NODE, &cfg_nat_msc_port_cmd); - install_element(NAT_NODE, &cfg_nat_auth_time_cmd); - install_element(NAT_NODE, &cfg_nat_ping_time_cmd); - install_element(NAT_NODE, &cfg_nat_pong_time_cmd); - install_element(NAT_NODE, &cfg_nat_token_cmd); - install_element(NAT_NODE, &cfg_nat_bsc_ip_dscp_cmd); - install_element(NAT_NODE, &cfg_nat_bsc_ip_tos_cmd); - install_element(NAT_NODE, &cfg_nat_acc_lst_name_cmd); - install_element(NAT_NODE, &cfg_nat_no_acc_lst_name_cmd); - install_element(NAT_NODE, &cfg_nat_include_cmd); - install_element(NAT_NODE, &cfg_nat_imsi_black_list_fn_cmd); - install_element(NAT_NODE, &cfg_nat_no_imsi_black_list_fn_cmd); - install_element(NAT_NODE, &cfg_nat_ussd_lst_name_cmd); - 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_use_ipa_for_mgcp_cmd); - - bsc_msg_lst_vty_init(nat, &nat->access_lists, NAT_NODE); - - /* number rewriting */ - install_element(NAT_NODE, &cfg_nat_number_rewrite_cmd); - install_element(NAT_NODE, &cfg_nat_no_number_rewrite_cmd); - install_element(NAT_NODE, &cfg_nat_number_rewrite_post_cmd); - install_element(NAT_NODE, &cfg_nat_no_number_rewrite_post_cmd); - install_element(NAT_NODE, &cfg_nat_smsc_addr_cmd); - install_element(NAT_NODE, &cfg_nat_smsc_tpdest_cmd); - install_element(NAT_NODE, &cfg_nat_sms_clear_tpsrr_cmd); - install_element(NAT_NODE, &cfg_nat_no_sms_clear_tpsrr_cmd); - install_element(NAT_NODE, &cfg_nat_sms_number_rewrite_cmd); - install_element(NAT_NODE, &cfg_nat_no_sms_number_rewrite_cmd); - install_element(NAT_NODE, &cfg_nat_prefix_trie_cmd); - install_element(NAT_NODE, &cfg_nat_no_prefix_trie_cmd); - - install_element(NAT_NODE, &cfg_nat_sdp_amr_mode_set_cmd); - install_element(NAT_NODE, &cfg_nat_no_sdp_amr_mode_set_cmd); - - install_element(NAT_NODE, &cfg_nat_pgroup_cmd); - install_element(NAT_NODE, &cfg_nat_no_pgroup_cmd); - install_node(&pgroup_node, config_write_pgroup); - vty_install_default(PGROUP_NODE); - install_element(PGROUP_NODE, &cfg_pgroup_lac_cmd); - install_element(PGROUP_NODE, &cfg_pgroup_no_lac_cmd); - - /* BSC subgroups */ - install_element(NAT_NODE, &cfg_bsc_cmd); - install_node(&bsc_node, NULL); - vty_install_default(NAT_BSC_NODE); - install_element(NAT_BSC_NODE, &cfg_bsc_token_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_auth_key_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_no_auth_key_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_lac_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_no_lac_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_paging_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_desc_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_acc_lst_name_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_no_acc_lst_name_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_max_endps_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_old_grp_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_paging_grp_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_no_paging_grp_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_osmux_cmd); - - mgcp_vty_init(); - - return 0; -} - - -/* called by the telnet interface... we have our own init above */ -int bsc_vty_init(struct gsm_network *network) -{ - logging_vty_add_cmds(NULL); - return 0; -} diff --git a/src/osmo-bsc_nat/bsc_sccp.c b/src/osmo-bsc_nat/bsc_sccp.c deleted file mode 100644 index c6c265f7a..000000000 --- a/src/osmo-bsc_nat/bsc_sccp.c +++ /dev/null @@ -1,247 +0,0 @@ -/* SCCP patching and handling routines */ -/* - * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org> - * (C) 2010 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <openbsc/debug.h> -#include <openbsc/bsc_nat.h> -#include <openbsc/bsc_nat_sccp.h> - -#include <osmocom/sccp/sccp.h> - -#include <osmocom/core/talloc.h> - -#include <string.h> -#include <time.h> - -static int equal(struct sccp_source_reference *ref1, struct sccp_source_reference *ref2) -{ - return memcmp(ref1, ref2, sizeof(*ref1)) == 0; -} - -/* - * SCCP patching below - */ - -/* check if we are using this ref for patched already */ -static int sccp_ref_is_free(struct sccp_source_reference *ref, struct bsc_nat *nat) -{ - struct nat_sccp_connection *conn; - - llist_for_each_entry(conn, &nat->sccp_connections, list_entry) { - if (equal(ref, &conn->patched_ref)) - return -1; - } - - return 0; -} - -/* copied from sccp.c */ -static int assign_src_local_reference(struct sccp_source_reference *ref, struct bsc_nat *nat) -{ - static uint32_t last_ref = 0x50000; - int wrapped = 0; - - do { - struct sccp_source_reference reference; - reference.octet1 = (last_ref >> 0) & 0xff; - reference.octet2 = (last_ref >> 8) & 0xff; - reference.octet3 = (last_ref >> 16) & 0xff; - - ++last_ref; - /* do not use the reversed word and wrap around */ - if ((last_ref & 0x00FFFFFF) == 0x00FFFFFF) { - LOGP(DNAT, LOGL_NOTICE, "Wrapped searching for a free code\n"); - last_ref = 0; - ++wrapped; - } - - if (sccp_ref_is_free(&reference, nat) == 0) { - *ref = reference; - return 0; - } - } while (wrapped != 2); - - LOGP(DNAT, LOGL_ERROR, "Finding a free reference failed\n"); - return -1; -} - -struct nat_sccp_connection *create_sccp_src_ref(struct bsc_connection *bsc, - struct bsc_nat_parsed *parsed) -{ - struct nat_sccp_connection *conn; - - /* Some commercial BSCs like to reassign there SRC ref */ - llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) { - if (conn->bsc != bsc) - continue; - if (!equal(parsed->src_local_ref, &conn->real_ref)) - continue; - - /* the BSC has reassigned the SRC ref and we failed to keep track */ - memset(&conn->remote_ref, 0, sizeof(conn->remote_ref)); - if (assign_src_local_reference(&conn->patched_ref, bsc->nat) != 0) { - LOGP(DNAT, LOGL_ERROR, "BSC %d reused src ref: %d and we failed to generate a new id.\n", - bsc->cfg->nr, sccp_src_ref_to_int(parsed->src_local_ref)); - bsc_mgcp_dlcx(conn); - llist_del(&conn->list_entry); - talloc_free(conn); - return NULL; - } else { - clock_gettime(CLOCK_MONOTONIC, &conn->creation_time); - bsc_mgcp_dlcx(conn); - return conn; - } - } - - - conn = talloc_zero(bsc->nat, struct nat_sccp_connection); - if (!conn) { - LOGP(DNAT, LOGL_ERROR, "Memory allocation failure.\n"); - return NULL; - } - - conn->bsc = bsc; - clock_gettime(CLOCK_MONOTONIC, &conn->creation_time); - conn->real_ref = *parsed->src_local_ref; - if (assign_src_local_reference(&conn->patched_ref, bsc->nat) != 0) { - LOGP(DNAT, LOGL_ERROR, "Failed to assign a ref.\n"); - talloc_free(conn); - return NULL; - } - - bsc_mgcp_init(conn); - llist_add_tail(&conn->list_entry, &bsc->nat->sccp_connections); - rate_ctr_inc(&bsc->cfg->stats.ctrg->ctr[BCFG_CTR_SCCP_CONN]); - osmo_counter_inc(bsc->cfg->nat->stats.sccp.conn); - - LOGP(DNAT, LOGL_DEBUG, "Created 0x%x <-> 0x%x mapping for con %p\n", - sccp_src_ref_to_int(&conn->real_ref), - sccp_src_ref_to_int(&conn->patched_ref), bsc); - - return conn; -} - -int update_sccp_src_ref(struct nat_sccp_connection *sccp, struct bsc_nat_parsed *parsed) -{ - if (!parsed->dest_local_ref || !parsed->src_local_ref) { - LOGP(DNAT, LOGL_ERROR, "CC MSG should contain both local and dest address.\n"); - return -1; - } - - sccp->remote_ref = *parsed->src_local_ref; - sccp->has_remote_ref = 1; - LOGP(DNAT, LOGL_DEBUG, "Updating 0x%x to remote 0x%x on %p\n", - sccp_src_ref_to_int(&sccp->patched_ref), - sccp_src_ref_to_int(&sccp->remote_ref), sccp->bsc); - - return 0; -} - -void remove_sccp_src_ref(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed) -{ - struct nat_sccp_connection *conn; - - llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) { - if (equal(parsed->src_local_ref, &conn->patched_ref)) { - sccp_connection_destroy(conn); - return; - } - } - - LOGP(DNAT, LOGL_ERROR, "Can not remove connection: 0x%x\n", - sccp_src_ref_to_int(parsed->src_local_ref)); -} - -/* - * We have a message from the MSC to the BSC. The MSC is using - * an address that was assigned by the MUX, we need to update the - * dest reference to the real network. - */ -struct nat_sccp_connection *patch_sccp_src_ref_to_bsc(struct msgb *msg, - struct bsc_nat_parsed *parsed, - struct bsc_nat *nat) -{ - struct nat_sccp_connection *conn; - - if (!parsed->dest_local_ref) { - LOGP(DNAT, LOGL_ERROR, "MSG should contain dest_local_ref.\n"); - return NULL; - } - - - llist_for_each_entry(conn, &nat->sccp_connections, list_entry) { - if (!equal(parsed->dest_local_ref, &conn->patched_ref)) - continue; - - /* Change the dest address to the real one */ - *parsed->dest_local_ref = conn->real_ref; - return conn; - } - - return NULL; -} - -/* - * These are message to the MSC. We will need to find the BSC - * Connection by either the SRC or the DST local reference. - * - * In case of a CR we need to work by the SRC local reference - * in all other cases we need to work by the destination local - * reference.. - */ -struct nat_sccp_connection *patch_sccp_src_ref_to_msc(struct msgb *msg, - struct bsc_nat_parsed *parsed, - struct bsc_connection *bsc) -{ - struct nat_sccp_connection *conn; - - llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) { - if (conn->bsc != bsc) - continue; - - if (parsed->src_local_ref) { - if (equal(parsed->src_local_ref, &conn->real_ref)) { - *parsed->src_local_ref = conn->patched_ref; - return conn; - } - } else if (parsed->dest_local_ref) { - if (equal(parsed->dest_local_ref, &conn->remote_ref)) - return conn; - } else { - LOGP(DNAT, LOGL_ERROR, "Header has neither loc/dst ref.\n"); - return NULL; - } - } - - return NULL; -} - -struct nat_sccp_connection *bsc_nat_find_con_by_bsc(struct bsc_nat *nat, - struct sccp_source_reference *ref) -{ - struct nat_sccp_connection *conn; - - llist_for_each_entry(conn, &nat->sccp_connections, list_entry) { - if (equal(ref, &conn->real_ref)) - return conn; - } - - return NULL; -} diff --git a/src/osmo-bsc_nat/bsc_ussd.c b/src/osmo-bsc_nat/bsc_ussd.c deleted file mode 100644 index 0ba63270d..000000000 --- a/src/osmo-bsc_nat/bsc_ussd.c +++ /dev/null @@ -1,453 +0,0 @@ -/* USSD Filter Code */ - -/* - * (C) 2010-2011 by Holger Hans Peter Freyther <zecke@selfish.org> - * (C) 2010-2011 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <openbsc/bsc_nat.h> -#include <openbsc/bsc_nat_sccp.h> -#include <openbsc/bsc_msg_filter.h> -#include <openbsc/ipaccess.h> -#include <openbsc/socket.h> - -#include <osmocom/gsm/protocol/gsm_08_08.h> -#include <osmocom/gsm/gsm0480.h> -#include <osmocom/core/talloc.h> -#include <osmocom/gsm/tlv.h> -#include <osmocom/gsm/ipa.h> - -#include <osmocom/sccp/sccp.h> - -#include <osmocom/abis/ipa.h> - -#include <sys/socket.h> -#include <string.h> -#include <unistd.h> - -#define USSD_LAC_IE 0 -#define USSD_CI_IE 1 - -static void ussd_auth_con(struct tlv_parsed *, struct bsc_nat_ussd_con *); - -static struct bsc_nat_ussd_con *bsc_nat_ussd_alloc(struct bsc_nat *nat) -{ - struct bsc_nat_ussd_con *con; - - con = talloc_zero(nat, struct bsc_nat_ussd_con); - if (!con) - return NULL; - - con->nat = nat; - return con; -} - -static void bsc_nat_ussd_destroy(struct bsc_nat_ussd_con *con) -{ - if (con->nat->ussd_con == con) { - bsc_ussd_close_connections(con->nat); - con->nat->ussd_con = NULL; - } - - close(con->queue.bfd.fd); - osmo_fd_unregister(&con->queue.bfd); - osmo_timer_del(&con->auth_timeout); - osmo_wqueue_clear(&con->queue); - - msgb_free(con->pending_msg); - talloc_free(con); -} - -static void ussd_pong(struct bsc_nat_ussd_con *conn) -{ - struct msgb *msg; - - msg = msgb_alloc_headroom(4096, 128, "pong message"); - if (!msg) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate pong msg\n"); - return; - } - - msgb_v_put(msg, IPAC_MSGT_PONG); - bsc_do_write(&conn->queue, msg, IPAC_PROTO_IPACCESS); -} - -static int forward_sccp(struct bsc_nat *nat, struct msgb *msg) -{ - struct nat_sccp_connection *con; - struct bsc_nat_parsed *parsed; - - - parsed = bsc_nat_parse(msg); - if (!parsed) { - LOGP(DNAT, LOGL_ERROR, "Can not parse msg from USSD.\n"); - msgb_free(msg); - return -1; - } - - if (!parsed->dest_local_ref) { - LOGP(DNAT, LOGL_ERROR, "No destination local reference.\n"); - msgb_free(msg); - return -1; - } - - con = bsc_nat_find_con_by_bsc(nat, parsed->dest_local_ref); - if (!con || !con->bsc) { - LOGP(DNAT, LOGL_ERROR, "No active connection found.\n"); - msgb_free(msg); - return -1; - } - - talloc_free(parsed); - bsc_write_msg(&con->bsc->write_queue, msg); - return 0; -} - -static int ussd_read_cb(struct osmo_fd *bfd) -{ - struct bsc_nat_ussd_con *conn = bfd->data; - struct msgb *msg = NULL; - struct ipaccess_head *hh; - int ret; - - ret = ipa_msg_recv_buffered(bfd->fd, &msg, &conn->pending_msg); - if (ret <= 0) { - if (ret == -EAGAIN) - return 0; - LOGP(DNAT, LOGL_ERROR, "USSD Connection was lost.\n"); - bsc_nat_ussd_destroy(conn); - return -1; - } - - LOGP(DNAT, LOGL_NOTICE, "MSG from USSD: %s proto: %d\n", - osmo_hexdump(msg->data, msg->len), msg->l2h[0]); - hh = (struct ipaccess_head *) msg->data; - - if (hh->proto == IPAC_PROTO_IPACCESS) { - if (msg->l2h[0] == IPAC_MSGT_ID_RESP) { - struct tlv_parsed tvp; - int ret; - ret = ipa_ccm_idtag_parse(&tvp, - (unsigned char *) msg->l2h + 2, - msgb_l2len(msg) - 2); - if (ret < 0) { - LOGP(DNAT, LOGL_ERROR, "ignoring IPA response " - "message with malformed TLVs\n"); - msgb_free(msg); - return ret; - } - if (TLVP_PRESENT(&tvp, IPAC_IDTAG_UNITNAME)) - ussd_auth_con(&tvp, conn); - } else if (msg->l2h[0] == IPAC_MSGT_PING) { - LOGP(DNAT, LOGL_DEBUG, "Got USSD ping request.\n"); - ussd_pong(conn); - } else { - LOGP(DNAT, LOGL_NOTICE, "Got unknown IPACCESS message 0x%02x.\n", msg->l2h[0]); - } - - msgb_free(msg); - } else if (hh->proto == IPAC_PROTO_SCCP) { - forward_sccp(conn->nat, msg); - } else { - msgb_free(msg); - } - - return 0; -} - -static void ussd_auth_cb(void *_data) -{ - LOGP(DNAT, LOGL_ERROR, "USSD module didn't authenticate\n"); - bsc_nat_ussd_destroy((struct bsc_nat_ussd_con *) _data); -} - -static void ussd_auth_con(struct tlv_parsed *tvp, struct bsc_nat_ussd_con *conn) -{ - const char *token; - int len; - if (!conn->nat->ussd_token) { - LOGP(DNAT, LOGL_ERROR, "No USSD token set. Closing\n"); - bsc_nat_ussd_destroy(conn); - return; - } - - token = (const char *) TLVP_VAL(tvp, IPAC_IDTAG_UNITNAME); - len = TLVP_LEN(tvp, IPAC_IDTAG_UNITNAME); - - /* last byte should be a NULL */ - if (strlen(conn->nat->ussd_token) != len - 1) - goto disconnect; - /* compare everything including the null byte */ - if (memcmp(conn->nat->ussd_token, token, len) != 0) - goto disconnect; - - /* it is authenticated now */ - if (conn->nat->ussd_con && conn->nat->ussd_con != conn) - bsc_nat_ussd_destroy(conn->nat->ussd_con); - - LOGP(DNAT, LOGL_ERROR, "USSD token specified. USSD provider is connected.\n"); - osmo_timer_del(&conn->auth_timeout); - conn->authorized = 1; - conn->nat->ussd_con = conn; - return; - -disconnect: - LOGP(DNAT, LOGL_ERROR, "Wrong USSD token by client: %d\n", - conn->queue.bfd.fd); - bsc_nat_ussd_destroy(conn); -} - -static void ussd_start_auth(struct bsc_nat_ussd_con *conn) -{ - struct msgb *msg; - - osmo_timer_setup(&conn->auth_timeout, ussd_auth_cb, conn); - osmo_timer_schedule(&conn->auth_timeout, conn->nat->auth_timeout, 0); - - msg = msgb_alloc_headroom(4096, 128, "auth message"); - if (!msg) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate auth msg\n"); - return; - } - - msgb_v_put(msg, IPAC_MSGT_ID_GET); - bsc_do_write(&conn->queue, msg, IPAC_PROTO_IPACCESS); -} - -static int ussd_listen_cb(struct osmo_fd *bfd, unsigned int what) -{ - struct bsc_nat_ussd_con *conn; - struct bsc_nat *nat; - struct sockaddr_in sa; - socklen_t sa_len = sizeof(sa); - int fd; - - if (!(what & BSC_FD_READ)) - return 0; - - fd = accept(bfd->fd, (struct sockaddr *) &sa, &sa_len); - if (fd < 0) { - perror("accept"); - return fd; - } - - nat = (struct bsc_nat *) bfd->data; - osmo_counter_inc(nat->stats.ussd.reconn); - - conn = bsc_nat_ussd_alloc(nat); - if (!conn) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate USSD con struct.\n"); - close(fd); - return -1; - } - - osmo_wqueue_init(&conn->queue, 10); - conn->queue.bfd.data = conn; - conn->queue.bfd.fd = fd; - conn->queue.bfd.when = BSC_FD_READ; - conn->queue.read_cb = ussd_read_cb; - conn->queue.write_cb = bsc_write_cb; - - if (osmo_fd_register(&conn->queue.bfd) < 0) { - LOGP(DNAT, LOGL_ERROR, "Failed to register USSD fd.\n"); - bsc_nat_ussd_destroy(conn); - return -1; - } - - LOGP(DNAT, LOGL_NOTICE, "USSD Connection on %d with IP: %s\n", - fd, inet_ntoa(sa.sin_addr)); - - /* do authentication */ - ussd_start_auth(conn); - return 0; -} - -int bsc_ussd_init(struct bsc_nat *nat) -{ - struct in_addr addr; - - addr.s_addr = INADDR_ANY; - if (nat->ussd_local) - inet_aton(nat->ussd_local, &addr); - - nat->ussd_listen.data = nat; - return make_sock(&nat->ussd_listen, IPPROTO_TCP, - ntohl(addr.s_addr), 5001, 0, ussd_listen_cb, nat); -} - -static int forward_ussd_simple(struct nat_sccp_connection *con, struct msgb *input) -{ - struct msgb *copy; - struct bsc_nat_ussd_con *ussd; - - if (!con->bsc->nat->ussd_con) - return -1; - - copy = msgb_alloc_headroom(4096, 128, "forward bts"); - if (!copy) { - LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n"); - return -1; - } - - /* copy the data into the copy */ - copy->l2h = msgb_put(copy, msgb_l2len(input)); - memcpy(copy->l2h, input->l2h, msgb_l2len(input)); - - /* send it out */ - ussd = con->bsc->nat->ussd_con; - bsc_do_write(&ussd->queue, copy, IPAC_PROTO_SCCP); - return 0; -} - -static int forward_ussd(struct nat_sccp_connection *con, const struct ussd_request *req, - struct msgb *input) -{ - struct msgb *msg, *copy; - struct ipac_msgt_sccp_state *state; - struct bsc_nat_ussd_con *ussd; - uint16_t lac, ci; - - if (!con->bsc->nat->ussd_con) - return -1; - - msg = msgb_alloc_headroom(4096, 128, "forward ussd"); - if (!msg) { - LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n"); - return -1; - } - - copy = msgb_alloc_headroom(4096, 128, "forward bts"); - if (!copy) { - LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n"); - msgb_free(msg); - return -1; - } - - copy->l2h = msgb_put(copy, msgb_l2len(input)); - memcpy(copy->l2h, input->l2h, msgb_l2len(input)); - - msg->l2h = msgb_put(msg, 1); - msg->l2h[0] = IPAC_MSGT_SCCP_OLD; - - /* fill out the data */ - state = (struct ipac_msgt_sccp_state *) msgb_put(msg, sizeof(*state)); - state->trans_id = req->transaction_id; - state->invoke_id = req->invoke_id; - memcpy(&state->src_ref, &con->remote_ref, sizeof(con->remote_ref)); - memcpy(&state->dst_ref, &con->real_ref, sizeof(con->real_ref)); - memcpy(state->imsi, con->filter_state.imsi, strlen(con->filter_state.imsi)); - - /* add additional tag/values */ - lac = htons(con->lac); - ci = htons(con->ci); - msgb_tv_fixed_put(msg, USSD_LAC_IE, sizeof(lac), (const uint8_t *) &lac); - msgb_tv_fixed_put(msg, USSD_CI_IE, sizeof(ci), (const uint8_t *) &ci); - - ussd = con->bsc->nat->ussd_con; - bsc_do_write(&ussd->queue, msg, IPAC_PROTO_IPACCESS); - bsc_do_write(&ussd->queue, copy, IPAC_PROTO_SCCP); - - return 0; -} - -int bsc_ussd_check(struct nat_sccp_connection *con, struct bsc_nat_parsed *parsed, - struct msgb *msg) -{ - uint32_t len; - uint8_t msg_type; - uint8_t proto; - uint8_t ti; - struct gsm48_hdr *hdr48; - struct bsc_msg_acc_lst *lst; - struct ussd_request req; - - /* - * various checks to avoid the decoding work. Right now we only want to - * decode if the connection was created for USSD, we do have a USSD access - * list, a query, a IMSI and such... - */ - if (con->filter_state.con_type != FLT_CON_TYPE_SSA) - return 0; - - if (!con->filter_state.imsi) - return 0; - - /* We have not verified the IMSI yet */ - if (!con->authorized) - return 0; - - if (!con->bsc->nat->ussd_lst_name) - return 0; - if (!con->bsc->nat->ussd_query) - return 0; - - if (parsed->bssap != BSSAP_MSG_DTAP) - return 0; - - if (strlen(con->filter_state.imsi) > GSM23003_IMSI_MAX_DIGITS) - return 0; - - hdr48 = bsc_unpack_dtap(parsed, msg, &len); - if (!hdr48) - return 0; - - proto = gsm48_hdr_pdisc(hdr48); - msg_type = gsm48_hdr_msg_type(hdr48); - ti = gsm48_hdr_trans_id_no_ti(hdr48); - if (proto != GSM48_PDISC_NC_SS) - return 0; - - if (msg_type == GSM0480_MTYPE_REGISTER) { - - /* now check if it is a IMSI we care about */ - lst = bsc_msg_acc_lst_find(&con->bsc->nat->access_lists, - con->bsc->nat->ussd_lst_name); - if (!lst) - return 0; - - if (bsc_msg_acc_lst_check_allow(lst, con->filter_state.imsi) != 0) - return 0; - - /* now decode the message and see if we really want to handle it */ - memset(&req, 0, sizeof(req)); - if (gsm0480_decode_ussd_request(hdr48, len, &req) != 1) - return 0; - if (req.text[0] == 0xff) - return 0; - - if (regexec(&con->bsc->nat->ussd_query_re, - req.text, 0, NULL, 0) == REG_NOMATCH) - return 0; - - /* found a USSD query for our subscriber */ - LOGP(DNAT, LOGL_NOTICE, "Found USSD query for %s\n", - con->filter_state.imsi); - con->ussd_ti[ti] = 1; - if (forward_ussd(con, &req, msg) != 0) - return 0; - return 1; - } else if (msg_type == GSM0480_MTYPE_FACILITY && con->ussd_ti[ti]) { - LOGP(DNAT, LOGL_NOTICE, "Forwarding message part of TI: %d %s\n", - ti, con->filter_state.imsi); - if (forward_ussd_simple(con, msg) != 0) - return 0; - return 1; - } - - return 0; -} |