2019-08-30 17:48:34 +00:00
|
|
|
/* NS-over-IP proxy */
|
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
/* (C) 2010-2020 by Harald Welte <laforge@gnumonks.org>
|
2019-08-30 17:48:34 +00:00
|
|
|
* (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>
|
|
|
|
|
2020-12-04 23:31:07 +00:00
|
|
|
#include <osmocom/core/hashtable.h>
|
2020-12-02 18:33:50 +00:00
|
|
|
#include <osmocom/core/logging.h>
|
2020-12-14 15:22:39 +00:00
|
|
|
#include <osmocom/core/linuxlist.h>
|
2019-08-30 17:48:34 +00:00
|
|
|
#include <osmocom/core/talloc.h>
|
|
|
|
#include <osmocom/core/select.h>
|
|
|
|
#include <osmocom/core/rate_ctr.h>
|
2021-02-11 22:51:49 +00:00
|
|
|
#include <osmocom/core/signal.h>
|
2019-08-30 17:48:34 +00:00
|
|
|
#include <osmocom/core/stats.h>
|
2020-12-21 17:53:55 +00:00
|
|
|
#include <osmocom/core/utils.h>
|
2019-08-30 17:48:34 +00:00
|
|
|
|
2020-09-22 11:21:46 +00:00
|
|
|
#include <osmocom/gprs/gprs_ns2.h>
|
2019-08-30 17:48:34 +00:00
|
|
|
#include <osmocom/gprs/gprs_bssgp.h>
|
2020-12-12 18:02:16 +00:00
|
|
|
#include <osmocom/gprs/gprs_bssgp2.h>
|
2020-09-22 11:21:46 +00:00
|
|
|
#include <osmocom/gprs/gprs_bssgp_bss.h>
|
2020-12-05 18:59:45 +00:00
|
|
|
#include <osmocom/gprs/bssgp_bvc_fsm.h>
|
2021-02-09 16:03:03 +00:00
|
|
|
#include <osmocom/gprs/protocol/gsm_08_18.h>
|
2019-08-30 17:48:34 +00:00
|
|
|
|
2020-12-21 17:53:55 +00:00
|
|
|
#include <osmocom/gsm/gsm23236.h>
|
2019-08-30 17:48:34 +00:00
|
|
|
#include <osmocom/gsm/gsm_utils.h>
|
|
|
|
|
2021-01-29 10:13:00 +00:00
|
|
|
#include "debug.h"
|
2021-03-10 08:57:12 +00:00
|
|
|
#include <osmocom/gbproxy/gb_proxy.h>
|
2019-08-30 17:48:34 +00:00
|
|
|
|
|
|
|
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
|
|
|
|
|
|
|
|
extern void *tall_sgsn_ctx;
|
|
|
|
|
|
|
|
static const struct rate_ctr_desc global_ctr_description[] = {
|
2021-09-27 13:39:44 +00:00
|
|
|
[GBPROX_GLOB_CTR_INV_BVCI] = { "inv-bvci", "Invalid BVC Identifier " },
|
|
|
|
[GBPROX_GLOB_CTR_INV_LAI] = { "inv-lai", "Invalid Location Area Identifier" },
|
|
|
|
[GBPROX_GLOB_CTR_INV_RAI] = { "inv-rai", "Invalid Routing Area Identifier " },
|
|
|
|
[GBPROX_GLOB_CTR_INV_NSEI] = { "inv-nsei", "No BVC established for NSEI " },
|
|
|
|
[GBPROX_GLOB_CTR_PROTO_ERR_BSS] = { "proto-err:bss", "BSSGP protocol error (BSS )" },
|
|
|
|
[GBPROX_GLOB_CTR_PROTO_ERR_SGSN] = { "proto-err:sgsn", "BSSGP protocol error (SGSN)" },
|
|
|
|
[GBPROX_GLOB_CTR_NOT_SUPPORTED_BSS] = { "not-supp:bss", "Feature not supported (BSS )" },
|
|
|
|
[GBPROX_GLOB_CTR_NOT_SUPPORTED_SGSN] = { "not-supp:sgsn", "Feature not supported (SGSN)" },
|
|
|
|
[GBPROX_GLOB_CTR_RESTART_RESET_SGSN] = { "restart:sgsn", "Restarted RESET procedure (SGSN)" },
|
|
|
|
[GBPROX_GLOB_CTR_TX_ERR_SGSN] = { "tx-err:sgsn", "NS Transmission error (SGSN)" },
|
|
|
|
[GBPROX_GLOB_CTR_OTHER_ERR] = { "error", "Other error " },
|
2019-08-30 17:48:34 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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,
|
|
|
|
};
|
|
|
|
|
2020-12-04 21:24:47 +00:00
|
|
|
static int gbprox_relay2peer(struct msgb *old_msg, struct gbproxy_bvc *bvc,
|
2020-11-03 20:11:45 +00:00
|
|
|
uint16_t ns_bvci);
|
2020-11-26 17:19:21 +00:00
|
|
|
|
2021-02-12 03:59:47 +00:00
|
|
|
int tx_status(struct gbproxy_nse *nse, uint16_t ns_bvci, enum gprs_bssgp_cause cause, const uint16_t *bvci, const struct msgb *old_msg)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
struct msgb *msg = bssgp2_enc_status(cause, bvci, old_msg, nse->max_sdu_len);
|
|
|
|
if (!msg) {
|
|
|
|
LOGPNSE(nse, LOGL_NOTICE, "Unable to encode STATUS message\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2019-08-30 17:48:34 +00:00
|
|
|
|
2021-02-12 03:59:47 +00:00
|
|
|
rc = bssgp2_nsi_tx_ptp(nse->cfg->nsi, nse->nsei, ns_bvci, msg, 0);
|
|
|
|
if (rc < 0)
|
|
|
|
LOGPNSE(nse, LOGL_NOTICE, "Unable to send STATUS message\n");
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* generate BVC-STATUS mess
|
|
|
|
age with cause value derived from TLV-parser error */
|
|
|
|
static int tx_status_from_tlvp(struct gbproxy_nse *nse, enum osmo_tlv_parser_error tlv_p_err, struct msgb *orig_msg)
|
2020-12-06 15:32:01 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, msgb_bvci(orig_msg), bssgp_cause, NULL, orig_msg);
|
2020-12-06 15:32:01 +00:00
|
|
|
}
|
|
|
|
|
2019-08-30 17:48:34 +00:00
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
#if 0
|
2020-12-04 21:24:47 +00:00
|
|
|
/* feed a message down the NS-VC associated with the specified bvc */
|
2019-08-30 17:48:34 +00:00
|
|
|
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() */
|
2020-09-22 11:21:46 +00:00
|
|
|
struct gprs_ns2_inst *nsi = cfg->nsi;
|
|
|
|
struct osmo_gprs_ns2_prim nsp = {};
|
2019-08-30 17:48:34 +00:00
|
|
|
struct msgb *msg = bssgp_msgb_copy(old_msg, "msgb_relay2sgsn");
|
|
|
|
int rc;
|
|
|
|
|
2020-12-02 15:08:02 +00:00
|
|
|
DEBUGP(DGPRS, "NSE(%05u/BSS)-BVC(%05u) proxying BTS->SGSN NSE(%05u/SGSN)\n",
|
2019-08-30 17:48:34 +00:00
|
|
|
msgb_nsei(msg), ns_bvci, sgsn_nsei);
|
|
|
|
|
2020-09-22 11:21:46 +00:00
|
|
|
nsp.bvci = ns_bvci;
|
|
|
|
nsp.nsei = sgsn_nsei;
|
2019-08-30 17:48:34 +00:00
|
|
|
|
|
|
|
strip_ns_hdr(msg);
|
2020-09-22 11:21:46 +00:00
|
|
|
osmo_prim_init(&nsp.oph, SAP_NS, PRIM_NS_UNIT_DATA,
|
|
|
|
PRIM_OP_REQUEST, msg);
|
|
|
|
rc = gprs_ns2_recv_prim(nsi, &nsp.oph);
|
2019-08-30 17:48:34 +00:00
|
|
|
if (rc < 0)
|
2021-06-04 16:03:44 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(cfg->ctrg, GBPROX_GLOB_CTR_TX_ERR_SGSN));
|
2019-08-30 17:48:34 +00:00
|
|
|
return rc;
|
|
|
|
}
|
2020-12-05 18:59:45 +00:00
|
|
|
#endif
|
2019-08-30 17:48:34 +00:00
|
|
|
|
2021-01-28 18:13:19 +00:00
|
|
|
/*! 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;
|
|
|
|
}
|
|
|
|
|
2020-11-30 16:08:58 +00:00
|
|
|
/* feed a message down the NSE */
|
|
|
|
static int gbprox_relay2nse(struct msgb *old_msg, struct gbproxy_nse *nse,
|
2020-11-03 20:11:45 +00:00
|
|
|
uint16_t ns_bvci)
|
2019-08-30 17:48:34 +00:00
|
|
|
{
|
2020-11-26 17:19:21 +00:00
|
|
|
OSMO_ASSERT(nse);
|
|
|
|
OSMO_ASSERT(nse->cfg);
|
|
|
|
|
2019-08-30 17:48:34 +00:00
|
|
|
/* create a copy of the message so the old one can
|
|
|
|
* be free()d safely when we return from gbprox_rcvmsg() */
|
2020-11-26 17:19:21 +00:00
|
|
|
struct gprs_ns2_inst *nsi = nse->cfg->nsi;
|
2020-11-30 16:08:58 +00:00
|
|
|
struct msgb *msg = bssgp_msgb_copy(old_msg, "msgb_relay2nse");
|
2021-02-12 03:55:40 +00:00
|
|
|
uint32_t tlli = 0;
|
2019-08-30 17:48:34 +00:00
|
|
|
int rc;
|
|
|
|
|
2020-12-21 09:40:27 +00:00
|
|
|
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");
|
2019-08-30 17:48:34 +00:00
|
|
|
|
|
|
|
/* Strip the old NS header, it will be replaced with a new one */
|
|
|
|
strip_ns_hdr(msg);
|
|
|
|
|
2020-11-18 11:01:46 +00:00
|
|
|
/* 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. */
|
2021-02-12 03:55:40 +00:00
|
|
|
gprs_gb_parse_tlli(msgb_data(msg), msgb_length(msg), &tlli);
|
2020-11-18 11:01:46 +00:00
|
|
|
|
2021-02-12 03:55:40 +00:00
|
|
|
rc = bssgp2_nsi_tx_ptp(nsi, nse->nsei, ns_bvci, msg, tlli);
|
2020-11-30 16:08:58 +00:00
|
|
|
/* FIXME: We need a counter group for gbproxy_nse */
|
|
|
|
//if (rc < 0)
|
2021-06-04 16:03:44 +00:00
|
|
|
// rate_ctr_inc(rate_ctr_group_get_ctr(bvc->ctrg, GBPROX_PEER_CTR_TX_ERR));
|
2020-11-30 16:08:58 +00:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2020-12-04 21:24:47 +00:00
|
|
|
/* feed a message down the NS-VC associated with the specified bvc */
|
|
|
|
static int gbprox_relay2peer(struct msgb *old_msg, struct gbproxy_bvc *bvc,
|
2020-11-30 16:08:58 +00:00
|
|
|
uint16_t ns_bvci)
|
|
|
|
{
|
|
|
|
int rc;
|
2020-12-04 21:24:47 +00:00
|
|
|
struct gbproxy_nse *nse = bvc->nse;
|
2020-11-30 16:08:58 +00:00
|
|
|
OSMO_ASSERT(nse);
|
|
|
|
|
|
|
|
rc = gbprox_relay2nse(old_msg, nse, ns_bvci);
|
2019-08-30 17:48:34 +00:00
|
|
|
if (rc < 0)
|
2021-06-04 16:03:44 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(bvc->ctrg, GBPROX_PEER_CTR_TX_ERR));
|
2019-08-30 17:48:34 +00:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
|
2019-08-30 17:48:34 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* PTP BVC handling
|
|
|
|
***********************************************************************/
|
|
|
|
|
2020-12-21 17:53:55 +00:00
|
|
|
/* 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)
|
2019-08-30 17:48:34 +00:00
|
|
|
{
|
2020-12-21 17:53:55 +00:00
|
|
|
struct gbproxy_sgsn *sgsn = NULL;
|
|
|
|
struct gbproxy_sgsn *sgsn_avoid = NULL;
|
2019-08-30 17:48:34 +00:00
|
|
|
|
2020-12-21 17:53:55 +00:00
|
|
|
int tlli_type;
|
|
|
|
int16_t nri;
|
|
|
|
bool null_nri = false;
|
2019-08-30 17:48:34 +00:00
|
|
|
|
2020-12-21 17:53:55 +00:00
|
|
|
if (!tlli) {
|
2021-05-27 16:13:36 +00:00
|
|
|
sgsn = gbproxy_sgsn_by_available(cfg);
|
2020-12-21 17:53:55 +00:00
|
|
|
if (!sgsn) {
|
2021-05-27 16:13:36 +00:00
|
|
|
LOGP(DGPRS, LOGL_ERROR, "Could not find any available SGSN\n");
|
2020-12-21 17:53:55 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2021-05-27 16:13:36 +00:00
|
|
|
LOGPSGSN(sgsn, LOGL_INFO, "Could not get TLLI, using first available SGSN\n");
|
2020-12-21 17:53:55 +00:00
|
|
|
return sgsn;
|
2020-12-05 18:59:45 +00:00
|
|
|
}
|
2020-12-21 17:53:55 +00:00
|
|
|
|
|
|
|
if (cfg->pool.nri_bitlen == 0) {
|
|
|
|
/* Pooling is disabled */
|
2021-05-27 16:13:36 +00:00
|
|
|
sgsn = gbproxy_sgsn_by_available(cfg);
|
2020-12-21 17:53:55 +00:00
|
|
|
if (!sgsn) {
|
2021-05-27 16:13:36 +00:00
|
|
|
LOGP(DGPRS, LOGL_ERROR, "Could not find any available SGSN\n");
|
2020-12-21 17:53:55 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-05-27 16:13:36 +00:00
|
|
|
LOGPSGSN(sgsn, LOGL_INFO, "Pooling disabled, using first available SGSN\n");
|
2020-12-21 17:53:55 +00:00
|
|
|
} 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 */
|
2021-01-21 17:44:51 +00:00
|
|
|
LOGP(DGPRS, LOGL_ERROR, "Could not extract NRI from local TLLI %08x\n", *tlli);
|
2020-12-21 17:53:55 +00:00
|
|
|
}
|
2021-01-21 17:44:51 +00:00
|
|
|
} else {
|
|
|
|
LOGP(DGPRS, LOGL_INFO, "TLLI %08x is neither local nor foreign, not routing by NRI\n", *tlli);
|
2020-12-21 17:53:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
2019-08-30 17:48:34 +00:00
|
|
|
}
|
|
|
|
|
2020-12-21 17:53:55 +00:00
|
|
|
/*! 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)
|
2019-08-30 17:48:34 +00:00
|
|
|
{
|
2020-12-21 17:53:55 +00:00
|
|
|
struct gbproxy_sgsn *sgsn;
|
|
|
|
struct gbproxy_bvc *sgsn_bvc = NULL;
|
2020-12-30 11:13:36 +00:00
|
|
|
int i;
|
2020-12-05 18:59:45 +00:00
|
|
|
|
2020-12-21 17:53:55 +00:00
|
|
|
sgsn = gbproxy_select_sgsn(cfg, tlli);
|
|
|
|
if (!sgsn) {
|
|
|
|
LOGPCELL(cell, LOGL_ERROR, "Could not find any SGSN, dropping message!\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
2020-12-05 18:59:45 +00:00
|
|
|
|
2020-12-21 17:53:55 +00:00
|
|
|
/* Get the BVC for this SGSN/NSE */
|
2020-12-30 11:13:36 +00:00
|
|
|
for (i = 0; i < ARRAY_SIZE(cell->sgsn_bvc); i++) {
|
2020-12-05 18:59:45 +00:00
|
|
|
sgsn_bvc = cell->sgsn_bvc[i];
|
2020-12-21 17:53:55 +00:00
|
|
|
if (!sgsn_bvc)
|
|
|
|
continue;
|
|
|
|
if (sgsn->nse != sgsn_bvc->nse)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
return sgsn_bvc;
|
2020-12-05 18:59:45 +00:00
|
|
|
}
|
2020-12-21 17:53:55 +00:00
|
|
|
|
|
|
|
/* This shouldn't happen */
|
2020-12-28 17:07:27 +00:00
|
|
|
LOGPCELL(cell, LOGL_ERROR, "Could not find matching BVC for SGSN %s, dropping message!\n", sgsn->name);
|
2020-12-21 17:53:55 +00:00
|
|
|
return NULL;
|
2019-08-30 17:48:34 +00:00
|
|
|
}
|
|
|
|
|
2020-12-21 17:53:55 +00:00
|
|
|
/*! 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);
|
|
|
|
}
|
2020-12-05 18:59:45 +00:00
|
|
|
|
2021-11-02 10:54:27 +00:00
|
|
|
static int gbproxy_decode_bssgp(const struct bssgp_normal_hdr *bgph, int msg_len, struct tlv_parsed *tp, const char *log_pfx)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
/* UNITDATA PDUs have a different header than the other PDUs */
|
|
|
|
if (bgph->pdu_type == BSSGP_PDUT_UL_UNITDATA || bgph->pdu_type == BSSGP_PDUT_DL_UNITDATA) {
|
|
|
|
const struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) bgph;
|
|
|
|
if (msg_len < sizeof(*budh))
|
2021-11-18 14:27:37 +00:00
|
|
|
return OSMO_TLVP_ERR_MAND_IE_MISSING;
|
2021-11-02 10:54:27 +00:00
|
|
|
rc = osmo_tlv_prot_parse(&osmo_pdef_bssgp, tp, 1, budh->pdu_type, budh->data,
|
|
|
|
msg_len - 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,
|
|
|
|
msg_len - sizeof(*bgph), 0, 0, DGPRS, log_pfx);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2021-10-29 16:28:13 +00:00
|
|
|
static int gbproxy_tlli_from_status_pdu(struct tlv_parsed *tp, uint32_t *tlli, char *log_pfx);
|
|
|
|
|
2019-08-30 17:48:34 +00:00
|
|
|
/* Receive an incoming PTP message from a BSS-side NS-VC */
|
2020-12-05 18:59:45 +00:00
|
|
|
static int gbprox_rx_ptp_from_bss(struct gbproxy_nse *nse, struct msgb *msg, uint16_t ns_bvci)
|
2019-08-30 17:48:34 +00:00
|
|
|
{
|
2020-12-06 12:35:24 +00:00
|
|
|
struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
|
2020-12-05 18:59:45 +00:00
|
|
|
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);
|
2019-08-30 17:48:34 +00:00
|
|
|
|
2020-12-10 16:59:46 +00:00
|
|
|
if (ns_bvci == 0 || ns_bvci == 1) {
|
2020-12-05 18:59:45 +00:00
|
|
|
LOGP(DGPRS, LOGL_NOTICE, "%s BVCI=%05u is not PTP\n", log_pfx, ns_bvci);
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
|
2020-12-06 12:35:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!(bssgp_pdu_type_flags(bgph->pdu_type) & BSSGP_PDUF_PTP)) {
|
2020-12-05 18:59:45 +00:00
|
|
|
LOGP(DGPRS, LOGL_NOTICE, "%s %s not allowed in PTP BVC\n", log_pfx, pdut_name);
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
|
2020-12-06 12:35:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!(bssgp_pdu_type_flags(bgph->pdu_type) & BSSGP_PDUF_UL)) {
|
2020-12-05 18:59:45 +00:00
|
|
|
LOGP(DGPRS, LOGL_NOTICE, "%s %s not allowed in uplink direction\n", log_pfx, pdut_name);
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
|
2020-12-06 12:35:24 +00:00
|
|
|
}
|
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
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);
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_UNKNOWN_BVCI, &ns_bvci, msg);
|
2020-12-05 18:59:45 +00:00
|
|
|
}
|
|
|
|
|
2021-11-02 10:54:27 +00:00
|
|
|
rc = gbproxy_decode_bssgp(bgph, msgb_bssgp_len(msg), &tp, log_pfx);
|
2020-12-05 18:59:45 +00:00
|
|
|
if (rc < 0) {
|
2021-06-04 16:03:44 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(nse->cfg->ctrg, GBPROX_GLOB_CTR_PROTO_ERR_BSS));
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status_from_tlvp(nse, rc, msg);
|
2019-08-30 17:48:34 +00:00
|
|
|
}
|
2020-12-08 20:43:22 +00:00
|
|
|
/* hack to get both msg + tlv_parsed passed via osmo_fsm_inst_dispatch */
|
|
|
|
msgb_bcid(msg) = (void *)&tp;
|
2019-08-30 17:48:34 +00:00
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
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));
|
2020-12-21 17:53:55 +00:00
|
|
|
rc = gbprox_bss2sgsn_tlli(bss_bvc->cell, msg, &tlli, false);
|
2020-12-05 18:59:45 +00:00
|
|
|
break;
|
|
|
|
case BSSGP_PDUT_RADIO_STATUS:
|
|
|
|
if (TLVP_PRESENT(&tp, BSSGP_IE_TLLI)) {
|
|
|
|
tlli = osmo_load32be(TLVP_VAL(&tp, BSSGP_IE_TLLI));
|
2020-12-21 17:53:55 +00:00
|
|
|
rc = gbprox_bss2sgsn_tlli(bss_bvc->cell, msg, &tlli, false);
|
2020-12-05 18:59:45 +00:00
|
|
|
} 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));
|
2021-01-21 17:46:51 +00:00
|
|
|
/* Convert the TMSI into a FOREIGN TLLI so it is routed appropriately */
|
|
|
|
tlli = gprs_tmsi2tlli(tlli, TLLI_FOREIGN);
|
2020-12-21 17:53:55 +00:00
|
|
|
rc = gbprox_bss2sgsn_tlli(bss_bvc->cell, msg, &tlli, false);
|
2020-12-05 18:59:45 +00:00
|
|
|
} else if (TLVP_PRESENT(&tp, BSSGP_IE_IMSI)) {
|
2021-01-11 04:00:46 +00:00
|
|
|
/* FIXME: Use the IMSI as selector? */
|
2020-12-21 17:53:55 +00:00
|
|
|
rc = gbprox_bss2sgsn_tlli(bss_bvc->cell, msg, NULL, false);
|
2020-12-05 18:59:45 +00:00
|
|
|
} 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:
|
2021-01-18 17:38:27 +00:00
|
|
|
{
|
|
|
|
/* 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);
|
2021-07-09 15:44:30 +00:00
|
|
|
nse = gbproxy_nse_by_imsi(nse->cfg, mi.imsi, CACHE_USAGE_PAGING);
|
2021-01-18 17:38:27 +00:00
|
|
|
if (nse) {
|
|
|
|
OSMO_ASSERT(nse->sgsn_facing);
|
|
|
|
rc = gbprox_relay2nse(msg, nse, ns_bvci);
|
|
|
|
} else {
|
2021-01-19 10:37:55 +00:00
|
|
|
LOGPBVC(bss_bvc, LOGL_ERROR, "Rx unmatched %s with IMSI %s\n", pdut_name, mi.imsi);
|
2021-01-18 17:38:27 +00:00
|
|
|
}
|
2020-12-05 18:59:45 +00:00
|
|
|
break;
|
2021-01-18 17:38:27 +00:00
|
|
|
}
|
2020-12-05 18:59:45 +00:00
|
|
|
case BSSGP_PDUT_FLOW_CONTROL_BVC:
|
2020-12-08 20:43:22 +00:00
|
|
|
osmo_fsm_inst_dispatch(bss_bvc->fi, BSSGP_BVCFSM_E_RX_FC_BVC, msg);
|
2020-12-05 18:59:45 +00:00
|
|
|
break;
|
|
|
|
case BSSGP_PDUT_STATUS:
|
2021-10-29 16:28:13 +00:00
|
|
|
{
|
|
|
|
struct gbproxy_sgsn *sgsn;
|
|
|
|
/* Check if the status needs to be terminated locally */
|
|
|
|
uint8_t cause = *TLVP_VAL(&tp, BSSGP_IE_CAUSE);
|
|
|
|
|
|
|
|
LOGPNSE(nse, LOGL_NOTICE, "Rx STATUS cause=0x%02x(%s)\n", cause,
|
|
|
|
bssgp_cause_str(cause));
|
|
|
|
|
|
|
|
if (gbproxy_tlli_from_status_pdu(&tp, &tlli, log_pfx) == 0)
|
|
|
|
sgsn = gbproxy_select_sgsn(nse->cfg, &tlli);
|
|
|
|
else
|
|
|
|
sgsn = gbproxy_select_sgsn(nse->cfg, NULL);
|
|
|
|
|
|
|
|
if (!sgsn) {
|
|
|
|
rc = -EINVAL;
|
2020-12-05 18:59:45 +00:00
|
|
|
break;
|
2021-10-29 16:28:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
rc = gbprox_relay2nse(msg, sgsn->nse, ns_bvci);
|
2020-12-05 18:59:45 +00:00
|
|
|
break;
|
|
|
|
}
|
2021-10-29 16:28:13 +00:00
|
|
|
}
|
2019-08-30 17:48:34 +00:00
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
return 0;
|
2019-08-30 17:48:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Receive an incoming PTP message from a SGSN-side NS-VC */
|
2020-12-05 18:59:45 +00:00
|
|
|
static int gbprox_rx_ptp_from_sgsn(struct gbproxy_nse *nse, struct msgb *msg, uint16_t ns_bvci)
|
2019-08-30 17:48:34 +00:00
|
|
|
{
|
2020-12-06 12:35:24 +00:00
|
|
|
struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
|
2020-12-05 18:59:45 +00:00
|
|
|
const char *pdut_name = osmo_tlv_prot_msg_name(&osmo_pdef_bssgp, bgph->pdu_type);
|
|
|
|
struct gbproxy_bvc *sgsn_bvc, *bss_bvc;
|
2020-12-08 20:43:22 +00:00
|
|
|
struct tlv_parsed tp;
|
2020-12-05 18:59:45 +00:00
|
|
|
char log_pfx[32];
|
2020-12-08 20:43:22 +00:00
|
|
|
int rc;
|
2020-12-05 18:59:45 +00:00
|
|
|
|
|
|
|
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);
|
2019-08-30 17:48:34 +00:00
|
|
|
|
2020-12-10 16:59:46 +00:00
|
|
|
if (ns_bvci == 0 || ns_bvci == 1) {
|
2020-12-05 18:59:45 +00:00
|
|
|
LOGP(DGPRS, LOGL_NOTICE, "%s BVCI is not PTP\n", log_pfx);
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
|
2020-12-06 12:35:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!(bssgp_pdu_type_flags(bgph->pdu_type) & BSSGP_PDUF_PTP)) {
|
2020-12-05 18:59:45 +00:00
|
|
|
LOGP(DGPRS, LOGL_NOTICE, "%s %s not allowed in PTP BVC\n", log_pfx, pdut_name);
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
|
2020-12-06 12:35:24 +00:00
|
|
|
}
|
2019-08-30 17:48:34 +00:00
|
|
|
|
2020-12-06 12:35:24 +00:00
|
|
|
if (!(bssgp_pdu_type_flags(bgph->pdu_type) & BSSGP_PDUF_DL)) {
|
2020-12-05 18:59:45 +00:00
|
|
|
LOGP(DGPRS, LOGL_NOTICE, "%s %s not allowed in downlink direction\n", log_pfx, pdut_name);
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
|
2020-12-06 12:35:24 +00:00
|
|
|
}
|
2019-08-30 17:48:34 +00:00
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
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);
|
2021-06-04 16:03:44 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(nse->cfg->ctrg, GBPROX_GLOB_CTR_INV_BVCI));
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_UNKNOWN_BVCI, &ns_bvci, msg);
|
2019-08-30 17:48:34 +00:00
|
|
|
}
|
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
if (!bssgp_bvc_fsm_is_unblocked(sgsn_bvc->fi)) {
|
|
|
|
LOGPBVC(sgsn_bvc, LOGL_NOTICE, "Rx %s: Dropping on blocked BVC\n", pdut_name);
|
2021-06-04 16:03:44 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(sgsn_bvc->ctrg, GBPROX_PEER_CTR_DROPPED));
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_BVCI_BLOCKED, &ns_bvci, msg);
|
2019-08-30 17:48:34 +00:00
|
|
|
}
|
2020-12-08 20:43:22 +00:00
|
|
|
|
2021-11-02 10:54:27 +00:00
|
|
|
rc = gbproxy_decode_bssgp(bgph, msgb_bssgp_len(msg), &tp, log_pfx);
|
2020-12-08 20:43:22 +00:00
|
|
|
if (rc < 0) {
|
2021-11-02 10:55:06 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(nse->cfg->ctrg, GBPROX_GLOB_CTR_PROTO_ERR_SGSN));
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status_from_tlvp(nse, rc, msg);
|
2020-12-08 20:43:22 +00:00
|
|
|
}
|
|
|
|
/* hack to get both msg + tlv_parsed passed via osmo_fsm_inst_dispatch */
|
|
|
|
msgb_bcid(msg) = (void *)&tp;
|
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
OSMO_ASSERT(sgsn_bvc->cell);
|
|
|
|
bss_bvc = sgsn_bvc->cell->bss_bvc;
|
|
|
|
|
2020-12-08 20:43:22 +00:00
|
|
|
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);
|
2021-01-18 17:38:27 +00:00
|
|
|
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);
|
2021-07-09 15:44:30 +00:00
|
|
|
gbproxy_imsi_cache_update(nse, mi.imsi, CACHE_USAGE_PAGING);
|
2021-01-18 17:38:27 +00:00
|
|
|
break;
|
|
|
|
}
|
2020-12-08 20:43:22 +00:00
|
|
|
default:
|
2021-01-18 17:38:27 +00:00
|
|
|
break;
|
2020-12-08 20:43:22 +00:00
|
|
|
}
|
2021-01-18 17:38:27 +00:00
|
|
|
return gbprox_relay2peer(msg, bss_bvc, bss_bvc->bvci);
|
2020-12-08 20:43:22 +00:00
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* 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;
|
2020-12-12 14:01:17 +00:00
|
|
|
struct gbproxy_nse *sgsn_nse;
|
2020-12-05 18:59:45 +00:00
|
|
|
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 */
|
|
|
|
|
2021-02-09 15:02:00 +00:00
|
|
|
bvc->cell = gbproxy_cell_alloc(cfg, bvci, ra_id, cell_id);
|
2020-12-05 18:59:45 +00:00
|
|
|
OSMO_ASSERT(bvc->cell);
|
|
|
|
|
|
|
|
/* link us to the cell and vice-versa */
|
|
|
|
bvc->cell->bss_bvc = bvc;
|
2020-12-12 14:01:17 +00:00
|
|
|
}
|
2020-12-05 18:59:45 +00:00
|
|
|
|
2021-04-08 06:39:12 +00:00
|
|
|
/* Ensure we have the correct RA/CELL ID */
|
|
|
|
if (!gsm48_ra_equal(&bvc->cell->id.raid, ra_id)) {
|
|
|
|
LOGPBVC(bvc, LOGL_NOTICE, "RAID changed from %s to %s, updating cell\n", osmo_rai_name(&bvc->cell->id.raid), osmo_rai_name(ra_id));
|
|
|
|
memcpy(&bvc->cell->id.raid, ra_id, sizeof(*ra_id));
|
|
|
|
}
|
|
|
|
if (bvc->cell->id.cid != cell_id) {
|
|
|
|
LOGPBVC(bvc, LOGL_NOTICE, "CellID changed from %05d to %05d, updating cell\n", bvc->cell->id.cid, cell_id);
|
|
|
|
bvc->cell->id.cid = cell_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reallocate SGSN-side BVCs of the cell, and reset them
|
2021-11-12 14:52:03 +00:00
|
|
|
* Removing and reallocating is needed because the ra_id/cell_id might have changed */
|
2020-12-12 14:01:17 +00:00
|
|
|
hash_for_each(cfg->sgsn_nses, i, sgsn_nse, list) {
|
|
|
|
struct gbproxy_bvc *sgsn_bvc = gbproxy_bvc_by_bvci(sgsn_nse, bvci);
|
2021-09-24 14:45:38 +00:00
|
|
|
if (!sgsn_bvc)
|
|
|
|
sgsn_bvc = gbproxy_bvc_by_bvci_inactive(sgsn_nse, bvci);
|
2020-12-12 14:01:17 +00:00
|
|
|
if (sgsn_bvc)
|
2021-04-08 06:39:12 +00:00
|
|
|
gbproxy_bvc_free(sgsn_bvc);
|
|
|
|
|
|
|
|
sgsn_bvc = gbproxy_bvc_alloc(sgsn_nse, bvci);
|
|
|
|
OSMO_ASSERT(sgsn_bvc);
|
|
|
|
sgsn_bvc->cell = bvc->cell;
|
|
|
|
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_max_pdu_len(sgsn_bvc->fi, sgsn_nse->max_sdu_len);
|
|
|
|
bssgp_bvc_fsm_set_ops(sgsn_bvc->fi, &sgsn_ptp_bvc_fsm_ops, sgsn_bvc);
|
|
|
|
gbproxy_cell_add_sgsn_bvc(bvc->cell, sgsn_bvc);
|
2020-12-05 18:59:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
}
|
2019-08-30 17:48:34 +00:00
|
|
|
|
2020-12-08 20:43:22 +00:00
|
|
|
/* 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)
|
|
|
|
{
|
2020-12-12 18:02:16 +00:00
|
|
|
struct bssgp2_flow_ctrl fc_reduced;
|
2020-12-08 20:43:22 +00:00
|
|
|
struct gbproxy_bvc *bss_bvc = priv;
|
2020-12-12 18:02:16 +00:00
|
|
|
struct gbproxy_cell *cell;
|
|
|
|
struct gbproxy_config *cfg;
|
2020-12-08 20:43:22 +00:00
|
|
|
|
2020-12-12 18:02:16 +00:00
|
|
|
OSMO_ASSERT(bss_bvc);
|
|
|
|
OSMO_ASSERT(fc);
|
|
|
|
|
|
|
|
cell = bss_bvc->cell;
|
2020-12-08 20:43:22 +00:00
|
|
|
if (!cell)
|
|
|
|
return;
|
|
|
|
|
2020-12-12 18:02:16 +00:00
|
|
|
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 */
|
2020-12-08 20:43:22 +00:00
|
|
|
|
2020-12-12 18:02:16 +00:00
|
|
|
dispatch_to_all_sgsn_bvc(cell, BSSGP_BVCFSM_E_REQ_FC_BVC, (void *) &fc_reduced);
|
2020-12-08 20:43:22 +00:00
|
|
|
}
|
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
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,
|
2020-12-08 20:43:22 +00:00
|
|
|
.rx_fc_bvc = bss_ptp_bvc_fc_bvc,
|
2020-12-05 18:59:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|
2019-08-30 17:48:34 +00:00
|
|
|
}
|
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
const struct bssgp_bvc_fsm_ops sgsn_sig_bvc_fsm_ops = {
|
|
|
|
.reset_notification = sgsn_sig_bvc_reset_notif,
|
|
|
|
};
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* Signaling BVC handling
|
|
|
|
***********************************************************************/
|
|
|
|
|
2020-12-02 21:53:26 +00:00
|
|
|
/* process a BVC-RESET message from the BSS side */
|
2020-12-05 18:59:45 +00:00
|
|
|
static int rx_bvc_reset_from_bss(struct gbproxy_nse *nse, struct msgb *msg, struct tlv_parsed *tp)
|
2020-12-02 21:53:26 +00:00
|
|
|
{
|
2020-12-04 21:24:47 +00:00
|
|
|
struct gbproxy_bvc *from_bvc = NULL;
|
2020-12-05 18:59:45 +00:00
|
|
|
uint16_t bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI));
|
|
|
|
uint32_t features = 0; // FIXME: make configurable
|
2020-12-02 21:53:26 +00:00
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
LOGPNSE(nse, LOGL_INFO, "Rx BVC-RESET (BVCI=%05u)\n", bvci);
|
2020-12-02 21:53:26 +00:00
|
|
|
|
2020-12-02 22:03:22 +00:00
|
|
|
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) */
|
2020-12-02 22:06:37 +00:00
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
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;
|
|
|
|
}
|
2021-02-12 04:05:14 +00:00
|
|
|
bssgp_bvc_fsm_set_max_pdu_len(from_bvc->fi, nse->max_sdu_len);
|
2020-12-05 18:59:45 +00:00
|
|
|
bssgp_bvc_fsm_set_ops(from_bvc->fi, &bss_sig_bvc_fsm_ops, from_bvc);
|
2020-12-02 21:53:26 +00:00
|
|
|
}
|
2020-12-02 22:03:22 +00:00
|
|
|
} else {
|
2020-12-05 18:59:45 +00:00
|
|
|
from_bvc = gbproxy_bvc_by_bvci(nse, bvci);
|
2020-12-04 21:24:47 +00:00
|
|
|
if (!from_bvc) {
|
2020-12-02 21:53:26 +00:00
|
|
|
/* if a PTP-BVC is reset, and we don't know that
|
2020-12-04 21:24:47 +00:00
|
|
|
* PTP-BVCI yet, we should allocate a new bvc */
|
|
|
|
from_bvc = gbproxy_bvc_alloc(nse, bvci);
|
|
|
|
OSMO_ASSERT(from_bvc);
|
2020-12-05 18:59:45 +00:00
|
|
|
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;
|
|
|
|
}
|
2021-02-12 04:05:14 +00:00
|
|
|
bssgp_bvc_fsm_set_max_pdu_len(from_bvc->fi, nse->max_sdu_len);
|
2020-12-05 18:59:45 +00:00
|
|
|
bssgp_bvc_fsm_set_ops(from_bvc->fi, &bss_ptp_bvc_fsm_ops, from_bvc);
|
2020-12-02 21:53:26 +00:00
|
|
|
}
|
|
|
|
}
|
2020-12-05 18:59:45 +00:00
|
|
|
/* hand into FSM for further processing */
|
|
|
|
osmo_fsm_inst_dispatch(from_bvc->fi, BSSGP_BVCFSM_E_RX_RESET, msg);
|
|
|
|
return 0;
|
2020-12-02 21:53:26 +00:00
|
|
|
}
|
|
|
|
|
2021-02-09 16:03:03 +00:00
|
|
|
/* Receive an incoming RIM message from a BSS-side NS-VC */
|
|
|
|
static int gbprox_rx_rim_from_bss(struct tlv_parsed *tp, struct gbproxy_nse *nse, struct msgb *msg, char *log_pfx,
|
|
|
|
const char *pdut_name)
|
|
|
|
{
|
|
|
|
struct gbproxy_sgsn *sgsn;
|
|
|
|
struct gbproxy_cell *dest_cell;
|
|
|
|
struct gbproxy_cell *src_cell;
|
|
|
|
struct bssgp_rim_routing_info dest_ri;
|
|
|
|
struct bssgp_rim_routing_info src_ri;
|
|
|
|
int rc;
|
2021-02-10 16:54:44 +00:00
|
|
|
char ri_src_str[64];
|
|
|
|
char ri_dest_str[64];
|
2021-02-12 03:59:47 +00:00
|
|
|
uint16_t ns_bvci = msgb_bvci(msg);
|
2021-02-09 16:03:03 +00:00
|
|
|
|
|
|
|
rc = bssgp_parse_rim_ri(&dest_ri, TLVP_VAL(&tp[0], BSSGP_IE_RIM_ROUTING_INFO),
|
|
|
|
TLVP_LEN(&tp[0], BSSGP_IE_RIM_ROUTING_INFO));
|
|
|
|
if (rc < 0) {
|
|
|
|
LOGP(DGPRS, LOGL_ERROR, "%s %s cannot parse destination RIM routing info\n", log_pfx, pdut_name);
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
|
2021-02-09 16:03:03 +00:00
|
|
|
}
|
|
|
|
rc = bssgp_parse_rim_ri(&src_ri, TLVP_VAL(&tp[1], BSSGP_IE_RIM_ROUTING_INFO),
|
|
|
|
TLVP_LEN(&tp[1], BSSGP_IE_RIM_ROUTING_INFO));
|
|
|
|
if (rc < 0) {
|
|
|
|
LOGP(DGPRS, LOGL_ERROR, "%s %s cannot parse source RIM routing info\n", log_pfx, pdut_name);
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
|
2021-02-09 16:03:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Since gbproxy is 2G only we do not expect to get RIM messages only from GERAN cells. */
|
|
|
|
if (src_ri.discr != BSSGP_RIM_ROUTING_INFO_GERAN) {
|
|
|
|
LOGP(DGPRS, LOGL_ERROR, "%s %s source RIM routing info is not GERAN (%s)\n", log_pfx, pdut_name,
|
|
|
|
bssgp_rim_ri_name(&src_ri));
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg);
|
2021-02-09 16:03:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Lookup source cell to make sure that the source RIM routing information actually belongs
|
|
|
|
* to a valid cell that we know */
|
|
|
|
src_cell = gbproxy_cell_by_cellid(nse->cfg, &src_ri.geran.raid, src_ri.geran.cid);
|
|
|
|
if (!src_cell) {
|
|
|
|
LOGP(DGPRS, LOGL_NOTICE, "%s %s cannot find cell for source RIM routing info (%s)\n", log_pfx,
|
|
|
|
pdut_name, bssgp_rim_ri_name(&src_ri));
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg);
|
2021-02-09 16:03:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: Use bssgp_bvc_get_features_negotiated(src_cell->bss_bvc->fi) to check if the the BSS sided BVC actually
|
|
|
|
* did negotiate RIM support. If not we should respond with a BSSGP STATUS message. The cause code should be
|
|
|
|
* BSSGP_CAUSE_PDU_INCOMP_FEAT. */
|
|
|
|
|
|
|
|
/* If Destination is known by gbproxy, route directly */
|
|
|
|
if (dest_ri.discr == BSSGP_RIM_ROUTING_INFO_GERAN) {
|
|
|
|
dest_cell = gbproxy_cell_by_cellid(nse->cfg, &dest_ri.geran.raid, dest_ri.geran.cid);
|
|
|
|
if (dest_cell) {
|
|
|
|
/* TODO: Also check if dest_cell->bss_bvc is RIM-capable (see also above). If not we should
|
|
|
|
* respond with a BSSGP STATUS message as well because it also would make no sense to try
|
|
|
|
* routing the RIM message to the next RIM-capable SGSN. */
|
2021-02-10 16:54:44 +00:00
|
|
|
LOGP(DLBSSGP, LOGL_DEBUG, "%s %s relaying to peer (nsei=%u) RIM-PDU: src=%s, dest=%s\n",
|
|
|
|
log_pfx, pdut_name, dest_cell->bss_bvc->nse->nsei,
|
|
|
|
bssgp_rim_ri_name_buf(ri_src_str, sizeof(ri_src_str), &src_ri),
|
|
|
|
bssgp_rim_ri_name_buf(ri_dest_str, sizeof(ri_dest_str), &dest_ri));
|
2021-02-09 16:03:03 +00:00
|
|
|
return gbprox_relay2peer(msg, dest_cell->bss_bvc, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Otherwise pass on to a RIM-capable SGSN */
|
|
|
|
/* TODO: We need to extend gbproxy_select_sgsn() so that it selects a RIM-capable SGSN, at the moment we just
|
|
|
|
* get any SGSN and just assume that it is RIM-capable. */
|
|
|
|
sgsn = gbproxy_select_sgsn(nse->cfg, NULL);
|
|
|
|
if (!sgsn) {
|
|
|
|
LOGP(DGPRS, LOGL_NOTICE,
|
|
|
|
"%s %s cannot route RIM message (%s to %s) since no RIM capable SGSN is found!\n", log_pfx,
|
|
|
|
pdut_name, bssgp_rim_ri_name(&src_ri), bssgp_rim_ri_name(&dest_ri));
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg);
|
2021-02-09 16:03:03 +00:00
|
|
|
}
|
2021-02-10 16:54:44 +00:00
|
|
|
LOGP(DLBSSGP, LOGL_DEBUG, "%s %s relaying to SGSN(%05u/%s) RIM-PDU: src=%s, dest=%s\n",
|
|
|
|
log_pfx, pdut_name, sgsn->nse->nsei, sgsn->name,
|
|
|
|
bssgp_rim_ri_name_buf(ri_src_str, sizeof(ri_src_str), &src_ri),
|
|
|
|
bssgp_rim_ri_name_buf(ri_dest_str, sizeof(ri_dest_str), &dest_ri));
|
2021-02-09 16:03:03 +00:00
|
|
|
|
|
|
|
return gbprox_relay2nse(msg, sgsn->nse, 0);
|
|
|
|
}
|
|
|
|
|
2021-10-28 14:13:03 +00:00
|
|
|
/* Extract the TLLI from the PDU-in-error of the STATUS PDU (if available) */
|
|
|
|
static int gbproxy_tlli_from_status_pdu(struct tlv_parsed *tp, uint32_t *tlli, char *log_pfx)
|
2021-09-29 09:51:51 +00:00
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
int pdu_len = TLVP_LEN(&tp[0], BSSGP_IE_PDU_IN_ERROR);
|
|
|
|
const uint8_t *pdu_data = TLVP_VAL(&tp[0], BSSGP_IE_PDU_IN_ERROR);
|
|
|
|
struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *)pdu_data;
|
2021-11-02 10:55:57 +00:00
|
|
|
struct tlv_parsed tp_inner;
|
2021-09-29 09:51:51 +00:00
|
|
|
|
2021-11-02 10:55:57 +00:00
|
|
|
rc = gbproxy_decode_bssgp(bgph, pdu_len, &tp_inner, log_pfx);
|
2021-11-18 14:27:49 +00:00
|
|
|
/* Ignore decode failure due to truncated message */
|
|
|
|
if (rc < 0 && rc != OSMO_TLVP_ERR_OFS_BEYOND_BUFFER)
|
2021-09-29 09:51:51 +00:00
|
|
|
return rc;
|
|
|
|
|
2021-11-02 10:55:57 +00:00
|
|
|
if (TLVP_PRESENT(&tp_inner, BSSGP_IE_TLLI)) {
|
|
|
|
*tlli = osmo_load32be(TLVP_VAL(&tp_inner, BSSGP_IE_TLLI));
|
|
|
|
} else if (TLVP_PRESENT(&tp_inner, BSSGP_IE_TMSI)) {
|
2021-09-29 09:51:56 +00:00
|
|
|
/* we treat the TMSI like a TLLI and extract the NRI from it */
|
2021-11-02 10:55:57 +00:00
|
|
|
*tlli = osmo_load32be(TLVP_VAL(&tp_inner, BSSGP_IE_TMSI));
|
2021-09-29 09:51:56 +00:00
|
|
|
/* Convert the TMSI into a FOREIGN TLLI so it is routed appropriately */
|
|
|
|
*tlli = gprs_tmsi2tlli(*tlli, TLLI_FOREIGN);
|
|
|
|
} else {
|
2021-09-29 09:51:51 +00:00
|
|
|
return -ENOENT;
|
2021-09-29 09:51:56 +00:00
|
|
|
}
|
2021-09-29 09:51:51 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-10-27 14:05:37 +00:00
|
|
|
/* Extract the BVCI from the PDU-in-error of the STATUS PDU (if available) */
|
|
|
|
static int gbproxy_bvci_from_status_pdu(struct tlv_parsed *tp, uint16_t *bvci, char *log_pfx)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
int pdu_len = TLVP_LEN(&tp[0], BSSGP_IE_PDU_IN_ERROR);
|
|
|
|
const uint8_t *pdu_data = TLVP_VAL(&tp[0], BSSGP_IE_PDU_IN_ERROR);
|
|
|
|
struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *)pdu_data;
|
2021-11-02 10:55:57 +00:00
|
|
|
struct tlv_parsed tp_inner;
|
2021-10-27 14:05:37 +00:00
|
|
|
|
2021-11-02 10:55:57 +00:00
|
|
|
rc = gbproxy_decode_bssgp(bgph, pdu_len, &tp_inner, log_pfx);
|
2021-11-18 14:27:49 +00:00
|
|
|
/* Ignore decode failure due to truncated message */
|
|
|
|
if (rc < 0 && rc != OSMO_TLVP_ERR_OFS_BEYOND_BUFFER)
|
2021-10-27 14:05:37 +00:00
|
|
|
return rc;
|
|
|
|
|
2021-11-02 10:55:57 +00:00
|
|
|
if (TLVP_PRESENT(&tp_inner, BSSGP_IE_BVCI))
|
|
|
|
*bvci = ntohs(tlvp_val16_unal(&tp_inner, BSSGP_IE_BVCI));
|
2021-10-27 14:05:37 +00:00
|
|
|
else
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-08-30 17:48:34 +00:00
|
|
|
/* Receive an incoming signalling message from a BSS-side NS-VC */
|
2020-12-05 18:59:45 +00:00
|
|
|
static int gbprox_rx_sig_from_bss(struct gbproxy_nse *nse, struct msgb *msg, uint16_t ns_bvci)
|
2019-08-30 17:48:34 +00:00
|
|
|
{
|
|
|
|
struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
|
|
|
|
uint8_t pdu_type = bgph->pdu_type;
|
2020-12-05 18:59:45 +00:00
|
|
|
const char *pdut_name = osmo_tlv_prot_msg_name(&osmo_pdef_bssgp, bgph->pdu_type);
|
2021-02-04 15:31:46 +00:00
|
|
|
struct tlv_parsed tp[2];
|
2019-08-30 17:48:34 +00:00
|
|
|
int data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
|
2020-12-04 21:24:47 +00:00
|
|
|
struct gbproxy_bvc *from_bvc = NULL;
|
2020-12-06 15:32:01 +00:00
|
|
|
char log_pfx[32];
|
2020-12-05 18:59:45 +00:00
|
|
|
uint16_t ptp_bvci;
|
|
|
|
uint32_t tlli;
|
2019-08-30 17:48:34 +00:00
|
|
|
int rc;
|
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
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);
|
2020-12-06 15:32:01 +00:00
|
|
|
|
2019-08-30 17:48:34 +00:00
|
|
|
if (ns_bvci != 0 && ns_bvci != 1) {
|
2020-12-05 18:59:45 +00:00
|
|
|
LOGP(DGPRS, LOGL_NOTICE, "%s %s BVCI=%05u is not signalling\n", log_pfx, pdut_name, ns_bvci);
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
|
2019-08-30 17:48:34 +00:00
|
|
|
}
|
|
|
|
|
2020-12-06 12:35:24 +00:00
|
|
|
if (!(bssgp_pdu_type_flags(pdu_type) & BSSGP_PDUF_SIG)) {
|
2020-12-05 18:59:45 +00:00
|
|
|
LOGP(DGPRS, LOGL_NOTICE, "%s %s not allowed in signalling BVC\n", log_pfx, pdut_name);
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
|
2020-12-06 12:35:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!(bssgp_pdu_type_flags(pdu_type) & BSSGP_PDUF_UL)) {
|
2020-12-05 18:59:45 +00:00
|
|
|
LOGP(DGPRS, LOGL_NOTICE, "%s %s not allowed in uplink direction\n", log_pfx, pdut_name);
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
|
2019-08-30 17:48:34 +00:00
|
|
|
}
|
|
|
|
|
2021-02-04 15:31:46 +00:00
|
|
|
rc = osmo_tlv_prot_parse(&osmo_pdef_bssgp, tp, ARRAY_SIZE(tp), pdu_type, bgph->data, data_len, 0, 0,
|
2020-12-06 15:32:01 +00:00
|
|
|
DGPRS, log_pfx);
|
|
|
|
if (rc < 0) {
|
2021-06-04 16:03:44 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(nse->cfg->ctrg, GBPROX_GLOB_CTR_PROTO_ERR_BSS));
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status_from_tlvp(nse, rc, msg);
|
2020-12-06 15:32:01 +00:00
|
|
|
}
|
2020-12-05 18:59:45 +00:00
|
|
|
/* hack to get both msg + tlv_parsed passed via osmo_fsm_inst_dispatch */
|
2021-02-04 15:31:46 +00:00
|
|
|
msgb_bcid(msg) = (void *)tp;
|
2019-08-30 17:48:34 +00:00
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
/* special case handling for some PDU types */
|
2019-08-30 17:48:34 +00:00
|
|
|
switch (pdu_type) {
|
2020-12-05 18:59:45 +00:00
|
|
|
case BSSGP_PDUT_BVC_RESET:
|
|
|
|
/* resolve or create gbproxy_bvc + handlei n BVC-FSM */
|
2021-02-04 15:31:46 +00:00
|
|
|
return rx_bvc_reset_from_bss(nse, msg, &tp[0]);
|
2020-12-05 18:59:45 +00:00
|
|
|
case BSSGP_PDUT_BVC_RESET_ACK:
|
2021-02-04 15:31:46 +00:00
|
|
|
ptp_bvci = ntohs(tlvp_val16_unal(&tp[0], BSSGP_IE_BVCI));
|
2020-12-05 18:59:45 +00:00
|
|
|
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:
|
2021-02-04 15:31:46 +00:00
|
|
|
ptp_bvci = ntohs(tlvp_val16_unal(&tp[0], BSSGP_IE_BVCI));
|
2020-12-05 18:59:45 +00:00
|
|
|
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:
|
2021-02-04 15:31:46 +00:00
|
|
|
ptp_bvci = ntohs(tlvp_val16_unal(&tp[0], BSSGP_IE_BVCI));
|
2020-12-05 18:59:45 +00:00
|
|
|
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);
|
2019-08-30 17:48:34 +00:00
|
|
|
case BSSGP_PDUT_SUSPEND:
|
|
|
|
case BSSGP_PDUT_RESUME:
|
2020-12-29 20:13:31 +00:00
|
|
|
{
|
|
|
|
struct gbproxy_sgsn *sgsn;
|
|
|
|
|
2021-02-04 15:31:46 +00:00
|
|
|
tlli = osmo_load32be(TLVP_VAL(&tp[0], BSSGP_IE_TLLI));
|
2020-12-29 20:13:31 +00:00
|
|
|
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);
|
2020-12-05 18:59:45 +00:00
|
|
|
#if 0
|
|
|
|
/* TODO: Validate the RAI for consistency with the RAI
|
|
|
|
* we expect for any of the BVC within this BSS side NSE */
|
2021-02-04 15:31:46 +00:00
|
|
|
memcpy(ra, TLVP_VAL(&tp[0], BSSGP_IE_ROUTEING_AREA), sizeof(from_bvc->ra));
|
2020-12-05 18:59:45 +00:00
|
|
|
gsm48_parse_ra(&raid, from_bvc->ra);
|
|
|
|
#endif
|
|
|
|
break;
|
2020-12-29 20:13:31 +00:00
|
|
|
}
|
2020-12-05 18:59:45 +00:00
|
|
|
case BSSGP_PDUT_STATUS:
|
2021-09-29 09:51:51 +00:00
|
|
|
{
|
|
|
|
struct gbproxy_sgsn *sgsn;
|
|
|
|
/* Check if the status needs to be terminated locally */
|
|
|
|
uint8_t cause = *TLVP_VAL(&tp[0], BSSGP_IE_CAUSE);
|
2021-09-30 14:57:51 +00:00
|
|
|
|
2021-09-29 09:51:51 +00:00
|
|
|
if (cause == BSSGP_CAUSE_UNKNOWN_BVCI || cause == BSSGP_CAUSE_BVCI_BLOCKED) {
|
|
|
|
/* Log and handle locally */
|
|
|
|
ptp_bvci = ntohs(tlvp_val16_unal(&tp[0], BSSGP_IE_BVCI));
|
2021-09-30 14:57:51 +00:00
|
|
|
LOGPNSE(nse, LOGL_ERROR, "Rx STATUS cause=0x%02x(%s) for PtP-BVC %05u\n", cause,
|
2021-09-29 09:51:51 +00:00
|
|
|
bssgp_cause_str(cause), ptp_bvci);
|
|
|
|
/* FIXME: Remove/block our BVC if present? */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-09-30 14:57:51 +00:00
|
|
|
LOGPNSE(nse, LOGL_NOTICE, "Rx STATUS cause=0x%02x(%s) ", cause,
|
|
|
|
bssgp_cause_str(cause));
|
|
|
|
|
2021-10-28 14:13:03 +00:00
|
|
|
if (gbproxy_tlli_from_status_pdu(tp, &tlli, log_pfx) == 0)
|
2021-09-29 09:51:51 +00:00
|
|
|
sgsn = gbproxy_select_sgsn(nse->cfg, &tlli);
|
|
|
|
else
|
|
|
|
sgsn = gbproxy_select_sgsn(nse->cfg, NULL);
|
|
|
|
|
|
|
|
if (!sgsn) {
|
|
|
|
rc = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = gbprox_relay2nse(msg, sgsn->nse, 0);
|
2020-12-05 18:59:45 +00:00
|
|
|
break;
|
2021-09-29 09:51:51 +00:00
|
|
|
}
|
2020-12-05 18:59:45 +00:00
|
|
|
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:
|
2021-02-09 16:03:03 +00:00
|
|
|
rc = gbprox_rx_rim_from_bss(tp, nse, msg, log_pfx, pdut_name);
|
2020-12-05 18:59:45 +00:00
|
|
|
break;
|
|
|
|
case BSSGP_PDUT_LLC_DISCARD:
|
|
|
|
/* route based on BVCI + TLLI */
|
2021-02-04 15:31:46 +00:00
|
|
|
ptp_bvci = ntohs(tlvp_val16_unal(&tp[0], BSSGP_IE_BVCI));
|
|
|
|
tlli = osmo_load32be(TLVP_VAL(&tp[0], BSSGP_IE_TLLI));
|
2020-12-05 18:59:45 +00:00
|
|
|
from_bvc = gbproxy_bvc_by_bvci(nse, ptp_bvci);
|
2020-12-04 21:24:47 +00:00
|
|
|
if (!from_bvc)
|
|
|
|
goto err_no_bvc;
|
2021-11-25 21:04:56 +00:00
|
|
|
rc = gbprox_bss2sgsn_tlli(from_bvc->cell, msg, &tlli, true);
|
|
|
|
break;
|
|
|
|
case BSSGP_PDUT_FLUSH_LL_ACK:
|
|
|
|
{
|
|
|
|
/* Route based on TLLI */
|
|
|
|
tlli = osmo_load32be(TLVP_VAL(&tp[0], BSSGP_IE_TLLI));
|
|
|
|
struct gbproxy_sgsn *sgsn = gbproxy_select_sgsn(nse->cfg, &tlli);
|
|
|
|
if (!sgsn) {
|
|
|
|
rc = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = gbprox_relay2nse(msg, sgsn->nse, 0);
|
2019-08-30 17:48:34 +00:00
|
|
|
break;
|
2021-11-25 21:04:56 +00:00
|
|
|
}
|
2021-01-17 12:40:38 +00:00
|
|
|
case BSSGP_PDUT_PAGING_PS_REJECT:
|
2021-01-18 17:38:27 +00:00
|
|
|
case BSSGP_PDUT_DUMMY_PAGING_PS_RESP:
|
2021-01-17 12:40:38 +00:00
|
|
|
{
|
|
|
|
/* Route according to IMSI<->NSE cache entry */
|
|
|
|
struct osmo_mobile_identity mi;
|
2021-02-04 15:31:46 +00:00
|
|
|
const uint8_t *mi_data = TLVP_VAL(&tp[0], BSSGP_IE_IMSI);
|
|
|
|
uint8_t mi_len = TLVP_LEN(&tp[0], BSSGP_IE_IMSI);
|
2021-01-17 12:40:38 +00:00
|
|
|
osmo_mobile_identity_decode(&mi, mi_data, mi_len, false);
|
2021-07-09 15:44:30 +00:00
|
|
|
nse = gbproxy_nse_by_imsi(nse->cfg, mi.imsi, CACHE_USAGE_PAGING);
|
2021-01-17 12:40:38 +00:00
|
|
|
if (!nse) {
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
|
2021-01-17 12:40:38 +00:00
|
|
|
}
|
2021-01-18 17:38:27 +00:00
|
|
|
OSMO_ASSERT(nse->sgsn_facing);
|
2021-01-17 12:40:38 +00:00
|
|
|
rc = gbprox_relay2nse(msg, nse, 0);
|
|
|
|
break;
|
|
|
|
}
|
2021-07-06 12:02:41 +00:00
|
|
|
case BSSGP_PDUT_MS_REGISTR_ENQ:
|
|
|
|
{
|
|
|
|
struct gbproxy_sgsn *sgsn;
|
|
|
|
struct osmo_mobile_identity mi;
|
|
|
|
const uint8_t *mi_data = TLVP_VAL(&tp[0], BSSGP_IE_IMSI);
|
|
|
|
uint8_t mi_len = TLVP_LEN(&tp[0], BSSGP_IE_IMSI);
|
|
|
|
osmo_mobile_identity_decode(&mi, mi_data, mi_len, false);
|
|
|
|
|
|
|
|
sgsn = gbproxy_select_sgsn(nse->cfg, NULL);
|
|
|
|
if (!sgsn) {
|
|
|
|
LOGP(DGPRS, LOGL_ERROR, "Could not find any SGSN, dropping message!\n");
|
|
|
|
rc = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
gbproxy_imsi_cache_update(nse, mi.imsi, CACHE_USAGE_MS_REG_ENQ);
|
|
|
|
|
|
|
|
rc = gbprox_relay2nse(msg, sgsn->nse, 0);
|
|
|
|
break;
|
|
|
|
}
|
2020-12-05 18:59:45 +00:00
|
|
|
default:
|
|
|
|
LOGPNSE(nse, LOGL_ERROR, "Rx %s: Implementation missing\n", pdut_name);
|
2019-08-30 17:48:34 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
return rc;
|
2020-12-04 21:24:47 +00:00
|
|
|
err_no_bvc:
|
2020-12-05 18:59:45 +00:00
|
|
|
LOGPNSE(nse, LOGL_ERROR, "Rx %s: cannot find BVC for BVCI=%05u\n", pdut_name, ptp_bvci);
|
2021-06-04 16:03:44 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(nse->cfg->ctrg, GBPROX_GLOB_CTR_INV_NSEI));
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
|
2019-08-30 17:48:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Receive paging request from SGSN, we need to relay to proper BSS */
|
2020-12-12 14:58:28 +00:00
|
|
|
static int gbprox_rx_paging(struct gbproxy_nse *sgsn_nse, struct msgb *msg, const char *pdut_name,
|
2021-01-18 17:38:27 +00:00
|
|
|
struct tlv_parsed *tp, uint16_t ns_bvci, bool broadcast)
|
2019-08-30 17:48:34 +00:00
|
|
|
{
|
2020-12-12 14:58:28 +00:00
|
|
|
struct gbproxy_config *cfg = sgsn_nse->cfg;
|
2020-12-05 18:59:45 +00:00
|
|
|
struct gbproxy_bvc *sgsn_bvc, *bss_bvc;
|
2020-12-12 14:58:28 +00:00
|
|
|
struct gbproxy_nse *nse;
|
2020-11-30 16:08:58 +00:00
|
|
|
unsigned int n_nses = 0;
|
2019-08-30 17:48:34 +00:00
|
|
|
int errctr = GBPROX_GLOB_CTR_PROTO_ERR_SGSN;
|
2020-12-05 09:14:49 +00:00
|
|
|
int i, j;
|
2019-08-30 17:48:34 +00:00
|
|
|
|
2020-12-03 14:36:59 +00:00
|
|
|
if (TLVP_PRES_LEN(tp, BSSGP_IE_BVCI, 2)) {
|
2019-08-30 17:48:34 +00:00
|
|
|
uint16_t bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI));
|
|
|
|
errctr = GBPROX_GLOB_CTR_OTHER_ERR;
|
2020-12-12 14:58:28 +00:00
|
|
|
sgsn_bvc = gbproxy_bvc_by_bvci(sgsn_nse, bvci);
|
2020-12-05 18:59:45 +00:00
|
|
|
if (!sgsn_bvc) {
|
2020-12-12 14:58:28 +00:00
|
|
|
LOGPNSE(sgsn_nse, LOGL_NOTICE, "Rx %s: unable to route: BVCI=%05u unknown\n",
|
2020-12-05 18:59:45 +00:00
|
|
|
pdut_name, bvci);
|
2021-06-04 16:03:44 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(cfg->ctrg, errctr));
|
2020-11-23 14:14:20 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2020-12-05 18:59:45 +00:00
|
|
|
LOGPBVC(sgsn_bvc, LOGL_INFO, "Rx %s: routing by BVCI\n", pdut_name);
|
|
|
|
return gbprox_relay2peer(msg, sgsn_bvc->cell->bss_bvc, ns_bvci);
|
2020-12-03 14:36:59 +00:00
|
|
|
} else if (TLVP_PRES_LEN(tp, BSSGP_IE_ROUTEING_AREA, 6)) {
|
2021-02-04 20:54:09 +00:00
|
|
|
struct gprs_ra_id raid;
|
2019-08-30 17:48:34 +00:00
|
|
|
errctr = GBPROX_GLOB_CTR_INV_RAI;
|
2021-02-04 20:54:09 +00:00
|
|
|
gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA));
|
2020-12-04 21:24:47 +00:00
|
|
|
/* iterate over all bvcs and dispatch the paging to each matching one */
|
2020-12-04 23:31:07 +00:00
|
|
|
hash_for_each(cfg->bss_nses, i, nse, list) {
|
2020-12-05 18:59:45 +00:00
|
|
|
hash_for_each(nse->bvcs, j, bss_bvc, list) {
|
2021-11-12 14:52:03 +00:00
|
|
|
/* Skip BVCs without a cell (e.g. signalling) */
|
|
|
|
if (!bss_bvc->cell)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (gsm48_ra_equal(&bss_bvc->cell->id.raid, &raid)) {
|
2020-12-05 18:59:45 +00:00
|
|
|
LOGPNSE(nse, LOGL_INFO, "Rx %s: routing to NSE (RAI match)\n",
|
|
|
|
pdut_name);
|
|
|
|
gbprox_relay2peer(msg, bss_bvc, ns_bvci);
|
2020-11-30 16:08:58 +00:00
|
|
|
n_nses++;
|
|
|
|
/* Only send it once to each NSE */
|
|
|
|
break;
|
2020-11-26 17:19:21 +00:00
|
|
|
}
|
2020-11-23 14:14:20 +00:00
|
|
|
}
|
|
|
|
}
|
2020-12-03 14:36:59 +00:00
|
|
|
} else if (TLVP_PRES_LEN(tp, BSSGP_IE_LOCATION_AREA, 5)) {
|
2021-02-04 20:54:09 +00:00
|
|
|
struct gsm48_ra_id lac;
|
2019-08-30 17:48:34 +00:00
|
|
|
errctr = GBPROX_GLOB_CTR_INV_LAI;
|
2020-12-04 21:24:47 +00:00
|
|
|
/* iterate over all bvcs and dispatch the paging to each matching one */
|
2020-12-04 23:31:07 +00:00
|
|
|
hash_for_each(cfg->bss_nses, i, nse, list) {
|
2020-12-05 18:59:45 +00:00
|
|
|
hash_for_each(nse->bvcs, j, bss_bvc, list) {
|
2021-11-12 14:52:03 +00:00
|
|
|
/* Skip BVCs without a cell (e.g. signalling) */
|
|
|
|
if (!bss_bvc->cell)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
gsm48_encode_ra(&lac, &bss_bvc->cell->id.raid);
|
2021-02-04 20:54:09 +00:00
|
|
|
if (!memcmp(&lac, TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA), 5)) {
|
2020-12-05 18:59:45 +00:00
|
|
|
LOGPNSE(nse, LOGL_INFO, "Rx %s: routing to NSE (LAI match)\n",
|
|
|
|
pdut_name);
|
|
|
|
gbprox_relay2peer(msg, bss_bvc, ns_bvci);
|
2020-11-30 16:08:58 +00:00
|
|
|
n_nses++;
|
|
|
|
/* Only send it once to each NSE */
|
|
|
|
break;
|
2020-11-26 17:19:21 +00:00
|
|
|
}
|
2020-11-23 14:14:20 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-18 17:38:27 +00:00
|
|
|
} else if (TLVP_PRES_LEN(tp, BSSGP_IE_BSS_AREA_ID, 1) || broadcast) {
|
2020-12-04 21:24:47 +00:00
|
|
|
/* iterate over all bvcs and dispatch the paging to each matching one */
|
2020-12-04 23:31:07 +00:00
|
|
|
hash_for_each(cfg->bss_nses, i, nse, list) {
|
2020-12-05 18:59:45 +00:00
|
|
|
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);
|
2020-11-30 16:08:58 +00:00
|
|
|
n_nses++;
|
|
|
|
/* Only send it once to each NSE */
|
|
|
|
break;
|
2020-11-26 17:19:21 +00:00
|
|
|
}
|
2020-11-24 10:31:13 +00:00
|
|
|
}
|
|
|
|
} else {
|
2020-12-12 14:58:28 +00:00
|
|
|
LOGPNSE(sgsn_nse, LOGL_ERROR, "BSSGP PAGING: unable to route, missing IE\n");
|
2021-06-04 16:03:44 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(cfg->ctrg, errctr));
|
2020-11-24 10:31:13 +00:00
|
|
|
}
|
2019-08-30 17:48:34 +00:00
|
|
|
|
2020-11-30 16:08:58 +00:00
|
|
|
if (n_nses == 0) {
|
2020-12-12 14:58:28 +00:00
|
|
|
LOGPNSE(sgsn_nse, LOGL_ERROR, "BSSGP PAGING: unable to route, no destination found\n");
|
2021-06-04 16:03:44 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(cfg->ctrg, errctr));
|
2019-08-30 17:48:34 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2020-11-23 14:14:20 +00:00
|
|
|
return 0;
|
2019-08-30 17:48:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Receive an incoming BVC-RESET message from the SGSN */
|
2020-12-05 18:59:45 +00:00
|
|
|
static int rx_bvc_reset_from_sgsn(struct gbproxy_nse *nse, struct msgb *msg, struct tlv_parsed *tp,
|
|
|
|
uint16_t ns_bvci)
|
2019-08-30 17:48:34 +00:00
|
|
|
{
|
2020-12-05 18:59:45 +00:00
|
|
|
uint16_t ptp_bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI));
|
|
|
|
struct gbproxy_bvc *from_bvc;
|
2019-08-30 17:48:34 +00:00
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
LOGPNSE(nse, LOGL_INFO, "Rx BVC-RESET (BVCI=%05u)\n", ptp_bvci);
|
2019-08-30 17:48:34 +00:00
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
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);
|
2021-06-04 16:03:44 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(nse->cfg->ctrg, GBPROX_GLOB_CTR_INV_BVCI));
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_UNKNOWN_BVCI, &ptp_bvci, msg);
|
2020-12-05 18:59:45 +00:00
|
|
|
}
|
|
|
|
osmo_fsm_inst_dispatch(from_bvc->fi, BSSGP_BVCFSM_E_RX_RESET, msg);
|
2020-11-26 17:19:21 +00:00
|
|
|
}
|
2019-08-30 17:48:34 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-02-09 16:03:03 +00:00
|
|
|
/* Receive an incoming RIM message from the SGSN-side NS-VC */
|
|
|
|
static int gbprox_rx_rim_from_sgsn(struct tlv_parsed *tp, struct gbproxy_nse *nse, struct msgb *msg, char *log_pfx,
|
|
|
|
const char *pdut_name)
|
|
|
|
{
|
|
|
|
struct gbproxy_sgsn *sgsn;
|
|
|
|
struct gbproxy_cell *dest_cell;
|
|
|
|
struct bssgp_rim_routing_info dest_ri;
|
|
|
|
struct bssgp_rim_routing_info src_ri;
|
|
|
|
int rc;
|
2021-02-10 16:54:44 +00:00
|
|
|
char ri_src_str[64];
|
|
|
|
char ri_dest_str[64];
|
2021-02-12 03:59:47 +00:00
|
|
|
uint16_t ns_bvci = msgb_bvci(msg);
|
2021-02-09 16:03:03 +00:00
|
|
|
|
|
|
|
/* TODO: Reply with STATUS if BSSGP didn't negotiate RIM feature, see also comments in
|
|
|
|
gbprox_rx_rim_from_bss() */
|
|
|
|
|
|
|
|
rc = bssgp_parse_rim_ri(&dest_ri, TLVP_VAL(&tp[0], BSSGP_IE_RIM_ROUTING_INFO),
|
|
|
|
TLVP_LEN(&tp[0], BSSGP_IE_RIM_ROUTING_INFO));
|
|
|
|
if (rc < 0) {
|
|
|
|
LOGP(DGPRS, LOGL_ERROR, "%s %s cannot parse destination RIM routing info\n", log_pfx, pdut_name);
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
|
2021-02-09 16:03:03 +00:00
|
|
|
}
|
|
|
|
rc = bssgp_parse_rim_ri(&src_ri, TLVP_VAL(&tp[1], BSSGP_IE_RIM_ROUTING_INFO),
|
|
|
|
TLVP_LEN(&tp[1], BSSGP_IE_RIM_ROUTING_INFO));
|
|
|
|
if (rc < 0) {
|
|
|
|
LOGP(DGPRS, LOGL_ERROR, "%s %s cannot parse source RIM routing info\n", log_pfx, pdut_name);
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
|
2021-02-09 16:03:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Since gbproxy is 2G only we do not expect to get RIM messages that target non-GERAN cells. */
|
|
|
|
if (dest_ri.discr != BSSGP_RIM_ROUTING_INFO_GERAN) {
|
|
|
|
LOGP(DGPRS, LOGL_ERROR, "%s %s destination RIM routing info is not GERAN (%s)\n", log_pfx, pdut_name,
|
|
|
|
bssgp_rim_ri_name(&dest_ri));
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg);
|
2021-02-09 16:03:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Lookup destination cell */
|
|
|
|
dest_cell = gbproxy_cell_by_cellid(nse->cfg, &dest_ri.geran.raid, dest_ri.geran.cid);
|
|
|
|
if (!dest_cell) {
|
|
|
|
LOGP(DGPRS, LOGL_NOTICE, "%s %s cannot find cell for destination RIM routing info (%s)\n", log_pfx,
|
|
|
|
pdut_name, bssgp_rim_ri_name(&dest_ri));
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg);
|
2021-02-09 16:03:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: Check if the BVC of the destination cell actually did negotiate RIM support, see also comments
|
|
|
|
* in gbprox_rx_rim_from_bss() */
|
|
|
|
sgsn = gbproxy_sgsn_by_nsei(nse->cfg, nse->nsei);
|
|
|
|
OSMO_ASSERT(sgsn);
|
|
|
|
|
2021-02-10 16:54:44 +00:00
|
|
|
LOGP(DLBSSGP, LOGL_DEBUG, "%s %s relaying from SGSN(%05u/%s) RIM-PDU: src=%s, dest=%s\n",
|
|
|
|
log_pfx, pdut_name, sgsn->nse->nsei, sgsn->name,
|
|
|
|
bssgp_rim_ri_name_buf(ri_src_str, sizeof(ri_src_str), &src_ri),
|
|
|
|
bssgp_rim_ri_name_buf(ri_dest_str, sizeof(ri_dest_str), &dest_ri));
|
2021-02-09 16:03:03 +00:00
|
|
|
|
|
|
|
return gbprox_relay2peer(msg, dest_cell->bss_bvc, 0);
|
|
|
|
}
|
|
|
|
|
2019-08-30 17:48:34 +00:00
|
|
|
/* Receive an incoming signalling message from the SGSN-side NS-VC */
|
2020-12-07 16:48:11 +00:00
|
|
|
static int gbprox_rx_sig_from_sgsn(struct gbproxy_nse *nse, struct msgb *msg, uint16_t ns_bvci)
|
2019-08-30 17:48:34 +00:00
|
|
|
{
|
2020-12-07 16:48:11 +00:00
|
|
|
struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
|
2019-08-30 17:48:34 +00:00
|
|
|
uint8_t pdu_type = bgph->pdu_type;
|
2020-12-05 18:59:45 +00:00
|
|
|
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;
|
2021-02-04 15:31:46 +00:00
|
|
|
struct tlv_parsed tp[2];
|
2019-08-30 17:48:34 +00:00
|
|
|
int data_len;
|
|
|
|
uint16_t bvci;
|
2020-12-06 15:32:01 +00:00
|
|
|
char log_pfx[32];
|
2019-08-30 17:48:34 +00:00
|
|
|
int rc = 0;
|
2020-12-04 23:31:07 +00:00
|
|
|
int i;
|
2021-01-18 17:38:27 +00:00
|
|
|
bool paging_bc = false;
|
2019-08-30 17:48:34 +00:00
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
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);
|
2020-12-06 15:32:01 +00:00
|
|
|
|
2019-08-30 17:48:34 +00:00
|
|
|
if (ns_bvci != 0 && ns_bvci != 1) {
|
2020-12-06 15:32:01 +00:00
|
|
|
LOGP(DGPRS, LOGL_NOTICE, "%s BVCI=%05u is not signalling\n", log_pfx, ns_bvci);
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
|
2020-12-06 12:35:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!(bssgp_pdu_type_flags(pdu_type) & BSSGP_PDUF_SIG)) {
|
2020-12-05 18:59:45 +00:00
|
|
|
LOGP(DGPRS, LOGL_NOTICE, "%s %s not allowed in signalling BVC\n", log_pfx, pdut_name);
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
|
2019-08-30 17:48:34 +00:00
|
|
|
}
|
|
|
|
|
2020-12-06 12:35:24 +00:00
|
|
|
if (!(bssgp_pdu_type_flags(pdu_type) & BSSGP_PDUF_DL)) {
|
2020-12-05 18:59:45 +00:00
|
|
|
LOGP(DGPRS, LOGL_NOTICE, "%s %s not allowed in downlink direction\n", log_pfx, pdut_name);
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
|
2019-08-30 17:48:34 +00:00
|
|
|
}
|
|
|
|
|
2020-12-07 16:48:11 +00:00
|
|
|
data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
|
2020-12-06 15:32:01 +00:00
|
|
|
|
2021-02-04 15:31:46 +00:00
|
|
|
rc = osmo_tlv_prot_parse(&osmo_pdef_bssgp, tp, ARRAY_SIZE(tp), pdu_type, bgph->data, data_len, 0, 0,
|
2020-12-06 15:32:01 +00:00
|
|
|
DGPRS, log_pfx);
|
|
|
|
if (rc < 0) {
|
2021-02-12 03:59:47 +00:00
|
|
|
rc = tx_status_from_tlvp(nse, rc, msg);
|
2021-06-04 16:03:44 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(cfg->ctrg, GBPROX_GLOB_CTR_PROTO_ERR_SGSN));
|
2020-12-06 15:32:01 +00:00
|
|
|
return rc;
|
|
|
|
}
|
2020-12-05 18:59:45 +00:00
|
|
|
/* hack to get both msg + tlv_parsed passed via osmo_fsm_inst_dispatch */
|
2021-02-04 15:31:46 +00:00
|
|
|
msgb_bcid(msg) = (void *)tp;
|
2019-08-30 17:48:34 +00:00
|
|
|
|
|
|
|
switch (pdu_type) {
|
|
|
|
case BSSGP_PDUT_BVC_RESET:
|
2020-12-05 18:59:45 +00:00
|
|
|
/* resolve or create ggbproxy_bvc + handle in BVC-FSM */
|
2021-02-04 15:31:46 +00:00
|
|
|
bvci = ntohs(tlvp_val16_unal(&tp[0], BSSGP_IE_BVCI));
|
|
|
|
rc = rx_bvc_reset_from_sgsn(nse, msg, &tp[0], ns_bvci);
|
2019-08-30 17:48:34 +00:00
|
|
|
break;
|
|
|
|
case BSSGP_PDUT_BVC_RESET_ACK:
|
2021-02-04 15:31:46 +00:00
|
|
|
bvci = ntohs(tlvp_val16_unal(&tp[0], BSSGP_IE_BVCI));
|
2020-12-05 18:59:45 +00:00
|
|
|
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:
|
2021-02-04 15:31:46 +00:00
|
|
|
bvci = ntohs(tlvp_val16_unal(&tp[0], BSSGP_IE_BVCI));
|
2020-12-05 18:59:45 +00:00
|
|
|
sgsn_bvc = gbproxy_bvc_by_bvci(nse, bvci);
|
2021-09-24 14:45:38 +00:00
|
|
|
if (!sgsn_bvc) {
|
|
|
|
/* Check if BVC was blocked before */
|
|
|
|
sgsn_bvc = gbproxy_bvc_by_bvci_inactive(nse, bvci);
|
|
|
|
if (!sgsn_bvc)
|
|
|
|
goto err_no_bvc;
|
|
|
|
}
|
2020-12-05 18:59:45 +00:00
|
|
|
rc = osmo_fsm_inst_dispatch(sgsn_bvc->fi, BSSGP_BVCFSM_E_RX_BLOCK_ACK, msg);
|
|
|
|
break;
|
|
|
|
case BSSGP_PDUT_BVC_UNBLOCK_ACK:
|
2021-02-04 15:31:46 +00:00
|
|
|
bvci = ntohs(tlvp_val16_unal(&tp[0], BSSGP_IE_BVCI));
|
2020-12-05 18:59:45 +00:00
|
|
|
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);
|
2020-11-03 20:12:42 +00:00
|
|
|
break;
|
2019-08-30 17:48:34 +00:00
|
|
|
case BSSGP_PDUT_FLUSH_LL:
|
2021-11-25 21:04:56 +00:00
|
|
|
/* TODO: If new BVCI is on different NSE we should remove the new BVCI so the
|
|
|
|
* message is interpreted as a request to delete the PDUs, not forward them.
|
|
|
|
* If we negotiate Inter-NSE re-routing or LCS-procedures we can also
|
|
|
|
* add the NSEI TLV to trigger re-routing the PDUs */
|
2019-08-30 17:48:34 +00:00
|
|
|
/* simple case: BVCI IE is mandatory */
|
2021-02-04 15:31:46 +00:00
|
|
|
bvci = ntohs(tlvp_val16_unal(&tp[0], BSSGP_IE_BVCI));
|
2020-12-05 18:59:45 +00:00
|
|
|
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);
|
2019-08-30 17:48:34 +00:00
|
|
|
break;
|
2021-01-18 17:38:27 +00:00
|
|
|
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 */
|
2019-08-30 17:48:34 +00:00
|
|
|
case BSSGP_PDUT_PAGING_PS:
|
2021-01-17 12:40:38 +00:00
|
|
|
{
|
|
|
|
/* Cache the IMSI<->NSE to route PAGING REJECT */
|
|
|
|
struct osmo_mobile_identity mi;
|
2021-02-04 15:31:46 +00:00
|
|
|
const uint8_t *mi_data = TLVP_VAL(&tp[0], BSSGP_IE_IMSI);
|
|
|
|
uint8_t mi_len = TLVP_LEN(&tp[0], BSSGP_IE_IMSI);
|
2021-01-17 12:40:38 +00:00
|
|
|
osmo_mobile_identity_decode(&mi, mi_data, mi_len, false);
|
2021-07-09 15:44:30 +00:00
|
|
|
gbproxy_imsi_cache_update(nse, mi.imsi, CACHE_USAGE_PAGING);
|
2021-01-17 12:40:38 +00:00
|
|
|
/* fall through */
|
|
|
|
}
|
2019-08-30 17:48:34 +00:00
|
|
|
case BSSGP_PDUT_PAGING_CS:
|
|
|
|
/* process the paging request (LAI/RAI lookup) */
|
2021-02-04 15:31:46 +00:00
|
|
|
rc = gbprox_rx_paging(nse, msg, pdut_name, &tp[0], ns_bvci, paging_bc);
|
2019-08-30 17:48:34 +00:00
|
|
|
break;
|
|
|
|
case BSSGP_PDUT_STATUS:
|
2021-09-30 14:58:46 +00:00
|
|
|
{
|
|
|
|
struct gbproxy_nse *nse_peer;
|
|
|
|
uint32_t tlli;
|
|
|
|
|
|
|
|
/* Check if the status needs to be terminated locally */
|
|
|
|
uint8_t cause = *TLVP_VAL(&tp[0], BSSGP_IE_CAUSE);
|
|
|
|
|
|
|
|
if (cause == BSSGP_CAUSE_UNKNOWN_BVCI || cause == BSSGP_CAUSE_BVCI_BLOCKED) {
|
|
|
|
/* Log and handle locally, BVCI should be present for these causes */
|
|
|
|
if (!TLVP_PRESENT(&tp[0], BSSGP_IE_BVCI)) {
|
|
|
|
LOGPNSE(nse, LOGL_ERROR, "Rx STATUS cause=0x%02x(%s), but BVCI is missing\n", cause,
|
|
|
|
bssgp_cause_str(cause));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
uint16_t ptp_bvci = ntohs(tlvp_val16_unal(&tp[0], BSSGP_IE_BVCI));
|
|
|
|
LOGPNSE(nse, LOGL_ERROR, "Rx STATUS cause=0x%02x(%s) for PtP-BVC %05u\n", cause,
|
|
|
|
bssgp_cause_str(cause), ptp_bvci);
|
|
|
|
/* FIXME: Remove/block the other BSS/SGSN BVCs if present? */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOGPNSE(nse, LOGL_NOTICE, "Rx STATUS cause=0x%02x(%s)\n", cause,
|
2019-08-30 17:48:34 +00:00
|
|
|
bssgp_cause_str(cause));
|
2021-09-30 14:58:46 +00:00
|
|
|
|
2021-10-27 14:05:37 +00:00
|
|
|
|
|
|
|
if (gbproxy_bvci_from_status_pdu(tp, &bvci, log_pfx) == 0 && bvci != 0) {
|
|
|
|
struct gbproxy_cell *cell = gbproxy_cell_by_bvci(cfg, bvci);
|
|
|
|
|
|
|
|
if ((!cell || !cell->bss_bvc || !cell->bss_bvc->nse)) {
|
|
|
|
LOGPNSE(nse, LOGL_ERROR, "Rx STATUS cause=0x%02x(%s), but can't find NSE for cell\n",
|
|
|
|
cause, bssgp_cause_str(cause));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return gbprox_relay2nse(msg, cell->bss_bvc->nse, 0);
|
|
|
|
}
|
|
|
|
|
2021-10-28 14:13:03 +00:00
|
|
|
/* We can only forward this TLLI if it's in the cache (which only happens on suspend/resume) */
|
|
|
|
if (gbproxy_tlli_from_status_pdu(tp, &tlli, log_pfx) == 0) {
|
2021-09-30 14:58:46 +00:00
|
|
|
nse_peer = gbproxy_nse_by_tlli(cfg, tlli);
|
|
|
|
if (nse_peer)
|
|
|
|
return gbprox_relay2nse(msg, nse_peer, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
LOGPNSE(nse, LOGL_ERROR, "Unable to handle STATUS cause=0x%02x(%s)\n", cause,
|
|
|
|
bssgp_cause_str(cause));
|
|
|
|
|
2019-08-30 17:48:34 +00:00
|
|
|
break;
|
2021-09-30 14:58:46 +00:00
|
|
|
}
|
2019-08-30 17:48:34 +00:00
|
|
|
/* 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:
|
2020-12-29 20:13:31 +00:00
|
|
|
{
|
|
|
|
struct gbproxy_nse *nse_peer;
|
2021-02-04 15:31:46 +00:00
|
|
|
uint32_t tlli = osmo_load32be(TLVP_VAL(&tp[0], BSSGP_IE_TLLI));
|
2020-12-29 20:13:31 +00:00
|
|
|
|
|
|
|
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 */
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
|
2020-12-29 20:13:31 +00:00
|
|
|
}
|
|
|
|
/* 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);
|
2019-08-30 17:48:34 +00:00
|
|
|
break;
|
2020-12-29 20:13:31 +00:00
|
|
|
}
|
2019-08-30 17:48:34 +00:00
|
|
|
case BSSGP_PDUT_SGSN_INVOKE_TRACE:
|
2020-12-02 19:06:04 +00:00
|
|
|
case BSSGP_PDUT_OVERLOAD:
|
2020-12-05 18:59:45 +00:00
|
|
|
LOGPNSE(nse, LOGL_DEBUG, "Rx %s: broadcasting\n", pdut_name);
|
2020-12-04 21:24:47 +00:00
|
|
|
/* broadcast to all BSS-side bvcs */
|
2020-12-04 23:31:07 +00:00
|
|
|
hash_for_each(cfg->bss_nses, i, nse, list) {
|
2020-12-02 19:06:04 +00:00
|
|
|
gbprox_relay2nse(msg, nse, 0);
|
|
|
|
}
|
2019-08-30 17:48:34 +00:00
|
|
|
break;
|
2020-12-05 18:59:45 +00:00
|
|
|
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:
|
2021-02-09 16:03:03 +00:00
|
|
|
rc = gbprox_rx_rim_from_sgsn(tp, nse, msg, log_pfx, pdut_name);
|
2021-05-07 11:33:34 +00:00
|
|
|
break;
|
2021-07-06 12:02:41 +00:00
|
|
|
case BSSGP_PDUT_MS_REGISTR_ENQ_RESP:
|
|
|
|
{
|
|
|
|
struct gbproxy_nse *nse_peer;
|
|
|
|
struct osmo_mobile_identity mi;
|
|
|
|
const uint8_t *mi_data = TLVP_VAL(&tp[0], BSSGP_IE_IMSI);
|
|
|
|
uint8_t mi_len = TLVP_LEN(&tp[0], BSSGP_IE_IMSI);
|
|
|
|
osmo_mobile_identity_decode(&mi, mi_data, mi_len, false);
|
|
|
|
nse_peer = gbproxy_nse_by_imsi(cfg, mi.imsi, CACHE_USAGE_MS_REG_ENQ);
|
|
|
|
if (!nse_peer) {
|
|
|
|
LOGPNSE(nse, LOGL_ERROR, "Rx %s: Cannot find NSE\n", pdut_name);
|
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
|
|
|
|
} else if (nse_peer->sgsn_facing) {
|
|
|
|
LOGPNSE(nse, LOGL_ERROR, "Forwarding %s failed: IMSI cache contains SGSN NSE", pdut_name);
|
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
|
|
|
|
}
|
|
|
|
gbproxy_imsi_cache_remove(cfg, mi.imsi, CACHE_USAGE_MS_REG_ENQ);
|
|
|
|
gbprox_relay2nse(msg, nse_peer, ns_bvci);
|
|
|
|
break;
|
|
|
|
}
|
2019-08-30 17:48:34 +00:00
|
|
|
default:
|
2020-12-05 18:59:45 +00:00
|
|
|
LOGPNSE(nse, LOGL_NOTICE, "Rx %s: Not supported\n", pdut_name);
|
2021-06-04 16:03:44 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(cfg->ctrg, GBPROX_GLOB_CTR_PROTO_ERR_SGSN));
|
2021-02-12 03:59:47 +00:00
|
|
|
rc = tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
|
2019-08-30 17:48:34 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
2020-12-05 18:59:45 +00:00
|
|
|
|
2020-12-04 21:24:47 +00:00
|
|
|
err_no_bvc:
|
2021-09-28 16:51:47 +00:00
|
|
|
LOGPNSE(nse, LOGL_ERROR, "Rx %s: Cannot find BVC %05u\n", pdut_name, bvci);
|
2021-09-24 14:43:42 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(cfg->ctrg, GBPROX_GLOB_CTR_INV_BVCI));
|
2021-02-12 03:59:47 +00:00
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
|
2019-08-30 17:48:34 +00:00
|
|
|
}
|
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* libosmogb NS/BSSGP integration
|
|
|
|
***********************************************************************/
|
|
|
|
|
2020-09-22 11:21:46 +00:00
|
|
|
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);
|
|
|
|
|
2021-01-27 19:56:55 +00:00
|
|
|
osmo_prim_init(&nsp.oph, SAP_NS, GPRS_NS2_PRIM_UNIT_DATA, PRIM_OP_REQUEST, msg);
|
2020-09-22 11:21:46 +00:00
|
|
|
rc = gprs_ns2_recv_prim(nsi, &nsp.oph);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2019-08-30 17:48:34 +00:00
|
|
|
/* Main input function for Gb proxy */
|
2020-09-22 11:21:46 +00:00
|
|
|
int gbprox_rcvmsg(void *ctx, struct msgb *msg)
|
2019-08-30 17:48:34 +00:00
|
|
|
{
|
2020-09-22 11:21:46 +00:00
|
|
|
struct gbproxy_config *cfg = (struct gbproxy_config *) ctx;
|
2020-12-05 18:59:45 +00:00
|
|
|
uint16_t ns_bvci = msgb_bvci(msg);
|
|
|
|
uint16_t nsei = msgb_nsei(msg);
|
|
|
|
struct gbproxy_nse *nse;
|
2019-08-30 17:48:34 +00:00
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
nse = gbproxy_nse_by_nsei(cfg, nsei, NSE_F_SGSN);
|
|
|
|
if (nse) {
|
2021-02-12 03:59:47 +00:00
|
|
|
/* ensure minimum length to decode PDU type */
|
|
|
|
if (msgb_bssgp_len(msg) < sizeof(struct bssgp_normal_hdr))
|
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_SEM_INCORR_PDU, NULL, msg);
|
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
if (ns_bvci == 0 || ns_bvci == 1)
|
|
|
|
return gbprox_rx_sig_from_sgsn(nse, msg, ns_bvci);
|
2019-08-30 17:48:34 +00:00
|
|
|
else
|
2020-12-05 18:59:45 +00:00
|
|
|
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) {
|
2021-02-12 03:59:47 +00:00
|
|
|
/* ensure minimum length to decode PDU type */
|
|
|
|
if (msgb_bssgp_len(msg) < sizeof(struct bssgp_normal_hdr))
|
|
|
|
return tx_status(nse, ns_bvci, BSSGP_CAUSE_SEM_INCORR_PDU, NULL, msg);
|
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
if (ns_bvci == 0 || ns_bvci == 1)
|
|
|
|
return gbprox_rx_sig_from_bss(nse, msg, ns_bvci);
|
2019-08-30 17:48:34 +00:00
|
|
|
else
|
2020-12-05 18:59:45 +00:00
|
|
|
return gbprox_rx_ptp_from_bss(nse, msg, ns_bvci);
|
2019-08-30 17:48:34 +00:00
|
|
|
}
|
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
return 0;
|
2019-08-30 17:48:34 +00:00
|
|
|
}
|
|
|
|
|
2020-09-22 11:21:46 +00:00
|
|
|
/* TODO: What about handling:
|
2021-01-27 19:56:55 +00:00
|
|
|
* GPRS_NS2_AFF_CAUSE_VC_FAILURE,
|
|
|
|
GPRS_NS2_AFF_CAUSE_VC_RECOVERY,
|
2020-09-22 11:21:46 +00:00
|
|
|
osmocom own causes
|
2021-01-27 19:56:55 +00:00
|
|
|
GPRS_NS2_AFF_CAUSE_SNS_CONFIGURED,
|
|
|
|
GPRS_NS2_AFF_CAUSE_SNS_FAILURE,
|
2020-09-22 11:21:46 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
void gprs_ns_prim_status_cb(struct gbproxy_config *cfg, struct osmo_gprs_ns2_prim *nsp)
|
2019-08-30 17:48:34 +00:00
|
|
|
{
|
2021-03-11 08:53:42 +00:00
|
|
|
int i;
|
2020-12-04 21:24:47 +00:00
|
|
|
struct gbproxy_bvc *bvc;
|
2021-03-09 15:14:18 +00:00
|
|
|
struct gbproxy_nse *nse;
|
2020-09-22 11:21:46 +00:00
|
|
|
|
|
|
|
switch (nsp->u.status.cause) {
|
2021-01-27 19:56:55 +00:00
|
|
|
case GPRS_NS2_AFF_CAUSE_SNS_FAILURE:
|
|
|
|
case GPRS_NS2_AFF_CAUSE_SNS_CONFIGURED:
|
2020-09-22 11:21:46 +00:00
|
|
|
break;
|
|
|
|
|
2021-01-27 19:56:55 +00:00
|
|
|
case GPRS_NS2_AFF_CAUSE_RECOVERY:
|
2021-01-18 12:55:51 +00:00
|
|
|
LOGP(DGPRS, LOGL_NOTICE, "NS-NSE %d became available\n", nsp->nsei);
|
2021-03-09 15:14:18 +00:00
|
|
|
nse = gbproxy_nse_by_nsei(cfg, nsp->nsei, NSE_F_SGSN);
|
|
|
|
if (nse) {
|
2021-05-27 16:13:36 +00:00
|
|
|
nse->alive = true;
|
2021-03-09 14:54:44 +00:00
|
|
|
// Update the NSE max SDU len
|
2021-03-09 15:14:18 +00:00
|
|
|
nse->max_sdu_len = nsp->u.status.mtu;
|
2021-03-09 14:54:44 +00:00
|
|
|
|
2020-12-05 18:59:45 +00:00
|
|
|
uint8_t cause = BSSGP_CAUSE_OML_INTERV;
|
2021-03-09 15:14:18 +00:00
|
|
|
bvc = gbproxy_bvc_by_bvci(nse, 0);
|
2021-03-09 14:54:44 +00:00
|
|
|
if (bvc) {
|
2021-03-09 15:14:18 +00:00
|
|
|
bssgp_bvc_fsm_set_max_pdu_len(bvc->fi, nse->max_sdu_len);
|
2021-02-10 12:41:14 +00:00
|
|
|
osmo_fsm_inst_dispatch(bvc->fi, BSSGP_BVCFSM_E_REQ_RESET, &cause);
|
2021-03-09 14:54:44 +00:00
|
|
|
}
|
2020-09-22 11:21:46 +00:00
|
|
|
}
|
|
|
|
break;
|
2021-01-27 19:56:55 +00:00
|
|
|
case GPRS_NS2_AFF_CAUSE_FAILURE:
|
2021-03-09 15:14:18 +00:00
|
|
|
nse = gbproxy_nse_by_nsei(cfg, nsp->nsei, NSE_F_BSS | NSE_F_SGSN);
|
|
|
|
if (!nse) {
|
|
|
|
LOGP(DGPRS, LOGL_ERROR, "Unknown NSE(%05d) became unavailable\n", nsp->nsei);
|
|
|
|
break;
|
|
|
|
}
|
2021-05-27 16:13:36 +00:00
|
|
|
|
|
|
|
nse->alive = false;
|
2021-03-09 15:14:18 +00:00
|
|
|
if (nse->sgsn_facing) {
|
2021-03-11 08:53:42 +00:00
|
|
|
struct hlist_node *ntmp;
|
2021-03-09 15:14:18 +00:00
|
|
|
/* SGSN */
|
|
|
|
/* TODO: When to block all PtP towards bss? Only if all SGSN are down? */
|
2021-03-11 08:53:42 +00:00
|
|
|
hash_for_each_safe(nse->bvcs, i, ntmp, bvc, list) {
|
|
|
|
if (bvc->bvci == 0)
|
|
|
|
continue;
|
|
|
|
gbproxy_bvc_free(bvc);
|
|
|
|
}
|
2021-06-04 16:03:44 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(cfg->ctrg, GBPROX_GLOB_CTR_RESTART_RESET_SGSN));
|
2020-09-22 11:21:46 +00:00
|
|
|
} else {
|
2021-03-09 15:14:18 +00:00
|
|
|
/* BSS became unavailable
|
|
|
|
* Block matching PtP-BVCs on SGSN-side */
|
|
|
|
hash_for_each(nse->bvcs, i, bvc, list) {
|
|
|
|
if (bvc->bvci == 0)
|
|
|
|
continue;
|
|
|
|
/* Get BVC for each SGSN and send block request */
|
|
|
|
struct gbproxy_cell *cell = bvc->cell;
|
|
|
|
for (int j = 0; j < GBPROXY_MAX_NR_SGSN; j++) {
|
|
|
|
struct gbproxy_bvc *sgsn_bvc = cell->sgsn_bvc[j];
|
|
|
|
if (!sgsn_bvc)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Block BVC, indicate BSS equipment failure */
|
|
|
|
uint8_t cause = BSSGP_CAUSE_EQUIP_FAIL;
|
|
|
|
osmo_fsm_inst_dispatch(sgsn_bvc->fi, BSSGP_BVCFSM_E_REQ_BLOCK, &cause);
|
2021-09-24 14:45:38 +00:00
|
|
|
sgsn_bvc->inactive = true;
|
2021-03-09 15:14:18 +00:00
|
|
|
}
|
2020-09-22 11:21:46 +00:00
|
|
|
}
|
|
|
|
|
2021-03-11 08:53:42 +00:00
|
|
|
/* This frees the BVCs for us as well */
|
|
|
|
gbproxy_nse_free(nse);
|
2020-09-22 11:21:46 +00:00
|
|
|
}
|
2021-01-18 12:55:51 +00:00
|
|
|
LOGP(DGPRS, LOGL_NOTICE, "NS-NSE %d became unavailable\n", nsp->nsei);
|
2020-09-22 11:21:46 +00:00
|
|
|
break;
|
|
|
|
default:
|
2021-01-18 12:55:51 +00:00
|
|
|
LOGP(DGPRS, LOGL_NOTICE, "NS: Unknown NS-STATUS.ind cause=%s from NS\n",
|
2020-11-30 09:55:22 +00:00
|
|
|
gprs_ns2_aff_cause_prim_str(nsp->u.status.cause));
|
2020-09-22 11:21:46 +00:00
|
|
|
break;
|
2019-08-30 17:48:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-22 11:21:46 +00:00
|
|
|
/* called by the ns layer */
|
|
|
|
int gprs_ns2_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
|
2019-08-30 17:48:34 +00:00
|
|
|
{
|
2020-09-22 11:21:46 +00:00
|
|
|
struct osmo_gprs_ns2_prim *nsp;
|
|
|
|
struct gbproxy_config *cfg = (struct gbproxy_config *) ctx;
|
2020-12-02 18:33:50 +00:00
|
|
|
uintptr_t bvci;
|
2020-09-22 11:21:46 +00:00
|
|
|
int rc = 0;
|
2019-08-30 17:48:34 +00:00
|
|
|
|
2020-09-22 11:21:46 +00:00
|
|
|
if (oph->sap != SAP_NS)
|
2019-08-30 17:48:34 +00:00
|
|
|
return 0;
|
|
|
|
|
2020-09-22 11:21:46 +00:00
|
|
|
nsp = container_of(oph, struct osmo_gprs_ns2_prim, oph);
|
2019-08-30 17:48:34 +00:00
|
|
|
|
2020-09-22 11:21:46 +00:00
|
|
|
if (oph->operation != PRIM_OP_INDICATION) {
|
2021-01-18 12:55:51 +00:00
|
|
|
LOGP(DGPRS, LOGL_NOTICE, "NS: Unexpected primitive operation %s from NS\n",
|
2020-11-30 09:55:22 +00:00
|
|
|
get_value_string(osmo_prim_op_names, oph->operation));
|
2020-09-22 11:21:46 +00:00
|
|
|
return 0;
|
2019-08-30 17:48:34 +00:00
|
|
|
}
|
|
|
|
|
2020-09-22 11:21:46 +00:00
|
|
|
switch (oph->primitive) {
|
2021-01-27 19:56:55 +00:00
|
|
|
case GPRS_NS2_PRIM_UNIT_DATA:
|
2020-12-02 18:33:50 +00:00
|
|
|
|
2020-09-22 11:21:46 +00:00
|
|
|
/* 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;
|
2020-12-02 18:33:50 +00:00
|
|
|
bvci = nsp->bvci | BVC_LOG_CTX_FLAG;
|
2020-09-22 11:21:46 +00:00
|
|
|
|
2020-12-02 18:33:50 +00:00
|
|
|
log_set_context(LOG_CTX_GB_BVC, (void *)bvci);
|
2020-09-22 11:21:46 +00:00
|
|
|
rc = gbprox_rcvmsg(cfg, oph->msg);
|
2020-11-04 16:32:56 +00:00
|
|
|
msgb_free(oph->msg);
|
2020-09-22 11:21:46 +00:00
|
|
|
break;
|
2021-01-27 19:56:55 +00:00
|
|
|
case GPRS_NS2_PRIM_STATUS:
|
2020-09-22 11:21:46 +00:00
|
|
|
gprs_ns_prim_status_cb(cfg, nsp);
|
|
|
|
break;
|
|
|
|
default:
|
2021-01-18 12:55:51 +00:00
|
|
|
LOGP(DGPRS, LOGL_NOTICE, "NS: Unknown prim %s %s from NS\n",
|
2020-11-30 09:55:22 +00:00
|
|
|
gprs_ns2_prim_str(oph->primitive),
|
|
|
|
get_value_string(osmo_prim_op_names, oph->operation));
|
2020-09-22 11:21:46 +00:00
|
|
|
break;
|
2019-08-30 17:48:34 +00:00
|
|
|
}
|
2020-09-22 11:21:46 +00:00
|
|
|
|
|
|
|
return rc;
|
2019-08-30 17:48:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void gbprox_reset(struct gbproxy_config *cfg)
|
|
|
|
{
|
2020-12-04 23:31:07 +00:00
|
|
|
struct gbproxy_nse *nse;
|
|
|
|
struct hlist_node *ntmp;
|
2020-12-05 09:14:49 +00:00
|
|
|
int i, j;
|
2019-08-30 17:48:34 +00:00
|
|
|
|
2020-12-04 23:31:07 +00:00
|
|
|
hash_for_each_safe(cfg->bss_nses, i, ntmp, nse, list) {
|
2020-12-05 09:14:49 +00:00
|
|
|
struct gbproxy_bvc *bvc;
|
|
|
|
struct hlist_node *tmp;
|
|
|
|
hash_for_each_safe(nse->bvcs, j, tmp, bvc, list)
|
2020-12-04 21:24:47 +00:00
|
|
|
gbproxy_bvc_free(bvc);
|
2020-11-26 17:19:21 +00:00
|
|
|
|
|
|
|
gbproxy_nse_free(nse);
|
|
|
|
}
|
2020-12-05 18:59:45 +00:00
|
|
|
/* FIXME: cells */
|
|
|
|
/* FIXME: SGSN side BVCs (except signaling) */
|
2019-08-30 17:48:34 +00:00
|
|
|
|
|
|
|
rate_ctr_group_free(cfg->ctrg);
|
|
|
|
gbproxy_init_config(cfg);
|
|
|
|
}
|
|
|
|
|
2020-12-29 20:13:31 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2021-01-17 12:11:41 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-08-30 17:48:34 +00:00
|
|
|
int gbproxy_init_config(struct gbproxy_config *cfg)
|
|
|
|
{
|
|
|
|
struct timespec tp;
|
|
|
|
|
2020-12-12 18:02:16 +00:00
|
|
|
/* by default we advertise 100% of the BSS-side capacity to _each_ SGSN */
|
|
|
|
cfg->pool.bvc_fc_ratio = 100;
|
2020-12-14 15:22:39 +00:00
|
|
|
cfg->pool.null_nri_ranges = osmo_nri_ranges_alloc(cfg);
|
2020-12-29 20:13:31 +00:00
|
|
|
/* TODO: Make configurable */
|
2021-01-13 17:16:04 +00:00
|
|
|
cfg->tlli_cache.timeout = 10;
|
2021-01-17 12:11:41 +00:00
|
|
|
cfg->imsi_cache.timeout = 10;
|
2020-12-14 15:22:39 +00:00
|
|
|
|
2020-12-04 23:31:07 +00:00
|
|
|
hash_init(cfg->bss_nses);
|
2020-12-21 17:08:21 +00:00
|
|
|
hash_init(cfg->sgsn_nses);
|
|
|
|
hash_init(cfg->cells);
|
2020-12-29 20:13:31 +00:00
|
|
|
hash_init(cfg->tlli_cache.entries);
|
2020-12-14 15:22:39 +00:00
|
|
|
INIT_LLIST_HEAD(&cfg->sgsns);
|
|
|
|
|
2020-12-29 20:13:31 +00:00
|
|
|
osmo_timer_setup(&cfg->tlli_cache.timer, tlli_cache_cleanup, cfg);
|
|
|
|
osmo_timer_schedule(&cfg->tlli_cache.timer, 2, 0);
|
|
|
|
|
2021-01-17 12:11:41 +00:00
|
|
|
/* 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);
|
|
|
|
|
2019-08-30 17:48:34 +00:00
|
|
|
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);
|
2020-12-07 12:12:13 +00:00
|
|
|
osmo_fsm_log_timeouts(true);
|
2019-08-30 17:48:34 +00:00
|
|
|
|
|
|
|
return 0;
|
2021-01-29 10:13:00 +00:00
|
|
|
}
|