blob: b4a53bc454edcc19b8e9dc8dd768ee408f7ee4b2 [file] [log] [blame]
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001/* NS-over-IP proxy */
2
Harald Weltee5209642020-12-05 19:59:45 +01003/* (C) 2010-2020 by Harald Welte <laforge@gnumonks.org>
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02004 * (C) 2010-2013 by On-Waves
5 * (C) 2013 by Holger Hans Peter Freyther
6 * All Rights Reserved
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23#include <unistd.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <getopt.h>
28#include <errno.h>
29#include <sys/fcntl.h>
30#include <sys/stat.h>
31#include <arpa/inet.h>
32#include <time.h>
33
Harald Welted2fef952020-12-05 00:31:07 +010034#include <osmocom/core/hashtable.h>
Daniel Willmann8f407b12020-12-02 19:33:50 +010035#include <osmocom/core/logging.h>
Daniel Willmannee834af2020-12-14 16:22:39 +010036#include <osmocom/core/linuxlist.h>
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020037#include <osmocom/core/talloc.h>
38#include <osmocom/core/select.h>
39#include <osmocom/core/rate_ctr.h>
Daniel Willmann1ac920b2021-02-11 23:51:49 +010040#include <osmocom/core/signal.h>
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020041#include <osmocom/core/stats.h>
Daniel Willmannd4ab1f92020-12-21 18:53:55 +010042#include <osmocom/core/utils.h>
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020043
Alexander Couzens951e1332020-09-22 13:21:46 +020044#include <osmocom/gprs/gprs_ns2.h>
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020045#include <osmocom/gprs/gprs_bssgp.h>
Harald Welte209dc9f2020-12-12 19:02:16 +010046#include <osmocom/gprs/gprs_bssgp2.h>
Alexander Couzens951e1332020-09-22 13:21:46 +020047#include <osmocom/gprs/gprs_bssgp_bss.h>
Harald Weltee5209642020-12-05 19:59:45 +010048#include <osmocom/gprs/bssgp_bvc_fsm.h>
Philipp Maier1c5766b2021-02-09 17:03:03 +010049#include <osmocom/gprs/protocol/gsm_08_18.h>
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020050
Daniel Willmannd4ab1f92020-12-21 18:53:55 +010051#include <osmocom/gsm/gsm23236.h>
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020052#include <osmocom/gsm/gsm_utils.h>
53
Oliver Smith29532c22021-01-29 11:13:00 +010054#include "debug.h"
Daniel Willmanna16ecc32021-03-10 09:57:12 +010055#include <osmocom/gbproxy/gb_proxy.h>
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020056
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020057#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020058
59extern void *tall_sgsn_ctx;
60
61static const struct rate_ctr_desc global_ctr_description[] = {
Daniel Willmann99a46ff2021-09-27 15:39:44 +020062 [GBPROX_GLOB_CTR_INV_BVCI] = { "inv-bvci", "Invalid BVC Identifier " },
63 [GBPROX_GLOB_CTR_INV_LAI] = { "inv-lai", "Invalid Location Area Identifier" },
64 [GBPROX_GLOB_CTR_INV_RAI] = { "inv-rai", "Invalid Routing Area Identifier " },
65 [GBPROX_GLOB_CTR_INV_NSEI] = { "inv-nsei", "No BVC established for NSEI " },
66 [GBPROX_GLOB_CTR_PROTO_ERR_BSS] = { "proto-err:bss", "BSSGP protocol error (BSS )" },
67 [GBPROX_GLOB_CTR_PROTO_ERR_SGSN] = { "proto-err:sgsn", "BSSGP protocol error (SGSN)" },
68 [GBPROX_GLOB_CTR_NOT_SUPPORTED_BSS] = { "not-supp:bss", "Feature not supported (BSS )" },
69 [GBPROX_GLOB_CTR_NOT_SUPPORTED_SGSN] = { "not-supp:sgsn", "Feature not supported (SGSN)" },
70 [GBPROX_GLOB_CTR_RESTART_RESET_SGSN] = { "restart:sgsn", "Restarted RESET procedure (SGSN)" },
71 [GBPROX_GLOB_CTR_TX_ERR_SGSN] = { "tx-err:sgsn", "NS Transmission error (SGSN)" },
72 [GBPROX_GLOB_CTR_OTHER_ERR] = { "error", "Other error " },
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020073};
74
75static const struct rate_ctr_group_desc global_ctrg_desc = {
76 .group_name_prefix = "gbproxy:global",
77 .group_description = "GBProxy Global Statistics",
78 .num_ctr = ARRAY_SIZE(global_ctr_description),
79 .ctr_desc = global_ctr_description,
80 .class_id = OSMO_STATS_CLASS_GLOBAL,
81};
82
Harald Welte560bdb32020-12-04 22:24:47 +010083static int gbprox_relay2peer(struct msgb *old_msg, struct gbproxy_bvc *bvc,
Daniel Willmann35f7d332020-11-03 21:11:45 +010084 uint16_t ns_bvci);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020085
Daniel Willmannf8cba652021-02-12 04:59:47 +010086int tx_status(struct gbproxy_nse *nse, uint16_t ns_bvci, enum gprs_bssgp_cause cause, const uint16_t *bvci, const struct msgb *old_msg)
87{
88 int rc;
Harald Weltea0f70732020-12-05 17:50:23 +010089
Daniel Willmannf8cba652021-02-12 04:59:47 +010090 struct msgb *msg = bssgp2_enc_status(cause, bvci, old_msg, nse->max_sdu_len);
91 if (!msg) {
92 LOGPNSE(nse, LOGL_NOTICE, "Unable to encode STATUS message\n");
93 return -ENOMEM;
94 }
95
96 rc = bssgp2_nsi_tx_ptp(nse->cfg->nsi, nse->nsei, ns_bvci, msg, 0);
97 if (rc < 0)
98 LOGPNSE(nse, LOGL_NOTICE, "Unable to send STATUS message\n");
99 return rc;
100}
101
102/* generate BVC-STATUS mess
103age with cause value derived from TLV-parser error */
104static int tx_status_from_tlvp(struct gbproxy_nse *nse, enum osmo_tlv_parser_error tlv_p_err, struct msgb *orig_msg)
Harald Welteec0f8012020-12-06 16:32:01 +0100105{
106 uint8_t bssgp_cause;
107 switch (tlv_p_err) {
108 case OSMO_TLVP_ERR_MAND_IE_MISSING:
109 bssgp_cause = BSSGP_CAUSE_MISSING_MAND_IE;
110 break;
111 default:
112 bssgp_cause = BSSGP_CAUSE_PROTO_ERR_UNSPEC;
113 }
Daniel Willmannf8cba652021-02-12 04:59:47 +0100114 return tx_status(nse, msgb_bvci(orig_msg), bssgp_cause, NULL, orig_msg);
Harald Welteec0f8012020-12-06 16:32:01 +0100115}
116
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200117/* strip off the NS header */
118static void strip_ns_hdr(struct msgb *msg)
119{
120 int strip_len = msgb_bssgph(msg) - msg->data;
121 msgb_pull(msg, strip_len);
122}
123
Harald Weltee5209642020-12-05 19:59:45 +0100124#if 0
Harald Welte560bdb32020-12-04 22:24:47 +0100125/* feed a message down the NS-VC associated with the specified bvc */
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200126static int gbprox_relay2sgsn(struct gbproxy_config *cfg, struct msgb *old_msg,
127 uint16_t ns_bvci, uint16_t sgsn_nsei)
128{
129 /* create a copy of the message so the old one can
130 * be free()d safely when we return from gbprox_rcvmsg() */
Alexander Couzens951e1332020-09-22 13:21:46 +0200131 struct gprs_ns2_inst *nsi = cfg->nsi;
132 struct osmo_gprs_ns2_prim nsp = {};
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200133 struct msgb *msg = bssgp_msgb_copy(old_msg, "msgb_relay2sgsn");
134 int rc;
135
Daniel Willmann3696dce2020-12-02 16:08:02 +0100136 DEBUGP(DGPRS, "NSE(%05u/BSS)-BVC(%05u) proxying BTS->SGSN NSE(%05u/SGSN)\n",
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200137 msgb_nsei(msg), ns_bvci, sgsn_nsei);
138
Alexander Couzens951e1332020-09-22 13:21:46 +0200139 nsp.bvci = ns_bvci;
140 nsp.nsei = sgsn_nsei;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200141
142 strip_ns_hdr(msg);
Alexander Couzens951e1332020-09-22 13:21:46 +0200143 osmo_prim_init(&nsp.oph, SAP_NS, PRIM_NS_UNIT_DATA,
144 PRIM_OP_REQUEST, msg);
145 rc = gprs_ns2_recv_prim(nsi, &nsp.oph);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200146 if (rc < 0)
Pau Espin Pedrol56438362021-06-04 18:03:44 +0200147 rate_ctr_inc(rate_ctr_group_get_ctr(cfg->ctrg, GBPROX_GLOB_CTR_TX_ERR_SGSN));
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200148 return rc;
149}
Harald Weltee5209642020-12-05 19:59:45 +0100150#endif
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200151
Harald Weltee30985e2021-01-28 19:13:19 +0100152/*! Determine the TLLI from the given BSSGP message.
153 * \param[in] bssgp pointer to start of BSSGP header
154 * \param[in] bssgp_len length of BSSGP message in octets
155 * \param[out] tlli TLLI (if any) in host byte order
156 * \returns 1 if TLLI found; 0 if none found; negative on parse error */
157int gprs_gb_parse_tlli(const uint8_t *bssgp, size_t bssgp_len, uint32_t *tlli)
158{
159 const struct bssgp_normal_hdr *bgph;
160 uint8_t pdu_type;
161
162 if (bssgp_len < sizeof(struct bssgp_normal_hdr))
163 return -EINVAL;
164
165 bgph = (struct bssgp_normal_hdr *)bssgp;
166 pdu_type = bgph->pdu_type;
167
168 if (pdu_type == BSSGP_PDUT_UL_UNITDATA ||
169 pdu_type == BSSGP_PDUT_DL_UNITDATA) {
170 const struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *)bssgp;
171 if (bssgp_len < sizeof(struct bssgp_ud_hdr))
172 return -EINVAL;
173 *tlli = osmo_load32be((const uint8_t *)&budh->tlli);
174 return 1;
175 } else {
176 const uint8_t *data = bgph->data;
177 size_t data_len = bssgp_len - sizeof(*bgph);
178 struct tlv_parsed tp;
179
180 if (bssgp_tlv_parse(&tp, data, data_len) < 0)
181 return -EINVAL;
182
183 if (TLVP_PRESENT(&tp, BSSGP_IE_TLLI)) {
184 *tlli = osmo_load32be(TLVP_VAL(&tp, BSSGP_IE_TLLI));
185 return 1;
186 }
187 }
188
189 /* No TLLI present in message */
190 return 0;
191}
192
Daniel Willmann76205712020-11-30 17:08:58 +0100193/* feed a message down the NSE */
194static int gbprox_relay2nse(struct msgb *old_msg, struct gbproxy_nse *nse,
Daniel Willmann35f7d332020-11-03 21:11:45 +0100195 uint16_t ns_bvci)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200196{
Daniel Willmanne50550e2020-11-26 18:19:21 +0100197 OSMO_ASSERT(nse);
198 OSMO_ASSERT(nse->cfg);
199
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200200 /* create a copy of the message so the old one can
201 * be free()d safely when we return from gbprox_rcvmsg() */
Daniel Willmanne50550e2020-11-26 18:19:21 +0100202 struct gprs_ns2_inst *nsi = nse->cfg->nsi;
Daniel Willmann76205712020-11-30 17:08:58 +0100203 struct msgb *msg = bssgp_msgb_copy(old_msg, "msgb_relay2nse");
Daniel Willmann44fa2012021-02-12 04:55:40 +0100204 uint32_t tlli = 0;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200205 int rc;
206
Daniel Willmann98b1b452020-12-21 10:40:27 +0100207 DEBUGP(DGPRS, "NSE(%05u/%s)-BVC(%05u/??) proxying to NSE(%05u/%s)\n", msgb_nsei(msg),
208 !nse->sgsn_facing ? "SGSN" : "BSS", ns_bvci, nse->nsei, nse->sgsn_facing ? "SGSN" : "BSS");
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200209
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200210 /* Strip the old NS header, it will be replaced with a new one */
211 strip_ns_hdr(msg);
212
Harald Weltefe059582020-11-18 12:01:46 +0100213 /* TS 48.018 Section 5.4.2: The link selector parameter is
214 * defined in 3GPP TS 48.016. At one side of the Gb interface,
215 * all BSSGP UNITDATA PDUs related to an MS shall be passed with
216 * the same LSP, e.g. the LSP contains the MS's TLLI, to the
217 * underlying network service. */
Daniel Willmann44fa2012021-02-12 04:55:40 +0100218 gprs_gb_parse_tlli(msgb_data(msg), msgb_length(msg), &tlli);
Harald Weltefe059582020-11-18 12:01:46 +0100219
Daniel Willmann44fa2012021-02-12 04:55:40 +0100220 rc = bssgp2_nsi_tx_ptp(nsi, nse->nsei, ns_bvci, msg, tlli);
Daniel Willmann76205712020-11-30 17:08:58 +0100221 /* FIXME: We need a counter group for gbproxy_nse */
222 //if (rc < 0)
Pau Espin Pedrol56438362021-06-04 18:03:44 +0200223 // rate_ctr_inc(rate_ctr_group_get_ctr(bvc->ctrg, GBPROX_PEER_CTR_TX_ERR));
Daniel Willmann76205712020-11-30 17:08:58 +0100224
225 return rc;
226}
227
Harald Welte560bdb32020-12-04 22:24:47 +0100228/* feed a message down the NS-VC associated with the specified bvc */
229static int gbprox_relay2peer(struct msgb *old_msg, struct gbproxy_bvc *bvc,
Daniel Willmann76205712020-11-30 17:08:58 +0100230 uint16_t ns_bvci)
231{
232 int rc;
Harald Welte560bdb32020-12-04 22:24:47 +0100233 struct gbproxy_nse *nse = bvc->nse;
Daniel Willmann76205712020-11-30 17:08:58 +0100234 OSMO_ASSERT(nse);
235
236 rc = gbprox_relay2nse(old_msg, nse, ns_bvci);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200237 if (rc < 0)
Pau Espin Pedrol56438362021-06-04 18:03:44 +0200238 rate_ctr_inc(rate_ctr_group_get_ctr(bvc->ctrg, GBPROX_PEER_CTR_TX_ERR));
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200239
240 return rc;
241}
242
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200243int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
244{
245 return 0;
246}
247
Harald Weltee5209642020-12-05 19:59:45 +0100248
249/***********************************************************************
250 * PTP BVC handling
251 ***********************************************************************/
252
Daniel Willmannd4ab1f92020-12-21 18:53:55 +0100253/* FIXME: Handle the tlli NULL case correctly,
254 * This function should take a generic selector
255 * and choose an sgsn based on that
256 */
257static struct gbproxy_sgsn *gbproxy_select_sgsn(struct gbproxy_config *cfg, const uint32_t *tlli)
258{
259 struct gbproxy_sgsn *sgsn = NULL;
260 struct gbproxy_sgsn *sgsn_avoid = NULL;
261
262 int tlli_type;
263 int16_t nri;
264 bool null_nri = false;
265
266 if (!tlli) {
Daniel Willmann37518b32021-05-27 18:13:36 +0200267 sgsn = gbproxy_sgsn_by_available(cfg);
Daniel Willmannd4ab1f92020-12-21 18:53:55 +0100268 if (!sgsn) {
Daniel Willmann37518b32021-05-27 18:13:36 +0200269 LOGP(DGPRS, LOGL_ERROR, "Could not find any available SGSN\n");
Daniel Willmannd4ab1f92020-12-21 18:53:55 +0100270 return NULL;
271 }
Daniel Willmann37518b32021-05-27 18:13:36 +0200272 LOGPSGSN(sgsn, LOGL_INFO, "Could not get TLLI, using first available SGSN\n");
Daniel Willmannd4ab1f92020-12-21 18:53:55 +0100273 return sgsn;
274 }
275
276 if (cfg->pool.nri_bitlen == 0) {
277 /* Pooling is disabled */
Daniel Willmann37518b32021-05-27 18:13:36 +0200278 sgsn = gbproxy_sgsn_by_available(cfg);
Daniel Willmannd4ab1f92020-12-21 18:53:55 +0100279 if (!sgsn) {
Daniel Willmann37518b32021-05-27 18:13:36 +0200280 LOGP(DGPRS, LOGL_ERROR, "Could not find any available SGSN\n");
Daniel Willmannd4ab1f92020-12-21 18:53:55 +0100281 return NULL;
282 }
283
Daniel Willmann37518b32021-05-27 18:13:36 +0200284 LOGPSGSN(sgsn, LOGL_INFO, "Pooling disabled, using first available SGSN\n");
Daniel Willmannd4ab1f92020-12-21 18:53:55 +0100285 } else {
286 /* Pooling is enabled, try to use the NRI for routing to an SGSN
287 * See 3GPP TS 23.236 Ch. 5.3.2 */
288 tlli_type = gprs_tlli_type(*tlli);
289 if (tlli_type == TLLI_LOCAL || tlli_type == TLLI_FOREIGN) {
290 /* Only get/use the NRI if tlli type is local */
291 osmo_tmsi_nri_v_get(&nri, *tlli, cfg->pool.nri_bitlen);
292 if (nri >= 0) {
293 /* Get the SGSN for the NRI */
294 sgsn = gbproxy_sgsn_by_nri(cfg, nri, &null_nri);
295 if (sgsn && !null_nri)
296 return sgsn;
297 /* If the NRI is the null NRI, we need to avoid the chosen SGSN */
298 if (null_nri && sgsn) {
299 sgsn_avoid = sgsn;
300 }
301 } else {
302 /* We couldn't get the NRI from the TLLI */
Daniel Willmanncd21afe2021-01-21 18:44:51 +0100303 LOGP(DGPRS, LOGL_ERROR, "Could not extract NRI from local TLLI %08x\n", *tlli);
Daniel Willmannd4ab1f92020-12-21 18:53:55 +0100304 }
Daniel Willmanncd21afe2021-01-21 18:44:51 +0100305 } else {
306 LOGP(DGPRS, LOGL_INFO, "TLLI %08x is neither local nor foreign, not routing by NRI\n", *tlli);
Daniel Willmannd4ab1f92020-12-21 18:53:55 +0100307 }
308 }
309
310 /* If we haven't found an SGSN yet we need to choose one, but avoid the one in sgsn_avoid
311 * NOTE: This function is not stable if the number of SGSNs or allow_attach changes
312 * We could implement TLLI tracking here, but 3GPP TS 23.236 Ch. 5.3.2 (see NOTE) argues that
313 * we can just wait for the MS to reattempt the procedure.
314 */
315 if (!sgsn)
316 sgsn = gbproxy_sgsn_by_tlli(cfg, sgsn_avoid, *tlli);
317
318 if (!sgsn) {
319 LOGP(DGPRS, LOGL_ERROR, "No suitable SGSN found for TLLI %u\n", *tlli);
320 return NULL;
321 }
322
323 return sgsn;
324}
325
326/*! Find the correct gbproxy_bvc given a cell and an SGSN
327 * \param[in] cfg The gbproxy configuration
328 * \param[in] cell The cell the message belongs to
329 * \param[in] tlli An optional TLLI used for tracking
330 * \return Returns 0 on success, otherwise a negative value
331 */
332static struct gbproxy_bvc *gbproxy_select_sgsn_bvc(struct gbproxy_config *cfg, struct gbproxy_cell *cell, const uint32_t *tlli)
333{
334 struct gbproxy_sgsn *sgsn;
335 struct gbproxy_bvc *sgsn_bvc = NULL;
Harald Welte02d7c482020-12-30 12:13:36 +0100336 int i;
Daniel Willmannd4ab1f92020-12-21 18:53:55 +0100337
338 sgsn = gbproxy_select_sgsn(cfg, tlli);
339 if (!sgsn) {
340 LOGPCELL(cell, LOGL_ERROR, "Could not find any SGSN, dropping message!\n");
341 return NULL;
342 }
343
344 /* Get the BVC for this SGSN/NSE */
Harald Welte02d7c482020-12-30 12:13:36 +0100345 for (i = 0; i < ARRAY_SIZE(cell->sgsn_bvc); i++) {
Daniel Willmannd4ab1f92020-12-21 18:53:55 +0100346 sgsn_bvc = cell->sgsn_bvc[i];
347 if (!sgsn_bvc)
348 continue;
349 if (sgsn->nse != sgsn_bvc->nse)
350 continue;
Daniel Willmann5ceeb2b2022-02-23 18:00:48 +0100351 if (!bssgp_bvc_fsm_is_unblocked(sgsn_bvc->fi))
352 continue;
Daniel Willmannd4ab1f92020-12-21 18:53:55 +0100353
354 return sgsn_bvc;
355 }
356
357 /* This shouldn't happen */
Daniel Willmanna648f3c2020-12-28 18:07:27 +0100358 LOGPCELL(cell, LOGL_ERROR, "Could not find matching BVC for SGSN %s, dropping message!\n", sgsn->name);
Daniel Willmannd4ab1f92020-12-21 18:53:55 +0100359 return NULL;
360}
361
362/*! Send a message to the next SGSN, possibly ignoring the null SGSN
363 * route an uplink message on a PTP-BVC to a SGSN using the TLLI
364 * \param[in] cell The cell the message belongs to
365 * \param[in] msg The BSSGP message
366 * \param[in] null_sgsn If not NULL then avoid this SGSN (because this message contains its null NRI)
367 * \param[in] tlli An optional TLLI used for tracking
368 * \return Returns 0 on success, otherwise a negative value
369 */
370static int gbprox_bss2sgsn_tlli(struct gbproxy_cell *cell, struct msgb *msg, const uint32_t *tlli,
Harald Weltee5209642020-12-05 19:59:45 +0100371 bool sig_bvci)
372{
Daniel Willmannd4ab1f92020-12-21 18:53:55 +0100373 struct gbproxy_config *cfg = cell->cfg;
Harald Weltee5209642020-12-05 19:59:45 +0100374 struct gbproxy_bvc *sgsn_bvc;
Harald Weltee5209642020-12-05 19:59:45 +0100375
Daniel Willmannd4ab1f92020-12-21 18:53:55 +0100376 sgsn_bvc = gbproxy_select_sgsn_bvc(cfg, cell, tlli);
377 if (!sgsn_bvc) {
378 LOGPCELL(cell, LOGL_NOTICE, "Could not find any SGSN for TLLI %u, dropping message!\n", *tlli);
379 return -EINVAL;
Harald Weltee5209642020-12-05 19:59:45 +0100380 }
Daniel Willmannd4ab1f92020-12-21 18:53:55 +0100381
382 return gbprox_relay2peer(msg, sgsn_bvc, sig_bvci ? 0 : sgsn_bvc->bvci);
Harald Weltee5209642020-12-05 19:59:45 +0100383}
384
Daniel Willmann22311802021-11-02 11:54:27 +0100385static int gbproxy_decode_bssgp(const struct bssgp_normal_hdr *bgph, int msg_len, struct tlv_parsed *tp, const char *log_pfx)
386{
387 int rc;
388
389 /* UNITDATA PDUs have a different header than the other PDUs */
390 if (bgph->pdu_type == BSSGP_PDUT_UL_UNITDATA || bgph->pdu_type == BSSGP_PDUT_DL_UNITDATA) {
391 const struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) bgph;
392 if (msg_len < sizeof(*budh))
Daniel Willmannf689a8a2021-11-18 15:27:37 +0100393 return OSMO_TLVP_ERR_MAND_IE_MISSING;
Daniel Willmann22311802021-11-02 11:54:27 +0100394 rc = osmo_tlv_prot_parse(&osmo_pdef_bssgp, tp, 1, budh->pdu_type, budh->data,
395 msg_len - sizeof(*budh), 0, 0, DGPRS, log_pfx);
396 /* populate TLLI from the fixed headser into the TLV-parsed array so later code
397 * doesn't have to worry where the TLLI came from */
398 tp->lv[BSSGP_IE_TLLI].len = 4;
399 tp->lv[BSSGP_IE_TLLI].val = (const uint8_t *) &budh->tlli;
400 } else {
401 rc = osmo_tlv_prot_parse(&osmo_pdef_bssgp, tp, 1, bgph->pdu_type, bgph->data,
402 msg_len - sizeof(*bgph), 0, 0, DGPRS, log_pfx);
403 }
404
405 return rc;
406}
407
Daniel Willmann5a148372021-10-29 18:28:13 +0200408static int gbproxy_tlli_from_status_pdu(struct tlv_parsed *tp, uint32_t *tlli, char *log_pfx);
409
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200410/* Receive an incoming PTP message from a BSS-side NS-VC */
Harald Weltee5209642020-12-05 19:59:45 +0100411static int gbprox_rx_ptp_from_bss(struct gbproxy_nse *nse, struct msgb *msg, uint16_t ns_bvci)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200412{
Harald Welte278dd272020-12-06 13:35:24 +0100413 struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
Harald Weltee5209642020-12-05 19:59:45 +0100414 const char *pdut_name = osmo_tlv_prot_msg_name(&osmo_pdef_bssgp, bgph->pdu_type);
415 struct gbproxy_bvc *bss_bvc;
416 struct tlv_parsed tp;
417 char log_pfx[32];
418 uint32_t tlli;
419 int rc;
420
421 snprintf(log_pfx, sizeof(log_pfx), "NSE(%05u/BSS)-BVC(%05u/??)", nse->nsei, ns_bvci);
422
423 LOGP(DGPRS, LOGL_DEBUG, "%s Rx %s\n", log_pfx, pdut_name);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200424
Daniel Willmann06331ac2020-12-10 17:59:46 +0100425 if (ns_bvci == 0 || ns_bvci == 1) {
Harald Weltee5209642020-12-05 19:59:45 +0100426 LOGP(DGPRS, LOGL_NOTICE, "%s BVCI=%05u is not PTP\n", log_pfx, ns_bvci);
Daniel Willmannf8cba652021-02-12 04:59:47 +0100427 return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
Harald Welte278dd272020-12-06 13:35:24 +0100428 }
429
430 if (!(bssgp_pdu_type_flags(bgph->pdu_type) & BSSGP_PDUF_PTP)) {
Harald Weltee5209642020-12-05 19:59:45 +0100431 LOGP(DGPRS, LOGL_NOTICE, "%s %s not allowed in PTP BVC\n", log_pfx, pdut_name);
Daniel Willmannf8cba652021-02-12 04:59:47 +0100432 return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
Harald Welte278dd272020-12-06 13:35:24 +0100433 }
434
435 if (!(bssgp_pdu_type_flags(bgph->pdu_type) & BSSGP_PDUF_UL)) {
Harald Weltee5209642020-12-05 19:59:45 +0100436 LOGP(DGPRS, LOGL_NOTICE, "%s %s not allowed in uplink direction\n", log_pfx, pdut_name);
Daniel Willmannf8cba652021-02-12 04:59:47 +0100437 return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
Harald Welte278dd272020-12-06 13:35:24 +0100438 }
439
Harald Weltee5209642020-12-05 19:59:45 +0100440 bss_bvc = gbproxy_bvc_by_bvci(nse, ns_bvci);
441 if (!bss_bvc) {
442 LOGP(DGPRS, LOGL_NOTICE, "%s %s - Didn't find BVC for PTP message, discarding\n",
443 log_pfx, pdut_name);
Daniel Willmannf8cba652021-02-12 04:59:47 +0100444 return tx_status(nse, ns_bvci, BSSGP_CAUSE_UNKNOWN_BVCI, &ns_bvci, msg);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200445 }
446
Daniel Willmann22311802021-11-02 11:54:27 +0100447 rc = gbproxy_decode_bssgp(bgph, msgb_bssgp_len(msg), &tp, log_pfx);
Harald Weltee5209642020-12-05 19:59:45 +0100448 if (rc < 0) {
Pau Espin Pedrol56438362021-06-04 18:03:44 +0200449 rate_ctr_inc(rate_ctr_group_get_ctr(nse->cfg->ctrg, GBPROX_GLOB_CTR_PROTO_ERR_BSS));
Daniel Willmannf8cba652021-02-12 04:59:47 +0100450 return tx_status_from_tlvp(nse, rc, msg);
Harald Weltee5209642020-12-05 19:59:45 +0100451 }
Harald Welte85a40272020-12-08 21:43:22 +0100452 /* hack to get both msg + tlv_parsed passed via osmo_fsm_inst_dispatch */
453 msgb_bcid(msg) = (void *)&tp;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200454
Harald Weltee5209642020-12-05 19:59:45 +0100455 switch (bgph->pdu_type) {
456 case BSSGP_PDUT_UL_UNITDATA:
457 case BSSGP_PDUT_RA_CAPA_UPDATE:
458 case BSSGP_PDUT_FLOW_CONTROL_MS:
459 case BSSGP_PDUT_DOWNLOAD_BSS_PFC:
460 case BSSGP_PDUT_CREATE_BSS_PFC_ACK:
461 case BSSGP_PDUT_CREATE_BSS_PFC_NACK:
462 case BSSGP_PDUT_MODIFY_BSS_PFC_ACK:
463 case BSSGP_PDUT_DELETE_BSS_PFC_ACK:
464 case BSSGP_PDUT_FLOW_CONTROL_PFC:
465 case BSSGP_PDUT_DELETE_BSS_PFC_REQ:
466 case BSSGP_PDUT_PS_HO_REQUIRED:
467 case BSSGP_PDUT_PS_HO_REQUEST_ACK:
468 case BSSGP_PDUT_PS_HO_REQUEST_NACK:
469 case BSSGP_PDUT_PS_HO_COMPLETE:
470 case BSSGP_PDUT_PS_HO_CANCEL:
471 /* We can route based on TLLI-NRI */
472 tlli = osmo_load32be(TLVP_VAL(&tp, BSSGP_IE_TLLI));
Daniel Willmannd4ab1f92020-12-21 18:53:55 +0100473 rc = gbprox_bss2sgsn_tlli(bss_bvc->cell, msg, &tlli, false);
Harald Weltee5209642020-12-05 19:59:45 +0100474 break;
475 case BSSGP_PDUT_RADIO_STATUS:
476 if (TLVP_PRESENT(&tp, BSSGP_IE_TLLI)) {
477 tlli = osmo_load32be(TLVP_VAL(&tp, BSSGP_IE_TLLI));
Daniel Willmannd4ab1f92020-12-21 18:53:55 +0100478 rc = gbprox_bss2sgsn_tlli(bss_bvc->cell, msg, &tlli, false);
Harald Weltee5209642020-12-05 19:59:45 +0100479 } else if (TLVP_PRESENT(&tp, BSSGP_IE_TMSI)) {
480 /* we treat the TMSI like a TLLI and extract the NRI from it */
481 tlli = osmo_load32be(TLVP_VAL(&tp, BSSGP_IE_TMSI));
Daniel Willmann8b3ed292021-01-21 18:46:51 +0100482 /* Convert the TMSI into a FOREIGN TLLI so it is routed appropriately */
483 tlli = gprs_tmsi2tlli(tlli, TLLI_FOREIGN);
Daniel Willmannd4ab1f92020-12-21 18:53:55 +0100484 rc = gbprox_bss2sgsn_tlli(bss_bvc->cell, msg, &tlli, false);
Harald Weltee5209642020-12-05 19:59:45 +0100485 } else if (TLVP_PRESENT(&tp, BSSGP_IE_IMSI)) {
Daniel Willmann5193f222021-01-11 05:00:46 +0100486 /* FIXME: Use the IMSI as selector? */
Daniel Willmannd4ab1f92020-12-21 18:53:55 +0100487 rc = gbprox_bss2sgsn_tlli(bss_bvc->cell, msg, NULL, false);
Harald Weltee5209642020-12-05 19:59:45 +0100488 } else
489 LOGPBVC(bss_bvc, LOGL_ERROR, "Rx RADIO-STATUS without any of the conditional IEs\n");
490 break;
491 case BSSGP_PDUT_DUMMY_PAGING_PS_RESP:
492 case BSSGP_PDUT_PAGING_PS_REJECT:
Daniel Willmann5614e572021-01-18 18:38:27 +0100493 {
494 /* Route according to IMSI<->NSE cache entry */
495 struct osmo_mobile_identity mi;
496 const uint8_t *mi_data = TLVP_VAL(&tp, BSSGP_IE_IMSI);
497 uint8_t mi_len = TLVP_LEN(&tp, BSSGP_IE_IMSI);
498 osmo_mobile_identity_decode(&mi, mi_data, mi_len, false);
Daniel Willmann361d0b52021-07-09 17:44:30 +0200499 nse = gbproxy_nse_by_imsi(nse->cfg, mi.imsi, CACHE_USAGE_PAGING);
Daniel Willmann5614e572021-01-18 18:38:27 +0100500 if (nse) {
501 OSMO_ASSERT(nse->sgsn_facing);
502 rc = gbprox_relay2nse(msg, nse, ns_bvci);
503 } else {
Daniel Willmann82669182021-01-19 11:37:55 +0100504 LOGPBVC(bss_bvc, LOGL_ERROR, "Rx unmatched %s with IMSI %s\n", pdut_name, mi.imsi);
Daniel Willmann5614e572021-01-18 18:38:27 +0100505 }
Harald Weltee5209642020-12-05 19:59:45 +0100506 break;
Daniel Willmann5614e572021-01-18 18:38:27 +0100507 }
Harald Weltee5209642020-12-05 19:59:45 +0100508 case BSSGP_PDUT_FLOW_CONTROL_BVC:
Harald Welte85a40272020-12-08 21:43:22 +0100509 osmo_fsm_inst_dispatch(bss_bvc->fi, BSSGP_BVCFSM_E_RX_FC_BVC, msg);
Harald Weltee5209642020-12-05 19:59:45 +0100510 break;
511 case BSSGP_PDUT_STATUS:
Daniel Willmann5a148372021-10-29 18:28:13 +0200512 {
513 struct gbproxy_sgsn *sgsn;
514 /* Check if the status needs to be terminated locally */
515 uint8_t cause = *TLVP_VAL(&tp, BSSGP_IE_CAUSE);
516
517 LOGPNSE(nse, LOGL_NOTICE, "Rx STATUS cause=0x%02x(%s)\n", cause,
518 bssgp_cause_str(cause));
519
520 if (gbproxy_tlli_from_status_pdu(&tp, &tlli, log_pfx) == 0)
521 sgsn = gbproxy_select_sgsn(nse->cfg, &tlli);
522 else
523 sgsn = gbproxy_select_sgsn(nse->cfg, NULL);
524
525 if (!sgsn) {
526 rc = -EINVAL;
Harald Weltee5209642020-12-05 19:59:45 +0100527 break;
Daniel Willmann5a148372021-10-29 18:28:13 +0200528 }
529
530 rc = gbprox_relay2nse(msg, sgsn->nse, ns_bvci);
Harald Weltee5209642020-12-05 19:59:45 +0100531 break;
532 }
Daniel Willmann5a148372021-10-29 18:28:13 +0200533 }
Harald Weltee5209642020-12-05 19:59:45 +0100534
Daniel Willmannd71aa6b2022-02-28 18:19:27 +0100535 if (rc < 0)
536 rate_ctr_inc(rate_ctr_group_get_ctr(bss_bvc->ctrg, GBPROX_PEER_CTR_FWD_FROM_BSS_ERR));
537
Harald Weltee5209642020-12-05 19:59:45 +0100538 return 0;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200539}
540
541/* Receive an incoming PTP message from a SGSN-side NS-VC */
Harald Weltee5209642020-12-05 19:59:45 +0100542static int gbprox_rx_ptp_from_sgsn(struct gbproxy_nse *nse, struct msgb *msg, uint16_t ns_bvci)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200543{
Harald Welte278dd272020-12-06 13:35:24 +0100544 struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
Harald Weltee5209642020-12-05 19:59:45 +0100545 const char *pdut_name = osmo_tlv_prot_msg_name(&osmo_pdef_bssgp, bgph->pdu_type);
546 struct gbproxy_bvc *sgsn_bvc, *bss_bvc;
Harald Welte85a40272020-12-08 21:43:22 +0100547 struct tlv_parsed tp;
Harald Weltee5209642020-12-05 19:59:45 +0100548 char log_pfx[32];
Harald Welte85a40272020-12-08 21:43:22 +0100549 int rc;
Harald Weltee5209642020-12-05 19:59:45 +0100550
551 snprintf(log_pfx, sizeof(log_pfx), "NSE(%05u/SGSN)-BVC(%05u/??)", nse->nsei, ns_bvci);
552
553 LOGP(DGPRS, LOGL_DEBUG, "%s Rx %s\n", log_pfx, pdut_name);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200554
Daniel Willmann06331ac2020-12-10 17:59:46 +0100555 if (ns_bvci == 0 || ns_bvci == 1) {
Harald Weltee5209642020-12-05 19:59:45 +0100556 LOGP(DGPRS, LOGL_NOTICE, "%s BVCI is not PTP\n", log_pfx);
Daniel Willmannf8cba652021-02-12 04:59:47 +0100557 return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
Harald Welte278dd272020-12-06 13:35:24 +0100558 }
559
560 if (!(bssgp_pdu_type_flags(bgph->pdu_type) & BSSGP_PDUF_PTP)) {
Harald Weltee5209642020-12-05 19:59:45 +0100561 LOGP(DGPRS, LOGL_NOTICE, "%s %s not allowed in PTP BVC\n", log_pfx, pdut_name);
Daniel Willmannf8cba652021-02-12 04:59:47 +0100562 return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
Harald Welte278dd272020-12-06 13:35:24 +0100563 }
564
565 if (!(bssgp_pdu_type_flags(bgph->pdu_type) & BSSGP_PDUF_DL)) {
Harald Weltee5209642020-12-05 19:59:45 +0100566 LOGP(DGPRS, LOGL_NOTICE, "%s %s not allowed in downlink direction\n", log_pfx, pdut_name);
Daniel Willmannf8cba652021-02-12 04:59:47 +0100567 return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
Harald Welte278dd272020-12-06 13:35:24 +0100568 }
569
Harald Weltee5209642020-12-05 19:59:45 +0100570 sgsn_bvc = gbproxy_bvc_by_bvci(nse, ns_bvci);
571 if (!sgsn_bvc) {
572 LOGP(DGPRS, LOGL_NOTICE, "%s %s - Didn't find BVC for for PTP message, discarding\n",
573 log_pfx, pdut_name);
Pau Espin Pedrol56438362021-06-04 18:03:44 +0200574 rate_ctr_inc(rate_ctr_group_get_ctr(nse->cfg->ctrg, GBPROX_GLOB_CTR_INV_BVCI));
Daniel Willmannf8cba652021-02-12 04:59:47 +0100575 return tx_status(nse, ns_bvci, BSSGP_CAUSE_UNKNOWN_BVCI, &ns_bvci, msg);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200576 }
577
Harald Weltee5209642020-12-05 19:59:45 +0100578 if (!bssgp_bvc_fsm_is_unblocked(sgsn_bvc->fi)) {
579 LOGPBVC(sgsn_bvc, LOGL_NOTICE, "Rx %s: Dropping on blocked BVC\n", pdut_name);
Pau Espin Pedrol56438362021-06-04 18:03:44 +0200580 rate_ctr_inc(rate_ctr_group_get_ctr(sgsn_bvc->ctrg, GBPROX_PEER_CTR_DROPPED));
Daniel Willmannf8cba652021-02-12 04:59:47 +0100581 return tx_status(nse, ns_bvci, BSSGP_CAUSE_BVCI_BLOCKED, &ns_bvci, msg);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200582 }
Harald Welte85a40272020-12-08 21:43:22 +0100583
Daniel Willmann22311802021-11-02 11:54:27 +0100584 rc = gbproxy_decode_bssgp(bgph, msgb_bssgp_len(msg), &tp, log_pfx);
Harald Welte85a40272020-12-08 21:43:22 +0100585 if (rc < 0) {
Daniel Willmanncfb6f172021-11-02 11:55:06 +0100586 rate_ctr_inc(rate_ctr_group_get_ctr(nse->cfg->ctrg, GBPROX_GLOB_CTR_PROTO_ERR_SGSN));
Daniel Willmannf8cba652021-02-12 04:59:47 +0100587 return tx_status_from_tlvp(nse, rc, msg);
Harald Welte85a40272020-12-08 21:43:22 +0100588 }
589 /* hack to get both msg + tlv_parsed passed via osmo_fsm_inst_dispatch */
590 msgb_bcid(msg) = (void *)&tp;
591
Harald Weltee5209642020-12-05 19:59:45 +0100592 OSMO_ASSERT(sgsn_bvc->cell);
593 bss_bvc = sgsn_bvc->cell->bss_bvc;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200594
Harald Welte85a40272020-12-08 21:43:22 +0100595 switch (bgph->pdu_type) {
596 case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK:
597 return osmo_fsm_inst_dispatch(sgsn_bvc->fi, BSSGP_BVCFSM_E_RX_FC_BVC_ACK, msg);
Daniel Willmann5614e572021-01-18 18:38:27 +0100598 case BSSGP_PDUT_DUMMY_PAGING_PS:
599 case BSSGP_PDUT_PAGING_PS:
600 {
601 /* Cache the IMSI<->NSE to route PAGING REJECT */
602 struct osmo_mobile_identity mi;
603 const uint8_t *mi_data = TLVP_VAL(&tp, BSSGP_IE_IMSI);
604 uint8_t mi_len = TLVP_LEN(&tp, BSSGP_IE_IMSI);
605 osmo_mobile_identity_decode(&mi, mi_data, mi_len, false);
Daniel Willmann361d0b52021-07-09 17:44:30 +0200606 gbproxy_imsi_cache_update(nse, mi.imsi, CACHE_USAGE_PAGING);
Daniel Willmann5614e572021-01-18 18:38:27 +0100607 break;
Harald Welte85a40272020-12-08 21:43:22 +0100608 }
Daniel Willmann5614e572021-01-18 18:38:27 +0100609 default:
610 break;
611 }
Daniel Willmannd71aa6b2022-02-28 18:19:27 +0100612 rc = gbprox_relay2peer(msg, bss_bvc, bss_bvc->bvci);
Harald Welte85a40272020-12-08 21:43:22 +0100613
Daniel Willmannd71aa6b2022-02-28 18:19:27 +0100614 if (rc < 0)
615 rate_ctr_inc(rate_ctr_group_get_ctr(bss_bvc->ctrg, GBPROX_PEER_CTR_FWD_FROM_SGSN_ERR));
616
617 return rc;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200618}
619
Harald Weltee5209642020-12-05 19:59:45 +0100620/***********************************************************************
621 * BVC FSM call-backs
622 ***********************************************************************/
Harald Welte7df1e5a2020-12-02 22:53:26 +0100623
Harald Weltee5209642020-12-05 19:59:45 +0100624/* helper function to dispatch a FSM event to all SGSN-side BVC FSMs of a cell */
625static void dispatch_to_all_sgsn_bvc(struct gbproxy_cell *cell, uint32_t event, void *priv)
626{
627 unsigned int i;
628
629 for (i = 0; i < ARRAY_SIZE(cell->sgsn_bvc); i++) {
630 struct gbproxy_bvc *sgsn_bvc = cell->sgsn_bvc[i];
631 if (!sgsn_bvc)
632 continue;
633 osmo_fsm_inst_dispatch(sgsn_bvc->fi, event, priv);
634 }
635}
636
637/* BVC FSM informs us about a BSS-side reset of the signaling BVC */
638static void bss_sig_bvc_reset_notif(uint16_t nsei, uint16_t bvci, const struct gprs_ra_id *ra_id,
639 uint16_t cell_id, uint8_t cause, void *priv)
640{
641 struct gbproxy_bvc *sig_bvc = priv;
642 struct gbproxy_nse *nse = sig_bvc->nse;
643 struct gbproxy_bvc *ptp_bvc;
644 unsigned int i;
645
646 /* BLOCK all SGSN-side PTP BVC within this NSE */
647 hash_for_each(nse->bvcs, i, ptp_bvc, list) {
648 if (ptp_bvc == sig_bvc)
649 continue;
650 OSMO_ASSERT(ptp_bvc->cell);
651
652 dispatch_to_all_sgsn_bvc(ptp_bvc->cell, BSSGP_BVCFSM_E_REQ_BLOCK, &cause);
Harald Weltef9e149b2020-12-02 23:29:38 +0100653 }
Harald Welte7df1e5a2020-12-02 22:53:26 +0100654
Harald Weltee5209642020-12-05 19:59:45 +0100655 /* Delete all BSS-side PTP BVC within this NSE */
656 gbproxy_cleanup_bvcs(nse, 0);
657
658 /* TODO: we keep the "CELL" around for now, re-connecting it to
659 * any (later) new PTP-BVC for that BVCI. Not sure if that's the
660 * best idea ? */
661}
662
663/* forward declaration */
664static const struct bssgp_bvc_fsm_ops sgsn_ptp_bvc_fsm_ops;
665
666static const struct bssgp_bvc_fsm_ops bss_sig_bvc_fsm_ops = {
667 .reset_notification = bss_sig_bvc_reset_notif,
668};
669
670/* BVC FSM informs us about a BSS-side reset of a PTP BVC */
671static void bss_ptp_bvc_reset_notif(uint16_t nsei, uint16_t bvci, const struct gprs_ra_id *ra_id,
672 uint16_t cell_id, uint8_t cause, void *priv)
673{
674 struct gbproxy_bvc *bvc = priv;
675 struct gbproxy_config *cfg = bvc->nse->cfg;
Harald Welte664c24e2020-12-12 15:01:17 +0100676 struct gbproxy_nse *sgsn_nse;
Harald Weltee5209642020-12-05 19:59:45 +0100677 unsigned int i;
678
679 OSMO_ASSERT(bvci != 0);
680
681 if (!bvc->cell) {
682 /* see if we have a CELL dangling around */
683 bvc->cell = gbproxy_cell_by_bvci(cfg, bvci);
684 if (bvc->cell) {
685 /* the CELL already exists. This means either it * was created before at an
686 * earlier PTP BVC-RESET, or that there are non-unique BVCIs and hence a
687 * malconfiguration */
688 if (bvc->cell->bss_bvc) {
689 LOGPBVC(bvc, LOGL_NOTICE, "Rx BVC-RESET via this NSE, but CELL already "
690 "has BVC on NSEI=%05u\n", bvc->cell->bss_bvc->nse->nsei);
691 LOGPBVC(bvc->cell->bss_bvc, LOGL_NOTICE, "Destroying due to conflicting "
692 "BVCI configuration (new NSEI=%05u)!\n", bvc->nse->nsei);
693 gbproxy_bvc_free(bvc->cell->bss_bvc);
Daniel Willmann28ec0e32021-12-06 16:47:22 +0100694 bvc->cell = NULL;
695 } else {
696 LOGPBVC(bvc, LOGL_ERROR, "Found cell without BSS BVC, this should not happen!");
Harald Weltee5209642020-12-05 19:59:45 +0100697 }
Harald Weltee5209642020-12-05 19:59:45 +0100698 }
699 }
700
701 if (!bvc->cell) {
Harald Weltee5209642020-12-05 19:59:45 +0100702 /* if we end up here, it means this is the first time we received a BVC-RESET
703 * for this BVC. We need to create the 'cell' data structure and the SGSN-side
704 * BVC counterparts */
705
Philipp Maiere4597ec2021-02-09 16:02:00 +0100706 bvc->cell = gbproxy_cell_alloc(cfg, bvci, ra_id, cell_id);
Harald Weltee5209642020-12-05 19:59:45 +0100707 OSMO_ASSERT(bvc->cell);
708
709 /* link us to the cell and vice-versa */
710 bvc->cell->bss_bvc = bvc;
Harald Welte664c24e2020-12-12 15:01:17 +0100711 }
Harald Weltee5209642020-12-05 19:59:45 +0100712
Daniel Willmann6701d272021-04-08 08:39:12 +0200713 /* Ensure we have the correct RA/CELL ID */
714 if (!gsm48_ra_equal(&bvc->cell->id.raid, ra_id)) {
715 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));
716 memcpy(&bvc->cell->id.raid, ra_id, sizeof(*ra_id));
717 }
718 if (bvc->cell->id.cid != cell_id) {
719 LOGPBVC(bvc, LOGL_NOTICE, "CellID changed from %05d to %05d, updating cell\n", bvc->cell->id.cid, cell_id);
720 bvc->cell->id.cid = cell_id;
721 }
722
723 /* Reallocate SGSN-side BVCs of the cell, and reset them
Daniel Willmannf9902c52021-11-12 15:52:03 +0100724 * Removing and reallocating is needed because the ra_id/cell_id might have changed */
Harald Welte664c24e2020-12-12 15:01:17 +0100725 hash_for_each(cfg->sgsn_nses, i, sgsn_nse, list) {
726 struct gbproxy_bvc *sgsn_bvc = gbproxy_bvc_by_bvci(sgsn_nse, bvci);
Daniel Willmanndc763fd2021-09-24 16:45:38 +0200727 if (!sgsn_bvc)
728 sgsn_bvc = gbproxy_bvc_by_bvci_inactive(sgsn_nse, bvci);
Harald Welte664c24e2020-12-12 15:01:17 +0100729 if (sgsn_bvc)
Daniel Willmann6701d272021-04-08 08:39:12 +0200730 gbproxy_bvc_free(sgsn_bvc);
Harald Weltee5209642020-12-05 19:59:45 +0100731
Daniel Willmann6701d272021-04-08 08:39:12 +0200732 sgsn_bvc = gbproxy_bvc_alloc(sgsn_nse, bvci);
733 OSMO_ASSERT(sgsn_bvc);
734 sgsn_bvc->cell = bvc->cell;
Daniel Willmann6701d272021-04-08 08:39:12 +0200735 sgsn_bvc->fi = bssgp_bvc_fsm_alloc_ptp_bss(sgsn_bvc, cfg->nsi, sgsn_nse->nsei,
736 bvci, ra_id, cell_id);
737 OSMO_ASSERT(sgsn_bvc->fi);
738 bssgp_bvc_fsm_set_max_pdu_len(sgsn_bvc->fi, sgsn_nse->max_sdu_len);
739 bssgp_bvc_fsm_set_ops(sgsn_bvc->fi, &sgsn_ptp_bvc_fsm_ops, sgsn_bvc);
740 gbproxy_cell_add_sgsn_bvc(bvc->cell, sgsn_bvc);
Harald Weltee5209642020-12-05 19:59:45 +0100741 }
742
743 /* Trigger outbound BVC-RESET procedure toward each SGSN */
744 dispatch_to_all_sgsn_bvc(bvc->cell, BSSGP_BVCFSM_E_REQ_RESET, &cause);
745}
746
747/* BVC FSM informs us about a BSS-side FSM state change */
748static void bss_ptp_bvc_state_chg_notif(uint16_t nsei, uint16_t bvci, int old_state, int state, void *priv)
749{
750 struct gbproxy_bvc *bvc = priv;
751 struct gbproxy_cell *cell = bvc->cell;
752 uint8_t cause = bssgp_bvc_fsm_get_block_cause(bvc->fi);
753
754 /* we have just been created but due to callback ordering the cell is not associated */
755 if (!cell)
756 return;
757
758 switch (state) {
759 case BSSGP_BVCFSM_S_BLOCKED:
760 /* block the corresponding SGSN-side PTP BVCs */
761 dispatch_to_all_sgsn_bvc(cell, BSSGP_BVCFSM_E_REQ_BLOCK, &cause);
762 break;
763 case BSSGP_BVCFSM_S_UNBLOCKED:
764 /* unblock the corresponding SGSN-side PTP BVCs */
765 dispatch_to_all_sgsn_bvc(cell, BSSGP_BVCFSM_E_REQ_UNBLOCK, NULL);
766 break;
767 }
768}
769
Harald Welte85a40272020-12-08 21:43:22 +0100770/* BVC FSM informs us about BVC-FC PDU receive */
771static void bss_ptp_bvc_fc_bvc(uint16_t nsei, uint16_t bvci, const struct bssgp2_flow_ctrl *fc, void *priv)
772{
Harald Welte209dc9f2020-12-12 19:02:16 +0100773 struct bssgp2_flow_ctrl fc_reduced;
Harald Welte85a40272020-12-08 21:43:22 +0100774 struct gbproxy_bvc *bss_bvc = priv;
Harald Welte209dc9f2020-12-12 19:02:16 +0100775 struct gbproxy_cell *cell;
776 struct gbproxy_config *cfg;
Harald Welte85a40272020-12-08 21:43:22 +0100777
Harald Welte209dc9f2020-12-12 19:02:16 +0100778 OSMO_ASSERT(bss_bvc);
779 OSMO_ASSERT(fc);
780
781 cell = bss_bvc->cell;
Harald Welte85a40272020-12-08 21:43:22 +0100782 if (!cell)
783 return;
784
Harald Welte209dc9f2020-12-12 19:02:16 +0100785 cfg = cell->cfg;
Harald Welte85a40272020-12-08 21:43:22 +0100786
Harald Welte209dc9f2020-12-12 19:02:16 +0100787 /* reduce / scale according to configuration to make sure we only advertise a fraction
788 * of the capacity to each of the SGSNs in the pool */
789 fc_reduced = *fc;
790 fc_reduced.bucket_size_max = (fc->bucket_size_max * cfg->pool.bvc_fc_ratio) / 100;
791 fc_reduced.bucket_leak_rate = (fc->bucket_leak_rate * cfg->pool.bvc_fc_ratio) / 100;
792 /* we don't modify the per-MS related values as any single MS is only served by one SGSN */
793
794 dispatch_to_all_sgsn_bvc(cell, BSSGP_BVCFSM_E_REQ_FC_BVC, (void *) &fc_reduced);
Harald Welte85a40272020-12-08 21:43:22 +0100795}
796
Harald Weltee5209642020-12-05 19:59:45 +0100797static const struct bssgp_bvc_fsm_ops bss_ptp_bvc_fsm_ops = {
798 .reset_notification = bss_ptp_bvc_reset_notif,
799 .state_chg_notification = bss_ptp_bvc_state_chg_notif,
Harald Welte85a40272020-12-08 21:43:22 +0100800 .rx_fc_bvc = bss_ptp_bvc_fc_bvc,
Harald Weltee5209642020-12-05 19:59:45 +0100801};
802
803/* BVC FSM informs us about a SGSN-side reset of a PTP BVC */
804static void sgsn_ptp_bvc_reset_notif(uint16_t nsei, uint16_t bvci, const struct gprs_ra_id *ra_id,
805 uint16_t cell_id, uint8_t cause, void *priv)
806{
807 struct gbproxy_bvc *bvc = priv;
808
809 if (!bvc->cell) {
810 LOGPBVC(bvc, LOGL_ERROR, "RESET of PTP BVC on SGSN side for which we have no BSS?\n");
811 return;
812 }
813
814 OSMO_ASSERT(bvc->cell->bss_bvc);
815
816 /* request reset of BSS-facing PTP-BVC */
817 osmo_fsm_inst_dispatch(bvc->cell->bss_bvc->fi, BSSGP_BVCFSM_E_REQ_RESET, &cause);
818}
819
820static const struct bssgp_bvc_fsm_ops sgsn_ptp_bvc_fsm_ops = {
821 .reset_notification = sgsn_ptp_bvc_reset_notif,
822};
823
824/* BVC FSM informs us about a SGSN-side reset of the signaling BVC */
825static void sgsn_sig_bvc_reset_notif(uint16_t nsei, uint16_t bvci, const struct gprs_ra_id *ra_id,
826 uint16_t cell_id, uint8_t cause, void *priv)
827{
828 struct gbproxy_bvc *bvc = priv;
829 struct gbproxy_config *cfg = bvc->nse->cfg;
830 struct gbproxy_nse *bss_nse;
831 unsigned int i;
832
833 /* delete all SGSN-side PTP BVC for this SGSN */
834 gbproxy_cleanup_bvcs(bvc->nse, 0);
835 /* FIXME: what to do about the cells? */
836 /* FIXME: do we really want to RESET all signaling BVC on the BSS and affect all other SGSN? */
837
838 /* we need to trigger generating a reset procedure towards each BSS side signaling BVC */
839 hash_for_each(cfg->bss_nses, i, bss_nse, list) {
840 struct gbproxy_bvc *bss_bvc = gbproxy_bvc_by_bvci(bss_nse, 0);
841 if (!bss_bvc) {
842 LOGPNSE(bss_nse, LOGL_ERROR, "Doesn't have BVC with BVCI=0 ?!?\n");
843 continue;
844 }
845 osmo_fsm_inst_dispatch(bss_bvc->fi, BSSGP_BVCFSM_E_REQ_RESET, &cause);
846 }
847}
848
849const struct bssgp_bvc_fsm_ops sgsn_sig_bvc_fsm_ops = {
850 .reset_notification = sgsn_sig_bvc_reset_notif,
Daniel Willmann2f30a0c2022-03-30 11:31:48 +0200851 .reset_ack_notification = sgsn_sig_bvc_reset_notif,
Harald Weltee5209642020-12-05 19:59:45 +0100852};
853
854/***********************************************************************
855 * Signaling BVC handling
856 ***********************************************************************/
857
858/* process a BVC-RESET message from the BSS side */
859static int rx_bvc_reset_from_bss(struct gbproxy_nse *nse, struct msgb *msg, struct tlv_parsed *tp)
860{
861 struct gbproxy_bvc *from_bvc = NULL;
862 uint16_t bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI));
863 uint32_t features = 0; // FIXME: make configurable
864
865 LOGPNSE(nse, LOGL_INFO, "Rx BVC-RESET (BVCI=%05u)\n", bvci);
866
Harald Welte314647b2020-12-02 23:03:22 +0100867 if (bvci == 0) {
868 /* If we receive a BVC reset on the signalling endpoint, we
869 * don't want the SGSN to reset, as the signalling endpoint
870 * is common for all point-to-point BVCs (and thus all BTS) */
Harald Welte324f0652020-12-02 23:06:37 +0100871
Harald Weltee5209642020-12-05 19:59:45 +0100872 from_bvc = gbproxy_bvc_by_bvci(nse, 0);
Harald Welte560bdb32020-12-04 22:24:47 +0100873 if (!from_bvc) {
Harald Weltee5209642020-12-05 19:59:45 +0100874 from_bvc = gbproxy_bvc_alloc(nse, 0);
875 OSMO_ASSERT(from_bvc);
876 from_bvc->fi = bssgp_bvc_fsm_alloc_sig_sgsn(from_bvc, nse->cfg->nsi, nse->nsei, features);
877 if (!from_bvc->fi) {
878 LOGPNSE(nse, LOGL_ERROR, "Cannot allocate SIG-BVC FSM\n");
879 gbproxy_bvc_free(from_bvc);
880 return -ENOMEM;
Harald Welte7df1e5a2020-12-02 22:53:26 +0100881 }
Daniel Willmanna8b61652021-02-12 05:05:14 +0100882 bssgp_bvc_fsm_set_max_pdu_len(from_bvc->fi, nse->max_sdu_len);
Harald Weltee5209642020-12-05 19:59:45 +0100883 bssgp_bvc_fsm_set_ops(from_bvc->fi, &bss_sig_bvc_fsm_ops, from_bvc);
884 }
885 } else {
886 from_bvc = gbproxy_bvc_by_bvci(nse, bvci);
887 if (!from_bvc) {
Harald Welte7df1e5a2020-12-02 22:53:26 +0100888 /* if a PTP-BVC is reset, and we don't know that
Harald Welte560bdb32020-12-04 22:24:47 +0100889 * PTP-BVCI yet, we should allocate a new bvc */
890 from_bvc = gbproxy_bvc_alloc(nse, bvci);
891 OSMO_ASSERT(from_bvc);
Harald Weltee5209642020-12-05 19:59:45 +0100892 from_bvc->fi = bssgp_bvc_fsm_alloc_ptp_sgsn(from_bvc, nse->cfg->nsi,
893 nse->nsei, bvci);
894 if (!from_bvc->fi) {
895 LOGPNSE(nse, LOGL_ERROR, "Cannot allocate SIG-BVC FSM\n");
896 gbproxy_bvc_free(from_bvc);
897 return -ENOMEM;
898 }
Daniel Willmanna8b61652021-02-12 05:05:14 +0100899 bssgp_bvc_fsm_set_max_pdu_len(from_bvc->fi, nse->max_sdu_len);
Harald Weltee5209642020-12-05 19:59:45 +0100900 bssgp_bvc_fsm_set_ops(from_bvc->fi, &bss_ptp_bvc_fsm_ops, from_bvc);
Harald Welte7df1e5a2020-12-02 22:53:26 +0100901 }
Harald Welte7df1e5a2020-12-02 22:53:26 +0100902 }
Harald Weltee5209642020-12-05 19:59:45 +0100903 /* hand into FSM for further processing */
904 osmo_fsm_inst_dispatch(from_bvc->fi, BSSGP_BVCFSM_E_RX_RESET, msg);
905 return 0;
Harald Welte7df1e5a2020-12-02 22:53:26 +0100906}
907
Philipp Maier1c5766b2021-02-09 17:03:03 +0100908/* Receive an incoming RIM message from a BSS-side NS-VC */
909static int gbprox_rx_rim_from_bss(struct tlv_parsed *tp, struct gbproxy_nse *nse, struct msgb *msg, char *log_pfx,
910 const char *pdut_name)
911{
912 struct gbproxy_sgsn *sgsn;
913 struct gbproxy_cell *dest_cell;
914 struct gbproxy_cell *src_cell;
915 struct bssgp_rim_routing_info dest_ri;
916 struct bssgp_rim_routing_info src_ri;
917 int rc;
Philipp Maier4499cf42021-02-10 17:54:44 +0100918 char ri_src_str[64];
919 char ri_dest_str[64];
Daniel Willmannf8cba652021-02-12 04:59:47 +0100920 uint16_t ns_bvci = msgb_bvci(msg);
Philipp Maier1c5766b2021-02-09 17:03:03 +0100921
922 rc = bssgp_parse_rim_ri(&dest_ri, TLVP_VAL(&tp[0], BSSGP_IE_RIM_ROUTING_INFO),
923 TLVP_LEN(&tp[0], BSSGP_IE_RIM_ROUTING_INFO));
924 if (rc < 0) {
925 LOGP(DGPRS, LOGL_ERROR, "%s %s cannot parse destination RIM routing info\n", log_pfx, pdut_name);
Daniel Willmannf8cba652021-02-12 04:59:47 +0100926 return tx_status(nse, ns_bvci, BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
Philipp Maier1c5766b2021-02-09 17:03:03 +0100927 }
928 rc = bssgp_parse_rim_ri(&src_ri, TLVP_VAL(&tp[1], BSSGP_IE_RIM_ROUTING_INFO),
929 TLVP_LEN(&tp[1], BSSGP_IE_RIM_ROUTING_INFO));
930 if (rc < 0) {
931 LOGP(DGPRS, LOGL_ERROR, "%s %s cannot parse source RIM routing info\n", log_pfx, pdut_name);
Daniel Willmannf8cba652021-02-12 04:59:47 +0100932 return tx_status(nse, ns_bvci, BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
Philipp Maier1c5766b2021-02-09 17:03:03 +0100933 }
934
935 /* Since gbproxy is 2G only we do not expect to get RIM messages only from GERAN cells. */
936 if (src_ri.discr != BSSGP_RIM_ROUTING_INFO_GERAN) {
937 LOGP(DGPRS, LOGL_ERROR, "%s %s source RIM routing info is not GERAN (%s)\n", log_pfx, pdut_name,
938 bssgp_rim_ri_name(&src_ri));
Daniel Willmannf8cba652021-02-12 04:59:47 +0100939 return tx_status(nse, ns_bvci, BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg);
Philipp Maier1c5766b2021-02-09 17:03:03 +0100940 }
941
942 /* Lookup source cell to make sure that the source RIM routing information actually belongs
943 * to a valid cell that we know */
944 src_cell = gbproxy_cell_by_cellid(nse->cfg, &src_ri.geran.raid, src_ri.geran.cid);
945 if (!src_cell) {
946 LOGP(DGPRS, LOGL_NOTICE, "%s %s cannot find cell for source RIM routing info (%s)\n", log_pfx,
947 pdut_name, bssgp_rim_ri_name(&src_ri));
Daniel Willmannf8cba652021-02-12 04:59:47 +0100948 return tx_status(nse, ns_bvci, BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg);
Philipp Maier1c5766b2021-02-09 17:03:03 +0100949 }
950
951 /* TODO: Use bssgp_bvc_get_features_negotiated(src_cell->bss_bvc->fi) to check if the the BSS sided BVC actually
952 * did negotiate RIM support. If not we should respond with a BSSGP STATUS message. The cause code should be
953 * BSSGP_CAUSE_PDU_INCOMP_FEAT. */
954
955 /* If Destination is known by gbproxy, route directly */
956 if (dest_ri.discr == BSSGP_RIM_ROUTING_INFO_GERAN) {
957 dest_cell = gbproxy_cell_by_cellid(nse->cfg, &dest_ri.geran.raid, dest_ri.geran.cid);
958 if (dest_cell) {
959 /* TODO: Also check if dest_cell->bss_bvc is RIM-capable (see also above). If not we should
960 * respond with a BSSGP STATUS message as well because it also would make no sense to try
961 * routing the RIM message to the next RIM-capable SGSN. */
Philipp Maier4499cf42021-02-10 17:54:44 +0100962 LOGP(DLBSSGP, LOGL_DEBUG, "%s %s relaying to peer (nsei=%u) RIM-PDU: src=%s, dest=%s\n",
963 log_pfx, pdut_name, dest_cell->bss_bvc->nse->nsei,
964 bssgp_rim_ri_name_buf(ri_src_str, sizeof(ri_src_str), &src_ri),
965 bssgp_rim_ri_name_buf(ri_dest_str, sizeof(ri_dest_str), &dest_ri));
Philipp Maier1c5766b2021-02-09 17:03:03 +0100966 return gbprox_relay2peer(msg, dest_cell->bss_bvc, 0);
967 }
968 }
969
970 /* Otherwise pass on to a RIM-capable SGSN */
971 /* TODO: We need to extend gbproxy_select_sgsn() so that it selects a RIM-capable SGSN, at the moment we just
972 * get any SGSN and just assume that it is RIM-capable. */
973 sgsn = gbproxy_select_sgsn(nse->cfg, NULL);
974 if (!sgsn) {
975 LOGP(DGPRS, LOGL_NOTICE,
976 "%s %s cannot route RIM message (%s to %s) since no RIM capable SGSN is found!\n", log_pfx,
977 pdut_name, bssgp_rim_ri_name(&src_ri), bssgp_rim_ri_name(&dest_ri));
Daniel Willmannf8cba652021-02-12 04:59:47 +0100978 return tx_status(nse, ns_bvci, BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg);
Philipp Maier1c5766b2021-02-09 17:03:03 +0100979 }
Philipp Maier4499cf42021-02-10 17:54:44 +0100980 LOGP(DLBSSGP, LOGL_DEBUG, "%s %s relaying to SGSN(%05u/%s) RIM-PDU: src=%s, dest=%s\n",
981 log_pfx, pdut_name, sgsn->nse->nsei, sgsn->name,
982 bssgp_rim_ri_name_buf(ri_src_str, sizeof(ri_src_str), &src_ri),
983 bssgp_rim_ri_name_buf(ri_dest_str, sizeof(ri_dest_str), &dest_ri));
Philipp Maier1c5766b2021-02-09 17:03:03 +0100984
985 return gbprox_relay2nse(msg, sgsn->nse, 0);
986}
987
Daniel Willmann6ec5f952021-10-28 16:13:03 +0200988/* Extract the TLLI from the PDU-in-error of the STATUS PDU (if available) */
989static int gbproxy_tlli_from_status_pdu(struct tlv_parsed *tp, uint32_t *tlli, char *log_pfx)
Daniel Willmann7d37cbb2021-09-29 11:51:51 +0200990{
991 int rc;
992 int pdu_len = TLVP_LEN(&tp[0], BSSGP_IE_PDU_IN_ERROR);
993 const uint8_t *pdu_data = TLVP_VAL(&tp[0], BSSGP_IE_PDU_IN_ERROR);
994 struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *)pdu_data;
Daniel Willmann01416282021-11-02 11:55:57 +0100995 struct tlv_parsed tp_inner;
Daniel Willmann7d37cbb2021-09-29 11:51:51 +0200996
Daniel Willmann01416282021-11-02 11:55:57 +0100997 rc = gbproxy_decode_bssgp(bgph, pdu_len, &tp_inner, log_pfx);
Daniel Willmannaf2e9b32021-11-18 15:27:49 +0100998 /* Ignore decode failure due to truncated message */
999 if (rc < 0 && rc != OSMO_TLVP_ERR_OFS_BEYOND_BUFFER)
Daniel Willmann7d37cbb2021-09-29 11:51:51 +02001000 return rc;
1001
Daniel Willmann01416282021-11-02 11:55:57 +01001002 if (TLVP_PRESENT(&tp_inner, BSSGP_IE_TLLI)) {
1003 *tlli = osmo_load32be(TLVP_VAL(&tp_inner, BSSGP_IE_TLLI));
1004 } else if (TLVP_PRESENT(&tp_inner, BSSGP_IE_TMSI)) {
Daniel Willmanncbfc7cf2021-09-29 11:51:56 +02001005 /* we treat the TMSI like a TLLI and extract the NRI from it */
Daniel Willmann01416282021-11-02 11:55:57 +01001006 *tlli = osmo_load32be(TLVP_VAL(&tp_inner, BSSGP_IE_TMSI));
Daniel Willmanncbfc7cf2021-09-29 11:51:56 +02001007 /* Convert the TMSI into a FOREIGN TLLI so it is routed appropriately */
1008 *tlli = gprs_tmsi2tlli(*tlli, TLLI_FOREIGN);
1009 } else {
Daniel Willmann7d37cbb2021-09-29 11:51:51 +02001010 return -ENOENT;
Daniel Willmanncbfc7cf2021-09-29 11:51:56 +02001011 }
Daniel Willmann7d37cbb2021-09-29 11:51:51 +02001012
1013 return 0;
1014}
1015
Daniel Willmann3d8f5992021-10-27 16:05:37 +02001016/* Extract the BVCI from the PDU-in-error of the STATUS PDU (if available) */
1017static int gbproxy_bvci_from_status_pdu(struct tlv_parsed *tp, uint16_t *bvci, char *log_pfx)
1018{
1019 int rc;
1020 int pdu_len = TLVP_LEN(&tp[0], BSSGP_IE_PDU_IN_ERROR);
1021 const uint8_t *pdu_data = TLVP_VAL(&tp[0], BSSGP_IE_PDU_IN_ERROR);
1022 struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *)pdu_data;
Daniel Willmann01416282021-11-02 11:55:57 +01001023 struct tlv_parsed tp_inner;
Daniel Willmann3d8f5992021-10-27 16:05:37 +02001024
Daniel Willmann01416282021-11-02 11:55:57 +01001025 rc = gbproxy_decode_bssgp(bgph, pdu_len, &tp_inner, log_pfx);
Daniel Willmannaf2e9b32021-11-18 15:27:49 +01001026 /* Ignore decode failure due to truncated message */
1027 if (rc < 0 && rc != OSMO_TLVP_ERR_OFS_BEYOND_BUFFER)
Daniel Willmann3d8f5992021-10-27 16:05:37 +02001028 return rc;
1029
Daniel Willmann01416282021-11-02 11:55:57 +01001030 if (TLVP_PRESENT(&tp_inner, BSSGP_IE_BVCI))
1031 *bvci = ntohs(tlvp_val16_unal(&tp_inner, BSSGP_IE_BVCI));
Daniel Willmann3d8f5992021-10-27 16:05:37 +02001032 else
1033 return -ENOENT;
1034
1035 return 0;
1036}
1037
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001038/* Receive an incoming signalling message from a BSS-side NS-VC */
Harald Weltee5209642020-12-05 19:59:45 +01001039static int gbprox_rx_sig_from_bss(struct gbproxy_nse *nse, struct msgb *msg, uint16_t ns_bvci)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001040{
1041 struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001042 uint8_t pdu_type = bgph->pdu_type;
Harald Weltee5209642020-12-05 19:59:45 +01001043 const char *pdut_name = osmo_tlv_prot_msg_name(&osmo_pdef_bssgp, bgph->pdu_type);
Philipp Maier74882dc2021-02-04 16:31:46 +01001044 struct tlv_parsed tp[2];
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001045 int data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
Harald Welte560bdb32020-12-04 22:24:47 +01001046 struct gbproxy_bvc *from_bvc = NULL;
Harald Welteec0f8012020-12-06 16:32:01 +01001047 char log_pfx[32];
Harald Weltee5209642020-12-05 19:59:45 +01001048 uint16_t ptp_bvci;
1049 uint32_t tlli;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001050 int rc;
1051
Harald Weltee5209642020-12-05 19:59:45 +01001052 snprintf(log_pfx, sizeof(log_pfx), "NSE(%05u/BSS)-BVC(%05u/??)", nse->nsei, ns_bvci);
1053
1054 LOGP(DGPRS, LOGL_DEBUG, "%s Rx %s\n", log_pfx, pdut_name);
Harald Welteec0f8012020-12-06 16:32:01 +01001055
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001056 if (ns_bvci != 0 && ns_bvci != 1) {
Harald Weltee5209642020-12-05 19:59:45 +01001057 LOGP(DGPRS, LOGL_NOTICE, "%s %s BVCI=%05u is not signalling\n", log_pfx, pdut_name, ns_bvci);
Daniel Willmannf8cba652021-02-12 04:59:47 +01001058 return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001059 }
1060
Harald Welte278dd272020-12-06 13:35:24 +01001061 if (!(bssgp_pdu_type_flags(pdu_type) & BSSGP_PDUF_SIG)) {
Harald Weltee5209642020-12-05 19:59:45 +01001062 LOGP(DGPRS, LOGL_NOTICE, "%s %s not allowed in signalling BVC\n", log_pfx, pdut_name);
Daniel Willmannf8cba652021-02-12 04:59:47 +01001063 return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
Harald Welte278dd272020-12-06 13:35:24 +01001064 }
1065
1066 if (!(bssgp_pdu_type_flags(pdu_type) & BSSGP_PDUF_UL)) {
Harald Weltee5209642020-12-05 19:59:45 +01001067 LOGP(DGPRS, LOGL_NOTICE, "%s %s not allowed in uplink direction\n", log_pfx, pdut_name);
Daniel Willmannf8cba652021-02-12 04:59:47 +01001068 return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001069 }
1070
Philipp Maier74882dc2021-02-04 16:31:46 +01001071 rc = osmo_tlv_prot_parse(&osmo_pdef_bssgp, tp, ARRAY_SIZE(tp), pdu_type, bgph->data, data_len, 0, 0,
Harald Welteec0f8012020-12-06 16:32:01 +01001072 DGPRS, log_pfx);
1073 if (rc < 0) {
Pau Espin Pedrol56438362021-06-04 18:03:44 +02001074 rate_ctr_inc(rate_ctr_group_get_ctr(nse->cfg->ctrg, GBPROX_GLOB_CTR_PROTO_ERR_BSS));
Daniel Willmannf8cba652021-02-12 04:59:47 +01001075 return tx_status_from_tlvp(nse, rc, msg);
Harald Welteec0f8012020-12-06 16:32:01 +01001076 }
Harald Weltee5209642020-12-05 19:59:45 +01001077 /* hack to get both msg + tlv_parsed passed via osmo_fsm_inst_dispatch */
Philipp Maier74882dc2021-02-04 16:31:46 +01001078 msgb_bcid(msg) = (void *)tp;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001079
Harald Weltee5209642020-12-05 19:59:45 +01001080 /* special case handling for some PDU types */
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001081 switch (pdu_type) {
Harald Weltee5209642020-12-05 19:59:45 +01001082 case BSSGP_PDUT_BVC_RESET:
1083 /* resolve or create gbproxy_bvc + handlei n BVC-FSM */
Philipp Maier74882dc2021-02-04 16:31:46 +01001084 return rx_bvc_reset_from_bss(nse, msg, &tp[0]);
Harald Weltee5209642020-12-05 19:59:45 +01001085 case BSSGP_PDUT_BVC_RESET_ACK:
Philipp Maier74882dc2021-02-04 16:31:46 +01001086 ptp_bvci = ntohs(tlvp_val16_unal(&tp[0], BSSGP_IE_BVCI));
Harald Weltee5209642020-12-05 19:59:45 +01001087 from_bvc = gbproxy_bvc_by_bvci(nse, ptp_bvci);
Harald Welte560bdb32020-12-04 22:24:47 +01001088 if (!from_bvc)
1089 goto err_no_bvc;
Harald Weltee5209642020-12-05 19:59:45 +01001090 return osmo_fsm_inst_dispatch(from_bvc->fi, BSSGP_BVCFSM_E_RX_RESET_ACK, msg);
1091 case BSSGP_PDUT_BVC_BLOCK:
Philipp Maier74882dc2021-02-04 16:31:46 +01001092 ptp_bvci = ntohs(tlvp_val16_unal(&tp[0], BSSGP_IE_BVCI));
Harald Weltee5209642020-12-05 19:59:45 +01001093 from_bvc = gbproxy_bvc_by_bvci(nse, ptp_bvci);
1094 if (!from_bvc)
1095 goto err_no_bvc;
1096 return osmo_fsm_inst_dispatch(from_bvc->fi, BSSGP_BVCFSM_E_RX_BLOCK, msg);
1097 case BSSGP_PDUT_BVC_UNBLOCK:
Philipp Maier74882dc2021-02-04 16:31:46 +01001098 ptp_bvci = ntohs(tlvp_val16_unal(&tp[0], BSSGP_IE_BVCI));
Harald Weltee5209642020-12-05 19:59:45 +01001099 from_bvc = gbproxy_bvc_by_bvci(nse, ptp_bvci);
1100 if (!from_bvc)
1101 goto err_no_bvc;
1102 return osmo_fsm_inst_dispatch(from_bvc->fi, BSSGP_BVCFSM_E_RX_UNBLOCK, msg);
1103 case BSSGP_PDUT_SUSPEND:
1104 case BSSGP_PDUT_RESUME:
Daniel Willmann77493b12020-12-29 21:13:31 +01001105 {
1106 struct gbproxy_sgsn *sgsn;
1107
Philipp Maier74882dc2021-02-04 16:31:46 +01001108 tlli = osmo_load32be(TLVP_VAL(&tp[0], BSSGP_IE_TLLI));
Daniel Willmann77493b12020-12-29 21:13:31 +01001109 sgsn = gbproxy_select_sgsn(nse->cfg, &tlli);
1110 if (!sgsn) {
1111 LOGP(DGPRS, LOGL_ERROR, "Could not find any SGSN for TLLI, dropping message!\n");
1112 rc = -EINVAL;
1113 break;
1114 }
1115
1116 gbproxy_tlli_cache_update(nse, tlli);
1117
1118 rc = gbprox_relay2nse(msg, sgsn->nse, 0);
Harald Weltee5209642020-12-05 19:59:45 +01001119#if 0
1120 /* TODO: Validate the RAI for consistency with the RAI
1121 * we expect for any of the BVC within this BSS side NSE */
Philipp Maier74882dc2021-02-04 16:31:46 +01001122 memcpy(ra, TLVP_VAL(&tp[0], BSSGP_IE_ROUTEING_AREA), sizeof(from_bvc->ra));
Harald Welte560bdb32020-12-04 22:24:47 +01001123 gsm48_parse_ra(&raid, from_bvc->ra);
Harald Weltee5209642020-12-05 19:59:45 +01001124#endif
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001125 break;
Daniel Willmann77493b12020-12-29 21:13:31 +01001126 }
Harald Weltee5209642020-12-05 19:59:45 +01001127 case BSSGP_PDUT_STATUS:
Daniel Willmann7d37cbb2021-09-29 11:51:51 +02001128 {
1129 struct gbproxy_sgsn *sgsn;
1130 /* Check if the status needs to be terminated locally */
1131 uint8_t cause = *TLVP_VAL(&tp[0], BSSGP_IE_CAUSE);
Daniel Willmannc5dcebd2021-09-30 16:57:51 +02001132
Daniel Willmann7d37cbb2021-09-29 11:51:51 +02001133 if (cause == BSSGP_CAUSE_UNKNOWN_BVCI || cause == BSSGP_CAUSE_BVCI_BLOCKED) {
1134 /* Log and handle locally */
1135 ptp_bvci = ntohs(tlvp_val16_unal(&tp[0], BSSGP_IE_BVCI));
Daniel Willmannc5dcebd2021-09-30 16:57:51 +02001136 LOGPNSE(nse, LOGL_ERROR, "Rx STATUS cause=0x%02x(%s) for PtP-BVC %05u\n", cause,
Daniel Willmann7d37cbb2021-09-29 11:51:51 +02001137 bssgp_cause_str(cause), ptp_bvci);
1138 /* FIXME: Remove/block our BVC if present? */
1139 break;
1140 }
1141
Daniel Willmannc5dcebd2021-09-30 16:57:51 +02001142 LOGPNSE(nse, LOGL_NOTICE, "Rx STATUS cause=0x%02x(%s) ", cause,
1143 bssgp_cause_str(cause));
1144
Daniel Willmann6ec5f952021-10-28 16:13:03 +02001145 if (gbproxy_tlli_from_status_pdu(tp, &tlli, log_pfx) == 0)
Daniel Willmann7d37cbb2021-09-29 11:51:51 +02001146 sgsn = gbproxy_select_sgsn(nse->cfg, &tlli);
1147 else
1148 sgsn = gbproxy_select_sgsn(nse->cfg, NULL);
1149
1150 if (!sgsn) {
1151 rc = -EINVAL;
1152 break;
1153 }
1154
1155 rc = gbprox_relay2nse(msg, sgsn->nse, 0);
Harald Weltee5209642020-12-05 19:59:45 +01001156 break;
Daniel Willmann7d37cbb2021-09-29 11:51:51 +02001157 }
Harald Weltee5209642020-12-05 19:59:45 +01001158 case BSSGP_PDUT_RAN_INFO:
1159 case BSSGP_PDUT_RAN_INFO_REQ:
1160 case BSSGP_PDUT_RAN_INFO_ACK:
1161 case BSSGP_PDUT_RAN_INFO_ERROR:
1162 case BSSGP_PDUT_RAN_INFO_APP_ERROR:
Philipp Maier1c5766b2021-02-09 17:03:03 +01001163 rc = gbprox_rx_rim_from_bss(tp, nse, msg, log_pfx, pdut_name);
Harald Weltee5209642020-12-05 19:59:45 +01001164 break;
1165 case BSSGP_PDUT_LLC_DISCARD:
Harald Weltee5209642020-12-05 19:59:45 +01001166 /* route based on BVCI + TLLI */
Philipp Maier74882dc2021-02-04 16:31:46 +01001167 ptp_bvci = ntohs(tlvp_val16_unal(&tp[0], BSSGP_IE_BVCI));
1168 tlli = osmo_load32be(TLVP_VAL(&tp[0], BSSGP_IE_TLLI));
Harald Weltee5209642020-12-05 19:59:45 +01001169 from_bvc = gbproxy_bvc_by_bvci(nse, ptp_bvci);
1170 if (!from_bvc)
1171 goto err_no_bvc;
Daniel Willmanne705b3f2021-11-25 22:04:56 +01001172 rc = gbprox_bss2sgsn_tlli(from_bvc->cell, msg, &tlli, true);
Harald Weltee5209642020-12-05 19:59:45 +01001173 break;
Daniel Willmanne705b3f2021-11-25 22:04:56 +01001174 case BSSGP_PDUT_FLUSH_LL_ACK:
1175 {
1176 /* Route based on TLLI */
1177 tlli = osmo_load32be(TLVP_VAL(&tp[0], BSSGP_IE_TLLI));
1178 struct gbproxy_sgsn *sgsn = gbproxy_select_sgsn(nse->cfg, &tlli);
1179 if (!sgsn) {
1180 rc = -EINVAL;
1181 break;
1182 }
1183
1184 rc = gbprox_relay2nse(msg, sgsn->nse, 0);
1185 break;
1186 }
Daniel Willmann8613c9d2021-01-17 13:40:38 +01001187 case BSSGP_PDUT_PAGING_PS_REJECT:
Daniel Willmann5614e572021-01-18 18:38:27 +01001188 case BSSGP_PDUT_DUMMY_PAGING_PS_RESP:
Daniel Willmann8613c9d2021-01-17 13:40:38 +01001189 {
1190 /* Route according to IMSI<->NSE cache entry */
1191 struct osmo_mobile_identity mi;
Philipp Maier74882dc2021-02-04 16:31:46 +01001192 const uint8_t *mi_data = TLVP_VAL(&tp[0], BSSGP_IE_IMSI);
1193 uint8_t mi_len = TLVP_LEN(&tp[0], BSSGP_IE_IMSI);
Daniel Willmann8613c9d2021-01-17 13:40:38 +01001194 osmo_mobile_identity_decode(&mi, mi_data, mi_len, false);
Daniel Willmann361d0b52021-07-09 17:44:30 +02001195 nse = gbproxy_nse_by_imsi(nse->cfg, mi.imsi, CACHE_USAGE_PAGING);
Daniel Willmann8613c9d2021-01-17 13:40:38 +01001196 if (!nse) {
Daniel Willmannf8cba652021-02-12 04:59:47 +01001197 return tx_status(nse, ns_bvci, BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
Daniel Willmann8613c9d2021-01-17 13:40:38 +01001198 }
Daniel Willmann5614e572021-01-18 18:38:27 +01001199 OSMO_ASSERT(nse->sgsn_facing);
Daniel Willmann8613c9d2021-01-17 13:40:38 +01001200 rc = gbprox_relay2nse(msg, nse, 0);
1201 break;
1202 }
Daniel Willmannf024eeb2021-07-06 14:02:41 +02001203 case BSSGP_PDUT_MS_REGISTR_ENQ:
1204 {
1205 struct gbproxy_sgsn *sgsn;
1206 struct osmo_mobile_identity mi;
1207 const uint8_t *mi_data = TLVP_VAL(&tp[0], BSSGP_IE_IMSI);
1208 uint8_t mi_len = TLVP_LEN(&tp[0], BSSGP_IE_IMSI);
1209 osmo_mobile_identity_decode(&mi, mi_data, mi_len, false);
1210
1211 sgsn = gbproxy_select_sgsn(nse->cfg, NULL);
1212 if (!sgsn) {
1213 LOGP(DGPRS, LOGL_ERROR, "Could not find any SGSN, dropping message!\n");
1214 rc = -EINVAL;
1215 break;
1216 }
1217
1218 gbproxy_imsi_cache_update(nse, mi.imsi, CACHE_USAGE_MS_REG_ENQ);
1219
1220 rc = gbprox_relay2nse(msg, sgsn->nse, 0);
1221 break;
1222 }
Harald Weltee5209642020-12-05 19:59:45 +01001223 default:
1224 LOGPNSE(nse, LOGL_ERROR, "Rx %s: Implementation missing\n", pdut_name);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001225 break;
1226 }
1227
Harald Weltee5209642020-12-05 19:59:45 +01001228 return rc;
Harald Welte560bdb32020-12-04 22:24:47 +01001229err_no_bvc:
Harald Weltee5209642020-12-05 19:59:45 +01001230 LOGPNSE(nse, LOGL_ERROR, "Rx %s: cannot find BVC for BVCI=%05u\n", pdut_name, ptp_bvci);
Pau Espin Pedrol56438362021-06-04 18:03:44 +02001231 rate_ctr_inc(rate_ctr_group_get_ctr(nse->cfg->ctrg, GBPROX_GLOB_CTR_INV_NSEI));
Daniel Willmannf8cba652021-02-12 04:59:47 +01001232 return tx_status(nse, ns_bvci, BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001233}
1234
1235/* Receive paging request from SGSN, we need to relay to proper BSS */
Harald Weltedf690e82020-12-12 15:58:28 +01001236static int gbprox_rx_paging(struct gbproxy_nse *sgsn_nse, struct msgb *msg, const char *pdut_name,
Daniel Willmann5614e572021-01-18 18:38:27 +01001237 struct tlv_parsed *tp, uint16_t ns_bvci, bool broadcast)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001238{
Harald Weltedf690e82020-12-12 15:58:28 +01001239 struct gbproxy_config *cfg = sgsn_nse->cfg;
Harald Weltee5209642020-12-05 19:59:45 +01001240 struct gbproxy_bvc *sgsn_bvc, *bss_bvc;
Harald Weltedf690e82020-12-12 15:58:28 +01001241 struct gbproxy_nse *nse;
Daniel Willmann76205712020-11-30 17:08:58 +01001242 unsigned int n_nses = 0;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001243 int errctr = GBPROX_GLOB_CTR_PROTO_ERR_SGSN;
Harald Welte8b4c7942020-12-05 10:14:49 +01001244 int i, j;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001245
Harald Welte173a1822020-12-03 15:36:59 +01001246 if (TLVP_PRES_LEN(tp, BSSGP_IE_BVCI, 2)) {
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001247 uint16_t bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI));
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001248 errctr = GBPROX_GLOB_CTR_OTHER_ERR;
Harald Weltedf690e82020-12-12 15:58:28 +01001249 sgsn_bvc = gbproxy_bvc_by_bvci(sgsn_nse, bvci);
Harald Weltee5209642020-12-05 19:59:45 +01001250 if (!sgsn_bvc) {
Harald Weltedf690e82020-12-12 15:58:28 +01001251 LOGPNSE(sgsn_nse, LOGL_NOTICE, "Rx %s: unable to route: BVCI=%05u unknown\n",
Harald Weltee5209642020-12-05 19:59:45 +01001252 pdut_name, bvci);
Pau Espin Pedrol56438362021-06-04 18:03:44 +02001253 rate_ctr_inc(rate_ctr_group_get_ctr(cfg->ctrg, errctr));
Harald Welte3d1bd4d2020-11-23 15:14:20 +01001254 return -EINVAL;
1255 }
Harald Weltee5209642020-12-05 19:59:45 +01001256 LOGPBVC(sgsn_bvc, LOGL_INFO, "Rx %s: routing by BVCI\n", pdut_name);
1257 return gbprox_relay2peer(msg, sgsn_bvc->cell->bss_bvc, ns_bvci);
Harald Welte173a1822020-12-03 15:36:59 +01001258 } else if (TLVP_PRES_LEN(tp, BSSGP_IE_ROUTEING_AREA, 6)) {
Philipp Maierda3af942021-02-04 21:54:09 +01001259 struct gprs_ra_id raid;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001260 errctr = GBPROX_GLOB_CTR_INV_RAI;
Philipp Maierda3af942021-02-04 21:54:09 +01001261 gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA));
Harald Welte560bdb32020-12-04 22:24:47 +01001262 /* iterate over all bvcs and dispatch the paging to each matching one */
Harald Welted2fef952020-12-05 00:31:07 +01001263 hash_for_each(cfg->bss_nses, i, nse, list) {
Harald Weltee5209642020-12-05 19:59:45 +01001264 hash_for_each(nse->bvcs, j, bss_bvc, list) {
Daniel Willmannf9902c52021-11-12 15:52:03 +01001265 /* Skip BVCs without a cell (e.g. signalling) */
1266 if (!bss_bvc->cell)
1267 continue;
1268
1269 if (gsm48_ra_equal(&bss_bvc->cell->id.raid, &raid)) {
Harald Weltee5209642020-12-05 19:59:45 +01001270 LOGPNSE(nse, LOGL_INFO, "Rx %s: routing to NSE (RAI match)\n",
1271 pdut_name);
1272 gbprox_relay2peer(msg, bss_bvc, ns_bvci);
Daniel Willmann76205712020-11-30 17:08:58 +01001273 n_nses++;
1274 /* Only send it once to each NSE */
1275 break;
Daniel Willmanne50550e2020-11-26 18:19:21 +01001276 }
Harald Welte3d1bd4d2020-11-23 15:14:20 +01001277 }
1278 }
Harald Welte173a1822020-12-03 15:36:59 +01001279 } else if (TLVP_PRES_LEN(tp, BSSGP_IE_LOCATION_AREA, 5)) {
Philipp Maierda3af942021-02-04 21:54:09 +01001280 struct gsm48_ra_id lac;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001281 errctr = GBPROX_GLOB_CTR_INV_LAI;
Harald Welte560bdb32020-12-04 22:24:47 +01001282 /* iterate over all bvcs and dispatch the paging to each matching one */
Harald Welted2fef952020-12-05 00:31:07 +01001283 hash_for_each(cfg->bss_nses, i, nse, list) {
Harald Weltee5209642020-12-05 19:59:45 +01001284 hash_for_each(nse->bvcs, j, bss_bvc, list) {
Daniel Willmannf9902c52021-11-12 15:52:03 +01001285 /* Skip BVCs without a cell (e.g. signalling) */
1286 if (!bss_bvc->cell)
1287 continue;
1288
1289 gsm48_encode_ra(&lac, &bss_bvc->cell->id.raid);
Philipp Maierda3af942021-02-04 21:54:09 +01001290 if (!memcmp(&lac, TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA), 5)) {
Harald Weltee5209642020-12-05 19:59:45 +01001291 LOGPNSE(nse, LOGL_INFO, "Rx %s: routing to NSE (LAI match)\n",
1292 pdut_name);
1293 gbprox_relay2peer(msg, bss_bvc, ns_bvci);
Daniel Willmann76205712020-11-30 17:08:58 +01001294 n_nses++;
1295 /* Only send it once to each NSE */
1296 break;
Daniel Willmanne50550e2020-11-26 18:19:21 +01001297 }
Harald Welte3d1bd4d2020-11-23 15:14:20 +01001298 }
1299 }
Daniel Willmann5614e572021-01-18 18:38:27 +01001300 } else if (TLVP_PRES_LEN(tp, BSSGP_IE_BSS_AREA_ID, 1) || broadcast) {
Harald Welte560bdb32020-12-04 22:24:47 +01001301 /* iterate over all bvcs and dispatch the paging to each matching one */
Harald Welted2fef952020-12-05 00:31:07 +01001302 hash_for_each(cfg->bss_nses, i, nse, list) {
Harald Weltee5209642020-12-05 19:59:45 +01001303 hash_for_each(nse->bvcs, j, bss_bvc, list) {
1304 LOGPNSE(nse, LOGL_INFO, "Rx %s:routing to NSE (broadcast)\n", pdut_name);
1305 gbprox_relay2peer(msg, bss_bvc, ns_bvci);
Daniel Willmann76205712020-11-30 17:08:58 +01001306 n_nses++;
1307 /* Only send it once to each NSE */
1308 break;
Daniel Willmanne50550e2020-11-26 18:19:21 +01001309 }
Harald Welte53ee2062020-11-24 11:31:13 +01001310 }
1311 } else {
Harald Weltedf690e82020-12-12 15:58:28 +01001312 LOGPNSE(sgsn_nse, LOGL_ERROR, "BSSGP PAGING: unable to route, missing IE\n");
Pau Espin Pedrol56438362021-06-04 18:03:44 +02001313 rate_ctr_inc(rate_ctr_group_get_ctr(cfg->ctrg, errctr));
Harald Welte53ee2062020-11-24 11:31:13 +01001314 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001315
Daniel Willmann76205712020-11-30 17:08:58 +01001316 if (n_nses == 0) {
Harald Weltedf690e82020-12-12 15:58:28 +01001317 LOGPNSE(sgsn_nse, LOGL_ERROR, "BSSGP PAGING: unable to route, no destination found\n");
Pau Espin Pedrol56438362021-06-04 18:03:44 +02001318 rate_ctr_inc(rate_ctr_group_get_ctr(cfg->ctrg, errctr));
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001319 return -EINVAL;
1320 }
Harald Welte3d1bd4d2020-11-23 15:14:20 +01001321 return 0;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001322}
1323
1324/* Receive an incoming BVC-RESET message from the SGSN */
Harald Weltee5209642020-12-05 19:59:45 +01001325static int rx_bvc_reset_from_sgsn(struct gbproxy_nse *nse, struct msgb *msg, struct tlv_parsed *tp,
1326 uint16_t ns_bvci)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001327{
Harald Weltee5209642020-12-05 19:59:45 +01001328 uint16_t ptp_bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI));
1329 struct gbproxy_bvc *from_bvc;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001330
Harald Weltee5209642020-12-05 19:59:45 +01001331 LOGPNSE(nse, LOGL_INFO, "Rx BVC-RESET (BVCI=%05u)\n", ptp_bvci);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001332
Harald Weltee5209642020-12-05 19:59:45 +01001333 if (ptp_bvci == 0) {
1334 from_bvc = gbproxy_bvc_by_bvci(nse, 0);
1335 OSMO_ASSERT(from_bvc);
1336 osmo_fsm_inst_dispatch(from_bvc->fi, BSSGP_BVCFSM_E_RX_RESET, msg);
1337 } else {
1338 from_bvc = gbproxy_bvc_by_bvci(nse, ptp_bvci);
1339 if (!from_bvc) {
1340 LOGPNSE(nse, LOGL_ERROR, "Rx BVC-RESET BVCI=%05u: Cannot find BVC\n", ptp_bvci);
Pau Espin Pedrol56438362021-06-04 18:03:44 +02001341 rate_ctr_inc(rate_ctr_group_get_ctr(nse->cfg->ctrg, GBPROX_GLOB_CTR_INV_BVCI));
Daniel Willmannf8cba652021-02-12 04:59:47 +01001342 return tx_status(nse, ns_bvci, BSSGP_CAUSE_UNKNOWN_BVCI, &ptp_bvci, msg);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001343 }
Harald Weltee5209642020-12-05 19:59:45 +01001344 osmo_fsm_inst_dispatch(from_bvc->fi, BSSGP_BVCFSM_E_RX_RESET, msg);
Daniel Willmanne50550e2020-11-26 18:19:21 +01001345 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001346
1347 return 0;
1348}
1349
Philipp Maier1c5766b2021-02-09 17:03:03 +01001350/* Receive an incoming RIM message from the SGSN-side NS-VC */
1351static int gbprox_rx_rim_from_sgsn(struct tlv_parsed *tp, struct gbproxy_nse *nse, struct msgb *msg, char *log_pfx,
1352 const char *pdut_name)
1353{
1354 struct gbproxy_sgsn *sgsn;
1355 struct gbproxy_cell *dest_cell;
1356 struct bssgp_rim_routing_info dest_ri;
1357 struct bssgp_rim_routing_info src_ri;
1358 int rc;
Philipp Maier4499cf42021-02-10 17:54:44 +01001359 char ri_src_str[64];
1360 char ri_dest_str[64];
Daniel Willmannf8cba652021-02-12 04:59:47 +01001361 uint16_t ns_bvci = msgb_bvci(msg);
Philipp Maier1c5766b2021-02-09 17:03:03 +01001362
1363 /* TODO: Reply with STATUS if BSSGP didn't negotiate RIM feature, see also comments in
1364 gbprox_rx_rim_from_bss() */
1365
1366 rc = bssgp_parse_rim_ri(&dest_ri, TLVP_VAL(&tp[0], BSSGP_IE_RIM_ROUTING_INFO),
1367 TLVP_LEN(&tp[0], BSSGP_IE_RIM_ROUTING_INFO));
1368 if (rc < 0) {
1369 LOGP(DGPRS, LOGL_ERROR, "%s %s cannot parse destination RIM routing info\n", log_pfx, pdut_name);
Daniel Willmannf8cba652021-02-12 04:59:47 +01001370 return tx_status(nse, ns_bvci, BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
Philipp Maier1c5766b2021-02-09 17:03:03 +01001371 }
1372 rc = bssgp_parse_rim_ri(&src_ri, TLVP_VAL(&tp[1], BSSGP_IE_RIM_ROUTING_INFO),
1373 TLVP_LEN(&tp[1], BSSGP_IE_RIM_ROUTING_INFO));
1374 if (rc < 0) {
1375 LOGP(DGPRS, LOGL_ERROR, "%s %s cannot parse source RIM routing info\n", log_pfx, pdut_name);
Daniel Willmannf8cba652021-02-12 04:59:47 +01001376 return tx_status(nse, ns_bvci, BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
Philipp Maier1c5766b2021-02-09 17:03:03 +01001377 }
1378
1379 /* Since gbproxy is 2G only we do not expect to get RIM messages that target non-GERAN cells. */
1380 if (dest_ri.discr != BSSGP_RIM_ROUTING_INFO_GERAN) {
1381 LOGP(DGPRS, LOGL_ERROR, "%s %s destination RIM routing info is not GERAN (%s)\n", log_pfx, pdut_name,
1382 bssgp_rim_ri_name(&dest_ri));
Daniel Willmannf8cba652021-02-12 04:59:47 +01001383 return tx_status(nse, ns_bvci, BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg);
Philipp Maier1c5766b2021-02-09 17:03:03 +01001384 }
1385
1386 /* Lookup destination cell */
1387 dest_cell = gbproxy_cell_by_cellid(nse->cfg, &dest_ri.geran.raid, dest_ri.geran.cid);
1388 if (!dest_cell) {
1389 LOGP(DGPRS, LOGL_NOTICE, "%s %s cannot find cell for destination RIM routing info (%s)\n", log_pfx,
1390 pdut_name, bssgp_rim_ri_name(&dest_ri));
Daniel Willmannf8cba652021-02-12 04:59:47 +01001391 return tx_status(nse, ns_bvci, BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg);
Philipp Maier1c5766b2021-02-09 17:03:03 +01001392 }
1393
1394 /* TODO: Check if the BVC of the destination cell actually did negotiate RIM support, see also comments
1395 * in gbprox_rx_rim_from_bss() */
1396 sgsn = gbproxy_sgsn_by_nsei(nse->cfg, nse->nsei);
1397 OSMO_ASSERT(sgsn);
1398
Philipp Maier4499cf42021-02-10 17:54:44 +01001399 LOGP(DLBSSGP, LOGL_DEBUG, "%s %s relaying from SGSN(%05u/%s) RIM-PDU: src=%s, dest=%s\n",
1400 log_pfx, pdut_name, sgsn->nse->nsei, sgsn->name,
1401 bssgp_rim_ri_name_buf(ri_src_str, sizeof(ri_src_str), &src_ri),
1402 bssgp_rim_ri_name_buf(ri_dest_str, sizeof(ri_dest_str), &dest_ri));
Philipp Maier1c5766b2021-02-09 17:03:03 +01001403
1404 return gbprox_relay2peer(msg, dest_cell->bss_bvc, 0);
1405}
1406
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001407/* Receive an incoming signalling message from the SGSN-side NS-VC */
Harald Weltedbef0aa2020-12-07 17:48:11 +01001408static int gbprox_rx_sig_from_sgsn(struct gbproxy_nse *nse, struct msgb *msg, uint16_t ns_bvci)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001409{
Harald Weltedbef0aa2020-12-07 17:48:11 +01001410 struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001411 uint8_t pdu_type = bgph->pdu_type;
Harald Weltee5209642020-12-05 19:59:45 +01001412 const char *pdut_name = osmo_tlv_prot_msg_name(&osmo_pdef_bssgp, bgph->pdu_type);
1413 struct gbproxy_config *cfg = nse->cfg;
1414 struct gbproxy_bvc *sgsn_bvc;
Philipp Maier74882dc2021-02-04 16:31:46 +01001415 struct tlv_parsed tp[2];
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001416 int data_len;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001417 uint16_t bvci;
Harald Welteec0f8012020-12-06 16:32:01 +01001418 char log_pfx[32];
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001419 int rc = 0;
Harald Welted2fef952020-12-05 00:31:07 +01001420 int i;
Daniel Willmann5614e572021-01-18 18:38:27 +01001421 bool paging_bc = false;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001422
Harald Weltee5209642020-12-05 19:59:45 +01001423 snprintf(log_pfx, sizeof(log_pfx), "NSE(%05u/SGSN)-BVC(%05u/??)", nse->nsei, ns_bvci);
1424
1425 LOGP(DGPRS, LOGL_DEBUG, "%s Rx %s\n", log_pfx, pdut_name);
Harald Welteec0f8012020-12-06 16:32:01 +01001426
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001427 if (ns_bvci != 0 && ns_bvci != 1) {
Harald Welteec0f8012020-12-06 16:32:01 +01001428 LOGP(DGPRS, LOGL_NOTICE, "%s BVCI=%05u is not signalling\n", log_pfx, ns_bvci);
Daniel Willmannf8cba652021-02-12 04:59:47 +01001429 return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001430 }
1431
Harald Welte278dd272020-12-06 13:35:24 +01001432 if (!(bssgp_pdu_type_flags(pdu_type) & BSSGP_PDUF_SIG)) {
Harald Weltee5209642020-12-05 19:59:45 +01001433 LOGP(DGPRS, LOGL_NOTICE, "%s %s not allowed in signalling BVC\n", log_pfx, pdut_name);
Daniel Willmannf8cba652021-02-12 04:59:47 +01001434 return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
Harald Welte278dd272020-12-06 13:35:24 +01001435 }
1436
1437 if (!(bssgp_pdu_type_flags(pdu_type) & BSSGP_PDUF_DL)) {
Harald Weltee5209642020-12-05 19:59:45 +01001438 LOGP(DGPRS, LOGL_NOTICE, "%s %s not allowed in downlink direction\n", log_pfx, pdut_name);
Daniel Willmannf8cba652021-02-12 04:59:47 +01001439 return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001440 }
1441
Harald Weltedbef0aa2020-12-07 17:48:11 +01001442 data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
Harald Welteec0f8012020-12-06 16:32:01 +01001443
Philipp Maier74882dc2021-02-04 16:31:46 +01001444 rc = osmo_tlv_prot_parse(&osmo_pdef_bssgp, tp, ARRAY_SIZE(tp), pdu_type, bgph->data, data_len, 0, 0,
Harald Welteec0f8012020-12-06 16:32:01 +01001445 DGPRS, log_pfx);
1446 if (rc < 0) {
Daniel Willmannf8cba652021-02-12 04:59:47 +01001447 rc = tx_status_from_tlvp(nse, rc, msg);
Pau Espin Pedrol56438362021-06-04 18:03:44 +02001448 rate_ctr_inc(rate_ctr_group_get_ctr(cfg->ctrg, GBPROX_GLOB_CTR_PROTO_ERR_SGSN));
Harald Welteec0f8012020-12-06 16:32:01 +01001449 return rc;
1450 }
Harald Weltee5209642020-12-05 19:59:45 +01001451 /* hack to get both msg + tlv_parsed passed via osmo_fsm_inst_dispatch */
Philipp Maier74882dc2021-02-04 16:31:46 +01001452 msgb_bcid(msg) = (void *)tp;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001453
1454 switch (pdu_type) {
1455 case BSSGP_PDUT_BVC_RESET:
Harald Weltee5209642020-12-05 19:59:45 +01001456 /* resolve or create ggbproxy_bvc + handle in BVC-FSM */
Philipp Maier74882dc2021-02-04 16:31:46 +01001457 bvci = ntohs(tlvp_val16_unal(&tp[0], BSSGP_IE_BVCI));
1458 rc = rx_bvc_reset_from_sgsn(nse, msg, &tp[0], ns_bvci);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001459 break;
1460 case BSSGP_PDUT_BVC_RESET_ACK:
Philipp Maier74882dc2021-02-04 16:31:46 +01001461 bvci = ntohs(tlvp_val16_unal(&tp[0], BSSGP_IE_BVCI));
Harald Weltee5209642020-12-05 19:59:45 +01001462 sgsn_bvc = gbproxy_bvc_by_bvci(nse, bvci);
1463 if (!sgsn_bvc)
1464 goto err_no_bvc;
1465 rc = osmo_fsm_inst_dispatch(sgsn_bvc->fi, BSSGP_BVCFSM_E_RX_RESET_ACK, msg);
1466 break;
1467 case BSSGP_PDUT_BVC_BLOCK_ACK:
Philipp Maier74882dc2021-02-04 16:31:46 +01001468 bvci = ntohs(tlvp_val16_unal(&tp[0], BSSGP_IE_BVCI));
Harald Weltee5209642020-12-05 19:59:45 +01001469 sgsn_bvc = gbproxy_bvc_by_bvci(nse, bvci);
Daniel Willmanndc763fd2021-09-24 16:45:38 +02001470 if (!sgsn_bvc) {
1471 /* Check if BVC was blocked before */
1472 sgsn_bvc = gbproxy_bvc_by_bvci_inactive(nse, bvci);
1473 if (!sgsn_bvc)
1474 goto err_no_bvc;
1475 }
Harald Weltee5209642020-12-05 19:59:45 +01001476 rc = osmo_fsm_inst_dispatch(sgsn_bvc->fi, BSSGP_BVCFSM_E_RX_BLOCK_ACK, msg);
1477 break;
1478 case BSSGP_PDUT_BVC_UNBLOCK_ACK:
Philipp Maier74882dc2021-02-04 16:31:46 +01001479 bvci = ntohs(tlvp_val16_unal(&tp[0], BSSGP_IE_BVCI));
Harald Weltee5209642020-12-05 19:59:45 +01001480 sgsn_bvc = gbproxy_bvc_by_bvci(nse, bvci);
1481 if (!sgsn_bvc)
1482 goto err_no_bvc;
1483 rc = osmo_fsm_inst_dispatch(sgsn_bvc->fi, BSSGP_BVCFSM_E_RX_UNBLOCK_ACK, msg);
Daniel Willmann8489e7a2020-11-03 21:12:42 +01001484 break;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001485 case BSSGP_PDUT_FLUSH_LL:
Daniel Willmanne705b3f2021-11-25 22:04:56 +01001486 /* TODO: If new BVCI is on different NSE we should remove the new BVCI so the
1487 * message is interpreted as a request to delete the PDUs, not forward them.
1488 * If we negotiate Inter-NSE re-routing or LCS-procedures we can also
1489 * add the NSEI TLV to trigger re-routing the PDUs */
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001490 /* simple case: BVCI IE is mandatory */
Philipp Maier74882dc2021-02-04 16:31:46 +01001491 bvci = ntohs(tlvp_val16_unal(&tp[0], BSSGP_IE_BVCI));
Harald Weltee5209642020-12-05 19:59:45 +01001492 sgsn_bvc = gbproxy_bvc_by_bvci(nse, bvci);
1493 if (!sgsn_bvc)
1494 goto err_no_bvc;
1495 if (sgsn_bvc->cell && sgsn_bvc->cell->bss_bvc)
1496 rc = gbprox_relay2peer(msg, sgsn_bvc->cell->bss_bvc, ns_bvci);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001497 break;
Daniel Willmann5614e572021-01-18 18:38:27 +01001498 case BSSGP_PDUT_DUMMY_PAGING_PS:
1499 /* Routing area is optional in dummy paging and we have nothing else to go by
1500 * so in case it is missing we need to broadcast the paging */
1501 paging_bc = true;
1502 /* fall through */
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001503 case BSSGP_PDUT_PAGING_PS:
Daniel Willmann8613c9d2021-01-17 13:40:38 +01001504 {
1505 /* Cache the IMSI<->NSE to route PAGING REJECT */
1506 struct osmo_mobile_identity mi;
Philipp Maier74882dc2021-02-04 16:31:46 +01001507 const uint8_t *mi_data = TLVP_VAL(&tp[0], BSSGP_IE_IMSI);
1508 uint8_t mi_len = TLVP_LEN(&tp[0], BSSGP_IE_IMSI);
Daniel Willmann8613c9d2021-01-17 13:40:38 +01001509 osmo_mobile_identity_decode(&mi, mi_data, mi_len, false);
Daniel Willmann361d0b52021-07-09 17:44:30 +02001510 gbproxy_imsi_cache_update(nse, mi.imsi, CACHE_USAGE_PAGING);
Daniel Willmann8613c9d2021-01-17 13:40:38 +01001511 /* fall through */
1512 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001513 case BSSGP_PDUT_PAGING_CS:
1514 /* process the paging request (LAI/RAI lookup) */
Philipp Maier74882dc2021-02-04 16:31:46 +01001515 rc = gbprox_rx_paging(nse, msg, pdut_name, &tp[0], ns_bvci, paging_bc);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001516 break;
1517 case BSSGP_PDUT_STATUS:
Daniel Willmann885f4302021-09-30 16:58:46 +02001518 {
1519 struct gbproxy_nse *nse_peer;
1520 uint32_t tlli;
1521
1522 /* Check if the status needs to be terminated locally */
1523 uint8_t cause = *TLVP_VAL(&tp[0], BSSGP_IE_CAUSE);
1524
1525 if (cause == BSSGP_CAUSE_UNKNOWN_BVCI || cause == BSSGP_CAUSE_BVCI_BLOCKED) {
1526 /* Log and handle locally, BVCI should be present for these causes */
1527 if (!TLVP_PRESENT(&tp[0], BSSGP_IE_BVCI)) {
1528 LOGPNSE(nse, LOGL_ERROR, "Rx STATUS cause=0x%02x(%s), but BVCI is missing\n", cause,
1529 bssgp_cause_str(cause));
1530 break;
1531 }
1532 uint16_t ptp_bvci = ntohs(tlvp_val16_unal(&tp[0], BSSGP_IE_BVCI));
1533 LOGPNSE(nse, LOGL_ERROR, "Rx STATUS cause=0x%02x(%s) for PtP-BVC %05u\n", cause,
1534 bssgp_cause_str(cause), ptp_bvci);
1535 /* FIXME: Remove/block the other BSS/SGSN BVCs if present? */
1536 break;
1537 }
1538
1539 LOGPNSE(nse, LOGL_NOTICE, "Rx STATUS cause=0x%02x(%s)\n", cause,
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001540 bssgp_cause_str(cause));
Daniel Willmann885f4302021-09-30 16:58:46 +02001541
Daniel Willmann3d8f5992021-10-27 16:05:37 +02001542
1543 if (gbproxy_bvci_from_status_pdu(tp, &bvci, log_pfx) == 0 && bvci != 0) {
1544 struct gbproxy_cell *cell = gbproxy_cell_by_bvci(cfg, bvci);
1545
1546 if ((!cell || !cell->bss_bvc || !cell->bss_bvc->nse)) {
1547 LOGPNSE(nse, LOGL_ERROR, "Rx STATUS cause=0x%02x(%s), but can't find NSE for cell\n",
1548 cause, bssgp_cause_str(cause));
1549 break;
1550 }
1551
1552 return gbprox_relay2nse(msg, cell->bss_bvc->nse, 0);
1553 }
1554
Daniel Willmann6ec5f952021-10-28 16:13:03 +02001555 /* We can only forward this TLLI if it's in the cache (which only happens on suspend/resume) */
1556 if (gbproxy_tlli_from_status_pdu(tp, &tlli, log_pfx) == 0) {
Daniel Willmann885f4302021-09-30 16:58:46 +02001557 nse_peer = gbproxy_nse_by_tlli(cfg, tlli);
1558 if (nse_peer)
1559 return gbprox_relay2nse(msg, nse_peer, 0);
1560 }
1561
1562 LOGPNSE(nse, LOGL_ERROR, "Unable to handle STATUS cause=0x%02x(%s)\n", cause,
1563 bssgp_cause_str(cause));
1564
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001565 break;
Daniel Willmann885f4302021-09-30 16:58:46 +02001566 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001567 /* those only exist in the SGSN -> BSS direction */
1568 case BSSGP_PDUT_SUSPEND_ACK:
1569 case BSSGP_PDUT_SUSPEND_NACK:
1570 case BSSGP_PDUT_RESUME_ACK:
1571 case BSSGP_PDUT_RESUME_NACK:
Daniel Willmann77493b12020-12-29 21:13:31 +01001572 {
1573 struct gbproxy_nse *nse_peer;
Philipp Maier74882dc2021-02-04 16:31:46 +01001574 uint32_t tlli = osmo_load32be(TLVP_VAL(&tp[0], BSSGP_IE_TLLI));
Daniel Willmann77493b12020-12-29 21:13:31 +01001575
1576 nse_peer = gbproxy_nse_by_tlli(cfg, tlli);
1577 if (!nse_peer) {
1578 LOGPNSE(nse, LOGL_ERROR, "Rx %s: Cannot find NSE\n", pdut_name);
1579 /* TODO: Counter */
Daniel Willmannf8cba652021-02-12 04:59:47 +01001580 return tx_status(nse, ns_bvci, BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
Daniel Willmann77493b12020-12-29 21:13:31 +01001581 }
1582 /* Delete the entry after we're done */
1583 gbproxy_tlli_cache_remove(cfg, tlli);
1584 LOGPNSE(nse_peer, LOGL_DEBUG, "Rx %s: forwarding\n", pdut_name);
1585 gbprox_relay2nse(msg, nse_peer, ns_bvci);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001586 break;
Daniel Willmann77493b12020-12-29 21:13:31 +01001587 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001588 case BSSGP_PDUT_SGSN_INVOKE_TRACE:
Harald Welte7479c4d2020-12-02 20:06:04 +01001589 case BSSGP_PDUT_OVERLOAD:
Harald Weltee5209642020-12-05 19:59:45 +01001590 LOGPNSE(nse, LOGL_DEBUG, "Rx %s: broadcasting\n", pdut_name);
Harald Welte560bdb32020-12-04 22:24:47 +01001591 /* broadcast to all BSS-side bvcs */
Harald Welted2fef952020-12-05 00:31:07 +01001592 hash_for_each(cfg->bss_nses, i, nse, list) {
Harald Welte7479c4d2020-12-02 20:06:04 +01001593 gbprox_relay2nse(msg, nse, 0);
1594 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001595 break;
Harald Weltee5209642020-12-05 19:59:45 +01001596 case BSSGP_PDUT_RAN_INFO:
1597 case BSSGP_PDUT_RAN_INFO_REQ:
1598 case BSSGP_PDUT_RAN_INFO_ACK:
1599 case BSSGP_PDUT_RAN_INFO_ERROR:
1600 case BSSGP_PDUT_RAN_INFO_APP_ERROR:
Philipp Maier1c5766b2021-02-09 17:03:03 +01001601 rc = gbprox_rx_rim_from_sgsn(tp, nse, msg, log_pfx, pdut_name);
Pau Espin Pedrola4296342021-05-07 13:33:34 +02001602 break;
Daniel Willmannf024eeb2021-07-06 14:02:41 +02001603 case BSSGP_PDUT_MS_REGISTR_ENQ_RESP:
1604 {
1605 struct gbproxy_nse *nse_peer;
1606 struct osmo_mobile_identity mi;
1607 const uint8_t *mi_data = TLVP_VAL(&tp[0], BSSGP_IE_IMSI);
1608 uint8_t mi_len = TLVP_LEN(&tp[0], BSSGP_IE_IMSI);
1609 osmo_mobile_identity_decode(&mi, mi_data, mi_len, false);
1610 nse_peer = gbproxy_nse_by_imsi(cfg, mi.imsi, CACHE_USAGE_MS_REG_ENQ);
1611 if (!nse_peer) {
1612 LOGPNSE(nse, LOGL_ERROR, "Rx %s: Cannot find NSE\n", pdut_name);
1613 return tx_status(nse, ns_bvci, BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
1614 } else if (nse_peer->sgsn_facing) {
1615 LOGPNSE(nse, LOGL_ERROR, "Forwarding %s failed: IMSI cache contains SGSN NSE", pdut_name);
1616 return tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
1617 }
1618 gbproxy_imsi_cache_remove(cfg, mi.imsi, CACHE_USAGE_MS_REG_ENQ);
1619 gbprox_relay2nse(msg, nse_peer, ns_bvci);
1620 break;
1621 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001622 default:
Harald Weltee5209642020-12-05 19:59:45 +01001623 LOGPNSE(nse, LOGL_NOTICE, "Rx %s: Not supported\n", pdut_name);
Pau Espin Pedrol56438362021-06-04 18:03:44 +02001624 rate_ctr_inc(rate_ctr_group_get_ctr(cfg->ctrg, GBPROX_GLOB_CTR_PROTO_ERR_SGSN));
Daniel Willmannf8cba652021-02-12 04:59:47 +01001625 rc = tx_status(nse, ns_bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001626 break;
1627 }
1628
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001629 return rc;
Harald Weltee5209642020-12-05 19:59:45 +01001630
Harald Welte560bdb32020-12-04 22:24:47 +01001631err_no_bvc:
Daniel Willmann723bb362021-09-28 18:51:47 +02001632 LOGPNSE(nse, LOGL_ERROR, "Rx %s: Cannot find BVC %05u\n", pdut_name, bvci);
Daniel Willmannc4b913b2021-09-24 16:43:42 +02001633 rate_ctr_inc(rate_ctr_group_get_ctr(cfg->ctrg, GBPROX_GLOB_CTR_INV_BVCI));
Daniel Willmannf8cba652021-02-12 04:59:47 +01001634 return tx_status(nse, ns_bvci, BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001635}
1636
Harald Weltee5209642020-12-05 19:59:45 +01001637
1638/***********************************************************************
1639 * libosmogb NS/BSSGP integration
1640 ***********************************************************************/
1641
Alexander Couzens951e1332020-09-22 13:21:46 +02001642int gbprox_bssgp_send_cb(void *ctx, struct msgb *msg)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001643{
1644 int rc;
Alexander Couzens951e1332020-09-22 13:21:46 +02001645 struct gbproxy_config *cfg = (struct gbproxy_config *) ctx;
1646 struct gprs_ns2_inst *nsi = cfg->nsi;
1647 struct osmo_gprs_ns2_prim nsp = {};
1648
1649 nsp.bvci = msgb_bvci(msg);
1650 nsp.nsei = msgb_nsei(msg);
1651
Alexander Couzens55c36f92021-01-27 20:56:55 +01001652 osmo_prim_init(&nsp.oph, SAP_NS, GPRS_NS2_PRIM_UNIT_DATA, PRIM_OP_REQUEST, msg);
Alexander Couzens951e1332020-09-22 13:21:46 +02001653 rc = gprs_ns2_recv_prim(nsi, &nsp.oph);
1654
1655 return rc;
1656}
1657
1658/* Main input function for Gb proxy */
1659int gbprox_rcvmsg(void *ctx, struct msgb *msg)
1660{
Alexander Couzens951e1332020-09-22 13:21:46 +02001661 struct gbproxy_config *cfg = (struct gbproxy_config *) ctx;
Harald Weltee5209642020-12-05 19:59:45 +01001662 uint16_t ns_bvci = msgb_bvci(msg);
1663 uint16_t nsei = msgb_nsei(msg);
1664 struct gbproxy_nse *nse;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001665
Harald Weltee5209642020-12-05 19:59:45 +01001666 nse = gbproxy_nse_by_nsei(cfg, nsei, NSE_F_SGSN);
1667 if (nse) {
Daniel Willmannf8cba652021-02-12 04:59:47 +01001668 /* ensure minimum length to decode PDU type */
1669 if (msgb_bssgp_len(msg) < sizeof(struct bssgp_normal_hdr))
1670 return tx_status(nse, ns_bvci, BSSGP_CAUSE_SEM_INCORR_PDU, NULL, msg);
1671
Harald Weltee5209642020-12-05 19:59:45 +01001672 if (ns_bvci == 0 || ns_bvci == 1)
1673 return gbprox_rx_sig_from_sgsn(nse, msg, ns_bvci);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001674 else
Harald Weltee5209642020-12-05 19:59:45 +01001675 return gbprox_rx_ptp_from_sgsn(nse, msg, ns_bvci);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001676 }
1677
Harald Weltee5209642020-12-05 19:59:45 +01001678 nse = gbproxy_nse_by_nsei(cfg, nsei, NSE_F_BSS);
1679 if (!nse) {
1680 LOGP(DGPRS, LOGL_NOTICE, "NSE(%05u/BSS) not known -> allocating\n", nsei);
1681 nse = gbproxy_nse_alloc(cfg, nsei, false);
1682 }
1683 if (nse) {
Daniel Willmannf8cba652021-02-12 04:59:47 +01001684 /* ensure minimum length to decode PDU type */
1685 if (msgb_bssgp_len(msg) < sizeof(struct bssgp_normal_hdr))
1686 return tx_status(nse, ns_bvci, BSSGP_CAUSE_SEM_INCORR_PDU, NULL, msg);
1687
Harald Weltee5209642020-12-05 19:59:45 +01001688 if (ns_bvci == 0 || ns_bvci == 1)
1689 return gbprox_rx_sig_from_bss(nse, msg, ns_bvci);
1690 else
1691 return gbprox_rx_ptp_from_bss(nse, msg, ns_bvci);
1692 }
1693
1694 return 0;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001695}
1696
Alexander Couzens951e1332020-09-22 13:21:46 +02001697/* TODO: What about handling:
Alexander Couzens55c36f92021-01-27 20:56:55 +01001698 * GPRS_NS2_AFF_CAUSE_VC_FAILURE,
1699 GPRS_NS2_AFF_CAUSE_VC_RECOVERY,
Alexander Couzens951e1332020-09-22 13:21:46 +02001700 osmocom own causes
Alexander Couzens55c36f92021-01-27 20:56:55 +01001701 GPRS_NS2_AFF_CAUSE_SNS_CONFIGURED,
1702 GPRS_NS2_AFF_CAUSE_SNS_FAILURE,
Alexander Couzens951e1332020-09-22 13:21:46 +02001703 */
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001704
Alexander Couzens951e1332020-09-22 13:21:46 +02001705void gprs_ns_prim_status_cb(struct gbproxy_config *cfg, struct osmo_gprs_ns2_prim *nsp)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001706{
Daniel Willmann1f3470f2021-03-11 09:53:42 +01001707 int i;
Harald Welte560bdb32020-12-04 22:24:47 +01001708 struct gbproxy_bvc *bvc;
Daniel Willmannf96cac52021-03-09 16:14:18 +01001709 struct gbproxy_nse *nse;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001710
Alexander Couzens951e1332020-09-22 13:21:46 +02001711 switch (nsp->u.status.cause) {
Alexander Couzens55c36f92021-01-27 20:56:55 +01001712 case GPRS_NS2_AFF_CAUSE_SNS_FAILURE:
1713 case GPRS_NS2_AFF_CAUSE_SNS_CONFIGURED:
Alexander Couzens951e1332020-09-22 13:21:46 +02001714 break;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001715
Alexander Couzens55c36f92021-01-27 20:56:55 +01001716 case GPRS_NS2_AFF_CAUSE_RECOVERY:
Harald Welte9b367d22021-01-18 13:55:51 +01001717 LOGP(DGPRS, LOGL_NOTICE, "NS-NSE %d became available\n", nsp->nsei);
Daniel Willmannf96cac52021-03-09 16:14:18 +01001718 nse = gbproxy_nse_by_nsei(cfg, nsp->nsei, NSE_F_SGSN);
1719 if (nse) {
Daniel Willmann37518b32021-05-27 18:13:36 +02001720 nse->alive = true;
Daniel Willmann38b9c9a2021-03-09 15:54:44 +01001721 // Update the NSE max SDU len
Daniel Willmannf96cac52021-03-09 16:14:18 +01001722 nse->max_sdu_len = nsp->u.status.mtu;
Daniel Willmann38b9c9a2021-03-09 15:54:44 +01001723
Harald Weltee5209642020-12-05 19:59:45 +01001724 uint8_t cause = BSSGP_CAUSE_OML_INTERV;
Daniel Willmannf96cac52021-03-09 16:14:18 +01001725 bvc = gbproxy_bvc_by_bvci(nse, 0);
Daniel Willmann38b9c9a2021-03-09 15:54:44 +01001726 if (bvc) {
Daniel Willmannf96cac52021-03-09 16:14:18 +01001727 bssgp_bvc_fsm_set_max_pdu_len(bvc->fi, nse->max_sdu_len);
Daniel Willmann3ea37932021-02-10 13:41:14 +01001728 osmo_fsm_inst_dispatch(bvc->fi, BSSGP_BVCFSM_E_REQ_RESET, &cause);
Daniel Willmann38b9c9a2021-03-09 15:54:44 +01001729 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001730 }
Alexander Couzens951e1332020-09-22 13:21:46 +02001731 break;
Alexander Couzens55c36f92021-01-27 20:56:55 +01001732 case GPRS_NS2_AFF_CAUSE_FAILURE:
Daniel Willmannf96cac52021-03-09 16:14:18 +01001733 nse = gbproxy_nse_by_nsei(cfg, nsp->nsei, NSE_F_BSS | NSE_F_SGSN);
1734 if (!nse) {
1735 LOGP(DGPRS, LOGL_ERROR, "Unknown NSE(%05d) became unavailable\n", nsp->nsei);
1736 break;
1737 }
Daniel Willmann37518b32021-05-27 18:13:36 +02001738
1739 nse->alive = false;
Daniel Willmannf96cac52021-03-09 16:14:18 +01001740 if (nse->sgsn_facing) {
Daniel Willmann1f3470f2021-03-11 09:53:42 +01001741 struct hlist_node *ntmp;
Daniel Willmannf96cac52021-03-09 16:14:18 +01001742 /* SGSN */
1743 /* TODO: When to block all PtP towards bss? Only if all SGSN are down? */
Daniel Willmann1f3470f2021-03-11 09:53:42 +01001744 hash_for_each_safe(nse->bvcs, i, ntmp, bvc, list) {
1745 if (bvc->bvci == 0)
1746 continue;
1747 gbproxy_bvc_free(bvc);
1748 }
Pau Espin Pedrol56438362021-06-04 18:03:44 +02001749 rate_ctr_inc(rate_ctr_group_get_ctr(cfg->ctrg, GBPROX_GLOB_CTR_RESTART_RESET_SGSN));
Alexander Couzens951e1332020-09-22 13:21:46 +02001750 } else {
Daniel Willmannf96cac52021-03-09 16:14:18 +01001751 /* BSS became unavailable
1752 * Block matching PtP-BVCs on SGSN-side */
1753 hash_for_each(nse->bvcs, i, bvc, list) {
1754 if (bvc->bvci == 0)
1755 continue;
1756 /* Get BVC for each SGSN and send block request */
1757 struct gbproxy_cell *cell = bvc->cell;
1758 for (int j = 0; j < GBPROXY_MAX_NR_SGSN; j++) {
1759 struct gbproxy_bvc *sgsn_bvc = cell->sgsn_bvc[j];
1760 if (!sgsn_bvc)
1761 continue;
1762
1763 /* Block BVC, indicate BSS equipment failure */
1764 uint8_t cause = BSSGP_CAUSE_EQUIP_FAIL;
1765 osmo_fsm_inst_dispatch(sgsn_bvc->fi, BSSGP_BVCFSM_E_REQ_BLOCK, &cause);
Daniel Willmanndc763fd2021-09-24 16:45:38 +02001766 sgsn_bvc->inactive = true;
Daniel Willmannf96cac52021-03-09 16:14:18 +01001767 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001768 }
Alexander Couzens951e1332020-09-22 13:21:46 +02001769
Daniel Willmann1f3470f2021-03-11 09:53:42 +01001770 /* This frees the BVCs for us as well */
1771 gbproxy_nse_free(nse);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001772 }
Harald Welte9b367d22021-01-18 13:55:51 +01001773 LOGP(DGPRS, LOGL_NOTICE, "NS-NSE %d became unavailable\n", nsp->nsei);
Alexander Couzens951e1332020-09-22 13:21:46 +02001774 break;
1775 default:
Harald Welte9b367d22021-01-18 13:55:51 +01001776 LOGP(DGPRS, LOGL_NOTICE, "NS: Unknown NS-STATUS.ind cause=%s from NS\n",
Harald Welte95cf9fb2020-11-30 10:55:22 +01001777 gprs_ns2_aff_cause_prim_str(nsp->u.status.cause));
Alexander Couzens951e1332020-09-22 13:21:46 +02001778 break;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001779 }
Alexander Couzens951e1332020-09-22 13:21:46 +02001780}
1781
Alexander Couzens951e1332020-09-22 13:21:46 +02001782/* called by the ns layer */
1783int gprs_ns2_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
1784{
1785 struct osmo_gprs_ns2_prim *nsp;
1786 struct gbproxy_config *cfg = (struct gbproxy_config *) ctx;
Daniel Willmann8f407b12020-12-02 19:33:50 +01001787 uintptr_t bvci;
Alexander Couzens951e1332020-09-22 13:21:46 +02001788 int rc = 0;
1789
1790 if (oph->sap != SAP_NS)
1791 return 0;
1792
1793 nsp = container_of(oph, struct osmo_gprs_ns2_prim, oph);
1794
1795 if (oph->operation != PRIM_OP_INDICATION) {
Harald Welte9b367d22021-01-18 13:55:51 +01001796 LOGP(DGPRS, LOGL_NOTICE, "NS: Unexpected primitive operation %s from NS\n",
Harald Welte95cf9fb2020-11-30 10:55:22 +01001797 get_value_string(osmo_prim_op_names, oph->operation));
Alexander Couzens951e1332020-09-22 13:21:46 +02001798 return 0;
1799 }
1800
1801 switch (oph->primitive) {
Alexander Couzens55c36f92021-01-27 20:56:55 +01001802 case GPRS_NS2_PRIM_UNIT_DATA:
Daniel Willmann8f407b12020-12-02 19:33:50 +01001803
Alexander Couzens951e1332020-09-22 13:21:46 +02001804 /* hand the message into the BSSGP implementation */
1805 msgb_bssgph(oph->msg) = oph->msg->l3h;
1806 msgb_bvci(oph->msg) = nsp->bvci;
1807 msgb_nsei(oph->msg) = nsp->nsei;
Daniel Willmann8f407b12020-12-02 19:33:50 +01001808 bvci = nsp->bvci | BVC_LOG_CTX_FLAG;
Alexander Couzens951e1332020-09-22 13:21:46 +02001809
Daniel Willmann8f407b12020-12-02 19:33:50 +01001810 log_set_context(LOG_CTX_GB_BVC, (void *)bvci);
Alexander Couzens951e1332020-09-22 13:21:46 +02001811 rc = gbprox_rcvmsg(cfg, oph->msg);
Daniel Willmannb6550102020-11-04 17:32:56 +01001812 msgb_free(oph->msg);
Alexander Couzens951e1332020-09-22 13:21:46 +02001813 break;
Alexander Couzens55c36f92021-01-27 20:56:55 +01001814 case GPRS_NS2_PRIM_STATUS:
Alexander Couzens951e1332020-09-22 13:21:46 +02001815 gprs_ns_prim_status_cb(cfg, nsp);
1816 break;
1817 default:
Harald Welte9b367d22021-01-18 13:55:51 +01001818 LOGP(DGPRS, LOGL_NOTICE, "NS: Unknown prim %s %s from NS\n",
Harald Welte95cf9fb2020-11-30 10:55:22 +01001819 gprs_ns2_prim_str(oph->primitive),
1820 get_value_string(osmo_prim_op_names, oph->operation));
Alexander Couzens951e1332020-09-22 13:21:46 +02001821 break;
1822 }
1823
1824 return rc;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001825}
1826
1827void gbprox_reset(struct gbproxy_config *cfg)
1828{
Harald Welted2fef952020-12-05 00:31:07 +01001829 struct gbproxy_nse *nse;
1830 struct hlist_node *ntmp;
Harald Welte8b4c7942020-12-05 10:14:49 +01001831 int i, j;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001832
Harald Welted2fef952020-12-05 00:31:07 +01001833 hash_for_each_safe(cfg->bss_nses, i, ntmp, nse, list) {
Harald Welte8b4c7942020-12-05 10:14:49 +01001834 struct gbproxy_bvc *bvc;
1835 struct hlist_node *tmp;
1836 hash_for_each_safe(nse->bvcs, j, tmp, bvc, list)
Harald Welte560bdb32020-12-04 22:24:47 +01001837 gbproxy_bvc_free(bvc);
Daniel Willmanne50550e2020-11-26 18:19:21 +01001838
1839 gbproxy_nse_free(nse);
1840 }
Harald Weltee5209642020-12-05 19:59:45 +01001841 /* FIXME: cells */
1842 /* FIXME: SGSN side BVCs (except signaling) */
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001843
1844 rate_ctr_group_free(cfg->ctrg);
1845 gbproxy_init_config(cfg);
1846}
1847
Daniel Willmann77493b12020-12-29 21:13:31 +01001848static void tlli_cache_cleanup(void *data)
1849{
1850 struct gbproxy_config *cfg = data;
1851 gbproxy_tlli_cache_cleanup(cfg);
1852
1853 /* TODO: Disable timer when cache is empty */
1854 osmo_timer_schedule(&cfg->tlli_cache.timer, 2, 0);
1855}
1856
Daniel Willmannc8a50092021-01-17 13:11:41 +01001857static void imsi_cache_cleanup(void *data)
1858{
1859 struct gbproxy_config *cfg = data;
1860 gbproxy_imsi_cache_cleanup(cfg);
1861
1862 /* TODO: Disable timer when cache is empty */
1863 osmo_timer_schedule(&cfg->imsi_cache.timer, 2, 0);
1864}
1865
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001866int gbproxy_init_config(struct gbproxy_config *cfg)
1867{
1868 struct timespec tp;
1869
Harald Welte209dc9f2020-12-12 19:02:16 +01001870 /* by default we advertise 100% of the BSS-side capacity to _each_ SGSN */
1871 cfg->pool.bvc_fc_ratio = 100;
Daniel Willmannee834af2020-12-14 16:22:39 +01001872 cfg->pool.null_nri_ranges = osmo_nri_ranges_alloc(cfg);
Daniel Willmann77493b12020-12-29 21:13:31 +01001873 /* TODO: Make configurable */
Daniel Willmannbd12f3f2021-01-13 18:16:04 +01001874 cfg->tlli_cache.timeout = 10;
Daniel Willmannc8a50092021-01-17 13:11:41 +01001875 cfg->imsi_cache.timeout = 10;
Daniel Willmannee834af2020-12-14 16:22:39 +01001876
Harald Welted2fef952020-12-05 00:31:07 +01001877 hash_init(cfg->bss_nses);
Daniel Willmann1e7be5d2020-12-21 18:08:21 +01001878 hash_init(cfg->sgsn_nses);
1879 hash_init(cfg->cells);
Daniel Willmann77493b12020-12-29 21:13:31 +01001880 hash_init(cfg->tlli_cache.entries);
Daniel Willmannee834af2020-12-14 16:22:39 +01001881 INIT_LLIST_HEAD(&cfg->sgsns);
1882
Daniel Willmann77493b12020-12-29 21:13:31 +01001883 osmo_timer_setup(&cfg->tlli_cache.timer, tlli_cache_cleanup, cfg);
1884 osmo_timer_schedule(&cfg->tlli_cache.timer, 2, 0);
1885
Daniel Willmannc8a50092021-01-17 13:11:41 +01001886 /* We could also combine both timers */
1887 osmo_timer_setup(&cfg->imsi_cache.timer, imsi_cache_cleanup, cfg);
1888 osmo_timer_schedule(&cfg->imsi_cache.timer, 2, 0);
1889
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001890 cfg->ctrg = rate_ctr_group_alloc(tall_sgsn_ctx, &global_ctrg_desc, 0);
1891 if (!cfg->ctrg) {
1892 LOGP(DGPRS, LOGL_ERROR, "Cannot allocate global counter group!\n");
1893 return -1;
1894 }
1895 osmo_clock_gettime(CLOCK_REALTIME, &tp);
Harald Weltec169de42020-12-07 13:12:13 +01001896 osmo_fsm_log_timeouts(true);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001897
1898 return 0;
Oliver Smith29532c22021-01-29 11:13:00 +01001899}