aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorOliver Smith <osmith@sysmocom.de>2021-02-01 12:48:48 +0100
committerosmith <osmith@sysmocom.de>2021-02-08 11:52:53 +0000
commit901ed14c898ef229c654009ac0dc078d09d7b054 (patch)
treebb23d5be682970a6a6a764b1b8c23f4e95d1cc96 /src
parent4be5ab37071d8102c133d65546b76117d1da18f1 (diff)
gbproxy: remove (moved to own repository)
New repository: https://git.osmocom.org/osmo-gbproxy/ Related: OS#4992 Change-Id: I37f7cebaf2a06bd93627a452f5df44edcfc0f87a
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am1
-rw-r--r--src/gbproxy/Makefile.am40
-rw-r--r--src/gbproxy/gb_proxy.c1549
-rw-r--r--src/gbproxy/gb_proxy_ctrl.c137
-rw-r--r--src/gbproxy/gb_proxy_main.c336
-rw-r--r--src/gbproxy/gb_proxy_peer.c759
-rw-r--r--src/gbproxy/gb_proxy_vty.c778
7 files changed, 0 insertions, 3600 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index c45d3ab4..e389b7f6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,6 +1,5 @@
SUBDIRS = \
gprs \
sgsn \
- gbproxy \
gtphub \
$(NULL)
diff --git a/src/gbproxy/Makefile.am b/src/gbproxy/Makefile.am
deleted file mode 100644
index 1500e117..00000000
--- a/src/gbproxy/Makefile.am
+++ /dev/null
@@ -1,40 +0,0 @@
-AM_CPPFLAGS = \
- $(all_includes) \
- -I$(top_srcdir)/include \
- -I$(top_builddir) \
- $(NULL)
-
-AM_CFLAGS = \
- -Wall \
- -fno-strict-aliasing \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOGSM_CFLAGS) \
- $(LIBOSMOVTY_CFLAGS) \
- $(LIBOSMOCTRL_CFLAGS) \
- $(LIBOSMOABIS_CFLAGS) \
- $(LIBOSMOGB_CFLAGS) \
- $(LIBOSMOGSUPCLIENT_CFLAGS) \
- $(COVERAGE_CFLAGS) \
- $(LIBGTP_CFLAGS) \
- $(NULL)
-
-bin_PROGRAMS = \
- osmo-gbproxy \
- $(NULL)
-
-osmo_gbproxy_SOURCES = \
- gb_proxy.c \
- gb_proxy_main.c \
- gb_proxy_vty.c \
- gb_proxy_ctrl.c \
- gb_proxy_peer.c \
- $(NULL)
-osmo_gbproxy_LDADD = \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- $(LIBOSMOVTY_LIBS) \
- $(LIBOSMOCTRL_LIBS) \
- $(LIBOSMOGB_LIBS) \
- $(LIBGTP_LIBS) \
- -lrt \
- $(NULL)
diff --git a/src/gbproxy/gb_proxy.c b/src/gbproxy/gb_proxy.c
deleted file mode 100644
index c882eb06..00000000
--- a/src/gbproxy/gb_proxy.c
+++ /dev/null
@@ -1,1549 +0,0 @@
-/* NS-over-IP proxy */
-
-/* (C) 2010-2020 by Harald Welte <laforge@gnumonks.org>
- * (C) 2010-2013 by On-Waves
- * (C) 2013 by Holger Hans Peter Freyther
- * 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 <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <getopt.h>
-#include <errno.h>
-#include <sys/fcntl.h>
-#include <sys/stat.h>
-#include <arpa/inet.h>
-#include <time.h>
-
-#include <osmocom/core/hashtable.h>
-#include <osmocom/core/logging.h>
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/rate_ctr.h>
-#include <osmocom/core/stats.h>
-#include <osmocom/core/utils.h>
-
-#include <osmocom/gprs/gprs_ns2.h>
-#include <osmocom/gprs/gprs_bssgp.h>
-#include <osmocom/gprs/gprs_bssgp2.h>
-#include <osmocom/gprs/gprs_bssgp_bss.h>
-#include <osmocom/gprs/bssgp_bvc_fsm.h>
-
-#include <osmocom/gsm/gsm23236.h>
-#include <osmocom/gsm/gsm_utils.h>
-
-#include <osmocom/sgsn/signal.h>
-#include <osmocom/sgsn/debug.h>
-#include <osmocom/sgsn/gprs_gb_parse.h>
-#include <osmocom/sgsn/gb_proxy.h>
-
-#include <osmocom/sgsn/gprs_llc.h>
-#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
-#include <osmocom/sgsn/gprs_utils.h>
-
-extern void *tall_sgsn_ctx;
-
-static const struct rate_ctr_desc global_ctr_description[] = {
- { "inv-bvci", "Invalid BVC Identifier " },
- { "inv-lai", "Invalid Location Area Identifier" },
- { "inv-rai", "Invalid Routing Area Identifier " },
- { "inv-nsei", "No BVC established for NSEI " },
- { "proto-err:bss", "BSSGP protocol error (BSS )" },
- { "proto-err:sgsn", "BSSGP protocol error (SGSN)" },
- { "not-supp:bss", "Feature not supported (BSS )" },
- { "not-supp:sgsn", "Feature not supported (SGSN)" },
- { "restart:sgsn", "Restarted RESET procedure (SGSN)" },
- { "tx-err:sgsn", "NS Transmission error (SGSN)" },
- { "error", "Other error " },
- { "mod-peer-err", "Patch error: no peer " },
-};
-
-static const struct rate_ctr_group_desc global_ctrg_desc = {
- .group_name_prefix = "gbproxy:global",
- .group_description = "GBProxy Global Statistics",
- .num_ctr = ARRAY_SIZE(global_ctr_description),
- .ctr_desc = global_ctr_description,
- .class_id = OSMO_STATS_CLASS_GLOBAL,
-};
-
-static int gbprox_relay2peer(struct msgb *old_msg, struct gbproxy_bvc *bvc,
- uint16_t ns_bvci);
-
-
-/* generate BVC-STATUS message with cause value derived from TLV-parser error */
-static int tx_status_from_tlvp(enum osmo_tlv_parser_error tlv_p_err, struct msgb *orig_msg)
-{
- uint8_t bssgp_cause;
- switch (tlv_p_err) {
- case OSMO_TLVP_ERR_MAND_IE_MISSING:
- bssgp_cause = BSSGP_CAUSE_MISSING_MAND_IE;
- break;
- default:
- bssgp_cause = BSSGP_CAUSE_PROTO_ERR_UNSPEC;
- }
- return bssgp_tx_status(bssgp_cause, NULL, orig_msg);
-}
-
-/* strip off the NS header */
-static void strip_ns_hdr(struct msgb *msg)
-{
- int strip_len = msgb_bssgph(msg) - msg->data;
- msgb_pull(msg, strip_len);
-}
-
-#if 0
-/* feed a message down the NS-VC associated with the specified bvc */
-static int gbprox_relay2sgsn(struct gbproxy_config *cfg, struct msgb *old_msg,
- uint16_t ns_bvci, uint16_t sgsn_nsei)
-{
- /* create a copy of the message so the old one can
- * be free()d safely when we return from gbprox_rcvmsg() */
- struct gprs_ns2_inst *nsi = cfg->nsi;
- struct osmo_gprs_ns2_prim nsp = {};
- struct msgb *msg = bssgp_msgb_copy(old_msg, "msgb_relay2sgsn");
- int rc;
-
- DEBUGP(DGPRS, "NSE(%05u/BSS)-BVC(%05u) proxying BTS->SGSN NSE(%05u/SGSN)\n",
- msgb_nsei(msg), ns_bvci, sgsn_nsei);
-
- nsp.bvci = ns_bvci;
- nsp.nsei = sgsn_nsei;
-
- strip_ns_hdr(msg);
- osmo_prim_init(&nsp.oph, SAP_NS, PRIM_NS_UNIT_DATA,
- PRIM_OP_REQUEST, msg);
- rc = gprs_ns2_recv_prim(nsi, &nsp.oph);
- if (rc < 0)
- rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_TX_ERR_SGSN]);
- return rc;
-}
-#endif
-
-/*! Determine the TLLI from the given BSSGP message.
- * \param[in] bssgp pointer to start of BSSGP header
- * \param[in] bssgp_len length of BSSGP message in octets
- * \param[out] tlli TLLI (if any) in host byte order
- * \returns 1 if TLLI found; 0 if none found; negative on parse error */
-int gprs_gb_parse_tlli(const uint8_t *bssgp, size_t bssgp_len, uint32_t *tlli)
-{
- const struct bssgp_normal_hdr *bgph;
- uint8_t pdu_type;
-
- if (bssgp_len < sizeof(struct bssgp_normal_hdr))
- return -EINVAL;
-
- bgph = (struct bssgp_normal_hdr *)bssgp;
- pdu_type = bgph->pdu_type;
-
- if (pdu_type == BSSGP_PDUT_UL_UNITDATA ||
- pdu_type == BSSGP_PDUT_DL_UNITDATA) {
- const struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *)bssgp;
- if (bssgp_len < sizeof(struct bssgp_ud_hdr))
- return -EINVAL;
- *tlli = osmo_load32be((const uint8_t *)&budh->tlli);
- return 1;
- } else {
- const uint8_t *data = bgph->data;
- size_t data_len = bssgp_len - sizeof(*bgph);
- struct tlv_parsed tp;
-
- if (bssgp_tlv_parse(&tp, data, data_len) < 0)
- return -EINVAL;
-
- if (TLVP_PRESENT(&tp, BSSGP_IE_TLLI)) {
- *tlli = osmo_load32be(TLVP_VAL(&tp, BSSGP_IE_TLLI));
- return 1;
- }
- }
-
- /* No TLLI present in message */
- return 0;
-}
-
-/* feed a message down the NSE */
-static int gbprox_relay2nse(struct msgb *old_msg, struct gbproxy_nse *nse,
- uint16_t ns_bvci)
-{
- OSMO_ASSERT(nse);
- OSMO_ASSERT(nse->cfg);
-
- /* create a copy of the message so the old one can
- * be free()d safely when we return from gbprox_rcvmsg() */
- struct gprs_ns2_inst *nsi = nse->cfg->nsi;
- struct osmo_gprs_ns2_prim nsp = {};
- struct msgb *msg = bssgp_msgb_copy(old_msg, "msgb_relay2nse");
- uint32_t tlli;
- int rc;
-
- DEBUGP(DGPRS, "NSE(%05u/%s)-BVC(%05u/??) proxying to NSE(%05u/%s)\n", msgb_nsei(msg),
- !nse->sgsn_facing ? "SGSN" : "BSS", ns_bvci, nse->nsei, nse->sgsn_facing ? "SGSN" : "BSS");
-
- nsp.bvci = ns_bvci;
- nsp.nsei = nse->nsei;
-
- /* Strip the old NS header, it will be replaced with a new one */
- strip_ns_hdr(msg);
-
- /* TS 48.018 Section 5.4.2: The link selector parameter is
- * defined in 3GPP TS 48.016. At one side of the Gb interface,
- * all BSSGP UNITDATA PDUs related to an MS shall be passed with
- * the same LSP, e.g. the LSP contains the MS's TLLI, to the
- * underlying network service. */
- if (gprs_gb_parse_tlli(msgb_data(msg), msgb_length(msg), &tlli) == 1)
- nsp.u.unitdata.link_selector = tlli;
-
- osmo_prim_init(&nsp.oph, SAP_NS, GPRS_NS2_PRIM_UNIT_DATA,
- PRIM_OP_REQUEST, msg);
- rc = gprs_ns2_recv_prim(nsi, &nsp.oph);
- /* FIXME: We need a counter group for gbproxy_nse */
- //if (rc < 0)
- // rate_ctr_inc(&bvc->ctrg->ctr[GBPROX_PEER_CTR_TX_ERR]);
-
- return rc;
-}
-
-/* feed a message down the NS-VC associated with the specified bvc */
-static int gbprox_relay2peer(struct msgb *old_msg, struct gbproxy_bvc *bvc,
- uint16_t ns_bvci)
-{
- int rc;
- struct gbproxy_nse *nse = bvc->nse;
- OSMO_ASSERT(nse);
-
- rc = gbprox_relay2nse(old_msg, nse, ns_bvci);
- if (rc < 0)
- rate_ctr_inc(&bvc->ctrg->ctr[GBPROX_PEER_CTR_TX_ERR]);
-
- return rc;
-}
-
-int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
-{
- return 0;
-}
-
-
-/***********************************************************************
- * PTP BVC handling
- ***********************************************************************/
-
-/* FIXME: Handle the tlli NULL case correctly,
- * This function should take a generic selector
- * and choose an sgsn based on that
- */
-static struct gbproxy_sgsn *gbproxy_select_sgsn(struct gbproxy_config *cfg, const uint32_t *tlli)
-{
- struct gbproxy_sgsn *sgsn = NULL;
- struct gbproxy_sgsn *sgsn_avoid = NULL;
-
- int tlli_type;
- int16_t nri;
- bool null_nri = false;
-
- if (!tlli) {
- sgsn = llist_first_entry(&cfg->sgsns, struct gbproxy_sgsn, list);
- if (!sgsn) {
- return NULL;
- }
- LOGPSGSN(sgsn, LOGL_INFO, "Could not get TLLI, using first SGSN\n");
- return sgsn;
- }
-
- if (cfg->pool.nri_bitlen == 0) {
- /* Pooling is disabled */
- sgsn = llist_first_entry(&cfg->sgsns, struct gbproxy_sgsn, list);
- if (!sgsn) {
- return NULL;
- }
-
- LOGPSGSN(sgsn, LOGL_INFO, "Pooling disabled, using first configured SGSN\n");
- } else {
- /* Pooling is enabled, try to use the NRI for routing to an SGSN
- * See 3GPP TS 23.236 Ch. 5.3.2 */
- tlli_type = gprs_tlli_type(*tlli);
- if (tlli_type == TLLI_LOCAL || tlli_type == TLLI_FOREIGN) {
- /* Only get/use the NRI if tlli type is local */
- osmo_tmsi_nri_v_get(&nri, *tlli, cfg->pool.nri_bitlen);
- if (nri >= 0) {
- /* Get the SGSN for the NRI */
- sgsn = gbproxy_sgsn_by_nri(cfg, nri, &null_nri);
- if (sgsn && !null_nri)
- return sgsn;
- /* If the NRI is the null NRI, we need to avoid the chosen SGSN */
- if (null_nri && sgsn) {
- sgsn_avoid = sgsn;
- }
- } else {
- /* We couldn't get the NRI from the TLLI */
- LOGP(DGPRS, LOGL_ERROR, "Could not extract NRI from local TLLI %08x\n", *tlli);
- }
- } else {
- LOGP(DGPRS, LOGL_INFO, "TLLI %08x is neither local nor foreign, not routing by NRI\n", *tlli);
- }
- }
-
- /* If we haven't found an SGSN yet we need to choose one, but avoid the one in sgsn_avoid
- * NOTE: This function is not stable if the number of SGSNs or allow_attach changes
- * We could implement TLLI tracking here, but 3GPP TS 23.236 Ch. 5.3.2 (see NOTE) argues that
- * we can just wait for the MS to reattempt the procedure.
- */
- if (!sgsn)
- sgsn = gbproxy_sgsn_by_tlli(cfg, sgsn_avoid, *tlli);
-
- if (!sgsn) {
- LOGP(DGPRS, LOGL_ERROR, "No suitable SGSN found for TLLI %u\n", *tlli);
- return NULL;
- }
-
- return sgsn;
-}
-
-/*! Find the correct gbproxy_bvc given a cell and an SGSN
- * \param[in] cfg The gbproxy configuration
- * \param[in] cell The cell the message belongs to
- * \param[in] tlli An optional TLLI used for tracking
- * \return Returns 0 on success, otherwise a negative value
- */
-static struct gbproxy_bvc *gbproxy_select_sgsn_bvc(struct gbproxy_config *cfg, struct gbproxy_cell *cell, const uint32_t *tlli)
-{
- struct gbproxy_sgsn *sgsn;
- struct gbproxy_bvc *sgsn_bvc = NULL;
- int i;
-
- sgsn = gbproxy_select_sgsn(cfg, tlli);
- if (!sgsn) {
- LOGPCELL(cell, LOGL_ERROR, "Could not find any SGSN, dropping message!\n");
- return NULL;
- }
-
- /* Get the BVC for this SGSN/NSE */
- for (i = 0; i < ARRAY_SIZE(cell->sgsn_bvc); i++) {
- sgsn_bvc = cell->sgsn_bvc[i];
- if (!sgsn_bvc)
- continue;
- if (sgsn->nse != sgsn_bvc->nse)
- continue;
-
- return sgsn_bvc;
- }
-
- /* This shouldn't happen */
- LOGPCELL(cell, LOGL_ERROR, "Could not find matching BVC for SGSN %s, dropping message!\n", sgsn->name);
- return NULL;
-}
-
-/*! Send a message to the next SGSN, possibly ignoring the null SGSN
- * route an uplink message on a PTP-BVC to a SGSN using the TLLI
- * \param[in] cell The cell the message belongs to
- * \param[in] msg The BSSGP message
- * \param[in] null_sgsn If not NULL then avoid this SGSN (because this message contains its null NRI)
- * \param[in] tlli An optional TLLI used for tracking
- * \return Returns 0 on success, otherwise a negative value
- */
-static int gbprox_bss2sgsn_tlli(struct gbproxy_cell *cell, struct msgb *msg, const uint32_t *tlli,
- bool sig_bvci)
-{
- struct gbproxy_config *cfg = cell->cfg;
- struct gbproxy_bvc *sgsn_bvc;
-
- sgsn_bvc = gbproxy_select_sgsn_bvc(cfg, cell, tlli);
- if (!sgsn_bvc) {
- LOGPCELL(cell, LOGL_NOTICE, "Could not find any SGSN for TLLI %u, dropping message!\n", *tlli);
- return -EINVAL;
- }
-
- return gbprox_relay2peer(msg, sgsn_bvc, sig_bvci ? 0 : sgsn_bvc->bvci);
-}
-
-/* Receive an incoming PTP message from a BSS-side NS-VC */
-static int gbprox_rx_ptp_from_bss(struct gbproxy_nse *nse, struct msgb *msg, uint16_t ns_bvci)
-{
- struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
- const char *pdut_name = osmo_tlv_prot_msg_name(&osmo_pdef_bssgp, bgph->pdu_type);
- struct gbproxy_bvc *bss_bvc;
- struct tlv_parsed tp;
- char log_pfx[32];
- uint32_t tlli;
- int rc;
-
- snprintf(log_pfx, sizeof(log_pfx), "NSE(%05u/BSS)-BVC(%05u/??)", nse->nsei, ns_bvci);
-
- LOGP(DGPRS, LOGL_DEBUG, "%s Rx %s\n", log_pfx, pdut_name);
-
- if (ns_bvci == 0 || ns_bvci == 1) {
- LOGP(DGPRS, LOGL_NOTICE, "%s BVCI=%05u is not PTP\n", log_pfx, ns_bvci);
- return bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
- }
-
- if (!(bssgp_pdu_type_flags(bgph->pdu_type) & BSSGP_PDUF_PTP)) {
- LOGP(DGPRS, LOGL_NOTICE, "%s %s not allowed in PTP BVC\n", log_pfx, pdut_name);
- return bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
- }
-
- if (!(bssgp_pdu_type_flags(bgph->pdu_type) & BSSGP_PDUF_UL)) {
- LOGP(DGPRS, LOGL_NOTICE, "%s %s not allowed in uplink direction\n", log_pfx, pdut_name);
- return bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
- }
-
- bss_bvc = gbproxy_bvc_by_bvci(nse, ns_bvci);
- if (!bss_bvc) {
- LOGP(DGPRS, LOGL_NOTICE, "%s %s - Didn't find BVC for PTP message, discarding\n",
- log_pfx, pdut_name);
- return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &ns_bvci, msg);
- }
-
- /* UL_UNITDATA has a different header than all other uplink PDUs */
- if (bgph->pdu_type == BSSGP_PDUT_UL_UNITDATA) {
- const struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
- if (msgb_bssgp_len(msg) < sizeof(*budh))
- return bssgp_tx_status(BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
- rc = osmo_tlv_prot_parse(&osmo_pdef_bssgp, &tp, 1, bgph->pdu_type, budh->data,
- msgb_bssgp_len(msg) - sizeof(*budh), 0, 0, DGPRS, log_pfx);
- /* populate TLLI from the fixed headser into the TLV-parsed array so later code
- * doesn't have to worry where the TLLI came from */
- tp.lv[BSSGP_IE_TLLI].len = 4;
- tp.lv[BSSGP_IE_TLLI].val = (const uint8_t *) &budh->tlli;
- } else {
- rc = osmo_tlv_prot_parse(&osmo_pdef_bssgp, &tp, 1, bgph->pdu_type, bgph->data,
- msgb_bssgp_len(msg) - sizeof(*bgph), 0, 0, DGPRS, log_pfx);
- }
- if (rc < 0) {
- rate_ctr_inc(&nse->cfg->ctrg->ctr[GBPROX_GLOB_CTR_PROTO_ERR_BSS]);
- return tx_status_from_tlvp(rc, msg);
- }
- /* hack to get both msg + tlv_parsed passed via osmo_fsm_inst_dispatch */
- msgb_bcid(msg) = (void *)&tp;
-
- switch (bgph->pdu_type) {
- case BSSGP_PDUT_UL_UNITDATA:
- case BSSGP_PDUT_RA_CAPA_UPDATE:
- case BSSGP_PDUT_FLOW_CONTROL_MS:
- case BSSGP_PDUT_DOWNLOAD_BSS_PFC:
- case BSSGP_PDUT_CREATE_BSS_PFC_ACK:
- case BSSGP_PDUT_CREATE_BSS_PFC_NACK:
- case BSSGP_PDUT_MODIFY_BSS_PFC_ACK:
- case BSSGP_PDUT_DELETE_BSS_PFC_ACK:
- case BSSGP_PDUT_FLOW_CONTROL_PFC:
- case BSSGP_PDUT_DELETE_BSS_PFC_REQ:
- case BSSGP_PDUT_PS_HO_REQUIRED:
- case BSSGP_PDUT_PS_HO_REQUEST_ACK:
- case BSSGP_PDUT_PS_HO_REQUEST_NACK:
- case BSSGP_PDUT_PS_HO_COMPLETE:
- case BSSGP_PDUT_PS_HO_CANCEL:
- /* We can route based on TLLI-NRI */
- tlli = osmo_load32be(TLVP_VAL(&tp, BSSGP_IE_TLLI));
- rc = gbprox_bss2sgsn_tlli(bss_bvc->cell, msg, &tlli, false);
- break;
- case BSSGP_PDUT_RADIO_STATUS:
- if (TLVP_PRESENT(&tp, BSSGP_IE_TLLI)) {
- tlli = osmo_load32be(TLVP_VAL(&tp, BSSGP_IE_TLLI));
- rc = gbprox_bss2sgsn_tlli(bss_bvc->cell, msg, &tlli, false);
- } else if (TLVP_PRESENT(&tp, BSSGP_IE_TMSI)) {
- /* we treat the TMSI like a TLLI and extract the NRI from it */
- tlli = osmo_load32be(TLVP_VAL(&tp, BSSGP_IE_TMSI));
- /* Convert the TMSI into a FOREIGN TLLI so it is routed appropriately */
- tlli = gprs_tmsi2tlli(tlli, TLLI_FOREIGN);
- rc = gbprox_bss2sgsn_tlli(bss_bvc->cell, msg, &tlli, false);
- } else if (TLVP_PRESENT(&tp, BSSGP_IE_IMSI)) {
- /* FIXME: Use the IMSI as selector? */
- rc = gbprox_bss2sgsn_tlli(bss_bvc->cell, msg, NULL, false);
- /* rc = gbprox_bss2sgsn_hashed(bss_bvc->cell, msg, NULL); */
- } else
- LOGPBVC(bss_bvc, LOGL_ERROR, "Rx RADIO-STATUS without any of the conditional IEs\n");
- break;
- case BSSGP_PDUT_DUMMY_PAGING_PS_RESP:
- case BSSGP_PDUT_PAGING_PS_REJECT:
- {
- /* Route according to IMSI<->NSE cache entry */
- struct osmo_mobile_identity mi;
- const uint8_t *mi_data = TLVP_VAL(&tp, BSSGP_IE_IMSI);
- uint8_t mi_len = TLVP_LEN(&tp, BSSGP_IE_IMSI);
- osmo_mobile_identity_decode(&mi, mi_data, mi_len, false);
- nse = gbproxy_nse_by_imsi(nse->cfg, mi.imsi);
- if (nse) {
- OSMO_ASSERT(nse->sgsn_facing);
- rc = gbprox_relay2nse(msg, nse, ns_bvci);
- } else {
- LOGPBVC(bss_bvc, LOGL_ERROR, "Rx unmatched %s with IMSI %s\n", pdut_name, mi.imsi);
- }
- break;
- }
- case BSSGP_PDUT_FLOW_CONTROL_BVC:
- osmo_fsm_inst_dispatch(bss_bvc->fi, BSSGP_BVCFSM_E_RX_FC_BVC, msg);
- break;
- case BSSGP_PDUT_STATUS:
- /* TODO: Implement by inspecting the contained PDU */
- if (!TLVP_PRESENT(&tp, BSSGP_IE_PDU_IN_ERROR))
- break;
- LOGPBVC(bss_bvc, LOGL_ERROR, "Rx %s: Implementation missing\n", pdut_name);
- break;
- }
-
- return 0;
-}
-
-/* Receive an incoming PTP message from a SGSN-side NS-VC */
-static int gbprox_rx_ptp_from_sgsn(struct gbproxy_nse *nse, struct msgb *msg, uint16_t ns_bvci)
-{
- struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
- const char *pdut_name = osmo_tlv_prot_msg_name(&osmo_pdef_bssgp, bgph->pdu_type);
- struct gbproxy_bvc *sgsn_bvc, *bss_bvc;
- struct tlv_parsed tp;
- char log_pfx[32];
- int rc;
-
- snprintf(log_pfx, sizeof(log_pfx), "NSE(%05u/SGSN)-BVC(%05u/??)", nse->nsei, ns_bvci);
-
- LOGP(DGPRS, LOGL_DEBUG, "%s Rx %s\n", log_pfx, pdut_name);
-
- if (ns_bvci == 0 || ns_bvci == 1) {
- LOGP(DGPRS, LOGL_NOTICE, "%s BVCI is not PTP\n", log_pfx);
- return bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
- }
-
- if (!(bssgp_pdu_type_flags(bgph->pdu_type) & BSSGP_PDUF_PTP)) {
- LOGP(DGPRS, LOGL_NOTICE, "%s %s not allowed in PTP BVC\n", log_pfx, pdut_name);
- return bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
- }
-
- if (!(bssgp_pdu_type_flags(bgph->pdu_type) & BSSGP_PDUF_DL)) {
- LOGP(DGPRS, LOGL_NOTICE, "%s %s not allowed in downlink direction\n", log_pfx, pdut_name);
- return bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
- }
-
- sgsn_bvc = gbproxy_bvc_by_bvci(nse, ns_bvci);
- if (!sgsn_bvc) {
- LOGP(DGPRS, LOGL_NOTICE, "%s %s - Didn't find BVC for for PTP message, discarding\n",
- log_pfx, pdut_name);
- rate_ctr_inc(&nse->cfg->ctrg-> ctr[GBPROX_GLOB_CTR_INV_BVCI]);
- return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &ns_bvci, msg);
- }
-
- if (!bssgp_bvc_fsm_is_unblocked(sgsn_bvc->fi)) {
- LOGPBVC(sgsn_bvc, LOGL_NOTICE, "Rx %s: Dropping on blocked BVC\n", pdut_name);
- rate_ctr_inc(&sgsn_bvc->ctrg->ctr[GBPROX_PEER_CTR_DROPPED]);
- return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, &ns_bvci, msg);
- }
-
- /* DL_UNITDATA has a different header than all other uplink PDUs */
- if (bgph->pdu_type == BSSGP_PDUT_DL_UNITDATA) {
- const struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
- if (msgb_bssgp_len(msg) < sizeof(*budh))
- return bssgp_tx_status(BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
- rc = osmo_tlv_prot_parse(&osmo_pdef_bssgp, &tp, 1, bgph->pdu_type, budh->data,
- msgb_bssgp_len(msg) - sizeof(*budh), 0, 0, DGPRS, log_pfx);
- /* populate TLLI from the fixed headser into the TLV-parsed array so later code
- * doesn't have to worry where the TLLI came from */
- tp.lv[BSSGP_IE_TLLI].len = 4;
- tp.lv[BSSGP_IE_TLLI].val = (const uint8_t *) &budh->tlli;
- } else {
- rc = osmo_tlv_prot_parse(&osmo_pdef_bssgp, &tp, 1, bgph->pdu_type, bgph->data,
- msgb_bssgp_len(msg) - sizeof(*bgph), 0, 0, DGPRS, log_pfx);
- }
- if (rc < 0) {
- rate_ctr_inc(&nse->cfg->ctrg->ctr[GBPROX_GLOB_CTR_PROTO_ERR_BSS]);
- return tx_status_from_tlvp(rc, msg);
- }
- /* hack to get both msg + tlv_parsed passed via osmo_fsm_inst_dispatch */
- msgb_bcid(msg) = (void *)&tp;
-
- OSMO_ASSERT(sgsn_bvc->cell);
- bss_bvc = sgsn_bvc->cell->bss_bvc;
-
- switch (bgph->pdu_type) {
- case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK:
- return osmo_fsm_inst_dispatch(sgsn_bvc->fi, BSSGP_BVCFSM_E_RX_FC_BVC_ACK, msg);
- case BSSGP_PDUT_DUMMY_PAGING_PS:
- case BSSGP_PDUT_PAGING_PS:
- {
- /* Cache the IMSI<->NSE to route PAGING REJECT */
- struct osmo_mobile_identity mi;
- const uint8_t *mi_data = TLVP_VAL(&tp, BSSGP_IE_IMSI);
- uint8_t mi_len = TLVP_LEN(&tp, BSSGP_IE_IMSI);
- osmo_mobile_identity_decode(&mi, mi_data, mi_len, false);
- gbproxy_imsi_cache_update(nse, mi.imsi);
- break;
- }
- default:
- break;
- }
- return gbprox_relay2peer(msg, bss_bvc, bss_bvc->bvci);
-
-}
-
-/***********************************************************************
- * BVC FSM call-backs
- ***********************************************************************/
-
-/* helper function to dispatch a FSM event to all SGSN-side BVC FSMs of a cell */
-static void dispatch_to_all_sgsn_bvc(struct gbproxy_cell *cell, uint32_t event, void *priv)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(cell->sgsn_bvc); i++) {
- struct gbproxy_bvc *sgsn_bvc = cell->sgsn_bvc[i];
- if (!sgsn_bvc)
- continue;
- osmo_fsm_inst_dispatch(sgsn_bvc->fi, event, priv);
- }
-}
-
-/* BVC FSM informs us about a BSS-side reset of the signaling BVC */
-static void bss_sig_bvc_reset_notif(uint16_t nsei, uint16_t bvci, const struct gprs_ra_id *ra_id,
- uint16_t cell_id, uint8_t cause, void *priv)
-{
- struct gbproxy_bvc *sig_bvc = priv;
- struct gbproxy_nse *nse = sig_bvc->nse;
- struct gbproxy_bvc *ptp_bvc;
- unsigned int i;
-
- /* BLOCK all SGSN-side PTP BVC within this NSE */
- hash_for_each(nse->bvcs, i, ptp_bvc, list) {
- if (ptp_bvc == sig_bvc)
- continue;
- OSMO_ASSERT(ptp_bvc->cell);
-
- dispatch_to_all_sgsn_bvc(ptp_bvc->cell, BSSGP_BVCFSM_E_REQ_BLOCK, &cause);
- }
-
- /* Delete all BSS-side PTP BVC within this NSE */
- gbproxy_cleanup_bvcs(nse, 0);
-
- /* TODO: we keep the "CELL" around for now, re-connecting it to
- * any (later) new PTP-BVC for that BVCI. Not sure if that's the
- * best idea ? */
-}
-
-/* forward declaration */
-static const struct bssgp_bvc_fsm_ops sgsn_ptp_bvc_fsm_ops;
-
-static const struct bssgp_bvc_fsm_ops bss_sig_bvc_fsm_ops = {
- .reset_notification = bss_sig_bvc_reset_notif,
-};
-
-/* BVC FSM informs us about a BSS-side reset of a PTP BVC */
-static void bss_ptp_bvc_reset_notif(uint16_t nsei, uint16_t bvci, const struct gprs_ra_id *ra_id,
- uint16_t cell_id, uint8_t cause, void *priv)
-{
- struct gbproxy_bvc *bvc = priv;
- struct gbproxy_config *cfg = bvc->nse->cfg;
- struct gbproxy_nse *sgsn_nse;
- unsigned int i;
-
- OSMO_ASSERT(bvci != 0);
-
- if (!bvc->cell) {
- /* see if we have a CELL dangling around */
- bvc->cell = gbproxy_cell_by_bvci(cfg, bvci);
- if (bvc->cell) {
- /* the CELL already exists. This means either it * was created before at an
- * earlier PTP BVC-RESET, or that there are non-unique BVCIs and hence a
- * malconfiguration */
- if (bvc->cell->bss_bvc) {
- LOGPBVC(bvc, LOGL_NOTICE, "Rx BVC-RESET via this NSE, but CELL already "
- "has BVC on NSEI=%05u\n", bvc->cell->bss_bvc->nse->nsei);
- LOGPBVC(bvc->cell->bss_bvc, LOGL_NOTICE, "Destroying due to conflicting "
- "BVCI configuration (new NSEI=%05u)!\n", bvc->nse->nsei);
- gbproxy_bvc_free(bvc->cell->bss_bvc);
- }
- bvc->cell->bss_bvc = bvc;
- }
- }
-
- if (!bvc->cell) {
- /* if we end up here, it means this is the first time we received a BVC-RESET
- * for this BVC. We need to create the 'cell' data structure and the SGSN-side
- * BVC counterparts */
-
- bvc->cell = gbproxy_cell_alloc(cfg, bvci);
- OSMO_ASSERT(bvc->cell);
- memcpy(bvc->cell->ra, bvc->ra, sizeof(bvc->cell->ra));
-
- /* link us to the cell and vice-versa */
- bvc->cell->bss_bvc = bvc;
- }
-
- /* allocate (any missing) SGSN-side BVCs within the cell, and reset them */
- hash_for_each(cfg->sgsn_nses, i, sgsn_nse, list) {
- struct gbproxy_bvc *sgsn_bvc = gbproxy_bvc_by_bvci(sgsn_nse, bvci);
- if (sgsn_bvc)
- OSMO_ASSERT(sgsn_bvc->cell == bvc->cell || !sgsn_bvc->cell);
-
- if (!sgsn_bvc) {
- sgsn_bvc = gbproxy_bvc_alloc(sgsn_nse, bvci);
- OSMO_ASSERT(sgsn_bvc);
-
- sgsn_bvc->cell = bvc->cell;
- memcpy(sgsn_bvc->ra, bvc->cell->ra, sizeof(sgsn_bvc->ra));
- sgsn_bvc->fi = bssgp_bvc_fsm_alloc_ptp_bss(sgsn_bvc, cfg->nsi, sgsn_nse->nsei,
- bvci, ra_id, cell_id);
- OSMO_ASSERT(sgsn_bvc->fi);
- bssgp_bvc_fsm_set_ops(sgsn_bvc->fi, &sgsn_ptp_bvc_fsm_ops, sgsn_bvc);
-
- gbproxy_cell_add_sgsn_bvc(bvc->cell, sgsn_bvc);
- }
- }
-
- /* Trigger outbound BVC-RESET procedure toward each SGSN */
- dispatch_to_all_sgsn_bvc(bvc->cell, BSSGP_BVCFSM_E_REQ_RESET, &cause);
-}
-
-/* BVC FSM informs us about a BSS-side FSM state change */
-static void bss_ptp_bvc_state_chg_notif(uint16_t nsei, uint16_t bvci, int old_state, int state, void *priv)
-{
- struct gbproxy_bvc *bvc = priv;
- struct gbproxy_cell *cell = bvc->cell;
- uint8_t cause = bssgp_bvc_fsm_get_block_cause(bvc->fi);
-
- /* we have just been created but due to callback ordering the cell is not associated */
- if (!cell)
- return;
-
- switch (state) {
- case BSSGP_BVCFSM_S_BLOCKED:
- /* block the corresponding SGSN-side PTP BVCs */
- dispatch_to_all_sgsn_bvc(cell, BSSGP_BVCFSM_E_REQ_BLOCK, &cause);
- break;
- case BSSGP_BVCFSM_S_UNBLOCKED:
- /* unblock the corresponding SGSN-side PTP BVCs */
- dispatch_to_all_sgsn_bvc(cell, BSSGP_BVCFSM_E_REQ_UNBLOCK, NULL);
- break;
- }
-}
-
-/* BVC FSM informs us about BVC-FC PDU receive */
-static void bss_ptp_bvc_fc_bvc(uint16_t nsei, uint16_t bvci, const struct bssgp2_flow_ctrl *fc, void *priv)
-{
- struct bssgp2_flow_ctrl fc_reduced;
- struct gbproxy_bvc *bss_bvc = priv;
- struct gbproxy_cell *cell;
- struct gbproxy_config *cfg;
-
- OSMO_ASSERT(bss_bvc);
- OSMO_ASSERT(fc);
-
- cell = bss_bvc->cell;
- if (!cell)
- return;
-
- cfg = cell->cfg;
-
- /* reduce / scale according to configuration to make sure we only advertise a fraction
- * of the capacity to each of the SGSNs in the pool */
- fc_reduced = *fc;
- fc_reduced.bucket_size_max = (fc->bucket_size_max * cfg->pool.bvc_fc_ratio) / 100;
- fc_reduced.bucket_leak_rate = (fc->bucket_leak_rate * cfg->pool.bvc_fc_ratio) / 100;
- /* we don't modify the per-MS related values as any single MS is only served by one SGSN */
-
- dispatch_to_all_sgsn_bvc(cell, BSSGP_BVCFSM_E_REQ_FC_BVC, (void *) &fc_reduced);
-}
-
-static const struct bssgp_bvc_fsm_ops bss_ptp_bvc_fsm_ops = {
- .reset_notification = bss_ptp_bvc_reset_notif,
- .state_chg_notification = bss_ptp_bvc_state_chg_notif,
- .rx_fc_bvc = bss_ptp_bvc_fc_bvc,
-};
-
-/* BVC FSM informs us about a SGSN-side reset of a PTP BVC */
-static void sgsn_ptp_bvc_reset_notif(uint16_t nsei, uint16_t bvci, const struct gprs_ra_id *ra_id,
- uint16_t cell_id, uint8_t cause, void *priv)
-{
- struct gbproxy_bvc *bvc = priv;
-
- if (!bvc->cell) {
- LOGPBVC(bvc, LOGL_ERROR, "RESET of PTP BVC on SGSN side for which we have no BSS?\n");
- return;
- }
-
- OSMO_ASSERT(bvc->cell->bss_bvc);
-
- /* request reset of BSS-facing PTP-BVC */
- osmo_fsm_inst_dispatch(bvc->cell->bss_bvc->fi, BSSGP_BVCFSM_E_REQ_RESET, &cause);
-}
-
-static const struct bssgp_bvc_fsm_ops sgsn_ptp_bvc_fsm_ops = {
- .reset_notification = sgsn_ptp_bvc_reset_notif,
-};
-
-/* BVC FSM informs us about a SGSN-side reset of the signaling BVC */
-static void sgsn_sig_bvc_reset_notif(uint16_t nsei, uint16_t bvci, const struct gprs_ra_id *ra_id,
- uint16_t cell_id, uint8_t cause, void *priv)
-{
- struct gbproxy_bvc *bvc = priv;
- struct gbproxy_config *cfg = bvc->nse->cfg;
- struct gbproxy_nse *bss_nse;
- unsigned int i;
-
- /* delete all SGSN-side PTP BVC for this SGSN */
- gbproxy_cleanup_bvcs(bvc->nse, 0);
- /* FIXME: what to do about the cells? */
- /* FIXME: do we really want to RESET all signaling BVC on the BSS and affect all other SGSN? */
-
- /* we need to trigger generating a reset procedure towards each BSS side signaling BVC */
- hash_for_each(cfg->bss_nses, i, bss_nse, list) {
- struct gbproxy_bvc *bss_bvc = gbproxy_bvc_by_bvci(bss_nse, 0);
- if (!bss_bvc) {
- LOGPNSE(bss_nse, LOGL_ERROR, "Doesn't have BVC with BVCI=0 ?!?\n");
- continue;
- }
- osmo_fsm_inst_dispatch(bss_bvc->fi, BSSGP_BVCFSM_E_REQ_RESET, &cause);
- }
-}
-
-const struct bssgp_bvc_fsm_ops sgsn_sig_bvc_fsm_ops = {
- .reset_notification = sgsn_sig_bvc_reset_notif,
-};
-
-/***********************************************************************
- * Signaling BVC handling
- ***********************************************************************/
-
-/* process a BVC-RESET message from the BSS side */
-static int rx_bvc_reset_from_bss(struct gbproxy_nse *nse, struct msgb *msg, struct tlv_parsed *tp)
-{
- struct gbproxy_bvc *from_bvc = NULL;
- uint16_t bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI));
- uint32_t features = 0; // FIXME: make configurable
-
- LOGPNSE(nse, LOGL_INFO, "Rx BVC-RESET (BVCI=%05u)\n", bvci);
-
- if (bvci == 0) {
- /* If we receive a BVC reset on the signalling endpoint, we
- * don't want the SGSN to reset, as the signalling endpoint
- * is common for all point-to-point BVCs (and thus all BTS) */
-
- from_bvc = gbproxy_bvc_by_bvci(nse, 0);
- if (!from_bvc) {
- from_bvc = gbproxy_bvc_alloc(nse, 0);
- OSMO_ASSERT(from_bvc);
- from_bvc->fi = bssgp_bvc_fsm_alloc_sig_sgsn(from_bvc, nse->cfg->nsi, nse->nsei, features);
- if (!from_bvc->fi) {
- LOGPNSE(nse, LOGL_ERROR, "Cannot allocate SIG-BVC FSM\n");
- gbproxy_bvc_free(from_bvc);
- return -ENOMEM;
- }
- bssgp_bvc_fsm_set_ops(from_bvc->fi, &bss_sig_bvc_fsm_ops, from_bvc);
- }
- } else {
- from_bvc = gbproxy_bvc_by_bvci(nse, bvci);
- if (!from_bvc) {
- /* if a PTP-BVC is reset, and we don't know that
- * PTP-BVCI yet, we should allocate a new bvc */
- from_bvc = gbproxy_bvc_alloc(nse, bvci);
- OSMO_ASSERT(from_bvc);
- from_bvc->fi = bssgp_bvc_fsm_alloc_ptp_sgsn(from_bvc, nse->cfg->nsi,
- nse->nsei, bvci);
- if (!from_bvc->fi) {
- LOGPNSE(nse, LOGL_ERROR, "Cannot allocate SIG-BVC FSM\n");
- gbproxy_bvc_free(from_bvc);
- return -ENOMEM;
- }
- bssgp_bvc_fsm_set_ops(from_bvc->fi, &bss_ptp_bvc_fsm_ops, from_bvc);
- }
-#if 0
- /* Could have moved to a different NSE */
- if (!check_bvc_nsei(from_bvc, nsei)) {
- LOGPBVC(from_bvc, LOGL_NOTICE, "moving bvc to NSE(%05u)\n", nsei);
-
- struct gbproxy_nse *nse_new = gbproxy_nse_by_nsei(cfg, nsei, false);
- if (!nse_new) {
- LOGP(DGPRS, LOGL_NOTICE, "NSE(%05u) Got PtP BVC reset before signalling reset for "
- "BVCI=%05u\n", bvci, nsei);
- bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_STATE, NULL, msg);
- return 0;
- }
-
- /* Move bvc to different NSE */
- gbproxy_bvc_move(from_bvc, nse_new);
- }
-#endif
- /* FIXME: do we need this, if it happens within FSM? */
- if (TLVP_PRES_LEN(tp, BSSGP_IE_CELL_ID, 8)) {
- struct gprs_ra_id raid;
- /* We have a Cell Identifier present in this
- * PDU, this means we can extend our local
- * state information about this particular cell
- * */
- memcpy(from_bvc->ra, TLVP_VAL(tp, BSSGP_IE_CELL_ID), sizeof(from_bvc->ra));
- gsm48_parse_ra(&raid, from_bvc->ra);
- LOGPBVC(from_bvc, LOGL_INFO, "Cell ID %s\n", osmo_rai_name(&raid));
- }
- }
- /* hand into FSM for further processing */
- osmo_fsm_inst_dispatch(from_bvc->fi, BSSGP_BVCFSM_E_RX_RESET, msg);
- return 0;
-}
-
-/* Receive an incoming signalling message from a BSS-side NS-VC */
-static int gbprox_rx_sig_from_bss(struct gbproxy_nse *nse, struct msgb *msg, uint16_t ns_bvci)
-{
- struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
- uint8_t pdu_type = bgph->pdu_type;
- const char *pdut_name = osmo_tlv_prot_msg_name(&osmo_pdef_bssgp, bgph->pdu_type);
- struct tlv_parsed tp;
- int data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
- struct gbproxy_bvc *from_bvc = NULL;
- char log_pfx[32];
- uint16_t ptp_bvci;
- uint32_t tlli;
- int rc;
-
- snprintf(log_pfx, sizeof(log_pfx), "NSE(%05u/BSS)-BVC(%05u/??)", nse->nsei, ns_bvci);
-
- LOGP(DGPRS, LOGL_DEBUG, "%s Rx %s\n", log_pfx, pdut_name);
-
- if (ns_bvci != 0 && ns_bvci != 1) {
- LOGP(DGPRS, LOGL_NOTICE, "%s %s BVCI=%05u is not signalling\n", log_pfx, pdut_name, ns_bvci);
- return bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
- }
-
- if (!(bssgp_pdu_type_flags(pdu_type) & BSSGP_PDUF_SIG)) {
- LOGP(DGPRS, LOGL_NOTICE, "%s %s not allowed in signalling BVC\n", log_pfx, pdut_name);
- return bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
- }
-
- if (!(bssgp_pdu_type_flags(pdu_type) & BSSGP_PDUF_UL)) {
- LOGP(DGPRS, LOGL_NOTICE, "%s %s not allowed in uplink direction\n", log_pfx, pdut_name);
- return bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
- }
-
- rc = osmo_tlv_prot_parse(&osmo_pdef_bssgp, &tp, 1, pdu_type, bgph->data, data_len, 0, 0,
- DGPRS, log_pfx);
- if (rc < 0) {
- rate_ctr_inc(&nse->cfg->ctrg->ctr[GBPROX_GLOB_CTR_PROTO_ERR_BSS]);
- return tx_status_from_tlvp(rc, msg);
- }
- /* hack to get both msg + tlv_parsed passed via osmo_fsm_inst_dispatch */
- msgb_bcid(msg) = (void *)&tp;
-
- /* special case handling for some PDU types */
- switch (pdu_type) {
- case BSSGP_PDUT_BVC_RESET:
- /* resolve or create gbproxy_bvc + handlei n BVC-FSM */
- return rx_bvc_reset_from_bss(nse, msg, &tp);
- case BSSGP_PDUT_BVC_RESET_ACK:
- ptp_bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI));
- from_bvc = gbproxy_bvc_by_bvci(nse, ptp_bvci);
- if (!from_bvc)
- goto err_no_bvc;
- return osmo_fsm_inst_dispatch(from_bvc->fi, BSSGP_BVCFSM_E_RX_RESET_ACK, msg);
- case BSSGP_PDUT_BVC_BLOCK:
- ptp_bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI));
- from_bvc = gbproxy_bvc_by_bvci(nse, ptp_bvci);
- if (!from_bvc)
- goto err_no_bvc;
- return osmo_fsm_inst_dispatch(from_bvc->fi, BSSGP_BVCFSM_E_RX_BLOCK, msg);
- case BSSGP_PDUT_BVC_UNBLOCK:
- ptp_bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI));
- from_bvc = gbproxy_bvc_by_bvci(nse, ptp_bvci);
- if (!from_bvc)
- goto err_no_bvc;
- return osmo_fsm_inst_dispatch(from_bvc->fi, BSSGP_BVCFSM_E_RX_UNBLOCK, msg);
- case BSSGP_PDUT_SUSPEND:
- case BSSGP_PDUT_RESUME:
- {
- struct gbproxy_sgsn *sgsn;
-
- tlli = osmo_load32be(TLVP_VAL(&tp, BSSGP_IE_TLLI));
- sgsn = gbproxy_select_sgsn(nse->cfg, &tlli);
- if (!sgsn) {
- LOGP(DGPRS, LOGL_ERROR, "Could not find any SGSN for TLLI, dropping message!\n");
- rc = -EINVAL;
- break;
- }
-
- gbproxy_tlli_cache_update(nse, tlli);
-
- rc = gbprox_relay2nse(msg, sgsn->nse, 0);
-#if 0
- /* TODO: Validate the RAI for consistency with the RAI
- * we expect for any of the BVC within this BSS side NSE */
- memcpy(ra, TLVP_VAL(&tp, BSSGP_IE_ROUTEING_AREA), sizeof(from_bvc->ra));
- gsm48_parse_ra(&raid, from_bvc->ra);
-#endif
- break;
- }
- case BSSGP_PDUT_STATUS:
- /* FIXME: inspect the erroneous PDU IE (if any) and check
- * if we can extract a TLLI/RNI to route it to the correct SGSN */
- break;
- case BSSGP_PDUT_RAN_INFO:
- case BSSGP_PDUT_RAN_INFO_REQ:
- case BSSGP_PDUT_RAN_INFO_ACK:
- case BSSGP_PDUT_RAN_INFO_ERROR:
- case BSSGP_PDUT_RAN_INFO_APP_ERROR:
- /* FIXME: route based in RIM Routing IE */
- rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
- break;
- case BSSGP_PDUT_LLC_DISCARD:
- case BSSGP_PDUT_FLUSH_LL_ACK:
- /* route based on BVCI + TLLI */
- ptp_bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI));
- tlli = osmo_load32be(TLVP_VAL(&tp, BSSGP_IE_TLLI));
- from_bvc = gbproxy_bvc_by_bvci(nse, ptp_bvci);
- if (!from_bvc)
- goto err_no_bvc;
- gbprox_bss2sgsn_tlli(from_bvc->cell, msg, &tlli, true);
- break;
- case BSSGP_PDUT_PAGING_PS_REJECT:
- case BSSGP_PDUT_DUMMY_PAGING_PS_RESP:
- {
- /* Route according to IMSI<->NSE cache entry */
- struct osmo_mobile_identity mi;
- const uint8_t *mi_data = TLVP_VAL(&tp, BSSGP_IE_IMSI);
- uint8_t mi_len = TLVP_LEN(&tp, BSSGP_IE_IMSI);
- osmo_mobile_identity_decode(&mi, mi_data, mi_len, false);
- nse = gbproxy_nse_by_imsi(nse->cfg, mi.imsi);
- if (!nse) {
- return bssgp_tx_status(BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
- }
- OSMO_ASSERT(nse->sgsn_facing);
- rc = gbprox_relay2nse(msg, nse, 0);
- break;
- }
- default:
- LOGPNSE(nse, LOGL_ERROR, "Rx %s: Implementation missing\n", pdut_name);
- break;
- }
-
- return rc;
-err_no_bvc:
- LOGPNSE(nse, LOGL_ERROR, "Rx %s: cannot find BVC for BVCI=%05u\n", pdut_name, ptp_bvci);
- rate_ctr_inc(&nse->cfg->ctrg->ctr[GBPROX_GLOB_CTR_INV_NSEI]);
- return bssgp_tx_status(BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
-}
-
-/* Receive paging request from SGSN, we need to relay to proper BSS */
-static int gbprox_rx_paging(struct gbproxy_nse *sgsn_nse, struct msgb *msg, const char *pdut_name,
- struct tlv_parsed *tp, uint16_t ns_bvci, bool broadcast)
-{
- struct gbproxy_config *cfg = sgsn_nse->cfg;
- struct gbproxy_bvc *sgsn_bvc, *bss_bvc;
- struct gbproxy_nse *nse;
- unsigned int n_nses = 0;
- int errctr = GBPROX_GLOB_CTR_PROTO_ERR_SGSN;
- int i, j;
-
- /* FIXME: Handle paging logic to only page each matching NSE */
-
- if (TLVP_PRES_LEN(tp, BSSGP_IE_BVCI, 2)) {
- uint16_t bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI));
- errctr = GBPROX_GLOB_CTR_OTHER_ERR;
- sgsn_bvc = gbproxy_bvc_by_bvci(sgsn_nse, bvci);
- if (!sgsn_bvc) {
- LOGPNSE(sgsn_nse, LOGL_NOTICE, "Rx %s: unable to route: BVCI=%05u unknown\n",
- pdut_name, bvci);
- rate_ctr_inc(&cfg->ctrg->ctr[errctr]);
- return -EINVAL;
- }
- LOGPBVC(sgsn_bvc, LOGL_INFO, "Rx %s: routing by BVCI\n", pdut_name);
- return gbprox_relay2peer(msg, sgsn_bvc->cell->bss_bvc, ns_bvci);
- } else if (TLVP_PRES_LEN(tp, BSSGP_IE_ROUTEING_AREA, 6)) {
- errctr = GBPROX_GLOB_CTR_INV_RAI;
- /* iterate over all bvcs and dispatch the paging to each matching one */
- hash_for_each(cfg->bss_nses, i, nse, list) {
- hash_for_each(nse->bvcs, j, bss_bvc, list) {
- if (!memcmp(bss_bvc->ra, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA), 6)) {
- LOGPNSE(nse, LOGL_INFO, "Rx %s: routing to NSE (RAI match)\n",
- pdut_name);
- gbprox_relay2peer(msg, bss_bvc, ns_bvci);
- n_nses++;
- /* Only send it once to each NSE */
- break;
- }
- }
- }
- } else if (TLVP_PRES_LEN(tp, BSSGP_IE_LOCATION_AREA, 5)) {
- errctr = GBPROX_GLOB_CTR_INV_LAI;
- /* iterate over all bvcs and dispatch the paging to each matching one */
- hash_for_each(cfg->bss_nses, i, nse, list) {
- hash_for_each(nse->bvcs, j, bss_bvc, list) {
- if (!memcmp(bss_bvc->ra, TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA), 5)) {
- LOGPNSE(nse, LOGL_INFO, "Rx %s: routing to NSE (LAI match)\n",
- pdut_name);
- gbprox_relay2peer(msg, bss_bvc, ns_bvci);
- n_nses++;
- /* Only send it once to each NSE */
- break;
- }
- }
- }
- } else if (TLVP_PRES_LEN(tp, BSSGP_IE_BSS_AREA_ID, 1) || broadcast) {
- /* iterate over all bvcs and dispatch the paging to each matching one */
- hash_for_each(cfg->bss_nses, i, nse, list) {
- hash_for_each(nse->bvcs, j, bss_bvc, list) {
- LOGPNSE(nse, LOGL_INFO, "Rx %s:routing to NSE (broadcast)\n", pdut_name);
- gbprox_relay2peer(msg, bss_bvc, ns_bvci);
- n_nses++;
- /* Only send it once to each NSE */
- break;
- }
- }
- } else {
- LOGPNSE(sgsn_nse, LOGL_ERROR, "BSSGP PAGING: unable to route, missing IE\n");
- rate_ctr_inc(&cfg->ctrg->ctr[errctr]);
- }
-
- if (n_nses == 0) {
- LOGPNSE(sgsn_nse, LOGL_ERROR, "BSSGP PAGING: unable to route, no destination found\n");
- rate_ctr_inc(&cfg->ctrg->ctr[errctr]);
- return -EINVAL;
- }
- return 0;
-}
-
-/* Receive an incoming BVC-RESET message from the SGSN */
-static int rx_bvc_reset_from_sgsn(struct gbproxy_nse *nse, struct msgb *msg, struct tlv_parsed *tp,
- uint16_t ns_bvci)
-{
- uint16_t ptp_bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI));
- struct gbproxy_bvc *from_bvc;
-
- LOGPNSE(nse, LOGL_INFO, "Rx BVC-RESET (BVCI=%05u)\n", ptp_bvci);
-
- if (ptp_bvci == 0) {
- from_bvc = gbproxy_bvc_by_bvci(nse, 0);
- OSMO_ASSERT(from_bvc);
- osmo_fsm_inst_dispatch(from_bvc->fi, BSSGP_BVCFSM_E_RX_RESET, msg);
- } else {
- from_bvc = gbproxy_bvc_by_bvci(nse, ptp_bvci);
- if (!from_bvc) {
- LOGPNSE(nse, LOGL_ERROR, "Rx BVC-RESET BVCI=%05u: Cannot find BVC\n", ptp_bvci);
- rate_ctr_inc(&nse->cfg->ctrg->ctr[GBPROX_GLOB_CTR_INV_BVCI]);
- return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &ptp_bvci, msg);
- }
- osmo_fsm_inst_dispatch(from_bvc->fi, BSSGP_BVCFSM_E_RX_RESET, msg);
- }
-
- return 0;
-}
-
-/* Receive an incoming signalling message from the SGSN-side NS-VC */
-static int gbprox_rx_sig_from_sgsn(struct gbproxy_nse *nse, struct msgb *msg, uint16_t ns_bvci)
-{
- struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
- uint8_t pdu_type = bgph->pdu_type;
- const char *pdut_name = osmo_tlv_prot_msg_name(&osmo_pdef_bssgp, bgph->pdu_type);
- struct gbproxy_config *cfg = nse->cfg;
- struct gbproxy_bvc *sgsn_bvc;
- struct tlv_parsed tp;
- int data_len;
- uint16_t bvci;
- char log_pfx[32];
- int rc = 0;
- int cause;
- int i;
- bool paging_bc = false;
-
- snprintf(log_pfx, sizeof(log_pfx), "NSE(%05u/SGSN)-BVC(%05u/??)", nse->nsei, ns_bvci);
-
- LOGP(DGPRS, LOGL_DEBUG, "%s Rx %s\n", log_pfx, pdut_name);
-
- if (ns_bvci != 0 && ns_bvci != 1) {
- LOGP(DGPRS, LOGL_NOTICE, "%s BVCI=%05u is not signalling\n", log_pfx, ns_bvci);
- return bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
- }
-
- if (!(bssgp_pdu_type_flags(pdu_type) & BSSGP_PDUF_SIG)) {
- LOGP(DGPRS, LOGL_NOTICE, "%s %s not allowed in signalling BVC\n", log_pfx, pdut_name);
- return bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
- }
-
- if (!(bssgp_pdu_type_flags(pdu_type) & BSSGP_PDUF_DL)) {
- LOGP(DGPRS, LOGL_NOTICE, "%s %s not allowed in downlink direction\n", log_pfx, pdut_name);
- return bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
- }
-
- data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
-
- rc = osmo_tlv_prot_parse(&osmo_pdef_bssgp, &tp, 1, pdu_type, bgph->data, data_len, 0, 0,
- DGPRS, log_pfx);
- if (rc < 0) {
- rc = tx_status_from_tlvp(rc, msg);
- rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_PROTO_ERR_SGSN]);
- return rc;
- }
- /* hack to get both msg + tlv_parsed passed via osmo_fsm_inst_dispatch */
- msgb_bcid(msg) = (void *)&tp;
-
- switch (pdu_type) {
- case BSSGP_PDUT_BVC_RESET:
- /* resolve or create ggbproxy_bvc + handle in BVC-FSM */
- bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI));
- rc = rx_bvc_reset_from_sgsn(nse, msg, &tp, ns_bvci);
- break;
- case BSSGP_PDUT_BVC_RESET_ACK:
- bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI));
- sgsn_bvc = gbproxy_bvc_by_bvci(nse, bvci);
- if (!sgsn_bvc)
- goto err_no_bvc;
- rc = osmo_fsm_inst_dispatch(sgsn_bvc->fi, BSSGP_BVCFSM_E_RX_RESET_ACK, msg);
- break;
- case BSSGP_PDUT_BVC_BLOCK_ACK:
- bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI));
- sgsn_bvc = gbproxy_bvc_by_bvci(nse, bvci);
- if (!sgsn_bvc)
- goto err_no_bvc;
- rc = osmo_fsm_inst_dispatch(sgsn_bvc->fi, BSSGP_BVCFSM_E_RX_BLOCK_ACK, msg);
- break;
- case BSSGP_PDUT_BVC_UNBLOCK_ACK:
- bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI));
- sgsn_bvc = gbproxy_bvc_by_bvci(nse, bvci);
- if (!sgsn_bvc)
- goto err_no_bvc;
- rc = osmo_fsm_inst_dispatch(sgsn_bvc->fi, BSSGP_BVCFSM_E_RX_UNBLOCK_ACK, msg);
- break;
- case BSSGP_PDUT_FLUSH_LL:
- /* simple case: BVCI IE is mandatory */
- bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI));
- sgsn_bvc = gbproxy_bvc_by_bvci(nse, bvci);
- if (!sgsn_bvc)
- goto err_no_bvc;
- if (sgsn_bvc->cell && sgsn_bvc->cell->bss_bvc)
- rc = gbprox_relay2peer(msg, sgsn_bvc->cell->bss_bvc, ns_bvci);
- break;
- case BSSGP_PDUT_DUMMY_PAGING_PS:
- /* Routing area is optional in dummy paging and we have nothing else to go by
- * so in case it is missing we need to broadcast the paging */
- paging_bc = true;
- /* fall through */
- case BSSGP_PDUT_PAGING_PS:
- {
- /* Cache the IMSI<->NSE to route PAGING REJECT */
- struct osmo_mobile_identity mi;
- const uint8_t *mi_data = TLVP_VAL(&tp, BSSGP_IE_IMSI);
- uint8_t mi_len = TLVP_LEN(&tp, BSSGP_IE_IMSI);
- osmo_mobile_identity_decode(&mi, mi_data, mi_len, false);
- gbproxy_imsi_cache_update(nse, mi.imsi);
- /* fall through */
- }
- case BSSGP_PDUT_PAGING_CS:
- /* process the paging request (LAI/RAI lookup) */
- rc = gbprox_rx_paging(nse, msg, pdut_name, &tp, ns_bvci, paging_bc);
- break;
- case BSSGP_PDUT_STATUS:
- /* Some exception has occurred */
- cause = *TLVP_VAL(&tp, BSSGP_IE_CAUSE);
- LOGPNSE(nse, LOGL_NOTICE, "Rx STATUS cause=0x%02x(%s) ", cause,
- bssgp_cause_str(cause));
- if (TLVP_PRES_LEN(&tp, BSSGP_IE_BVCI, 2)) {
- bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI));
- LOGPC(DGPRS, LOGL_NOTICE, "BVCI=%05u\n", bvci);
- sgsn_bvc = gbproxy_bvc_by_bvci(nse, bvci);
- /* don't send STATUS in response to STATUS if !bvc */
- if (sgsn_bvc && sgsn_bvc->cell && sgsn_bvc->cell->bss_bvc)
- rc = gbprox_relay2peer(msg, sgsn_bvc->cell->bss_bvc, ns_bvci);
- } else
- LOGPC(DGPRS, LOGL_NOTICE, "\n");
- break;
- /* those only exist in the SGSN -> BSS direction */
- case BSSGP_PDUT_SUSPEND_ACK:
- case BSSGP_PDUT_SUSPEND_NACK:
- case BSSGP_PDUT_RESUME_ACK:
- case BSSGP_PDUT_RESUME_NACK:
- {
- struct gbproxy_nse *nse_peer;
- uint32_t tlli = osmo_load32be(TLVP_VAL(&tp, BSSGP_IE_TLLI));
-
- nse_peer = gbproxy_nse_by_tlli(cfg, tlli);
- if (!nse_peer) {
- LOGPNSE(nse, LOGL_ERROR, "Rx %s: Cannot find NSE\n", pdut_name);
- /* TODO: Counter */
- return bssgp_tx_status(BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
- }
- /* Delete the entry after we're done */
- gbproxy_tlli_cache_remove(cfg, tlli);
- LOGPNSE(nse_peer, LOGL_DEBUG, "Rx %s: forwarding\n", pdut_name);
- gbprox_relay2nse(msg, nse_peer, ns_bvci);
- break;
- }
- case BSSGP_PDUT_SGSN_INVOKE_TRACE:
- case BSSGP_PDUT_OVERLOAD:
- LOGPNSE(nse, LOGL_DEBUG, "Rx %s: broadcasting\n", pdut_name);
- /* broadcast to all BSS-side bvcs */
- hash_for_each(cfg->bss_nses, i, nse, list) {
- gbprox_relay2nse(msg, nse, 0);
- }
- break;
- case BSSGP_PDUT_RAN_INFO:
- case BSSGP_PDUT_RAN_INFO_REQ:
- case BSSGP_PDUT_RAN_INFO_ACK:
- case BSSGP_PDUT_RAN_INFO_ERROR:
- case BSSGP_PDUT_RAN_INFO_APP_ERROR:
- /* FIXME: route based in RIM Routing IE */
- rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
- break;
- default:
- LOGPNSE(nse, LOGL_NOTICE, "Rx %s: Not supported\n", pdut_name);
- rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_PROTO_ERR_SGSN]);
- rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
- break;
- }
-
- return rc;
-
-err_no_bvc:
- LOGPNSE(nse, LOGL_ERROR, "Rx %s: Cannot find BVC\n", pdut_name);
- rate_ctr_inc(&cfg->ctrg-> ctr[GBPROX_GLOB_CTR_INV_RAI]);
- return bssgp_tx_status(BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
-}
-
-
-/***********************************************************************
- * libosmogb NS/BSSGP integration
- ***********************************************************************/
-
-int gbprox_bssgp_send_cb(void *ctx, struct msgb *msg)
-{
- int rc;
- struct gbproxy_config *cfg = (struct gbproxy_config *) ctx;
- struct gprs_ns2_inst *nsi = cfg->nsi;
- struct osmo_gprs_ns2_prim nsp = {};
-
- nsp.bvci = msgb_bvci(msg);
- nsp.nsei = msgb_nsei(msg);
-
- osmo_prim_init(&nsp.oph, SAP_NS, GPRS_NS2_PRIM_UNIT_DATA, PRIM_OP_REQUEST, msg);
- rc = gprs_ns2_recv_prim(nsi, &nsp.oph);
-
- return rc;
-}
-
-/* Main input function for Gb proxy */
-int gbprox_rcvmsg(void *ctx, struct msgb *msg)
-{
- struct gbproxy_config *cfg = (struct gbproxy_config *) ctx;
- uint16_t ns_bvci = msgb_bvci(msg);
- uint16_t nsei = msgb_nsei(msg);
- struct gbproxy_nse *nse;
-
- /* ensure minimum length to decode PCU type */
- if (msgb_bssgp_len(msg) < sizeof(struct bssgp_normal_hdr))
- return bssgp_tx_status(BSSGP_CAUSE_SEM_INCORR_PDU, NULL, msg);
-
- nse = gbproxy_nse_by_nsei(cfg, nsei, NSE_F_SGSN);
- if (nse) {
- if (ns_bvci == 0 || ns_bvci == 1)
- return gbprox_rx_sig_from_sgsn(nse, msg, ns_bvci);
- else
- return gbprox_rx_ptp_from_sgsn(nse, msg, ns_bvci);
- }
-
- nse = gbproxy_nse_by_nsei(cfg, nsei, NSE_F_BSS);
- if (!nse) {
- LOGP(DGPRS, LOGL_NOTICE, "NSE(%05u/BSS) not known -> allocating\n", nsei);
- nse = gbproxy_nse_alloc(cfg, nsei, false);
- }
- if (nse) {
- if (ns_bvci == 0 || ns_bvci == 1)
- return gbprox_rx_sig_from_bss(nse, msg, ns_bvci);
- else
- return gbprox_rx_ptp_from_bss(nse, msg, ns_bvci);
- }
-
- return 0;
-}
-
-/* TODO: What about handling:
- * GPRS_NS2_AFF_CAUSE_VC_FAILURE,
- GPRS_NS2_AFF_CAUSE_VC_RECOVERY,
- GPRS_NS2_AFF_CAUSE_FAILURE,
- GPRS_NS2_AFF_CAUSE_RECOVERY,
- osmocom own causes
- GPRS_NS2_AFF_CAUSE_SNS_CONFIGURED,
- GPRS_NS2_AFF_CAUSE_SNS_FAILURE,
- */
-
-void gprs_ns_prim_status_cb(struct gbproxy_config *cfg, struct osmo_gprs_ns2_prim *nsp)
-{
- /* TODO: bss nsei available/unavailable bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK, nsvc->nsei, bvc->bvci, 0);
- * TODO: sgsn nsei available/unavailable
- */
-
- struct gbproxy_bvc *bvc;
- struct gbproxy_nse *sgsn_nse;
-
- switch (nsp->u.status.cause) {
- case GPRS_NS2_AFF_CAUSE_SNS_FAILURE:
- case GPRS_NS2_AFF_CAUSE_SNS_CONFIGURED:
- break;
-
- case GPRS_NS2_AFF_CAUSE_RECOVERY:
- LOGP(DGPRS, LOGL_NOTICE, "NS-NSE %d became available\n", nsp->nsei);
- sgsn_nse = gbproxy_nse_by_nsei(cfg, nsp->nsei, NSE_F_SGSN);
- if (sgsn_nse) {
- uint8_t cause = BSSGP_CAUSE_OML_INTERV;
- bvc = gbproxy_bvc_by_bvci(sgsn_nse, 0);
- if (bvc)
- osmo_fsm_inst_dispatch(bvc->fi, BSSGP_BVCFSM_E_REQ_RESET, &cause);
- }
- break;
- case GPRS_NS2_AFF_CAUSE_FAILURE:
-#if 0
- if (gbproxy_is_sgsn_nsei(cfg, nsp->nsei)) {
- /* sgsn */
- /* TODO: BSVC: block all PtP towards bss */
- rate_ctr_inc(&cfg->ctrg->
- ctr[GBPROX_GLOB_CTR_RESTART_RESET_SGSN]);
- } else {
- /* bss became unavailable
- * TODO: Block all BVC belonging to that NSE */
- bvc = gbproxy_bvc_by_nsei(cfg, nsp->nsei);
- if (!bvc) {
- /* TODO: use primitive name + status cause name */
- LOGP(DGPRS, LOGL_NOTICE, "Received ns2 primitive %d for unknown bvc NSEI=%u\n",
- nsp->u.status.cause, nsp->nsei);
- break;
- }
-
- if (!bvc->blocked)
- break;
- hash_for_each(cfg->sgsn_nses, _sgsn, sgsn_nse, list) {
- bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK, sgsn_nse->nsei, bvc->bvci, 0);
- }
- }
-#endif
- LOGP(DGPRS, LOGL_NOTICE, "NS-NSE %d became unavailable\n", nsp->nsei);
- break;
- default:
- LOGP(DGPRS, LOGL_NOTICE, "NS: Unknown NS-STATUS.ind cause=%s from NS\n",
- gprs_ns2_aff_cause_prim_str(nsp->u.status.cause));
- break;
- }
-}
-
-/* called by the ns layer */
-int gprs_ns2_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
-{
- struct osmo_gprs_ns2_prim *nsp;
- struct gbproxy_config *cfg = (struct gbproxy_config *) ctx;
- uintptr_t bvci;
- int rc = 0;
-
- if (oph->sap != SAP_NS)
- return 0;
-
- nsp = container_of(oph, struct osmo_gprs_ns2_prim, oph);
-
- if (oph->operation != PRIM_OP_INDICATION) {
- LOGP(DGPRS, LOGL_NOTICE, "NS: Unexpected primitive operation %s from NS\n",
- get_value_string(osmo_prim_op_names, oph->operation));
- return 0;
- }
-
- switch (oph->primitive) {
- case GPRS_NS2_PRIM_UNIT_DATA:
-
- /* hand the message into the BSSGP implementation */
- msgb_bssgph(oph->msg) = oph->msg->l3h;
- msgb_bvci(oph->msg) = nsp->bvci;
- msgb_nsei(oph->msg) = nsp->nsei;
- bvci = nsp->bvci | BVC_LOG_CTX_FLAG;
-
- log_set_context(LOG_CTX_GB_BVC, (void *)bvci);
- rc = gbprox_rcvmsg(cfg, oph->msg);
- msgb_free(oph->msg);
- break;
- case GPRS_NS2_PRIM_STATUS:
- gprs_ns_prim_status_cb(cfg, nsp);
- break;
- default:
- LOGP(DGPRS, LOGL_NOTICE, "NS: Unknown prim %s %s from NS\n",
- gprs_ns2_prim_str(oph->primitive),
- get_value_string(osmo_prim_op_names, oph->operation));
- break;
- }
-
- return rc;
-}
-
-void gbprox_reset(struct gbproxy_config *cfg)
-{
- struct gbproxy_nse *nse;
- struct hlist_node *ntmp;
- int i, j;
-
- hash_for_each_safe(cfg->bss_nses, i, ntmp, nse, list) {
- struct gbproxy_bvc *bvc;
- struct hlist_node *tmp;
- hash_for_each_safe(nse->bvcs, j, tmp, bvc, list)
- gbproxy_bvc_free(bvc);
-
- gbproxy_nse_free(nse);
- }
- /* FIXME: cells */
- /* FIXME: SGSN side BVCs (except signaling) */
-
- rate_ctr_group_free(cfg->ctrg);
- gbproxy_init_config(cfg);
-}
-
-static void tlli_cache_cleanup(void *data)
-{
- struct gbproxy_config *cfg = data;
- gbproxy_tlli_cache_cleanup(cfg);
-
- /* TODO: Disable timer when cache is empty */
- osmo_timer_schedule(&cfg->tlli_cache.timer, 2, 0);
-}
-
-static void imsi_cache_cleanup(void *data)
-{
- struct gbproxy_config *cfg = data;
- gbproxy_imsi_cache_cleanup(cfg);
-
- /* TODO: Disable timer when cache is empty */
- osmo_timer_schedule(&cfg->imsi_cache.timer, 2, 0);
-}
-
-int gbproxy_init_config(struct gbproxy_config *cfg)
-{
- struct timespec tp;
-
- /* by default we advertise 100% of the BSS-side capacity to _each_ SGSN */
- cfg->pool.bvc_fc_ratio = 100;
- cfg->pool.null_nri_ranges = osmo_nri_ranges_alloc(cfg);
- /* TODO: Make configurable */
- cfg->tlli_cache.timeout = 10;
- cfg->imsi_cache.timeout = 10;
-
- hash_init(cfg->bss_nses);
- hash_init(cfg->sgsn_nses);
- hash_init(cfg->cells);
- hash_init(cfg->tlli_cache.entries);
- INIT_LLIST_HEAD(&cfg->sgsns);
-
- osmo_timer_setup(&cfg->tlli_cache.timer, tlli_cache_cleanup, cfg);
- osmo_timer_schedule(&cfg->tlli_cache.timer, 2, 0);
-
- /* We could also combine both timers */
- osmo_timer_setup(&cfg->imsi_cache.timer, imsi_cache_cleanup, cfg);
- osmo_timer_schedule(&cfg->imsi_cache.timer, 2, 0);
-
- cfg->ctrg = rate_ctr_group_alloc(tall_sgsn_ctx, &global_ctrg_desc, 0);
- if (!cfg->ctrg) {
- LOGP(DGPRS, LOGL_ERROR, "Cannot allocate global counter group!\n");
- return -1;
- }
- osmo_clock_gettime(CLOCK_REALTIME, &tp);
- osmo_fsm_log_timeouts(true);
-
- return 0;
-} \ No newline at end of file
diff --git a/src/gbproxy/gb_proxy_ctrl.c b/src/gbproxy/gb_proxy_ctrl.c
deleted file mode 100644
index 45616346..00000000
--- a/src/gbproxy/gb_proxy_ctrl.c
+++ /dev/null
@@ -1,137 +0,0 @@
-/* Control Interface Implementation for the Gb-proxy */
-/*
- * (C) 2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
- * All Rights Reserved
- *
- * Author: Daniel Willmann
- *
- * 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/gprs/gprs_bssgp.h>
-#include <osmocom/gprs/gprs_ns.h>
-
-#include <osmocom/ctrl/control_if.h>
-#include <osmocom/ctrl/control_cmd.h>
-#include <osmocom/sgsn/gb_proxy.h>
-#include <osmocom/sgsn/debug.h>
-
-extern vector ctrl_node_vec;
-
-struct nsvc_cb_data {
- struct ctrl_cmd *cmd;
- uint16_t nsei;
- bool is_sgsn;
-};
-
-static int ctrl_nsvc_state_cb(struct gprs_ns2_vc *nsvc, void *ctx) {
- struct nsvc_cb_data *data = (struct nsvc_cb_data *)ctx;
- struct ctrl_cmd *cmd = (struct ctrl_cmd *)data->cmd;
-
- cmd->reply = talloc_asprintf_append(cmd->reply, "%u,%s,%s,%s\n",
- data->nsei, gprs_ns2_ll_str(nsvc), gprs_ns2_nsvc_state_name(nsvc),
- data->is_sgsn ? "SGSN" : "BSS" );
-
- return 0;
-}
-
-static int get_nsvc_state(struct ctrl_cmd *cmd, void *data)
-{
- struct gbproxy_config *cfg = data;
- struct gprs_ns2_inst *nsi = cfg->nsi;
- struct gprs_ns2_nse *nse;
- struct gbproxy_nse *nse_peer;
- int i;
-
- cmd->reply = talloc_strdup(cmd, "");
-
- /* NS-VCs for SGSN */
- hash_for_each(cfg->sgsn_nses, i, nse_peer, list) {
- nse = gprs_ns2_nse_by_nsei(nsi, nse_peer->nsei);
- if (nse)
- gprs_ns2_nse_foreach_nsvc(nse, &ctrl_nsvc_state_cb, cmd);
- }
-
- /* NS-VCs for BSS peers */
- hash_for_each(cfg->bss_nses, i, nse_peer, list) {
- nse = gprs_ns2_nse_by_nsei(nsi, nse_peer->nsei);
- if (nse)
- gprs_ns2_nse_foreach_nsvc(nse, &ctrl_nsvc_state_cb, cmd);
- }
-
- return CTRL_CMD_REPLY;
-}
-
-CTRL_CMD_DEFINE_RO(nsvc_state, "nsvc-state");
-
-static int get_gbproxy_state(struct ctrl_cmd *cmd, void *data)
-{
- struct gbproxy_config *cfg = data;
- struct gbproxy_nse *nse_peer;
- int i, j;
-
- cmd->reply = talloc_strdup(cmd, "");
-
- hash_for_each(cfg->bss_nses, i, nse_peer, list) {
- struct gbproxy_bvc *bvc;
- hash_for_each(nse_peer->bvcs, j, bvc, list) {
- struct gprs_ra_id raid;
- gsm48_parse_ra(&raid, bvc->ra);
-
- cmd->reply = talloc_asprintf_append(cmd->reply, "%u,%u,%u,%u,%u,%u,%s\n",
- nse_peer->nsei, bvc->bvci,
- raid.mcc, raid.mnc,
- raid.lac, raid.rac,
- osmo_fsm_inst_state_name(bvc->fi));
- }
- }
-
- return CTRL_CMD_REPLY;
-}
-
-CTRL_CMD_DEFINE_RO(gbproxy_state, "gbproxy-state");
-
-static int get_num_peers(struct ctrl_cmd *cmd, void *data)
-{
- struct gbproxy_config *cfg = data;
- struct gbproxy_nse *nse_peer;
- struct gbproxy_bvc *bvc;
- uint32_t count = 0;
- int i, j;
-
- hash_for_each(cfg->bss_nses, i, nse_peer, list) {
- hash_for_each(nse_peer->bvcs, j, bvc, list)
- count++;
- }
-
- cmd->reply = talloc_strdup(cmd, "");
- cmd->reply = talloc_asprintf_append(cmd->reply, "%u", count);
-
- return CTRL_CMD_REPLY;
-}
-CTRL_CMD_DEFINE_RO(num_peers, "number-of-peers");
-
-int gb_ctrl_cmds_install(void)
-{
- int rc = 0;
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_nsvc_state);
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_gbproxy_state);
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_num_peers);
-
- return rc;
-}
diff --git a/src/gbproxy/gb_proxy_main.c b/src/gbproxy/gb_proxy_main.c
deleted file mode 100644
index b76e0fbb..00000000
--- a/src/gbproxy/gb_proxy_main.c
+++ /dev/null
@@ -1,336 +0,0 @@
-/* NS-over-IP proxy */
-
-/* (C) 2010 by Harald Welte <laforge@gnumonks.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 <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <getopt.h>
-#include <errno.h>
-#include <signal.h>
-#include <sys/fcntl.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <osmocom/core/application.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/rate_ctr.h>
-#include <osmocom/core/stats.h>
-
-#include <osmocom/gprs/gprs_ns2.h>
-#include <osmocom/gprs/gprs_bssgp.h>
-
-#include <osmocom/sgsn/signal.h>
-#include <osmocom/sgsn/debug.h>
-#include <osmocom/sgsn/vty.h>
-#include <osmocom/sgsn/gb_proxy.h>
-
-#include <osmocom/ctrl/control_vty.h>
-#include <osmocom/ctrl/control_if.h>
-#include <osmocom/ctrl/ports.h>
-
-#include <osmocom/vty/command.h>
-#include <osmocom/vty/telnet_interface.h>
-#include <osmocom/vty/logging.h>
-#include <osmocom/vty/stats.h>
-#include <osmocom/vty/ports.h>
-#include <osmocom/vty/misc.h>
-
-#include "../../bscconfig.h"
-
-#define _GNU_SOURCE
-#include <getopt.h>
-
-void *tall_sgsn_ctx;
-
-const char *openbsc_copyright =
- "Copyright (C) 2010 Harald Welte 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";
-
-#define CONFIG_FILE_DEFAULT "osmo-gbproxy.cfg"
-#define CONFIG_FILE_LEGACY "osmo_gbproxy.cfg"
-
-static char *config_file = NULL;
-struct gbproxy_config *gbcfg;
-static int daemonize = 0;
-
-/* Pointer to the SGSN peer */
-extern struct gbprox_peer *gbprox_peer_sgsn;
-
-static void signal_handler(int signum)
-{
- fprintf(stdout, "signal %u received\n", signum);
-
- switch (signum) {
- case SIGINT:
- case SIGTERM:
- osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL);
- sleep(1);
- exit(0);
- break;
- case SIGABRT:
- /* in case of abort, we want to obtain a talloc report and
- * then run default SIGABRT handler, who will generate coredump
- * and abort the process. abort() should do this for us after we
- * return, but program wouldn't exit if an external SIGABRT is
- * received.
- */
- talloc_report(tall_vty_ctx, stderr);
- talloc_report_full(tall_sgsn_ctx, stderr);
- signal(SIGABRT, SIG_DFL);
- raise(SIGABRT);
- break;
- case SIGUSR1:
- talloc_report(tall_vty_ctx, stderr);
- talloc_report_full(tall_sgsn_ctx, stderr);
- break;
- case SIGUSR2:
- talloc_report_full(tall_vty_ctx, stderr);
- break;
- default:
- break;
- }
-}
-
-static void print_usage()
-{
- printf("Usage: bsc_hack\n");
-}
-
-static void print_help()
-{
- printf(" Some useful help...\n");
- printf(" -h --help this text\n");
- printf(" -d option --debug=DNS:DGPRS,0:0 enable debugging\n");
- printf(" -D --daemonize Fork the process into a background daemon\n");
- printf(" -c --config-file filename The config file to use [%s]\n", CONFIG_FILE_DEFAULT);
- printf(" -s --disable-color\n");
- printf(" -T --timestamp Prefix every log line with a timestamp\n");
- printf(" -V --version. Print the version.\n");
- printf(" -e --log-level number. Set a global loglevel.\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' },
- { "version", 0, 0, 'V' },
- { "log-level", 1, 0, 'e' },
- { 0, 0, 0, 0 }
- };
-
- c = getopt_long(argc, argv, "hd:Dc:sTVe:",
- 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 'e':
- log_set_log_level(osmo_stderr_target, atoi(optarg));
- break;
- case 'V':
- print_version(1);
- exit(0);
- break;
- default:
- break;
- }
- }
-
- if (argc > optind) {
- fprintf(stderr, "Unsupported positional arguments on command line\n");
- exit(2);
- }
-}
-
-static struct vty_app_info vty_info = {
- .name = "OsmoGbProxy",
- .version = PACKAGE_VERSION,
-};
-
-/* default categories */
-static struct log_info_cat gprs_categories[] = {
- [DGPRS] = {
- .name = "DGPRS",
- .description = "GPRS Packet Service",
- .enabled = 1, .loglevel = LOGL_DEBUG,
- },
- [DNS] = {
- .name = "DNS",
- .description = "GPRS Network Service (NS)",
- .enabled = 1, .loglevel = LOGL_INFO,
- },
- [DOBJ] = {
- .name = "DOBJ",
- .description = "GbProxy object allocation/release",
- .enabled = 1,
- .color = "\033[38;5;121m"
- },
-};
-
-static const struct log_info gprs_log_info = {
- .filter_fn = gprs_log_filter_fn,
- .cat = gprs_categories,
- .num_cat = ARRAY_SIZE(gprs_categories),
-};
-
-static bool file_exists(const char *path)
-{
- struct stat sb;
- return stat(path, &sb) ? false : true;
-}
-
-int gbprox_bssgp_send_cb(void *ctx, struct msgb *msg);
-
-int main(int argc, char **argv)
-{
- int rc;
- struct ctrl_handle *ctrl;
-
- tall_sgsn_ctx = talloc_named_const(NULL, 0, "nsip_proxy");
- msgb_talloc_ctx_init(tall_sgsn_ctx, 0);
- vty_info.tall_ctx = tall_sgsn_ctx;
-
- signal(SIGINT, &signal_handler);
- signal(SIGTERM, &signal_handler);
- signal(SIGABRT, &signal_handler);
- signal(SIGUSR1, &signal_handler);
- signal(SIGUSR2, &signal_handler);
- osmo_init_ignore_signals();
-
- osmo_init_logging2(tall_sgsn_ctx, &gprs_log_info);
-
- vty_info.copyright = openbsc_copyright;
- vty_init(&vty_info);
- logging_vty_add_cmds();
- osmo_talloc_vty_add_cmds();
- osmo_stats_vty_add_cmds();
- osmo_fsm_vty_add_cmds();
- gbproxy_vty_init();
-
- handle_options(argc, argv);
-
- /* Backwards compatibility: for years, the default config file name was
- * osmo_gbproxy.cfg. All other Osmocom programs use osmo-*.cfg with a
- * dash. To be able to use the new config file name without breaking
- * previous setups that might rely on the legacy default config file
- * name, we need to look for the old config file if no -c option was
- * passed AND no file exists with the new default file name. */
- if (!config_file) {
- /* No -c option was passed */
- if (file_exists(CONFIG_FILE_LEGACY)
- && !file_exists(CONFIG_FILE_DEFAULT))
- config_file = CONFIG_FILE_LEGACY;
- else
- config_file = CONFIG_FILE_DEFAULT;
- }
-
- rate_ctr_init(tall_sgsn_ctx);
- osmo_stats_init(tall_sgsn_ctx);
-
- gbcfg = talloc_zero(tall_sgsn_ctx, struct gbproxy_config);
- if (!gbcfg) {
- LOGP(DGPRS, LOGL_FATAL, "Unable to allocate config\n");
- exit(1);
- }
- gbproxy_init_config(gbcfg);
- gbcfg->nsi = gprs_ns2_instantiate(tall_sgsn_ctx, gprs_ns2_prim_cb, gbcfg);
- if (!gbcfg->nsi) {
- LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n");
- exit(1);
- }
- gprs_ns2_vty_init(gbcfg->nsi);
- logging_vty_add_deprecated_subsys(tall_sgsn_ctx, "bssgp");
-
- bssgp_set_bssgp_callback(gbprox_bssgp_send_cb, gbcfg);
-
- rc = gbproxy_parse_config(config_file, gbcfg);
- if (rc < 0) {
- LOGP(DGPRS, LOGL_FATAL, "Cannot parse config file '%s'\n", config_file);
- exit(2);
- }
-
- /* start telnet after reading config for vty_get_bind_addr() */
- rc = telnet_init_dynif(tall_sgsn_ctx, NULL,
- vty_get_bind_addr(), OSMO_VTY_PORT_GBPROXY);
- if (rc < 0)
- exit(1);
-
- /* Start control interface after getting config for
- * ctrl_vty_get_bind_addr() */
- ctrl = ctrl_interface_setup_dynip(gbcfg, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_GBPROXY, NULL);
- if (!ctrl) {
- LOGP(DGPRS, LOGL_FATAL, "Failed to create CTRL interface.\n");
- exit(1);
- }
-
- if (gb_ctrl_cmds_install() != 0) {
- LOGP(DGPRS, LOGL_FATAL, "Failed to install CTRL commands.\n");
- exit(1);
- }
-
- if (daemonize) {
- rc = osmo_daemonize();
- if (rc < 0) {
- perror("Error during daemonize");
- exit(1);
- }
- }
-
- while (1) {
- rc = osmo_select_main(0);
- if (rc < 0)
- exit(3);
- }
-
- exit(0);
-}
diff --git a/src/gbproxy/gb_proxy_peer.c b/src/gbproxy/gb_proxy_peer.c
deleted file mode 100644
index 7d6ba864..00000000
--- a/src/gbproxy/gb_proxy_peer.c
+++ /dev/null
@@ -1,759 +0,0 @@
-/* Gb proxy peer handling */
-
-/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2010-2013 by On-Waves
- * (C) 2013 by Holger Hans Peter Freyther
- * 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/sgsn/gb_proxy.h>
-
-#include <osmocom/sgsn/debug.h>
-
-#include <osmocom/gprs/protocol/gsm_08_18.h>
-#include <osmocom/core/crc16.h>
-#include <osmocom/core/logging.h>
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/rate_ctr.h>
-#include <osmocom/core/stats.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/gsm/tlv.h>
-
-#include <string.h>
-
-extern void *tall_sgsn_ctx;
-
-static const struct rate_ctr_desc bvc_ctr_description[] = {
- { "blocked", "BVC Block " },
- { "unblocked", "BVC Unblock " },
- { "dropped", "BVC blocked, dropped packet " },
- { "inv-nsei", "NSEI mismatch " },
- { "tx-err", "NS Transmission error " },
-};
-
-osmo_static_assert(ARRAY_SIZE(bvc_ctr_description) == GBPROX_PEER_CTR_LAST, everything_described);
-
-static const struct rate_ctr_group_desc bvc_ctrg_desc = {
- .group_name_prefix = "gbproxy:peer",
- .group_description = "GBProxy Peer Statistics",
- .num_ctr = ARRAY_SIZE(bvc_ctr_description),
- .ctr_desc = bvc_ctr_description,
- .class_id = OSMO_STATS_CLASS_PEER,
-};
-
-
-/* Find the gbproxy_bvc by its BVCI. There can only be one match */
-struct gbproxy_bvc *gbproxy_bvc_by_bvci(struct gbproxy_nse *nse, uint16_t bvci)
-{
- struct gbproxy_bvc *bvc;
- hash_for_each_possible(nse->bvcs, bvc, list, bvci) {
- if (bvc->bvci == bvci)
- return bvc;
- }
- return NULL;
-}
-
-struct gbproxy_bvc *gbproxy_bvc_alloc(struct gbproxy_nse *nse, uint16_t bvci)
-{
- struct gbproxy_bvc *bvc;
- OSMO_ASSERT(nse);
- struct gbproxy_config *cfg = nse->cfg;
- OSMO_ASSERT(cfg);
-
- bvc = talloc_zero(tall_sgsn_ctx, struct gbproxy_bvc);
- if (!bvc)
- return NULL;
-
- bvc->bvci = bvci;
- bvc->ctrg = rate_ctr_group_alloc(bvc, &bvc_ctrg_desc, (nse->nsei << 16) | bvci);
- if (!bvc->ctrg) {
- talloc_free(bvc);
- return NULL;
- }
- bvc->nse = nse;
-
- hash_add(nse->bvcs, &bvc->list, bvc->bvci);
-
- LOGPBVC_CAT(bvc, DOBJ, LOGL_INFO, "BVC Created\n");
-
- /* We leave allocating the bvc->fi to the caller, as the FSM details depend
- * on the type of BVC (SIG/PTP) and role (SGSN/BSS) */
-
- return bvc;
-}
-
-void gbproxy_bvc_free(struct gbproxy_bvc *bvc)
-{
- struct gbproxy_cell *cell;
-
- if (!bvc)
- return;
-
- LOGPBVC_CAT(bvc, DOBJ, LOGL_INFO, "BVC Destroying\n");
-
- hash_del(&bvc->list);
-
- rate_ctr_group_free(bvc->ctrg);
- bvc->ctrg = NULL;
-
- osmo_fsm_inst_free(bvc->fi);
-
- cell = bvc->cell;
- if (cell) {
- int i;
-
- if (cell->bss_bvc == bvc)
- cell->bss_bvc = NULL;
-
- /* we could also be a SGSN-side BVC */
- for (i = 0; i < ARRAY_SIZE(cell->sgsn_bvc); i++) {
- if (cell->sgsn_bvc[i] == bvc)
- cell->sgsn_bvc[i] = NULL;
- }
- bvc->cell = NULL;
- }
-
- talloc_free(bvc);
-}
-
-/*! remove BVCs on NSE specified by NSEI.
- * \param[in] cfg proxy in which we operate
- * \param[in] nsei NS entity in which we should clean up
- * \param[in] bvci if 0: remove all PTP BVCs; if != 0: BVCI of the single BVC to clean up */
-int gbproxy_cleanup_bvcs(struct gbproxy_nse *nse, uint16_t bvci)
-{
- struct hlist_node *btmp;
- struct gbproxy_bvc *bvc;
- int j, counter = 0;
-
- if (!nse)
- return 0;
-
- hash_for_each_safe(nse->bvcs, j, btmp, bvc, list) {
- if (bvci && bvc->bvci != bvci)
- continue;
- if (bvci == 0 && bvc->bvci == 0)
- continue;
-
- gbproxy_bvc_free(bvc);
- counter += 1;
- }
-
- return counter;
-}
-
-
-/***********************************************************************
- * CELL
- ***********************************************************************/
-
-/* Allocate a new 'cell' object */
-struct gbproxy_cell *gbproxy_cell_alloc(struct gbproxy_config *cfg, uint16_t bvci)
-{
- struct gbproxy_cell *cell;
- OSMO_ASSERT(cfg);
-
- cell = talloc_zero(cfg, struct gbproxy_cell);
- if (!cell)
- return NULL;
-
- cell->cfg = cfg;
- cell->bvci = bvci;
-
- hash_add(cfg->cells, &cell->list, cell->bvci);
-
- LOGPCELL_CAT(cell, DOBJ, LOGL_INFO, "CELL Created\n");
-
- return cell;
-}
-
-/* Find cell by BVCI */
-struct gbproxy_cell *gbproxy_cell_by_bvci(struct gbproxy_config *cfg, uint16_t bvci)
-{
- struct gbproxy_cell *cell;
-
- hash_for_each_possible(cfg->cells, cell, list, bvci) {
- if (cell->bvci == bvci)
- return cell;
- }
- return NULL;
-}
-
-struct gbproxy_cell *gbproxy_cell_by_bvci_or_new(struct gbproxy_config *cfg, uint16_t bvci)
-{
- struct gbproxy_cell *cell;
- OSMO_ASSERT(cfg);
-
- cell = gbproxy_cell_by_bvci(cfg, bvci);
- if (!cell)
- cell = gbproxy_cell_alloc(cfg, bvci);
-
- return cell;
-}
-
-void gbproxy_cell_free(struct gbproxy_cell *cell)
-{
- unsigned int i;
-
- if (!cell)
- return;
-
- LOGPCELL_CAT(cell, DOBJ, LOGL_INFO, "CELL Destroying\n");
-
- /* remove from cfg.cells */
- hash_del(&cell->list);
-
- /* remove back-pointers from the BSS side */
- if (cell->bss_bvc && cell->bss_bvc->cell)
- cell->bss_bvc->cell = NULL;
-
- /* remove back-pointers from the SGSN side */
- for (i = 0; i < ARRAY_SIZE(cell->sgsn_bvc); i++) {
- if (!cell->sgsn_bvc[i])
- continue;
- if (cell->sgsn_bvc[i]->cell)
- cell->sgsn_bvc[i]->cell = NULL;
- }
-
- talloc_free(cell);
-}
-
-bool gbproxy_cell_add_sgsn_bvc(struct gbproxy_cell *cell, struct gbproxy_bvc *bvc)
-{
- unsigned int i;
- for (i = 0; i < ARRAY_SIZE(cell->sgsn_bvc); i++) {
- if (!cell->sgsn_bvc[i]) {
- cell->sgsn_bvc[i] = bvc;
- LOGPCELL_CAT(cell, DOBJ, LOGL_DEBUG, "CELL linked to SGSN\n");
- LOGPBVC_CAT(bvc, DOBJ, LOGL_DEBUG, "BVC linked to CELL\n");
- return true;
- }
- }
- return false;
-}
-
-
-/***********************************************************************
- * TLLI cache
- ***********************************************************************/
-
-static inline struct gbproxy_tlli_cache_entry *_get_tlli_entry(struct gbproxy_config *cfg, uint32_t tlli)
-{
- struct gbproxy_tlli_cache_entry *cache_entry;
-
- hash_for_each_possible(cfg->tlli_cache.entries, cache_entry, list, tlli) {
- if (cache_entry->tlli == tlli)
- return cache_entry;
- }
- return NULL;
-}
-
-void gbproxy_tlli_cache_update(struct gbproxy_nse *nse, uint32_t tlli)
-{
- struct gbproxy_config *cfg = nse->cfg;
- struct timespec now;
- struct gbproxy_tlli_cache_entry *cache_entry = _get_tlli_entry(cfg, tlli);
-
- osmo_clock_gettime(CLOCK_MONOTONIC, &now);
-
- if (cache_entry) {
- /* Update the entry if it already exists */
- cache_entry->nse = nse;
- cache_entry->tstamp = now.tv_sec;
- return;
- }
-
- cache_entry = talloc_zero(cfg, struct gbproxy_tlli_cache_entry);
- cache_entry->tlli = tlli;
- cache_entry->nse = nse;
- cache_entry->tstamp = now.tv_sec;
- hash_add(cfg->tlli_cache.entries, &cache_entry->list, cache_entry->tlli);
-}
-
-static void _tlli_cache_remove_nse(struct gbproxy_nse *nse) {
- uint i;
- struct gbproxy_config *cfg = nse->cfg;
- struct gbproxy_tlli_cache_entry *tlli_cache;
- struct hlist_node *tmp;
-
- hash_for_each_safe(cfg->tlli_cache.entries, i, tmp, tlli_cache, list) {
- if (tlli_cache->nse == nse) {
- hash_del(&tlli_cache->list);
- talloc_free(tlli_cache);
- }
- }
-}
-
-void gbproxy_tlli_cache_remove(struct gbproxy_config *cfg, uint32_t tlli)
-{
- struct gbproxy_tlli_cache_entry *tlli_cache;
- struct hlist_node *tmp;
-
- hash_for_each_possible_safe(cfg->tlli_cache.entries, tlli_cache, tmp, list, tlli) {
- if (tlli_cache->tlli == tlli) {
- hash_del(&tlli_cache->list);
- talloc_free(tlli_cache);
- return;
- }
- }
-}
-
-int gbproxy_tlli_cache_cleanup(struct gbproxy_config *cfg)
-{
- int i, count = 0;
- struct gbproxy_tlli_cache_entry *tlli_cache;
- struct hlist_node *tmp;
- struct timespec now;
- time_t expiry;
-
- osmo_clock_gettime(CLOCK_MONOTONIC, &now);
- expiry = now.tv_sec - cfg->tlli_cache.timeout;
-
- hash_for_each_safe(cfg->tlli_cache.entries, i, tmp, tlli_cache, list) {
- if (tlli_cache->tstamp < expiry) {
- count++;
- LOGP(DGPRS, LOGL_NOTICE, "Cache entry for TLLI %08x expired, removing\n", tlli_cache->tlli);
- hash_del(&tlli_cache->list);
- talloc_free(tlli_cache);
- }
- }
- return count;
-
-}
-/***********************************************************************
- * IMSI cache
- ***********************************************************************/
-static inline uint16_t _checksum_imsi(const char *imsi)
-{
- size_t len = strlen(imsi);
- return osmo_crc16(0, (const uint8_t *)imsi, len);
-}
-
-static inline struct gbproxy_imsi_cache_entry *_get_imsi_entry(struct gbproxy_config *cfg, const char *imsi)
-{
- struct gbproxy_imsi_cache_entry *cache_entry;
- uint16_t imsi_hash = _checksum_imsi(imsi);
-
- hash_for_each_possible(cfg->imsi_cache.entries, cache_entry, list, imsi_hash) {
- if (!strncmp(cache_entry->imsi, imsi, sizeof(cache_entry->imsi)))
- return cache_entry;
- }
- return NULL;
-}
-
-void gbproxy_imsi_cache_update(struct gbproxy_nse *nse, const char *imsi)
-{
- struct gbproxy_config *cfg = nse->cfg;
- struct timespec now;
- struct gbproxy_imsi_cache_entry *cache_entry = _get_imsi_entry(cfg, imsi);
- uint16_t imsi_hash = _checksum_imsi(imsi);
-
- osmo_clock_gettime(CLOCK_MONOTONIC, &now);
-
- if (cache_entry) {
- /* Update the entry if it already exists */
- cache_entry->nse = nse;
- cache_entry->tstamp = now.tv_sec;
- return;
- }
-
- cache_entry = talloc_zero(cfg, struct gbproxy_imsi_cache_entry);
- OSMO_STRLCPY_ARRAY(cache_entry->imsi, imsi);
- cache_entry->nse = nse;
- cache_entry->tstamp = now.tv_sec;
- hash_add(cfg->imsi_cache.entries, &cache_entry->list, imsi_hash);
-}
-
-static void _imsi_cache_remove_nse(struct gbproxy_nse *nse) {
- uint i;
- struct gbproxy_config *cfg = nse->cfg;
- struct gbproxy_imsi_cache_entry *imsi_cache;
- struct hlist_node *tmp;
-
- hash_for_each_safe(cfg->imsi_cache.entries, i, tmp, imsi_cache, list) {
- if (imsi_cache->nse == nse) {
- hash_del(&imsi_cache->list);
- talloc_free(imsi_cache);
- }
- }
-}
-
-void gbproxy_imsi_cache_remove(struct gbproxy_config *cfg, const char *imsi)
-{
- struct gbproxy_imsi_cache_entry *imsi_cache;
- struct hlist_node *tmp;
- uint16_t imsi_hash = _checksum_imsi(imsi);
-
- hash_for_each_possible_safe(cfg->imsi_cache.entries, imsi_cache, tmp, list, imsi_hash) {
- if (!(strncmp(imsi_cache->imsi, imsi, sizeof(imsi_cache->imsi)))) {
- hash_del(&imsi_cache->list);
- talloc_free(imsi_cache);
- return;
- }
- }
-}
-
-int gbproxy_imsi_cache_cleanup(struct gbproxy_config *cfg)
-{
- int i, count = 0;
- struct gbproxy_imsi_cache_entry *imsi_cache;
- struct hlist_node *tmp;
- struct timespec now;
- time_t expiry;
-
- osmo_clock_gettime(CLOCK_MONOTONIC, &now);
- expiry = now.tv_sec - cfg->imsi_cache.timeout;
-
- hash_for_each_safe(cfg->imsi_cache.entries, i, tmp, imsi_cache, list) {
- if (imsi_cache->tstamp < expiry) {
- count++;
- LOGP(DGPRS, LOGL_NOTICE, "Cache entry for IMSI %s expired, removing\n", imsi_cache->imsi);
- hash_del(&imsi_cache->list);
- talloc_free(imsi_cache);
- }
- }
- return count;
-}
-
-/***********************************************************************
- * NSE - NS Entity
- ***********************************************************************/
-
-struct gbproxy_nse *gbproxy_nse_alloc(struct gbproxy_config *cfg, uint16_t nsei, bool sgsn_facing)
-{
- struct gbproxy_nse *nse;
- OSMO_ASSERT(cfg);
-
- nse = talloc_zero(tall_sgsn_ctx, struct gbproxy_nse);
- if (!nse)
- return NULL;
-
- nse->nsei = nsei;
- nse->cfg = cfg;
- nse->sgsn_facing = sgsn_facing;
-
- if (sgsn_facing)
- hash_add(cfg->sgsn_nses, &nse->list, nsei);
- else
- hash_add(cfg->bss_nses, &nse->list, nsei);
-
- hash_init(nse->bvcs);
-
- LOGPNSE_CAT(nse, DOBJ, LOGL_INFO, "NSE Created\n");
-
- return nse;
-}
-
-static void _nse_free(struct gbproxy_nse *nse)
-{
- struct gbproxy_bvc *bvc;
- struct hlist_node *tmp;
- int i;
-
- if (!nse)
- return;
-
- LOGPNSE_CAT(nse, DOBJ, LOGL_INFO, "NSE Destroying\n");
-
- hash_del(&nse->list);
- /* Clear the cache entries of this NSE */
- _tlli_cache_remove_nse(nse);
- _imsi_cache_remove_nse(nse);
-
- hash_for_each_safe(nse->bvcs, i, tmp, bvc, list)
- gbproxy_bvc_free(bvc);
-
- talloc_free(nse);
-}
-static void _sgsn_free(struct gbproxy_sgsn *sgsn);
-
-void gbproxy_nse_free(struct gbproxy_nse *nse)
-{
- if (!nse)
- return;
- OSMO_ASSERT(nse->cfg);
-
- if (nse->sgsn_facing) {
- struct gbproxy_sgsn *sgsn = gbproxy_sgsn_by_nsei(nse->cfg, nse->nsei);
- OSMO_ASSERT(sgsn);
- _sgsn_free(sgsn);
- }
-
- _nse_free(nse);
-}
-
-struct gbproxy_nse *gbproxy_nse_by_nsei(struct gbproxy_config *cfg, uint16_t nsei, uint32_t flags)
-{
- struct gbproxy_nse *nse;
- OSMO_ASSERT(cfg);
-
- if (flags & NSE_F_SGSN) {
- hash_for_each_possible(cfg->sgsn_nses, nse, list, nsei) {
- if (nse->nsei == nsei)
- return nse;
- }
- }
-
- if (flags & NSE_F_BSS) {
- hash_for_each_possible(cfg->bss_nses, nse, list, nsei) {
- if (nse->nsei == nsei)
- return nse;
- }
- }
-
- return NULL;
-}
-
-struct gbproxy_nse *gbproxy_nse_by_nsei_or_new(struct gbproxy_config *cfg, uint16_t nsei, bool sgsn_facing)
-{
- struct gbproxy_nse *nse;
- OSMO_ASSERT(cfg);
-
- nse = gbproxy_nse_by_nsei(cfg, nsei, sgsn_facing ? NSE_F_SGSN : NSE_F_BSS);
- if (!nse)
- nse = gbproxy_nse_alloc(cfg, nsei, sgsn_facing);
-
- return nse;
-}
-
-struct gbproxy_nse *gbproxy_nse_by_tlli(struct gbproxy_config *cfg, uint32_t tlli)
-{
- struct gbproxy_tlli_cache_entry *tlli_cache;
-
- hash_for_each_possible(cfg->tlli_cache.entries, tlli_cache, list, tlli) {
- if (tlli_cache->tlli == tlli)
- return tlli_cache->nse;
- }
- return NULL;
-}
-
-struct gbproxy_nse *gbproxy_nse_by_imsi(struct gbproxy_config *cfg, const char *imsi)
-{
- struct gbproxy_imsi_cache_entry *imsi_cache;
- uint16_t imsi_hash = _checksum_imsi(imsi);
-
- hash_for_each_possible(cfg->imsi_cache.entries, imsi_cache, list, imsi_hash) {
- if (!strncmp(imsi_cache->imsi, imsi, sizeof(imsi_cache->imsi)))
- return imsi_cache->nse;
- }
- return NULL;
-}
-
-/***********************************************************************
- * SGSN - Serving GPRS Support Node
- ***********************************************************************/
-
-/*! Allocate a new SGSN. This ensures the corresponding gbproxy_nse is allocated as well
- * \param[in] cfg The gbproxy configuration
- * \param[in] nsei The nsei where the SGSN can be reached
- * \param[in] name A name to give the SGSN
- * \return The SGSN, NULL if it couldn't be allocated
- */
-struct gbproxy_sgsn *gbproxy_sgsn_alloc(struct gbproxy_config *cfg, uint16_t nsei, const char *name)
-{
- struct gbproxy_sgsn *sgsn;
- OSMO_ASSERT(cfg);
-
- sgsn = talloc_zero(tall_sgsn_ctx, struct gbproxy_sgsn);
- if (!sgsn)
- return NULL;
-
- sgsn->nse = gbproxy_nse_alloc(cfg, nsei, true);
- if (!sgsn->nse) {
- LOGP(DOBJ, LOGL_ERROR, "Could not allocate NSE(%05u) for SGSN(%s)\n",
- nsei, sgsn->name);
- goto free_sgsn;
- }
-
- if (name)
- sgsn->name = talloc_strdup(sgsn, name);
- else
- sgsn->name = talloc_asprintf(sgsn, "NSE(%05u)", sgsn->nse->nsei);
- if (!sgsn->name)
- goto free_sgsn;
-
- sgsn->pool.allow_attach = true;
- sgsn->pool.nri_ranges = osmo_nri_ranges_alloc(sgsn);
-
- llist_add_tail(&sgsn->list, &cfg->sgsns);
- LOGPSGSN_CAT(sgsn, DOBJ, LOGL_INFO, "SGSN Created\n");
- return sgsn;
-
-free_sgsn:
- talloc_free(sgsn);
- return NULL;
-}
-
-/* Only free gbproxy_sgsn, sgsn can't be NULL */
-static void _sgsn_free(struct gbproxy_sgsn *sgsn) {
- struct gbproxy_config *cfg;
-
- OSMO_ASSERT(sgsn->nse);
- cfg = sgsn->nse->cfg;
- OSMO_ASSERT(cfg);
-
- LOGPSGSN_CAT(sgsn, DOBJ, LOGL_INFO, "SGSN Destroying\n");
- llist_del(&sgsn->list);
- /* talloc will free ->name and ->pool.nri_ranges */
- talloc_free(sgsn);
-}
-
-/*! Free the SGSN. This ensures the corresponding gbproxy_nse is freed as well
- * \param[in] sgsn The SGSN
- */
-void gbproxy_sgsn_free(struct gbproxy_sgsn *sgsn)
-{
- if (!sgsn)
- return;
-
- OSMO_ASSERT(sgsn->nse)
-
- _nse_free(sgsn->nse);
- _sgsn_free(sgsn);
-}
-
-/*! Return the SGSN for a given NSEI
- * \param[in] cfg The gbproxy configuration
- * \param[in] nsei The nsei where the SGSN can be reached
- * \return Returns the matching SGSN or NULL if it couldn't be found
- */
-struct gbproxy_sgsn *gbproxy_sgsn_by_name(struct gbproxy_config *cfg, const char *name)
-{
- struct gbproxy_sgsn *sgsn;
- OSMO_ASSERT(cfg);
-
- llist_for_each_entry(sgsn, &cfg->sgsns, list) {
- if (!strcmp(sgsn->name, name))
- return sgsn;
- }
-
- return NULL;
-}
-
-/*! Return the SGSN for a given NSEI
- * \param[in] cfg The gbproxy configuration
- * \param[in] nsei The nsei where the SGSN can be reached
- * \return Returns the matching SGSN or NULL if it couldn't be found
- */
-struct gbproxy_sgsn *gbproxy_sgsn_by_nsei(struct gbproxy_config *cfg, uint16_t nsei)
-{
- struct gbproxy_sgsn *sgsn;
- OSMO_ASSERT(cfg);
-
- llist_for_each_entry(sgsn, &cfg->sgsns, list) {
- if (sgsn->nse->nsei == nsei)
- return sgsn;
- }
-
- return NULL;
-}
-
-/*! Return the SGSN for a given NSEI, creating a new one if none exists
- * \param[in] cfg The gbproxy configuration
- * \param[in] nsei The nsei where the SGSN can be reached
- * \return Returns the SGSN
- */
-struct gbproxy_sgsn *gbproxy_sgsn_by_nsei_or_new(struct gbproxy_config *cfg, uint16_t nsei)
-{
- struct gbproxy_sgsn *sgsn;
- OSMO_ASSERT(cfg);
-
- sgsn = gbproxy_sgsn_by_nsei(cfg, nsei);
- if (!sgsn)
- sgsn = gbproxy_sgsn_alloc(cfg, nsei, NULL);
-
- return sgsn;
-}
-
-/*! Return the gbproxy_sgsn matching that NRI
- * \param[in] cfg proxy in which we operate
- * \param[in] nri NRI to look for
- * \param[out] null_nri If not NULL this indicates whether the NRI is a null NRI
- * \return The SGSN this NRI has been added to, NULL if no matching SGSN could be found
- */
-struct gbproxy_sgsn *gbproxy_sgsn_by_nri(struct gbproxy_config *cfg, uint16_t nri, bool *null_nri)
-{
- struct gbproxy_sgsn *sgsn;
- OSMO_ASSERT(cfg);
-
- llist_for_each_entry(sgsn, &cfg->sgsns, list) {
- if (osmo_nri_v_matches_ranges(nri, sgsn->pool.nri_ranges)) {
- /* Also check if the NRI we're looking for is a NULL NRI */
- if (null_nri) {
- if (osmo_nri_v_matches_ranges(nri, cfg->pool.null_nri_ranges))
- *null_nri = true;
- else
- *null_nri = false;
- }
- return sgsn;
- }
- }
-
- return NULL;
-}
-
-/*! Seleect a pseudo-random SGSN for a given TLLI, ignoring any SGSN that is not accepting connections
- * \param[in] cfg The gbproxy configuration
- * \param[in] sgsn_avoid If not NULL then avoid this SGSN when selecting a new one. Use for load redistribution
- * \param[in] tlli The tlli to choose an SGSN for. The same tlli will map to the same SGSN as long as no SGSN is
- * added/removed or allow_attach changes.
- * \return Returns the sgsn on success, NULL if no SGSN that allows new connections could be found
- */
-struct gbproxy_sgsn *gbproxy_sgsn_by_tlli(struct gbproxy_config *cfg, struct gbproxy_sgsn *sgsn_avoid,
- uint32_t tlli)
-{
- uint32_t i = 0;
- uint32_t index, num_sgsns;
- OSMO_ASSERT(cfg);
-
- struct gbproxy_sgsn *sgsn = cfg->pool.nsf_override;
-
- if (sgsn) {
- LOGPSGSN(sgsn, LOGL_DEBUG, "Node selection function is overridden by config\n");
- return sgsn;
- }
-
- /* TODO: We should keep track of count in cfg */
- num_sgsns = llist_count(&cfg->sgsns);
-
- if (num_sgsns == 0)
- return NULL;
-
- /* FIXME: 256 SGSNs ought to be enough for everyone */
- index = hash_32(tlli, 8) % num_sgsns;
-
- /* Get the first enabled SGSN after index */
- llist_for_each_entry(sgsn, &cfg->sgsns, list) {
- if (i >= index && sgsn->pool.allow_attach) {
- return sgsn;
- }
- i++;
- }
- /* Start again from the beginning */
- i = 0;
- llist_for_each_entry(sgsn, &cfg->sgsns, list) {
- if (i >= index) {
- break;
- } else if (sgsn->pool.allow_attach) {
- return sgsn;
- }
- i++;
- }
-
- return NULL;
-}
diff --git a/src/gbproxy/gb_proxy_vty.c b/src/gbproxy/gb_proxy_vty.c
deleted file mode 100644
index 7ae65d20..00000000
--- a/src/gbproxy/gb_proxy_vty.c
+++ /dev/null
@@ -1,778 +0,0 @@
-/*
- * (C) 2010 by Harald Welte <laforge@gnumonks.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 <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <string.h>
-#include <time.h>
-#include <inttypes.h>
-
-#include <osmocom/core/hashtable.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/timer.h>
-#include <osmocom/core/rate_ctr.h>
-#include <osmocom/core/utils.h>
-
-#include <osmocom/gprs/gprs_ns2.h>
-#include <osmocom/gprs/bssgp_bvc_fsm.h>
-
-#include <osmocom/gsm/apn.h>
-#include <osmocom/gsm/gsm23236.h>
-#include <osmocom/gsm/gsm48.h>
-
-#include <osmocom/sgsn/debug.h>
-#include <osmocom/sgsn/gb_proxy.h>
-#include <osmocom/sgsn/gprs_utils.h>
-#include <osmocom/sgsn/vty.h>
-
-#include <osmocom/vty/command.h>
-#include <osmocom/vty/logging.h>
-#include <osmocom/vty/vty.h>
-#include <osmocom/vty/misc.h>
-
-#define GBPROXY_STR "Display information about the Gb proxy\n"
-#define NRI_STR "Mapping of Network Resource Indicators to this SGSN, for SGSN pooling\n"
-#define NULL_NRI_STR "Define NULL-NRI values that cause re-assignment of an MS to a different SGSN, for SGSN pooling.\n"
-#define NRI_FIRST_LAST_STR "First value of the NRI value range, should not surpass the configured 'nri bitlen'.\n" \
- "Last value of the NRI value range, should not surpass the configured 'nri bitlen' and be larger than the" \
- " first value; if omitted, apply only the first value.\n"
-#define NRI_ARGS_TO_STR_FMT "%s%s%s"
-#define NRI_ARGS_TO_STR_ARGS(ARGC, ARGV) ARGV[0], (ARGC>1)? ".." : "", (ARGC>1)? ARGV[1] : ""
-#define NRI_WARN(SGSN, FORMAT, args...) do { \
- vty_out(vty, "%% Warning: NSE(%05d/SGSN): " FORMAT "%s", (SGSN)->nse->nsei, ##args, VTY_NEWLINE); \
- LOGP(DLBSSGP, LOGL_ERROR, "NSE(%05d/SGSN): " FORMAT "\n", (SGSN)->nse->nsei, ##args); \
- } while (0)
-
-static struct gbproxy_config *g_cfg = NULL;
-
-/*
- * vty code for gbproxy below
- */
-static struct cmd_node gbproxy_node = {
- GBPROXY_NODE,
- "%s(config-gbproxy)# ",
- 1,
-};
-
-static void gbprox_vty_print_bvc(struct vty *vty, struct gbproxy_bvc *bvc)
-{
-
- if (bvc->bvci == 0) {
- vty_out(vty, "NSEI %5u, SIG-BVCI %5u [%s]%s", bvc->nse->nsei, bvc->bvci,
- osmo_fsm_inst_state_name(bvc->fi), VTY_NEWLINE);
- } else {
- struct gprs_ra_id raid;
- gsm48_parse_ra(&raid, bvc->ra);
- vty_out(vty, "NSEI %5u, PTP-BVCI %5u, RAI %s [%s]%s", bvc->nse->nsei, bvc->bvci,
- osmo_rai_name(&raid), osmo_fsm_inst_state_name(bvc->fi), VTY_NEWLINE);
- }
-}
-
-static void gbproxy_vty_print_nse(struct vty *vty, struct gbproxy_nse *nse, bool show_stats)
-{
- struct gbproxy_bvc *bvc;
- int j;
-
- hash_for_each(nse->bvcs, j, bvc, list) {
- gbprox_vty_print_bvc(vty, bvc);
-
- if (show_stats)
- vty_out_rate_ctr_group(vty, " ", bvc->ctrg);
- }
-}
-
-static void gbproxy_vty_print_cell(struct vty *vty, struct gbproxy_cell *cell, bool show_stats)
-{
- struct gprs_ra_id raid;
- gsm48_parse_ra(&raid, cell->ra);
- unsigned int num_sgsn_bvc = 0;
- unsigned int i;
-
- vty_out(vty, "BVCI %5u RAI %s: ", cell->bvci, osmo_rai_name(&raid));
- if (cell->bss_bvc)
- vty_out(vty, "BSS NSEI %5u, SGSN NSEI ", cell->bss_bvc->nse->nsei);
- else
- vty_out(vty, "BSS NSEI <none>, SGSN NSEI ");
-
- for (i = 0; i < ARRAY_SIZE(cell->sgsn_bvc); i++) {
- struct gbproxy_bvc *sgsn_bvc = cell->sgsn_bvc[i];
- if (sgsn_bvc) {
- vty_out(vty, "%5u ", sgsn_bvc->nse->nsei);
- num_sgsn_bvc++;
- }
- }
- if (num_sgsn_bvc)
- vty_out(vty, "%s", VTY_NEWLINE);
- else
- vty_out(vty, "<none>%s", VTY_NEWLINE);
-}
-
-static int config_write_gbproxy(struct vty *vty)
-{
- struct osmo_nri_range *r;
-
- vty_out(vty, "gbproxy%s", VTY_NEWLINE);
-
- if (g_cfg->pool.bvc_fc_ratio != 100)
- vty_out(vty, " pool bvc-flow-control-ratio %u%s", g_cfg->pool.bvc_fc_ratio, VTY_NEWLINE);
-
- if (g_cfg->pool.nri_bitlen != OSMO_NRI_BITLEN_DEFAULT)
- vty_out(vty, " nri bitlen %u%s", g_cfg->pool.nri_bitlen, VTY_NEWLINE);
-
- llist_for_each_entry(r, &g_cfg->pool.null_nri_ranges->entries, entry) {
- vty_out(vty, " nri null add %d", r->first);
- if (r->first != r->last)
- vty_out(vty, " %d", r->last);
- vty_out(vty, "%s", VTY_NEWLINE);
- }
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_gbproxy,
- cfg_gbproxy_cmd,
- "gbproxy",
- "Configure the Gb proxy")
-{
- vty->node = GBPROXY_NODE;
- return CMD_SUCCESS;
-}
-
-/* VTY code for SGSN (pool) configuration */
-extern const struct bssgp_bvc_fsm_ops sgsn_sig_bvc_fsm_ops;
-#include <osmocom/gprs/protocol/gsm_08_18.h>
-
-static struct cmd_node sgsn_node = {
- SGSN_NODE,
- "%s(config-sgsn)# ",
- 1,
-};
-
-static void sgsn_write_nri(struct vty *vty, struct gbproxy_sgsn *sgsn, bool verbose)
-{
- struct osmo_nri_range *r;
-
- if (verbose) {
- vty_out(vty, "sgsn nsei %d%s", sgsn->nse->nsei, VTY_NEWLINE);
- if (llist_empty(&sgsn->pool.nri_ranges->entries)) {
- vty_out(vty, " %% no NRI mappings%s", VTY_NEWLINE);
- return;
- }
- }
-
- llist_for_each_entry(r, &sgsn->pool.nri_ranges->entries, entry) {
- if (osmo_nri_range_validate(r, 255))
- vty_out(vty, " %% INVALID RANGE:");
- vty_out(vty, " nri add %d", r->first);
- if (r->first != r->last)
- vty_out(vty, " %d", r->last);
- vty_out(vty, "%s", VTY_NEWLINE);
- }
-}
-
-static void write_sgsn(struct vty *vty, struct gbproxy_sgsn *sgsn)
-{
- vty_out(vty, "sgsn nsei %u%s", sgsn->nse->nsei, VTY_NEWLINE);
- vty_out(vty, " name %s%s", sgsn->name, VTY_NEWLINE);
- vty_out(vty, " %sallow-attach%s", sgsn->pool.allow_attach ? "" : "no ", VTY_NEWLINE);
- sgsn_write_nri(vty, sgsn, false);
-}
-
-static int config_write_sgsn(struct vty *vty)
-{
- struct gbproxy_sgsn *sgsn;
-
- llist_for_each_entry(sgsn, &g_cfg->sgsns, list)
- write_sgsn(vty, sgsn);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_sgsn_nsei,
- cfg_sgsn_nsei_cmd,
- "sgsn nsei <0-65534>",
- "Configure the SGSN\n"
- "NSEI to be used in the connection with the SGSN\n"
- "The NSEI\n")
-{
- uint32_t features = 0; // FIXME: make configurable
- unsigned int nsei = atoi(argv[0]);
- unsigned int num_sgsn = llist_count(&g_cfg->sgsns);
- struct gbproxy_sgsn *sgsn;
- struct gbproxy_nse *nse;
- struct gbproxy_bvc *bvc;
-
- if (num_sgsn >= GBPROXY_MAX_NR_SGSN) {
- vty_out(vty, "%% Too many SGSN NSE defined (%d), increase GBPROXY_MAX_NR_SGSN%s",
- num_sgsn, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- /* This will have created the gbproxy_nse as well */
- sgsn = gbproxy_sgsn_by_nsei_or_new(g_cfg, nsei);
- if (!sgsn)
- goto free_nothing;
- nse = sgsn->nse;
- if (num_sgsn > 1 && g_cfg->pool.nri_bitlen == 0)
- vty_out(vty, "%% Multiple SGSNs defined, but no pooling enabled%s", VTY_NEWLINE);
-
-
- if (!gbproxy_bvc_by_bvci(nse, 0)) {
- uint8_t cause = BSSGP_CAUSE_OML_INTERV;
- bvc = gbproxy_bvc_alloc(nse, 0);
- if (!bvc)
- goto free_sgsn;
- bvc->fi = bssgp_bvc_fsm_alloc_sig_bss(bvc, nse->cfg->nsi, nsei, features);
- if (!bvc->fi)
- goto free_bvc;
- bssgp_bvc_fsm_set_ops(bvc->fi, &sgsn_sig_bvc_fsm_ops, bvc);
- osmo_fsm_inst_dispatch(bvc->fi, BSSGP_BVCFSM_E_REQ_RESET, &cause);
- }
-
- vty->node = SGSN_NODE;
- vty->index = sgsn;
- return CMD_SUCCESS;
-
-free_bvc:
- gbproxy_bvc_free(bvc);
-free_sgsn:
- gbproxy_sgsn_free(sgsn);
-free_nothing:
- vty_out(vty, "%% Unable to create NSE for NSEI=%05u%s", nsei, VTY_NEWLINE);
- return CMD_WARNING;
-}
-
-DEFUN(cfg_sgsn_name,
- cfg_sgsn_name_cmd,
- "name NAME",
- "Configure the SGSN\n"
- "Name the SGSN\n"
- "The name\n")
-{
- struct gbproxy_sgsn *sgsn = vty->index;
- const char *name = argv[0];
-
-
- osmo_talloc_replace_string(sgsn, &sgsn->name, name);
- if (!sgsn->name) {
- vty_out(vty, "%% Unable to set name for SGSN with nsei %05u%s", sgsn->nse->nsei, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN_ATTR(cfg_sgsn_nri_add, cfg_sgsn_nri_add_cmd,
- "nri add <0-32767> [<0-32767>]",
- NRI_STR "Add NRI value or range to the NRI mapping for this MSC\n"
- NRI_FIRST_LAST_STR,
- CMD_ATTR_IMMEDIATE)
-{
- struct gbproxy_sgsn *sgsn = vty->index;
- struct gbproxy_sgsn *other_sgsn;
- bool before;
- int rc;
- const char *message;
- struct osmo_nri_range add_range;
-
- rc = osmo_nri_ranges_vty_add(&message, &add_range, sgsn->pool.nri_ranges, argc, argv, g_cfg->pool.nri_bitlen);
- if (message) {
- NRI_WARN(sgsn, "%s: " NRI_ARGS_TO_STR_FMT, message, NRI_ARGS_TO_STR_ARGS(argc, argv));
- }
- if (rc < 0)
- return CMD_WARNING;
-
- /* Issue a warning about NRI range overlaps (but still allow them).
- * Overlapping ranges will map to whichever SGSN comes fist in the gbproxy_config->sgsns llist,
- * which should be the first one defined in the config */
- before = true;
-
- llist_for_each_entry(other_sgsn, &g_cfg->sgsns, list) {
- if (other_sgsn == sgsn) {
- before = false;
- continue;
- }
- if (osmo_nri_range_overlaps_ranges(&add_range, other_sgsn->pool.nri_ranges)) {
- uint16_t nsei = sgsn->nse->nsei;
- uint16_t other_nsei = other_sgsn->nse->nsei;
- NRI_WARN(sgsn, "NRI range [%d..%d] overlaps between NSE %05d and NSE %05d."
- " For overlaps, NSE %05d has higher priority than NSE %05d",
- add_range.first, add_range.last, nsei, other_nsei,
- before ? other_nsei : nsei, before ? nsei : other_nsei);
- }
- }
- return CMD_SUCCESS;
-}
-
-DEFUN_ATTR(cfg_sgsn_nri_del, cfg_sgsn_nri_del_cmd,
- "nri del <0-32767> [<0-32767>]",
- NRI_STR "Remove NRI value or range from the NRI mapping for this MSC\n"
- NRI_FIRST_LAST_STR,
- CMD_ATTR_IMMEDIATE)
-{
- struct gbproxy_sgsn *sgsn = vty->index;
- int rc;
- const char *message;
-
- rc = osmo_nri_ranges_vty_del(&message, NULL, sgsn->pool.nri_ranges, argc, argv);
- if (message) {
- NRI_WARN(sgsn, "%s: " NRI_ARGS_TO_STR_FMT, message, NRI_ARGS_TO_STR_ARGS(argc, argv));
- }
- if (rc < 0)
- return CMD_WARNING;
- return CMD_SUCCESS;
-}
-
-DEFUN_ATTR(cfg_sgsn_allow_attach, cfg_sgsn_allow_attach_cmd,
- "allow-attach",
- "Allow this SGSN to attach new subscribers (default).\n",
- CMD_ATTR_IMMEDIATE)
-{
- struct gbproxy_sgsn *sgsn = vty->index;
- sgsn->pool.allow_attach = true;
- return CMD_SUCCESS;
-}
-
-DEFUN_ATTR(cfg_sgsn_no_allow_attach, cfg_sgsn_no_allow_attach_cmd,
- "no allow-attach",
- NO_STR
- "Do not assign new subscribers to this MSC."
- " Useful if an MSC in an MSC pool is configured to off-load subscribers."
- " The MSC will still be operational for already IMSI-Attached subscribers,"
- " but the NAS node selection function will skip this MSC for new subscribers\n",
- CMD_ATTR_IMMEDIATE)
-{
- struct gbproxy_sgsn *sgsn = vty->index;
- sgsn->pool.allow_attach = false;
- return CMD_SUCCESS;
-}
-
-DEFUN(sgsn_show_nri_all, show_nri_all_cmd,
- "show nri all",
- SHOW_STR NRI_STR "Show all SGSNs\n")
-{
- struct gbproxy_sgsn *sgsn;
-
- llist_for_each_entry(sgsn, &g_cfg->sgsns, list)
- sgsn_write_nri(vty, sgsn, true);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(show_nri_nsei, show_nri_nsei_cmd,
- "show nri nsei <0-65535>",
- SHOW_STR NRI_STR "Identify SGSN by NSEI\n"
- "NSEI of the SGSN\n")
-{
- struct gbproxy_sgsn *sgsn;
- int nsei = atoi(argv[0]);
-
- sgsn = gbproxy_sgsn_by_nsei(g_cfg, nsei);
- if (!sgsn) {
- vty_out(vty, "%% No SGSN with found for NSEI %05d%s", nsei, VTY_NEWLINE);
- return CMD_SUCCESS;
- }
- sgsn_write_nri(vty, sgsn, true);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_pool_bvc_fc_ratio,
- cfg_pool_bvc_fc_ratio_cmd,
- "pool bvc-flow-control-ratio <1-100>",
- "SGSN Pool related configuration\n"
- "Ratio of BSS-advertised bucket size + leak rate advertised to each SGSN\n"
- "Ratio of BSS-advertised bucket size + leak rate advertised to each SGSN (Percent)\n")
-{
- g_cfg->pool.bvc_fc_ratio = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-DEFUN_ATTR(cfg_gbproxy_nri_bitlen,
- cfg_gbproxy_nri_bitlen_cmd,
- "nri bitlen <0-15>",
- NRI_STR
- "Set number of bits that an NRI has, to extract from TMSI identities (always starting just after the TMSI's most significant octet).\n"
- "bit count (0 disables) pooling)\n",
- CMD_ATTR_IMMEDIATE)
-{
- g_cfg->pool.nri_bitlen = atoi(argv[0]);
-
- if (llist_count(&g_cfg->sgsns) > 1 && g_cfg->pool.nri_bitlen == 0)
- vty_out(vty, "%% Pooling disabled, but multiple SGSNs defined%s", VTY_NEWLINE);
-
- /* TODO: Verify all nri ranges and warn on mismatch */
-
- return CMD_SUCCESS;
-}
-
-DEFUN_ATTR(cfg_gbproxy_nri_null_add,
- cfg_gbproxy_nri_null_add_cmd,
- "nri null add <0-32767> [<0-32767>]",
- NRI_STR NULL_NRI_STR "Add NULL-NRI value (or range)\n"
- NRI_FIRST_LAST_STR,
- CMD_ATTR_IMMEDIATE)
-{
- int rc;
- const char *message;
-
- rc = osmo_nri_ranges_vty_add(&message, NULL, g_cfg->pool.null_nri_ranges, argc, argv,
- g_cfg->pool.nri_bitlen);
- if (message) {
- vty_out(vty, "%% nri null add: %s: " NRI_ARGS_TO_STR_FMT "%s", message, NRI_ARGS_TO_STR_ARGS(argc, argv),
- VTY_NEWLINE);
- vty_out(vty, "%s: \n" NRI_ARGS_TO_STR_FMT, message, NRI_ARGS_TO_STR_ARGS(argc, argv));
- }
- if (rc < 0)
- return CMD_WARNING;
- return CMD_SUCCESS;
-}
-
-DEFUN_ATTR(cfg_gbproxy_nri_null_del,
- cfg_gbproxy_nri_null_del_cmd,
- "nri null del <0-32767> [<0-32767>]",
- NRI_STR NULL_NRI_STR "Remove NRI value or range from the NRI mapping for this MSC\n"
- NRI_FIRST_LAST_STR,
- CMD_ATTR_IMMEDIATE)
-{
- int rc;
- const char *message;
- rc = osmo_nri_ranges_vty_del(&message, NULL, g_cfg->pool.null_nri_ranges, argc, argv);
- if (message) {
- vty_out(vty, "%% %s: " NRI_ARGS_TO_STR_FMT "%s", message, NRI_ARGS_TO_STR_ARGS(argc, argv),
- VTY_NEWLINE);
- }
- if (rc < 0)
- return CMD_WARNING;
- return CMD_SUCCESS;
-}
-
-static void log_set_bvc_filter(struct log_target *target,
- const uint16_t *bvci)
-{
- if (bvci) {
- uintptr_t bvci_filter = *bvci | BVC_LOG_CTX_FLAG;
- target->filter_map |= (1 << LOG_FLT_GB_BVC);
- target->filter_data[LOG_FLT_GB_BVC] = (void *)bvci_filter;
- } else if (target->filter_data[LOG_FLT_GB_BVC]) {
- target->filter_map = ~(1 << LOG_FLT_GB_BVC);
- target->filter_data[LOG_FLT_GB_BVC] = NULL;
- }
-}
-
-DEFUN(logging_fltr_bvc,
- logging_fltr_bvc_cmd,
- "logging filter bvc bvci <0-65535>",
- LOGGING_STR FILTER_STR
- "Filter based on BSSGP VC\n"
- "Identify BVC by BVCI\n"
- "Numeric identifier\n")
-{
- struct log_target *tgt;
- uint16_t id = atoi(argv[0]);
-
- log_tgt_mutex_lock();
- tgt = osmo_log_vty2tgt(vty);
- if (!tgt) {
- log_tgt_mutex_unlock();
- return CMD_WARNING;
- }
-
- log_set_bvc_filter(tgt, &id);
- log_tgt_mutex_unlock();
- return CMD_SUCCESS;
-}
-
-DEFUN(show_gbproxy_bvc, show_gbproxy_bvc_cmd, "show gbproxy bvc (bss|sgsn) [stats]",
- SHOW_STR GBPROXY_STR
- "Show BSSGP Virtual Connections\n"
- "Display BSS-side BVCs\n"
- "Display SGSN-side BVCs\n"
- "Show statistics\n")
-{
- struct gbproxy_nse *nse;
- bool show_stats = argc >= 2;
- int i;
-
- if (show_stats)
- vty_out_rate_ctr_group(vty, "", g_cfg->ctrg);
-
- if (!strcmp(argv[0], "bss")) {
- hash_for_each(g_cfg->bss_nses, i, nse, list)
- gbproxy_vty_print_nse(vty, nse, show_stats);
- } else {
- hash_for_each(g_cfg->sgsn_nses, i, nse, list)
- gbproxy_vty_print_nse(vty, nse, show_stats);
- }
- return CMD_SUCCESS;
-}
-
-DEFUN(show_gbproxy_cell, show_gbproxy_cell_cmd, "show gbproxy cell [stats]",
- SHOW_STR GBPROXY_STR
- "Show GPRS Cell Information\n"
- "Show statistics\n")
-{
- struct gbproxy_cell *cell;
- bool show_stats = argc >= 1;
- int i;
-
- hash_for_each(g_cfg->cells, i, cell, list)
- gbproxy_vty_print_cell(vty, cell, show_stats);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(show_gbproxy_links, show_gbproxy_links_cmd, "show gbproxy links",
- SHOW_STR GBPROXY_STR "Show logical links\n")
-{
- struct gbproxy_nse *nse;
- int i, j;
-
- hash_for_each(g_cfg->bss_nses, i, nse, list) {
- struct gbproxy_bvc *bvc;
- hash_for_each(nse->bvcs, j, bvc, list) {
- gbprox_vty_print_bvc(vty, bvc);
- }
- }
- return CMD_SUCCESS;
-}
-
-DEFUN(show_gbproxy_tlli_cache, show_gbproxy_tlli_cache_cmd,
- "show gbproxy tlli-cache",
- SHOW_STR GBPROXY_STR "Show TLLI cache entries\n")
-{
- struct gbproxy_tlli_cache_entry *entry;
- struct timespec now;
- time_t expiry;
- int i, count = 0;
-
- osmo_clock_gettime(CLOCK_MONOTONIC, &now);
- expiry = now.tv_sec - g_cfg->tlli_cache.timeout;
-
- vty_out(vty, "TLLI cache timeout %us%s", g_cfg->tlli_cache.timeout, VTY_NEWLINE);
- hash_for_each(g_cfg->tlli_cache.entries, i, entry, list) {
- time_t valid = entry->tstamp - expiry;
- struct gbproxy_nse *nse = entry->nse;
-
- vty_out(vty, " TLLI %08x -> NSE(%05u/%s) valid %lds%s", entry->tlli, nse->nsei,
- nse->sgsn_facing ? "SGSN" : "BSS", valid, VTY_NEWLINE);
- count++;
- }
- vty_out(vty, "TLLI cache contains %u entries%s", count, VTY_NEWLINE);
- return CMD_SUCCESS;
-}
-
-DEFUN(show_gbproxy_imsi_cache, show_gbproxy_imsi_cache_cmd,
- "show gbproxy imsi-cache",
- SHOW_STR GBPROXY_STR "Show IMSI cache entries\n")
-{
- struct gbproxy_imsi_cache_entry *entry;
- struct timespec now;
- time_t expiry;
- int i, count = 0;
-
- osmo_clock_gettime(CLOCK_MONOTONIC, &now);
- expiry = now.tv_sec - g_cfg->imsi_cache.timeout;
-
- vty_out(vty, "IMSI cache timeout %us%s", g_cfg->imsi_cache.timeout, VTY_NEWLINE);
- hash_for_each(g_cfg->imsi_cache.entries, i, entry, list) {
- time_t valid = entry->tstamp - expiry;
- struct gbproxy_nse *nse = entry->nse;
- vty_out(vty, " IMSI %s -> NSE(%05u/%s): valid %lds%s", entry->imsi, nse->nsei,
- nse->sgsn_facing ? "SGSN" : "BSS", valid, VTY_NEWLINE);
- count++;
- }
- vty_out(vty, "IMSI cache contains %u entries%s", count, VTY_NEWLINE);
- return CMD_SUCCESS;
-}
-
-DEFUN(delete_gb_bvci, delete_gb_bvci_cmd,
- "delete-gbproxy-peer <0-65534> bvci <2-65534>",
- "Delete a GBProxy bvc by NSEI and optionally BVCI\n"
- "NSEI number\n"
- "Only delete bvc with a matching BVCI\n"
- "BVCI number\n")
-{
- const uint16_t nsei = atoi(argv[0]);
- const uint16_t bvci = atoi(argv[1]);
- struct gbproxy_nse *nse = gbproxy_nse_by_nsei(g_cfg, nsei, NSE_F_BSS);
- int counter;
-
- if (!nse) {
- vty_out(vty, "NSE not found%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- counter = gbproxy_cleanup_bvcs(nse, bvci);
-
- if (counter == 0) {
- vty_out(vty, "BVC not found%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(delete_gb_nsei, delete_gb_nsei_cmd,
- "delete-gbproxy-peer <0-65534> (only-bvc|only-nsvc|all) [dry-run]",
- "Delete a GBProxy bvc by NSEI and optionally BVCI\n"
- "NSEI number\n"
- "Only delete BSSGP connections (BVC)\n"
- "Only delete dynamic NS connections (NS-VC)\n"
- "Delete BVC and dynamic NS connections\n"
- "Show what would be deleted instead of actually deleting\n"
- )
-{
- const uint16_t nsei = atoi(argv[0]);
- const char *mode = argv[1];
- int dry_run = argc > 2;
- int delete_bvc = 0;
- int delete_nsvc = 0;
- int counter;
-
- if (strcmp(mode, "only-bvc") == 0)
- delete_bvc = 1;
- else if (strcmp(mode, "only-nsvc") == 0)
- delete_nsvc = 1;
- else
- delete_bvc = delete_nsvc = 1;
-
- if (delete_bvc) {
- if (!dry_run) {
- struct gbproxy_nse *nse = gbproxy_nse_by_nsei(g_cfg, nsei, NSE_F_BSS);
- counter = gbproxy_cleanup_bvcs(nse, 0);
- gbproxy_nse_free(nse);
- } else {
- struct gbproxy_nse *nse;
- struct gbproxy_bvc *bvc;
- int i, j;
- counter = 0;
- hash_for_each(g_cfg->bss_nses, i, nse, list) {
- if (nse->nsei != nsei)
- continue;
- hash_for_each(nse->bvcs, j, bvc, list) {
- vty_out(vty, "BVC: ");
- gbprox_vty_print_bvc(vty, bvc);
- counter += 1;
- }
- }
- }
- vty_out(vty, "%sDeleted %d BVC%s",
- dry_run ? "Not " : "", counter, VTY_NEWLINE);
- }
-
- if (delete_nsvc) {
- struct gprs_ns2_inst *nsi = g_cfg->nsi;
- struct gprs_ns2_nse *nse;
-
- nse = gprs_ns2_nse_by_nsei(nsi, nsei);
- if (!nse) {
- vty_out(vty, "NSEI not found%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- /* TODO: We should NOT delete a persistent NSEI/NSVC as soon as we can check for these */
- if (!dry_run)
- gprs_ns2_free_nse(nse);
-
- vty_out(vty, "%sDeleted NS-VCs for NSEI %d%s",
- dry_run ? "Not " : "", nsei, VTY_NEWLINE);
- }
-
- return CMD_SUCCESS;
-}
-
-/* Only for ttcn3 testing */
-DEFUN_HIDDEN(sgsn_pool_nsf_fixed, sgsn_pool_nsf_fixed_cmd,
- "sgsn-pool nsf fixed NAME",
- "SGSN pooling: load balancing across multiple SGSNs.\n"
- "Customize the Network Selection Function.\n"
- "Set a fixed SGSN to use (for testing).\n"
- "The name of the SGSN to use.\n")
-{
- const char *name = argv[0];
- struct gbproxy_sgsn *sgsn = gbproxy_sgsn_by_name(g_cfg, name);
-
- if (!sgsn) {
- vty_out(vty, "%% Could not find SGSN with name %s%s", name, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- g_cfg->pool.nsf_override = sgsn;
- return CMD_SUCCESS;
-}
-
-DEFUN_HIDDEN(sgsn_pool_nsf_normal, sgsn_pool_nsf_normal_cmd,
- "sgsn-pool nsf normal",
- "SGSN pooling: load balancing across multiple SGSNs.\n"
- "Customize the Network Selection Function.\n"
- "Reset the NSF back to regular operation (for testing).\n")
-{
- g_cfg->pool.nsf_override = NULL;
- return CMD_SUCCESS;
-}
-
-int gbproxy_vty_init(void)
-{
- install_element_ve(&show_gbproxy_bvc_cmd);
- install_element_ve(&show_gbproxy_cell_cmd);
- install_element_ve(&show_gbproxy_links_cmd);
- install_element_ve(&show_gbproxy_tlli_cache_cmd);
- install_element_ve(&show_gbproxy_imsi_cache_cmd);
- install_element_ve(&show_nri_all_cmd);
- install_element_ve(&show_nri_nsei_cmd);
- install_element_ve(&logging_fltr_bvc_cmd);
-
- install_element(ENABLE_NODE, &delete_gb_bvci_cmd);
- install_element(ENABLE_NODE, &delete_gb_nsei_cmd);
- install_element(ENABLE_NODE, &sgsn_pool_nsf_fixed_cmd);
- install_element(ENABLE_NODE, &sgsn_pool_nsf_normal_cmd);
-
- install_element(CONFIG_NODE, &cfg_gbproxy_cmd);
- install_node(&gbproxy_node, config_write_gbproxy);
- install_element(GBPROXY_NODE, &cfg_pool_bvc_fc_ratio_cmd);
- install_element(GBPROXY_NODE, &cfg_gbproxy_nri_bitlen_cmd);
- install_element(GBPROXY_NODE, &cfg_gbproxy_nri_null_add_cmd);
- install_element(GBPROXY_NODE, &cfg_gbproxy_nri_null_del_cmd);
-
- install_element(CONFIG_NODE, &cfg_sgsn_nsei_cmd);
- install_node(&sgsn_node, config_write_sgsn);
- install_element(SGSN_NODE, &cfg_sgsn_name_cmd);
- install_element(SGSN_NODE, &cfg_sgsn_allow_attach_cmd);
- install_element(SGSN_NODE, &cfg_sgsn_no_allow_attach_cmd);
- install_element(SGSN_NODE, &cfg_sgsn_nri_add_cmd);
- install_element(SGSN_NODE, &cfg_sgsn_nri_del_cmd);
-
-
- return 0;
-}
-
-int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg)
-{
- int rc;
-
- g_cfg = cfg;
- rc = vty_read_config_file(config_file, NULL);
- if (rc < 0) {
- fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
- return rc;
- }
-
- return 0;
-}