daniel has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-sgsn/+/21406 )
Change subject: gbproxy: Add NSE peer that can have multiple gbproxy_peers ...................................................................... gbproxy: Add NSE peer that can have multiple gbproxy_peers We want this level of indirection to support multiple BVCs per NSE. The current code assumes that an NSE only has one BVC which breaks messages on the signalling BVC which should only be sent once to an NSE regardless of the number of BVCs it contains. Change-Id: I97cc6c8f8c0f1b91577ab8f679c4ae217cc88076 Related: SYS#5226 --- M include/osmocom/sgsn/gb_proxy.h M src/gbproxy/gb_proxy.c M src/gbproxy/gb_proxy_ctrl.c M src/gbproxy/gb_proxy_patch.c M src/gbproxy/gb_proxy_peer.c M src/gbproxy/gb_proxy_tlli.c M src/gbproxy/gb_proxy_vty.c M tests/gbproxy/gbproxy_test.c 8 files changed, 315 insertions(+), 114 deletions(-) git pull ssh://gerrit.osmocom.org:29418/osmo-sgsn refs/changes/06/21406/1 diff --git a/include/osmocom/sgsn/gb_proxy.h b/include/osmocom/sgsn/gb_proxy.h index 4466f15..ab9f3f3 100644 --- a/include/osmocom/sgsn/gb_proxy.h +++ b/include/osmocom/sgsn/gb_proxy.h @@ -15,6 +15,13 @@ #define GBPROXY_INIT_VU_GEN_TX 256 +/* Iterate through all BVCs of all NSEs + * Since these are two for loops the break keyword + * doesn't work as expected (only breaks out of the inner loop) */ +#define gbproxy_for_each_bvc(cfg, nse, bvc) \ + llist_for_each_entry(nse, &cfg->nse_peers, list) \ + llist_for_each_entry(bvc, &nse->bts_peers, list) + struct rate_ctr_group; struct gprs_gb_parse_context; struct tlv_parsed; @@ -99,7 +106,7 @@ struct gprs_ns2_inst *nsi; /* Linked list of all Gb peers (except SGSN) */ - struct llist_head bts_peers; + struct llist_head nse_peers; /* Counter */ struct rate_ctr_group *ctrg; @@ -143,24 +150,23 @@ int logical_link_count; }; -/* one peer at NS level that we interact with (BSS/PCU) */ +/* One BVC inside an NSE */ struct gbproxy_peer { - /* linked to gbproxy_config.bts_peers */ + /* linked to gbproxy_peer.bts_peers */ struct llist_head list; - /* point back to the config */ - struct gbproxy_config *cfg; - - /* NSEI of the peer entity */ - uint16_t nsei; + /* The peer this BVC belongs to */ + struct gbproxy_nse *nse; /* BVCI used for Point-to-Point to this peer */ uint16_t bvci; - bool blocked; - /* Routeing Area that this peer is part of (raw 04.08 encoding) */ + /* Routing Area that this peer is part of (raw 04.08 encoding) */ uint8_t ra[6]; + /* true if this BVC is blocked */ + bool blocked; + /* Counter */ struct rate_ctr_group *ctrg; @@ -171,6 +177,21 @@ struct osmo_timer_list clean_stale_timer; }; +/* one peer at NS level that we interact with (BSS/PCU) */ +struct gbproxy_nse { + /* linked to gbproxy_config.bts_peers */ + struct llist_head list; + + /* point back to the config */ + struct gbproxy_config *cfg; + + /* NSEI of the peer entity */ + uint16_t nsei; + + /* List of all BVCs in this NSE */ + struct llist_head bts_peers; +}; + struct gbproxy_tlli_state { /* currently active TLLI */ uint32_t current; @@ -328,8 +349,14 @@ struct gbproxy_config *cfg, const uint8_t *la); struct gbproxy_peer *gbproxy_peer_by_bssgp_tlv( struct gbproxy_config *cfg, struct tlv_parsed *tp); -struct gbproxy_peer *gbproxy_peer_alloc(struct gbproxy_config *cfg, uint16_t bvci); +struct gbproxy_peer *gbproxy_peer_alloc(struct gbproxy_nse *nse, uint16_t bvci); void gbproxy_peer_free(struct gbproxy_peer *peer); int gbproxy_cleanup_peers(struct gbproxy_config *cfg, uint16_t nsei, uint16_t bvci); +/* NSE handling */ +struct gbproxy_nse *gbproxy_nse_alloc(struct gbproxy_config *cfg, uint16_t nsei); +void gbproxy_nse_free(struct gbproxy_nse *nse); +struct gbproxy_nse *gbproxy_nse_by_nsei(struct gbproxy_config *cfg, uint16_t nsei); +struct gbproxy_nse *gbproxy_nse_by_nsei_or_new(struct gbproxy_config *cfg, uint16_t nsei); + #endif diff --git a/src/gbproxy/gb_proxy.c b/src/gbproxy/gb_proxy.c index 39b9ffd..d313255 100644 --- a/src/gbproxy/gb_proxy.c +++ b/src/gbproxy/gb_proxy.c @@ -84,10 +84,12 @@ static int check_peer_nsei(struct gbproxy_peer *peer, uint16_t nsei) { - if (peer->nsei != nsei) { + OSMO_ASSERT(peer->nse); + + if (peer->nse->nsei != nsei) { LOGP(DGPRS, LOGL_NOTICE, "Peer entry doesn't match current NSEI " "BVCI=%u via NSEI=%u (expected NSEI=%u)\n", - peer->bvci, nsei, peer->nsei); + peer->bvci, nsei, peer->nse->nsei); rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_INV_NSEI]); return 0; } @@ -195,6 +197,9 @@ struct gbproxy_patch_state *state = &peer->patch_state; const struct osmo_plmn_id old_plmn = state->local_plmn; struct gprs_ra_id raid; + OSMO_ASSERT(peer->nse); + struct gbproxy_config *cfg = peer->nse->cfg; + OSMO_ASSERT(cfg); if (!raid_enc) return; @@ -202,15 +207,15 @@ gsm48_parse_ra(&raid, raid_enc); /* save source side MCC/MNC */ - if (!peer->cfg->core_plmn.mcc || raid.mcc == peer->cfg->core_plmn.mcc) { + if (!cfg->core_plmn.mcc || raid.mcc == cfg->core_plmn.mcc) { state->local_plmn.mcc = 0; } else { state->local_plmn.mcc = raid.mcc; } - if (!peer->cfg->core_plmn.mnc + if (!cfg->core_plmn.mnc || !osmo_mnc_cmp(raid.mnc, raid.mnc_3_digits, - peer->cfg->core_plmn.mnc, peer->cfg->core_plmn.mnc_3_digits)) { + cfg->core_plmn.mnc, cfg->core_plmn.mnc_3_digits)) { state->local_plmn.mnc = 0; state->local_plmn.mnc_3_digits = false; } else { @@ -226,7 +231,7 @@ "" : "de", log_text, osmo_plmn_name(&state->local_plmn), - osmo_plmn_name2(&peer->cfg->core_plmn)); + osmo_plmn_name2(&cfg->core_plmn)); } uint32_t gbproxy_make_bss_ptmsi(struct gbproxy_peer *peer, @@ -234,7 +239,7 @@ { uint32_t bss_ptmsi; int max_retries = 23, rc = 0; - if (!peer->cfg->patch_ptmsi) { + if (!peer->nse->cfg->patch_ptmsi) { bss_ptmsi = sgsn_ptmsi; } else { do { @@ -263,7 +268,7 @@ { uint32_t sgsn_tlli; int max_retries = 23, rc = 0; - if (!peer->cfg->patch_ptmsi) { + if (!peer->nse->cfg->patch_ptmsi) { sgsn_tlli = bss_tlli; } else if (link_info->sgsn_tlli.ptmsi != GSM_RESERVED_TMSI && gprs_tlli_type(bss_tlli) == TLLI_FOREIGN) { @@ -330,6 +335,9 @@ { int rc; struct msgb *stored_msg; + OSMO_ASSERT(peer->nse); + struct gbproxy_config *cfg = peer->nse->cfg; + OSMO_ASSERT(cfg); /* Patch and flush stored messages towards the SGSN */ while ((stored_msg = msgb_dequeue_count(&link_info->stored_msgs, @@ -355,7 +363,7 @@ return -1; } - rc = gbprox_relay2sgsn(peer->cfg, stored_msg, + rc = gbprox_relay2sgsn(cfg, stored_msg, msgb_bvci(stored_msg), link_info->sgsn_nsei); if (rc < 0) @@ -425,6 +433,9 @@ struct gprs_gb_parse_context *parse_ctx) { struct msgb *stored_msg; + OSMO_ASSERT(peer->nse); + struct gbproxy_config *cfg = peer->nse->cfg; + OSMO_ASSERT(cfg); if (!link_info) return 1; @@ -493,9 +504,9 @@ /* The message cannot be processed since the IMSI is still missing */ /* If queue is getting too large, drop oldest msgb before adding new one */ - if (peer->cfg->stored_msgs_max_len > 0) { + if (cfg->stored_msgs_max_len > 0) { int exceeded_max_len = link_info->stored_msgs_len - + 1 - peer->cfg->stored_msgs_max_len; + + 1 - cfg->stored_msgs_max_len; for (; exceeded_max_len > 0; exceeded_max_len--) { struct msgb *msgb_drop; @@ -806,18 +817,22 @@ static int gbprox_relay2peer(struct msgb *old_msg, struct gbproxy_peer *peer, uint16_t ns_bvci) { + struct gbproxy_nse *nse = peer->nse; + OSMO_ASSERT(nse); + OSMO_ASSERT(nse->cfg); + /* create a copy of the message so the old one can * be free()d safely when we return from gbprox_rcvmsg() */ - struct gprs_ns2_inst *nsi = peer->cfg->nsi; + struct gprs_ns2_inst *nsi = nse->cfg->nsi; struct osmo_gprs_ns2_prim nsp = {}; struct msgb *msg = bssgp_msgb_copy(old_msg, "msgb_relay2peer"); int rc; DEBUGP(DGPRS, "NSEI=%u proxying SGSN->BSS (NS_BVCI=%u, NSEI=%u)\n", - msgb_nsei(msg), ns_bvci, peer->nsei); + msgb_nsei(msg), ns_bvci, nse->nsei); nsp.bvci = ns_bvci; - nsp.nsei = peer->nsei; + nsp.nsei = nse->nsei; /* Strip the old NS header, it will be replaced with a new one */ strip_ns_hdr(msg); @@ -1034,6 +1049,14 @@ LOGP(DGPRS, LOGL_INFO, "NSEI=%u Rx BVC RESET (BVCI=%u)\n", nsei, bvci); if (bvci == 0) { + struct gbproxy_nse *nse; + /* Ensure the NSE peer is there and clear all PtP BVCs */ + nse = gbproxy_nse_by_nsei_or_new(cfg, nsei); + if (!nse) + LOGP(DGPRS, LOGL_ERROR, "Could not allocate NSE\n"); + + gbproxy_cleanup_peers(cfg, nsei, 0); + /* FIXME: only do this if SGSN is alive! */ LOGP(DGPRS, LOGL_INFO, "NSEI=%u Tx fake " "BVC RESET ACK of BVCI=0\n", nsei); @@ -1042,16 +1065,34 @@ } from_peer = gbproxy_peer_by_bvci(cfg, bvci); if (!from_peer) { + struct gbproxy_nse *nse = gbproxy_nse_by_nsei(cfg, nsei); + if (!nse) { + LOGP(DGPRS, LOGL_NOTICE, "Got PtP BVC reset before signalling reset for " + "BVCI=%u NSEI=%u\n", bvci, nsei); + return bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_STATE, NULL, msg); + } /* if a PTP-BVC is reset, and we don't know that * PTP-BVCI yet, we should allocate a new peer */ LOGP(DGPRS, LOGL_INFO, "Allocationg new peer for BVCI=%u via NSEI=%u\n", bvci, nsei); - from_peer = gbproxy_peer_alloc(cfg, bvci); + from_peer = gbproxy_peer_alloc(nse, bvci); OSMO_ASSERT(from_peer); - from_peer->nsei = nsei; } - if (!check_peer_nsei(from_peer, nsei)) - from_peer->nsei = nsei; + /* Could have moved to a different NSE */ + if (!check_peer_nsei(from_peer, nsei)) { + struct gbproxy_nse *nse_old = from_peer->nse; + struct gbproxy_nse *nse_new = gbproxy_nse_by_nsei(cfg, nsei); + if (!nse_new) { + LOGP(DGPRS, LOGL_NOTICE, "Got PtP BVC reset before signalling reset for " + "BVCI=%u NSEI=%u\n", bvci, nsei); + return bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_STATE, NULL, msg); + } + LOGP(DGPRS, LOGL_NOTICE, "Peer for BVCI=%u moved from NSEI=%u to NSEI=%u\n", bvci, nse_old->nsei, nsei); + + /* Move peer to different NSE */ + llist_del(&from_peer->list); + llist_add(&from_peer->list, &nse_new->bts_peers); + } if (TLVP_PRESENT(&tp, BSSGP_IE_CELL_ID)) { struct gprs_ra_id raid; @@ -1098,10 +1139,13 @@ static int gbprox_rx_paging(struct gbproxy_config *cfg, struct msgb *msg, struct tlv_parsed *tp, uint32_t nsei, uint16_t ns_bvci) { + struct gbproxy_nse *nse; struct gbproxy_peer *peer; unsigned int n_peers = 0; int errctr = GBPROX_GLOB_CTR_PROTO_ERR_SGSN; + /* FIXME: Handle paging logic to only page each matching NSE */ + LOGP(DGPRS, LOGL_INFO, "NSEI=%u(SGSN) BSSGP PAGING ", nsei); if (TLVP_PRESENT(tp, BSSGP_IE_BVCI)) { @@ -1119,7 +1163,7 @@ } else if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) { errctr = GBPROX_GLOB_CTR_INV_RAI; /* iterate over all peers and dispatch the paging to each matching one */ - llist_for_each_entry(peer, &cfg->bts_peers, list) { + gbproxy_for_each_bvc(cfg, nse, peer) { if (!memcmp(peer->ra, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA), 6)) { LOGPC(DGPRS, LOGL_INFO, "routing by RAI to peer BVCI=%u\n", peer->bvci); gbprox_relay2peer(msg, peer, ns_bvci); @@ -1129,7 +1173,7 @@ } else if (TLVP_PRESENT(tp, BSSGP_IE_LOCATION_AREA)) { errctr = GBPROX_GLOB_CTR_INV_LAI; /* iterate over all peers and dispatch the paging to each matching one */ - llist_for_each_entry(peer, &cfg->bts_peers, list) { + gbproxy_for_each_bvc(cfg, nse, peer) { if (!memcmp(peer->ra, TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA), 5)) { LOGPC(DGPRS, LOGL_INFO, "routing by LAI to peer BVCI=%u\n", peer->bvci); gbprox_relay2peer(msg, peer, ns_bvci); @@ -1138,7 +1182,7 @@ } } else if (TLVP_PRESENT(tp, BSSGP_IE_BSS_AREA_ID)) { /* iterate over all peers and dispatch the paging to each matching one */ - llist_for_each_entry(peer, &cfg->bts_peers, list) { + gbproxy_for_each_bvc(cfg, nse, peer) { LOGPC(DGPRS, LOGL_INFO, "broadcasting to peer BVCI=%u\n", peer->bvci); gbprox_relay2peer(msg, peer, ns_bvci); n_peers++; @@ -1165,6 +1209,7 @@ struct msgb *msg, struct tlv_parsed *tp, uint32_t nsei, uint16_t ns_bvci) { + struct gbproxy_nse *nse; struct gbproxy_peer *peer; uint16_t ptp_bvci; @@ -1195,7 +1240,7 @@ * from the SGSN. As the signalling BVCI is shared * among all the BSS's that we multiplex, it needs to * be relayed */ - llist_for_each_entry(peer, &cfg->bts_peers, list) + gbproxy_for_each_bvc(cfg, nse, peer) gbprox_relay2peer(msg, peer, ns_bvci); return 0; @@ -1445,7 +1490,8 @@ rate_ctr_inc(&cfg->ctrg-> ctr[GBPROX_GLOB_CTR_RESTART_RESET_SGSN]); } else { - /* bss became unavailable */ + /* bss became unavailable + * TODO: Block all BVC belonging to that NSE */ peer = gbproxy_peer_by_nsei(cfg, nsp->nsei); if (!peer) { /* TODO: use primitive name + status cause name */ @@ -1516,10 +1562,15 @@ void gbprox_reset(struct gbproxy_config *cfg) { + struct gbproxy_nse *nse, *ntmp; struct gbproxy_peer *peer, *tmp; - llist_for_each_entry_safe(peer, tmp, &cfg->bts_peers, list) - gbproxy_peer_free(peer); + llist_for_each_entry_safe(nse, ntmp, &cfg->nse_peers, list) { + llist_for_each_entry_safe(peer, tmp, &nse->bts_peers, list) + gbproxy_peer_free(peer); + + gbproxy_nse_free(nse); + } rate_ctr_group_free(cfg->ctrg); gbproxy_init_config(cfg); @@ -1529,7 +1580,7 @@ { struct timespec tp; - INIT_LLIST_HEAD(&cfg->bts_peers); + INIT_LLIST_HEAD(&cfg->nse_peers); cfg->ctrg = rate_ctr_group_alloc(tall_sgsn_ctx, &global_ctrg_desc, 0); if (!cfg->ctrg) { LOGP(DGPRS, LOGL_ERROR, "Cannot allocate global counter group!\n"); diff --git a/src/gbproxy/gb_proxy_ctrl.c b/src/gbproxy/gb_proxy_ctrl.c index 9577383..28b36a8 100644 --- a/src/gbproxy/gb_proxy_ctrl.c +++ b/src/gbproxy/gb_proxy_ctrl.c @@ -55,7 +55,7 @@ struct gbproxy_config *cfg = data; struct gprs_ns2_inst *nsi = cfg->nsi; struct gprs_ns2_nse *nse; - struct gbproxy_peer *peer; + struct gbproxy_nse *nse_peer; cmd->reply = talloc_strdup(cmd, ""); @@ -69,8 +69,8 @@ gprs_ns2_nse_foreach_nsvc(nse, &ctrl_nsvc_state_cb, cmd); /* NS-VCs for BSS peers */ - llist_for_each_entry(peer, &cfg->bts_peers, list) { - nse = gprs_ns2_nse_by_nsei(nsi, peer->nsei); + llist_for_each_entry(nse_peer, &cfg->nse_peers, list) { + nse = gprs_ns2_nse_by_nsei(nsi, nse_peer->nsei); if (nse) gprs_ns2_nse_foreach_nsvc(nse, &ctrl_nsvc_state_cb, cmd); } @@ -83,16 +83,17 @@ static int get_gbproxy_state(struct ctrl_cmd *cmd, void *data) { struct gbproxy_config *cfg = data; + struct gbproxy_nse *nse_peer; struct gbproxy_peer *peer; cmd->reply = talloc_strdup(cmd, ""); - llist_for_each_entry(peer, &cfg->bts_peers, list) { + gbproxy_for_each_bvc(cfg, nse_peer, peer) { struct gprs_ra_id raid; gsm48_parse_ra(&raid, peer->ra); cmd->reply = talloc_asprintf_append(cmd->reply, "%u,%u,%u,%u,%u,%u,%s\n", - peer->nsei, peer->bvci, + nse_peer->nsei, peer->bvci, raid.mcc, raid.mnc, raid.lac, raid.rac, peer->blocked ? "BLOCKED" : "UNBLOCKED"); @@ -106,9 +107,14 @@ static int get_num_peers(struct ctrl_cmd *cmd, void *data) { struct gbproxy_config *cfg = data; + struct gbproxy_nse *nse_peer; + uint32_t count = 0; + + llist_for_each_entry(nse_peer, &cfg->nse_peers, list) + count += llist_count(&nse_peer->bts_peers); cmd->reply = talloc_strdup(cmd, ""); - cmd->reply = talloc_asprintf_append(cmd->reply, "%u", llist_count(&cfg->bts_peers)); + cmd->reply = talloc_asprintf_append(cmd->reply, "%u", count); return CTRL_CMD_REPLY; } diff --git a/src/gbproxy/gb_proxy_patch.c b/src/gbproxy/gb_proxy_patch.c index 9c70d3f..6601657 100644 --- a/src/gbproxy/gb_proxy_patch.c +++ b/src/gbproxy/gb_proxy_patch.c @@ -44,6 +44,10 @@ GBPROX_PEER_CTR_RAID_PATCHED_SGSN : GBPROX_PEER_CTR_RAID_PATCHED_BSS; + OSMO_ASSERT(peer->nse); + struct gbproxy_config *cfg = peer->nse->cfg; + OSMO_ASSERT(cfg); + if (!state->local_plmn.mcc || !state->local_plmn.mnc) return; @@ -58,11 +62,11 @@ if (!to_bss) { /* BSS -> SGSN */ if (state->local_plmn.mcc) - raid.mcc = peer->cfg->core_plmn.mcc; + raid.mcc = cfg->core_plmn.mcc; if (state->local_plmn.mnc) { - raid.mnc = peer->cfg->core_plmn.mnc; - raid.mnc_3_digits = peer->cfg->core_plmn.mnc_3_digits; + raid.mnc = cfg->core_plmn.mnc; + raid.mnc_3_digits = cfg->core_plmn.mnc_3_digits; } } else { /* SGSN -> BSS */ @@ -100,11 +104,14 @@ size_t apn_len = hdr->apn_len; uint8_t *apn = hdr->apn; + OSMO_ASSERT(peer->nse); + struct gbproxy_config *cfg = peer->nse->cfg; + OSMO_ASSERT(cfg); OSMO_ASSERT(apn_ie_len == apn_len + sizeof(struct apn_ie_hdr)); OSMO_ASSERT(apn_ie_len > 2 && apn_ie_len <= 102); - if (peer->cfg->core_apn_size == 0) { + if (cfg->core_apn_size == 0) { char str1[110]; /* Remove the IE */ LOGP(DGPRS, LOGL_DEBUG, @@ -119,20 +126,20 @@ char str1[110]; char str2[110]; - OSMO_ASSERT(peer->cfg->core_apn_size <= 100); + OSMO_ASSERT(cfg->core_apn_size <= 100); LOGP(DGPRS, LOGL_DEBUG, "Patching %s to SGSN: " "Replacing APN '%s' -> '%s'\n", log_text, osmo_apn_to_str(str1, apn, apn_len), - osmo_apn_to_str(str2, peer->cfg->core_apn, - peer->cfg->core_apn_size)); + osmo_apn_to_str(str2, cfg->core_apn, + cfg->core_apn_size)); - *new_apn_ie_len = peer->cfg->core_apn_size + 2; - msgb_resize_area(msg, apn, apn_len, peer->cfg->core_apn_size); - memcpy(apn, peer->cfg->core_apn, peer->cfg->core_apn_size); - hdr->apn_len = peer->cfg->core_apn_size; + *new_apn_ie_len = cfg->core_apn_size + 2; + msgb_resize_area(msg, apn, apn_len, cfg->core_apn_size); + memcpy(apn, cfg->core_apn, cfg->core_apn_size); + hdr->apn_len = cfg->core_apn_size; } rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_APN_PATCHED]); @@ -207,10 +214,12 @@ struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed; int have_patched = 0; int fcs; - struct gbproxy_config *cfg = peer->cfg; + OSMO_ASSERT(peer->nse); + struct gbproxy_config *cfg = peer->nse->cfg; + OSMO_ASSERT(cfg); if (parse_ctx->ptmsi_enc && link_info && - !parse_ctx->old_raid_is_foreign && peer->cfg->patch_ptmsi) { + !parse_ctx->old_raid_is_foreign && cfg->patch_ptmsi) { uint32_t ptmsi; if (parse_ctx->to_bss) ptmsi = link_info->tlli.ptmsi; @@ -291,13 +300,16 @@ { const char *err_info = NULL; int err_ctr = -1; + OSMO_ASSERT(peer->nse); + struct gbproxy_config *cfg = peer->nse->cfg; + OSMO_ASSERT(cfg); if (parse_ctx->bssgp_raid_enc) gbproxy_patch_raid((struct gsm48_ra_id *)parse_ctx->bssgp_raid_enc, peer, parse_ctx->to_bss, "BSSGP"); if (parse_ctx->need_decryption && - (peer->cfg->patch_ptmsi || peer->cfg->core_apn)) { + (cfg->patch_ptmsi || cfg->core_apn)) { /* Patching LLC messages has been requested * explicitly, but the message (including the * type) is encrypted, so we possibly fail to @@ -319,7 +331,7 @@ if (!link_info) return; - if (parse_ctx->tlli_enc && peer->cfg->patch_ptmsi) { + if (parse_ctx->tlli_enc && cfg->patch_ptmsi) { uint32_t tlli = gbproxy_map_tlli(parse_ctx->tlli, link_info, parse_ctx->to_bss); @@ -335,7 +347,7 @@ } } - if (parse_ctx->bssgp_ptmsi_enc && peer->cfg->patch_ptmsi) { + if (parse_ctx->bssgp_ptmsi_enc && cfg->patch_ptmsi) { uint32_t ptmsi; if (parse_ctx->to_bss) ptmsi = link_info->tlli.ptmsi; diff --git a/src/gbproxy/gb_proxy_peer.c b/src/gbproxy/gb_proxy_peer.c index 48482b6..0649d7c 100644 --- a/src/gbproxy/gb_proxy_peer.c +++ b/src/gbproxy/gb_proxy_peer.c @@ -20,6 +20,7 @@ * */ +#include "osmocom/vty/command.h" #include <osmocom/sgsn/gb_proxy.h> #include <osmocom/sgsn/debug.h> @@ -81,47 +82,55 @@ }; -/* Find the gbprox_peer by its BVCI */ +/* Find the gbproxy_peer by its BVCI. There can only be one match */ struct gbproxy_peer *gbproxy_peer_by_bvci(struct gbproxy_config *cfg, uint16_t bvci) { + struct gbproxy_nse *nse; struct gbproxy_peer *peer; - llist_for_each_entry(peer, &cfg->bts_peers, list) { + + gbproxy_for_each_bvc(cfg, nse, peer) { if (peer->bvci == bvci) return peer; } return NULL; } -/* Find the gbprox_peer by its NSEI */ +/* Find the gbproxy_peer by its NSEI */ +/* FIXME: Only returns the first peer, but we could have multiple on this nsei */ struct gbproxy_peer *gbproxy_peer_by_nsei(struct gbproxy_config *cfg, uint16_t nsei) { - struct gbproxy_peer *peer; - llist_for_each_entry(peer, &cfg->bts_peers, list) { - if (peer->nsei == nsei) - return peer; + struct gbproxy_nse *nse; + llist_for_each_entry(nse, &cfg->nse_peers, list) { + if (nse->nsei == nsei && !llist_empty(&nse->bts_peers)) + return llist_first_entry(&nse->bts_peers, struct gbproxy_peer, list); } return NULL; } -/* look-up a peer by its Routeing Area Identification (RAI) */ +/* look-up a peer by its Routeing Area Identification (RAI). There can only be one match */ struct gbproxy_peer *gbproxy_peer_by_rai(struct gbproxy_config *cfg, const uint8_t *ra) { + struct gbproxy_nse *nse; struct gbproxy_peer *peer; - llist_for_each_entry(peer, &cfg->bts_peers, list) { + + gbproxy_for_each_bvc(cfg, nse, peer) { if (!memcmp(peer->ra, ra, 6)) return peer; } + return NULL; } /* look-up a peer by its Location Area Identification (LAI) */ +/* FIXME: Only returns the first matching peer, but there could be multiple with the same LA */ struct gbproxy_peer *gbproxy_peer_by_lai(struct gbproxy_config *cfg, const uint8_t *la) { + struct gbproxy_nse *nse; struct gbproxy_peer *peer; - llist_for_each_entry(peer, &cfg->bts_peers, list) { + gbproxy_for_each_bvc(cfg, nse, peer) { if (!memcmp(peer->ra, la, 5)) return peer; } @@ -129,11 +138,13 @@ } /* look-up a peer by its Location Area Code (LAC) */ +/* FIXME: Only returns the first matching peer, but there could be multiple with the same LAC */ struct gbproxy_peer *gbproxy_peer_by_lac(struct gbproxy_config *cfg, const uint8_t *la) { + struct gbproxy_nse *nse; struct gbproxy_peer *peer; - llist_for_each_entry(peer, &cfg->bts_peers, list) { + gbproxy_for_each_bvc(cfg, nse, peer) { if (!memcmp(peer->ra + 3, la + 3, 2)) return peer; } @@ -172,18 +183,25 @@ time_t now; struct timespec ts = {0,}; struct gbproxy_peer *peer = (struct gbproxy_peer *) data; + OSMO_ASSERT(peer); + OSMO_ASSERT(peer->nse); + struct gbproxy_config *cfg = peer->nse->cfg; + OSMO_ASSERT(cfg); osmo_clock_gettime(CLOCK_MONOTONIC, &ts); now = ts.tv_sec; gbproxy_remove_stale_link_infos(peer, now); - if (peer->cfg->clean_stale_timer_freq != 0) + if (cfg->clean_stale_timer_freq != 0) osmo_timer_schedule(&peer->clean_stale_timer, - peer->cfg->clean_stale_timer_freq, 0); + cfg->clean_stale_timer_freq, 0); } -struct gbproxy_peer *gbproxy_peer_alloc(struct gbproxy_config *cfg, uint16_t bvci) +struct gbproxy_peer *gbproxy_peer_alloc(struct gbproxy_nse *nse, uint16_t bvci) { struct gbproxy_peer *peer; + OSMO_ASSERT(nse); + struct gbproxy_config *cfg = nse->cfg; + OSMO_ASSERT(cfg); peer = talloc_zero(tall_sgsn_ctx, struct gbproxy_peer); if (!peer) @@ -195,22 +213,24 @@ talloc_free(peer); return NULL; } - peer->cfg = cfg; + peer->nse = nse; - llist_add(&peer->list, &cfg->bts_peers); + llist_add(&peer->list, &nse->bts_peers); INIT_LLIST_HEAD(&peer->patch_state.logical_links); osmo_timer_setup(&peer->clean_stale_timer, clean_stale_timer_cb, peer); - if (peer->cfg->clean_stale_timer_freq != 0) + if (cfg->clean_stale_timer_freq != 0) osmo_timer_schedule(&peer->clean_stale_timer, - peer->cfg->clean_stale_timer_freq, 0); + cfg->clean_stale_timer_freq, 0); return peer; } void gbproxy_peer_free(struct gbproxy_peer *peer) { + OSMO_ASSERT(peer); + llist_del(&peer->list); osmo_timer_del(&peer->clean_stale_timer); gbproxy_delete_link_infos(peer); @@ -224,17 +244,78 @@ int gbproxy_cleanup_peers(struct gbproxy_config *cfg, uint16_t nsei, uint16_t bvci) { int counter = 0; - struct gbproxy_peer *peer, *tmp; + struct gbproxy_nse *nse, *ntmp; + OSMO_ASSERT(cfg); - llist_for_each_entry_safe(peer, tmp, &cfg->bts_peers, list) { - if (peer->nsei != nsei) + llist_for_each_entry_safe(nse, ntmp, &cfg->nse_peers, list) { + struct gbproxy_peer *peer, *tmp; + if (nse->nsei != nsei) continue; - if (bvci && peer->bvci != bvci) - continue; + llist_for_each_entry_safe(peer, tmp, &nse->bts_peers, list) { + if (bvci && peer->bvci != bvci) + continue; - gbproxy_peer_free(peer); - counter += 1; + gbproxy_peer_free(peer); + counter += 1; + } } return counter; } + +struct gbproxy_nse *gbproxy_nse_alloc(struct gbproxy_config *cfg, uint16_t nsei) +{ + struct gbproxy_nse *nse; + OSMO_ASSERT(cfg); + + nse = talloc_zero(tall_sgsn_ctx, struct gbproxy_nse); + if (!nse) + return NULL; + + nse->nsei = nsei; + nse->cfg = cfg; + + llist_add(&nse->list, &cfg->nse_peers); + + INIT_LLIST_HEAD(&nse->bts_peers); + + return nse; +} + +void gbproxy_nse_free(struct gbproxy_nse *nse) +{ + struct gbproxy_peer *peer, *tmp; + OSMO_ASSERT(nse); + + llist_del(&nse->list); + + llist_for_each_entry_safe(peer, tmp, &nse->bts_peers, list) + gbproxy_peer_free(peer); + + talloc_free(nse); +} + +struct gbproxy_nse *gbproxy_nse_by_nsei(struct gbproxy_config *cfg, uint16_t nsei) +{ + struct gbproxy_nse *nse; + OSMO_ASSERT(cfg); + + llist_for_each_entry(nse, &cfg->nse_peers, list) { + if (nse->nsei == nsei) + return nse; + } + + return NULL; +} + +struct gbproxy_nse *gbproxy_nse_by_nsei_or_new(struct gbproxy_config *cfg, uint16_t nsei) +{ + struct gbproxy_nse *nse; + OSMO_ASSERT(cfg); + + nse = gbproxy_nse_by_nsei(cfg, nsei); + if (!nse) + nse = gbproxy_nse_alloc(cfg, nsei); + + return nse; +} \ No newline at end of file diff --git a/src/gbproxy/gb_proxy_tlli.c b/src/gbproxy/gb_proxy_tlli.c index e9271c2..9487459 100644 --- a/src/gbproxy/gb_proxy_tlli.c +++ b/src/gbproxy/gb_proxy_tlli.c @@ -183,12 +183,15 @@ int exceeded_max_len = 0; int deleted_count = 0; int check_for_age; + OSMO_ASSERT(peer->nse); + struct gbproxy_config *cfg = peer->nse->cfg; + OSMO_ASSERT(cfg); - if (peer->cfg->tlli_max_len > 0) + if (cfg->tlli_max_len > 0) exceeded_max_len = - state->logical_link_count - peer->cfg->tlli_max_len; + state->logical_link_count - cfg->tlli_max_len; - check_for_age = peer->cfg->tlli_max_age > 0; + check_for_age = cfg->tlli_max_age > 0; for (; exceeded_max_len > 0; exceeded_max_len--) { struct gbproxy_link_info *link_info; @@ -213,7 +216,7 @@ list); age = now - link_info->timestamp; /* age < 0 only happens after system time jumps, discard entry */ - if (age <= peer->cfg->tlli_max_age && age >= 0) { + if (age <= cfg->tlli_max_age && age >= 0) { check_for_age = 0; continue; } @@ -395,6 +398,9 @@ int imsi_matches; struct gbproxy_link_info *other_link_info; enum gbproxy_match_id match_id; + OSMO_ASSERT(peer->nse); + struct gbproxy_config *cfg = peer->nse->cfg; + OSMO_ASSERT(cfg); /* Make sure that there is a second entry with the same IMSI */ other_link_info = gbproxy_link_info_by_imsi( @@ -419,11 +425,11 @@ /* Check, whether the IMSI matches */ OSMO_ASSERT(ARRAY_SIZE(link_info->is_matching) == - ARRAY_SIZE(peer->cfg->matches)); + ARRAY_SIZE(cfg->matches)); for (match_id = 0; match_id < ARRAY_SIZE(link_info->is_matching); ++match_id) { imsi_matches = gbproxy_check_imsi( - &peer->cfg->matches[match_id], + &cfg->matches[match_id], parse_ctx->imsi, parse_ctx->imsi_len); if (imsi_matches >= 0) link_info->is_matching[match_id] = imsi_matches ? true : false; @@ -590,6 +596,9 @@ struct gprs_gb_parse_context *parse_ctx) { struct gbproxy_link_info *link_info = NULL; + OSMO_ASSERT(peer->nse); + struct gbproxy_config *cfg = peer->nse->cfg; + OSMO_ASSERT(cfg); link_info = gbproxy_get_link_info_dl(peer, parse_ctx); @@ -613,7 +622,7 @@ link_info->sgsn_tlli.ptmsi = new_sgsn_ptmsi; link_info->tlli.ptmsi = new_bss_ptmsi; } else if (parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc && !link_info && - !peer->cfg->patch_ptmsi) { + !cfg->patch_ptmsi) { /* A new P-TMSI has been signalled in the message with an unknown * TLLI, create a new link_info */ /* TODO: Add a test case for this branch */ @@ -631,7 +640,7 @@ link_info->tlli.ptmsi = new_ptmsi; gbproxy_attach_link_info(peer, now, link_info); } else if (parse_ctx->tlli_enc && parse_ctx->llc && !link_info && - !peer->cfg->patch_ptmsi) { + !cfg->patch_ptmsi) { /* Unknown SGSN TLLI, create a new link_info */ uint32_t new_ptmsi; link_info = gbproxy_link_info_alloc(peer); @@ -677,12 +686,16 @@ struct gprs_gb_parse_context *parse_ctx) { int rc = 0; + OSMO_ASSERT(peer->nse); + struct gbproxy_config *cfg = peer->nse->cfg; + OSMO_ASSERT(cfg); + if (parse_ctx->invalidate_tlli && link_info) { int keep_info = - peer->cfg->keep_link_infos == GBPROX_KEEP_ALWAYS || - (peer->cfg->keep_link_infos == GBPROX_KEEP_REATTACH && + cfg->keep_link_infos == GBPROX_KEEP_ALWAYS || + (cfg->keep_link_infos == GBPROX_KEEP_REATTACH && parse_ctx->await_reattach) || - (peer->cfg->keep_link_infos == GBPROX_KEEP_IDENTIFIED && + (cfg->keep_link_infos == GBPROX_KEEP_IDENTIFIED && link_info->imsi_len > 0); if (keep_info) { LOGP(DGPRS, LOGL_INFO, "Unregistering TLLI %08x\n", diff --git a/src/gbproxy/gb_proxy_vty.c b/src/gbproxy/gb_proxy_vty.c index 236d5d3..def550e 100644 --- a/src/gbproxy/gb_proxy_vty.c +++ b/src/gbproxy/gb_proxy_vty.c @@ -73,7 +73,7 @@ gsm48_parse_ra(&raid, peer->ra); vty_out(vty, "NSEI %5u, PTP-BVCI %5u, " - "RAI %s", peer->nsei, peer->bvci, osmo_rai_name(&raid)); + "RAI %s", peer->nse->nsei, peer->bvci, osmo_rai_name(&raid)); if (peer->blocked) vty_out(vty, " [BVC-BLOCKED]"); @@ -420,6 +420,7 @@ GBPROXY_LINK_LIST_STR GBPROXY_CLEAN_STALE_TIMER_STR "Frequency at which the periodic timer is fired (in seconds)\n") { + struct gbproxy_nse *nse; struct gbproxy_peer *peer; g_cfg->clean_stale_timer_freq = (unsigned int) atoi(argv[0]); @@ -427,7 +428,7 @@ and new frequency is desired to be lower. After initial run, periodic time is used. Use random() to avoid firing timers for all peers at the same time */ - llist_for_each_entry(peer, &g_cfg->bts_peers, list) + gbproxy_for_each_bvc(g_cfg, nse, peer) osmo_timer_schedule(&peer->clean_stale_timer, random() % 5, random() % 1000000); @@ -440,10 +441,11 @@ NO_STR GBPROXY_LINK_LIST_STR GBPROXY_CLEAN_STALE_TIMER_STR) { + struct gbproxy_nse *nse; struct gbproxy_peer *peer; g_cfg->clean_stale_timer_freq = 0; - llist_for_each_entry(peer, &g_cfg->bts_peers, list) + gbproxy_for_each_bvc(g_cfg, nse, peer) osmo_timer_del(&peer->clean_stale_timer); return CMD_SUCCESS; @@ -536,13 +538,14 @@ DEFUN(show_gbproxy, show_gbproxy_cmd, "show gbproxy [stats]", SHOW_STR "Display information about the Gb proxy\n" "Show statistics\n") { + struct gbproxy_nse *nse; struct gbproxy_peer *peer; int show_stats = argc >= 1; if (show_stats) vty_out_rate_ctr_group(vty, "", g_cfg->ctrg); - llist_for_each_entry(peer, &g_cfg->bts_peers, list) { + gbproxy_for_each_bvc(g_cfg, nse, peer) { gbprox_vty_print_peer(vty, peer); if (show_stats) @@ -554,6 +557,7 @@ DEFUN(show_gbproxy_links, show_gbproxy_links_cmd, "show gbproxy links", SHOW_STR "Display information about the Gb proxy\n" "Show logical links\n") { + struct gbproxy_nse *nse; struct gbproxy_peer *peer; time_t now; struct timespec ts = {0,}; @@ -561,7 +565,7 @@ osmo_clock_gettime(CLOCK_MONOTONIC, &ts); now = ts.tv_sec; - llist_for_each_entry(peer, &g_cfg->bts_peers, list) { + gbproxy_for_each_bvc(g_cfg, nse, peer) { struct gbproxy_link_info *link_info; struct gbproxy_patch_state *state = &peer->patch_state; @@ -651,15 +655,17 @@ if (!dry_run) counter = gbproxy_cleanup_peers(g_cfg, nsei, 0); else { + struct gbproxy_nse *nse; struct gbproxy_peer *peer; counter = 0; - llist_for_each_entry(peer, &g_cfg->bts_peers, list) { - if (peer->nsei != nsei) + llist_for_each_entry(nse, &g_cfg->nse_peers, list) { + if (nse->nsei != nsei) continue; - - vty_out(vty, "BVC: "); - gbprox_vty_print_peer(vty, peer); - counter += 1; + llist_for_each_entry(peer, &nse->bts_peers, list) { + vty_out(vty, "BVC: "); + gbprox_vty_print_peer(vty, peer); + counter += 1; + } } } vty_out(vty, "%sDeleted %d BVC%s", diff --git a/tests/gbproxy/gbproxy_test.c b/tests/gbproxy/gbproxy_test.c index 76fc000..b3d12c0 100644 --- a/tests/gbproxy/gbproxy_test.c +++ b/tests/gbproxy/gbproxy_test.c @@ -118,6 +118,7 @@ static int dump_peers(FILE *stream, int indent, time_t now, struct gbproxy_config *cfg) { + struct gbproxy_nse *nse; struct gbproxy_peer *peer; struct gprs_ra_id raid; unsigned int i; @@ -128,14 +129,15 @@ if (rc < 0) return rc; - llist_for_each_entry(peer, &cfg->bts_peers, list) { + + gbproxy_for_each_bvc(cfg, nse, peer) { struct gbproxy_link_info *link_info; struct gbproxy_patch_state *state = &peer->patch_state; gsm48_parse_ra(&raid, peer->ra); rc = fprintf(stream, "%*s NSEI %u, BVCI %u, %sblocked, RAI %s\n", indent, "", - peer->nsei, peer->bvci, + nse->nsei, peer->bvci, peer->blocked ? "" : "not ", osmo_rai_name(&raid)); @@ -927,6 +929,7 @@ "NSEI 0x%04x(%d)\n\n", nsei, nsei); send_ns_avail(nsi, nsei); + send_bssgp_reset(nsi, nsei, 0); } static void setup_bssgp(struct gprs_ns2_inst *nsi, @@ -4249,7 +4252,7 @@ struct gbproxy_link_info *link_info; int imsi_matches = -1; int tlli_already_known = 0; - struct gbproxy_config *cfg = peer->cfg; + struct gbproxy_config *cfg = peer->nse->cfg; /* Check, whether the IMSI matches */ if (gprs_is_mi_imsi(imsi, imsi_len)) { @@ -4298,6 +4301,7 @@ static void test_gbproxy_tlli_expire(void) { struct gbproxy_config cfg = {0}; + struct gbproxy_nse *nse; struct gbproxy_peer *peer; const char *err_msg = NULL; const uint8_t imsi1[] = { GSM_MI_TYPE_IMSI, 0x23, 0x24, 0x25, 0xf6 }; @@ -4312,6 +4316,7 @@ printf("Test TLLI info expiry\n\n"); gbproxy_init_config(&cfg); + nse = gbproxy_nse_by_nsei_or_new(&cfg, 0); if (gbproxy_set_patch_filter(&cfg.matches[GBPROX_MATCH_PATCHING], filter_re, &err_msg) != 0) { @@ -4327,7 +4332,7 @@ cfg.tlli_max_len = 0; cfg.tlli_max_age = 0; - peer = gbproxy_peer_alloc(&cfg, 20); + peer = gbproxy_peer_alloc(nse, 20); OSMO_ASSERT(peer->patch_state.logical_link_count == 0); printf(" Add TLLI 1, IMSI 1\n"); @@ -4366,7 +4371,7 @@ cfg.tlli_max_len = 0; cfg.tlli_max_age = 0; - peer = gbproxy_peer_alloc(&cfg, 20); + peer = gbproxy_peer_alloc(nse, 20); OSMO_ASSERT(peer->patch_state.logical_link_count == 0); printf(" Add TLLI 1, IMSI 1\n"); @@ -4406,7 +4411,7 @@ cfg.tlli_max_len = 1; cfg.tlli_max_age = 0; - peer = gbproxy_peer_alloc(&cfg, 20); + peer = gbproxy_peer_alloc(nse, 20); OSMO_ASSERT(peer->patch_state.logical_link_count == 0); printf(" Add TLLI 1, IMSI 1\n"); @@ -4444,7 +4449,7 @@ cfg.tlli_max_len = 0; cfg.tlli_max_age = 1; - peer = gbproxy_peer_alloc(&cfg, 20); + peer = gbproxy_peer_alloc(nse, 20); OSMO_ASSERT(peer->patch_state.logical_link_count == 0); printf(" Add TLLI 1, IMSI 1 (should expire after timeout)\n"); @@ -4482,7 +4487,7 @@ cfg.tlli_max_len = 0; cfg.tlli_max_age = 1; - peer = gbproxy_peer_alloc(&cfg, 20); + peer = gbproxy_peer_alloc(nse, 20); OSMO_ASSERT(peer->patch_state.logical_link_count == 0); printf(" Add TLLI 1, IMSI 1 (should expire)\n"); -- To view, visit https://gerrit.osmocom.org/c/osmo-sgsn/+/21406 To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: osmo-sgsn Gerrit-Branch: master Gerrit-Change-Id: I97cc6c8f8c0f1b91577ab8f679c4ae217cc88076 Gerrit-Change-Number: 21406 Gerrit-PatchSet: 1 Gerrit-Owner: daniel <dwillm...@sysmocom.de> Gerrit-MessageType: newchange