aboutsummaryrefslogtreecommitdiffstats
path: root/src/gbproxy/gb_proxy_tlli.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gbproxy/gb_proxy_tlli.c')
-rw-r--r--src/gbproxy/gb_proxy_tlli.c723
1 files changed, 723 insertions, 0 deletions
diff --git a/src/gbproxy/gb_proxy_tlli.c b/src/gbproxy/gb_proxy_tlli.c
new file mode 100644
index 000000000..4e21ede86
--- /dev/null
+++ b/src/gbproxy/gb_proxy_tlli.c
@@ -0,0 +1,723 @@
+/* Gb-proxy TLLI state handling */
+
+/* (C) 2014 by On-Waves
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <osmocom/gsm/gsm48.h>
+
+#include <osmocom/sgsn/gb_proxy.h>
+
+#include <osmocom/sgsn/gprs_utils.h>
+#include <osmocom/sgsn/gprs_gb_parse.h>
+
+#include <osmocom/sgsn/debug.h>
+
+#include <osmocom/gsm/gsm_utils.h>
+
+#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/talloc.h>
+
+struct gbproxy_link_info *gbproxy_link_info_by_tlli(struct gbproxy_peer *peer,
+ uint32_t tlli)
+{
+ struct gbproxy_link_info *link_info;
+ struct gbproxy_patch_state *state = &peer->patch_state;
+
+ if (!tlli)
+ return NULL;
+
+ llist_for_each_entry(link_info, &state->logical_links, list)
+ if (link_info->tlli.current == tlli ||
+ link_info->tlli.assigned == tlli)
+ return link_info;
+
+ return NULL;
+}
+
+struct gbproxy_link_info *gbproxy_link_info_by_ptmsi(
+ struct gbproxy_peer *peer,
+ uint32_t ptmsi)
+{
+ struct gbproxy_link_info *link_info;
+ struct gbproxy_patch_state *state = &peer->patch_state;
+
+ if (ptmsi == GSM_RESERVED_TMSI)
+ return NULL;
+
+ llist_for_each_entry(link_info, &state->logical_links, list)
+ if (link_info->tlli.ptmsi == ptmsi)
+ return link_info;
+
+ return NULL;
+}
+
+struct gbproxy_link_info *gbproxy_link_info_by_any_sgsn_tlli(
+ struct gbproxy_peer *peer,
+ uint32_t tlli)
+{
+ struct gbproxy_link_info *link_info;
+ struct gbproxy_patch_state *state = &peer->patch_state;
+
+ if (!tlli)
+ return NULL;
+
+ /* Don't care about the NSEI */
+ llist_for_each_entry(link_info, &state->logical_links, list)
+ if (link_info->sgsn_tlli.current == tlli ||
+ link_info->sgsn_tlli.assigned == tlli)
+ return link_info;
+
+ return NULL;
+}
+
+struct gbproxy_link_info *gbproxy_link_info_by_sgsn_tlli(
+ struct gbproxy_peer *peer,
+ uint32_t tlli, uint32_t sgsn_nsei)
+{
+ struct gbproxy_link_info *link_info;
+ struct gbproxy_patch_state *state = &peer->patch_state;
+
+ if (!tlli)
+ return NULL;
+
+ llist_for_each_entry(link_info, &state->logical_links, list)
+ if ((link_info->sgsn_tlli.current == tlli ||
+ link_info->sgsn_tlli.assigned == tlli) &&
+ link_info->sgsn_nsei == sgsn_nsei)
+ return link_info;
+
+ return NULL;
+}
+
+struct gbproxy_link_info *gbproxy_link_info_by_imsi(
+ struct gbproxy_peer *peer,
+ const uint8_t *imsi,
+ size_t imsi_len)
+{
+ struct gbproxy_link_info *link_info;
+ struct gbproxy_patch_state *state = &peer->patch_state;
+
+ if (!gprs_is_mi_imsi(imsi, imsi_len))
+ return NULL;
+
+ llist_for_each_entry(link_info, &state->logical_links, list) {
+ if (link_info->imsi_len != imsi_len)
+ continue;
+ if (memcmp(link_info->imsi, imsi, imsi_len) != 0)
+ continue;
+
+ return link_info;
+ }
+
+ return NULL;
+}
+
+void gbproxy_link_info_discard_messages(struct gbproxy_link_info *link_info)
+{
+ struct msgb *msg, *nxt;
+
+ llist_for_each_entry_safe(msg, nxt, &link_info->stored_msgs, list) {
+ llist_del(&msg->list);
+ msgb_free(msg);
+ }
+}
+
+void gbproxy_delete_link_info(struct gbproxy_peer *peer,
+ struct gbproxy_link_info *link_info)
+{
+ struct gbproxy_patch_state *state = &peer->patch_state;
+
+ gbproxy_link_info_discard_messages(link_info);
+
+ llist_del(&link_info->list);
+ talloc_free(link_info);
+ state->logical_link_count -= 1;
+
+ peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current =
+ state->logical_link_count;
+}
+
+void gbproxy_delete_link_infos(struct gbproxy_peer *peer)
+{
+ struct gbproxy_link_info *link_info, *nxt;
+ struct gbproxy_patch_state *state = &peer->patch_state;
+
+ llist_for_each_entry_safe(link_info, nxt, &state->logical_links, list)
+ gbproxy_delete_link_info(peer, link_info);
+
+ OSMO_ASSERT(state->logical_link_count == 0);
+ OSMO_ASSERT(llist_empty(&state->logical_links));
+}
+
+void gbproxy_attach_link_info(struct gbproxy_peer *peer, time_t now,
+ struct gbproxy_link_info *link_info)
+{
+ struct gbproxy_patch_state *state = &peer->patch_state;
+
+ link_info->timestamp = now;
+ llist_add(&link_info->list, &state->logical_links);
+ state->logical_link_count += 1;
+
+ peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current =
+ state->logical_link_count;
+}
+
+int gbproxy_remove_stale_link_infos(struct gbproxy_peer *peer, time_t now)
+{
+ struct gbproxy_patch_state *state = &peer->patch_state;
+ int exceeded_max_len = 0;
+ int deleted_count = 0;
+ int check_for_age;
+
+ if (peer->cfg->tlli_max_len > 0)
+ exceeded_max_len =
+ state->logical_link_count - peer->cfg->tlli_max_len;
+
+ check_for_age = peer->cfg->tlli_max_age > 0;
+
+ for (; exceeded_max_len > 0; exceeded_max_len--) {
+ struct gbproxy_link_info *link_info;
+ OSMO_ASSERT(!llist_empty(&state->logical_links));
+ link_info = llist_entry(state->logical_links.prev,
+ struct gbproxy_link_info,
+ list);
+ LOGP(DGPRS, LOGL_INFO,
+ "Removing TLLI %08x from list "
+ "(stale, length %d, max_len exceeded)\n",
+ link_info->tlli.current, state->logical_link_count);
+
+ gbproxy_delete_link_info(peer, link_info);
+ deleted_count += 1;
+ }
+
+ while (check_for_age && !llist_empty(&state->logical_links)) {
+ time_t age;
+ struct gbproxy_link_info *link_info;
+ link_info = llist_entry(state->logical_links.prev,
+ struct gbproxy_link_info,
+ 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) {
+ check_for_age = 0;
+ continue;
+ }
+
+ LOGP(DGPRS, LOGL_INFO,
+ "Removing TLLI %08x from list "
+ "(stale, age %d, max_age exceeded)\n",
+ link_info->tlli.current, (int)age);
+
+ gbproxy_delete_link_info(peer, link_info);
+ deleted_count += 1;
+ }
+
+ return deleted_count;
+}
+
+struct gbproxy_link_info *gbproxy_link_info_alloc( struct gbproxy_peer *peer)
+{
+ struct gbproxy_link_info *link_info;
+
+ link_info = talloc_zero(peer, struct gbproxy_link_info);
+ link_info->tlli.ptmsi = GSM_RESERVED_TMSI;
+ link_info->sgsn_tlli.ptmsi = GSM_RESERVED_TMSI;
+
+ link_info->vu_gen_tx_bss = GBPROXY_INIT_VU_GEN_TX;
+
+ INIT_LLIST_HEAD(&link_info->stored_msgs);
+
+ return link_info;
+}
+
+void gbproxy_detach_link_info(
+ struct gbproxy_peer *peer,
+ struct gbproxy_link_info *link_info)
+{
+ struct gbproxy_patch_state *state = &peer->patch_state;
+
+ llist_del(&link_info->list);
+ OSMO_ASSERT(state->logical_link_count > 0);
+ state->logical_link_count -= 1;
+
+ peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current =
+ state->logical_link_count;
+}
+
+void gbproxy_update_link_info(struct gbproxy_link_info *link_info,
+ const uint8_t *imsi, size_t imsi_len)
+{
+ if (!gprs_is_mi_imsi(imsi, imsi_len))
+ return;
+
+ link_info->imsi_len = imsi_len;
+ link_info->imsi =
+ talloc_realloc_size(link_info, link_info->imsi, imsi_len);
+ OSMO_ASSERT(link_info->imsi != NULL);
+ memcpy(link_info->imsi, imsi, imsi_len);
+}
+
+void gbproxy_reassign_tlli(struct gbproxy_tlli_state *tlli_state,
+ struct gbproxy_peer *peer, uint32_t new_tlli)
+{
+ if (new_tlli == tlli_state->current)
+ return;
+
+ LOGP(DGPRS, LOGL_INFO,
+ "The TLLI has been reassigned from %08x to %08x\n",
+ tlli_state->current, new_tlli);
+
+ /* Remember assigned TLLI */
+ tlli_state->assigned = new_tlli;
+ tlli_state->bss_validated = false;
+ tlli_state->net_validated = false;
+}
+
+uint32_t gbproxy_map_tlli(uint32_t other_tlli,
+ struct gbproxy_link_info *link_info, int to_bss)
+{
+ uint32_t tlli = 0;
+ struct gbproxy_tlli_state *src, *dst;
+ if (to_bss) {
+ src = &link_info->sgsn_tlli;
+ dst = &link_info->tlli;
+ } else {
+ src = &link_info->tlli;
+ dst = &link_info->sgsn_tlli;
+ }
+ if (src->current == other_tlli)
+ tlli = dst->current;
+ else if (src->assigned == other_tlli)
+ tlli = dst->assigned;
+
+ return tlli;
+}
+
+static void gbproxy_validate_tlli(struct gbproxy_tlli_state *tlli_state,
+ uint32_t tlli, int to_bss)
+{
+ LOGP(DGPRS, LOGL_DEBUG,
+ "%s({current = %08x, assigned = %08x, net_vld = %d, bss_vld = %d}, %08x)\n",
+ __func__, tlli_state->current, tlli_state->assigned,
+ tlli_state->net_validated, tlli_state->bss_validated, tlli);
+
+ if (!tlli_state->assigned || tlli_state->assigned != tlli)
+ return;
+
+ /* TODO: Is this ok? Check spec */
+ if (gprs_tlli_type(tlli) != TLLI_LOCAL)
+ return;
+
+ /* See GSM 04.08, 4.7.1.5 */
+ if (to_bss)
+ tlli_state->net_validated = true;
+ else
+ tlli_state->bss_validated = true;
+
+ if (!tlli_state->bss_validated || !tlli_state->net_validated)
+ return;
+
+ LOGP(DGPRS, LOGL_INFO,
+ "The TLLI %08x has been validated (was %08x)\n",
+ tlli_state->assigned, tlli_state->current);
+
+ tlli_state->current = tlli;
+ tlli_state->assigned = 0;
+}
+
+static void gbproxy_touch_link_info(struct gbproxy_peer *peer,
+ struct gbproxy_link_info *link_info,
+ time_t now)
+{
+ gbproxy_detach_link_info(peer, link_info);
+ gbproxy_attach_link_info(peer, now, link_info);
+}
+
+static int gbproxy_unregister_link_info(struct gbproxy_peer *peer,
+ struct gbproxy_link_info *link_info)
+{
+ if (!link_info)
+ return 1;
+
+ if (link_info->tlli.ptmsi == GSM_RESERVED_TMSI && !link_info->imsi_len) {
+ LOGP(DGPRS, LOGL_INFO,
+ "Removing TLLI %08x from list (P-TMSI or IMSI are not set)\n",
+ link_info->tlli.current);
+ gbproxy_delete_link_info(peer, link_info);
+ return 1;
+ }
+
+ link_info->tlli.current = 0;
+ link_info->tlli.assigned = 0;
+ link_info->sgsn_tlli.current = 0;
+ link_info->sgsn_tlli.assigned = 0;
+
+ link_info->is_deregistered = true;
+
+ gbproxy_reset_link(link_info);
+
+ return 0;
+}
+
+int gbproxy_imsi_matches(struct gbproxy_config *cfg,
+ enum gbproxy_match_id match_id,
+ struct gbproxy_link_info *link_info)
+{
+ struct gbproxy_match *match;
+ OSMO_ASSERT(match_id >= 0 && match_id < ARRAY_SIZE(cfg->matches));
+
+ match = &cfg->matches[match_id];
+ if (!match->enable)
+ return 1;
+
+ return link_info != NULL && link_info->is_matching[match_id];
+}
+
+static void gbproxy_assign_imsi(struct gbproxy_peer *peer,
+ struct gbproxy_link_info *link_info,
+ struct gprs_gb_parse_context *parse_ctx)
+{
+ int imsi_matches;
+ struct gbproxy_link_info *other_link_info;
+ enum gbproxy_match_id match_id;
+
+ /* Make sure that there is a second entry with the same IMSI */
+ other_link_info = gbproxy_link_info_by_imsi(
+ peer, parse_ctx->imsi, parse_ctx->imsi_len);
+
+ if (other_link_info && other_link_info != link_info) {
+ char mi_buf[200];
+ mi_buf[0] = '\0';
+ gsm48_mi_to_string(mi_buf, sizeof(mi_buf),
+ parse_ctx->imsi, parse_ctx->imsi_len);
+ LOGP(DGPRS, LOGL_INFO,
+ "Removing TLLI %08x from list (IMSI %s re-used)\n",
+ other_link_info->tlli.current, mi_buf);
+ gbproxy_delete_link_info(peer, other_link_info);
+ }
+
+ /* Update the IMSI field */
+ gbproxy_update_link_info(link_info,
+ parse_ctx->imsi, parse_ctx->imsi_len);
+
+ /* Check, whether the IMSI matches */
+ OSMO_ASSERT(ARRAY_SIZE(link_info->is_matching) ==
+ ARRAY_SIZE(peer->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],
+ parse_ctx->imsi, parse_ctx->imsi_len);
+ if (imsi_matches >= 0)
+ link_info->is_matching[match_id] = imsi_matches ? true : false;
+ }
+}
+
+static int gbproxy_tlli_match(const struct gbproxy_tlli_state *a,
+ const struct gbproxy_tlli_state *b)
+{
+ if (a->current && a->current == b->current)
+ return 1;
+
+ if (a->assigned && a->assigned == b->assigned)
+ return 1;
+
+ if (a->ptmsi != GSM_RESERVED_TMSI && a->ptmsi == b->ptmsi)
+ return 1;
+
+ return 0;
+}
+
+static void gbproxy_remove_matching_link_infos(
+ struct gbproxy_peer *peer, struct gbproxy_link_info *link_info)
+{
+ struct gbproxy_link_info *info, *nxt;
+ struct gbproxy_patch_state *state = &peer->patch_state;
+
+ /* Make sure that there is no second entry with the same P-TMSI or TLLI */
+ llist_for_each_entry_safe(info, nxt, &state->logical_links, list) {
+ if (info == link_info)
+ continue;
+
+ if (!gbproxy_tlli_match(&link_info->tlli, &info->tlli) &&
+ (link_info->sgsn_nsei != info->sgsn_nsei ||
+ !gbproxy_tlli_match(&link_info->sgsn_tlli, &info->sgsn_tlli)))
+ continue;
+
+ LOGP(DGPRS, LOGL_INFO,
+ "Removing TLLI %08x from list (P-TMSI/TLLI re-used)\n",
+ info->tlli.current);
+ gbproxy_delete_link_info(peer, info);
+ }
+}
+
+static struct gbproxy_link_info *gbproxy_get_link_info_ul(
+ struct gbproxy_peer *peer,
+ int *tlli_is_valid,
+ struct gprs_gb_parse_context *parse_ctx)
+{
+ struct gbproxy_link_info *link_info = NULL;
+
+ if (parse_ctx->tlli_enc) {
+ link_info = gbproxy_link_info_by_tlli(peer, parse_ctx->tlli);
+
+ if (link_info) {
+ *tlli_is_valid = 1;
+ return link_info;
+ }
+ }
+
+ *tlli_is_valid = 0;
+
+ if (!link_info && parse_ctx->imsi) {
+ link_info = gbproxy_link_info_by_imsi(
+ peer, parse_ctx->imsi, parse_ctx->imsi_len);
+ }
+
+ if (!link_info && parse_ctx->ptmsi_enc && !parse_ctx->old_raid_is_foreign) {
+ uint32_t bss_ptmsi;
+ gprs_parse_tmsi(parse_ctx->ptmsi_enc, &bss_ptmsi);
+ link_info = gbproxy_link_info_by_ptmsi(peer, bss_ptmsi);
+ }
+
+ if (!link_info)
+ return NULL;
+
+ link_info->is_deregistered = false;
+
+ return link_info;
+}
+
+struct gbproxy_link_info *gbproxy_update_link_state_ul(
+ struct gbproxy_peer *peer,
+ time_t now,
+ struct gprs_gb_parse_context *parse_ctx)
+{
+ struct gbproxy_link_info *link_info;
+ int tlli_is_valid;
+
+ link_info = gbproxy_get_link_info_ul(peer, &tlli_is_valid, parse_ctx);
+
+ if (parse_ctx->tlli_enc && parse_ctx->llc) {
+ uint32_t sgsn_tlli;
+
+ if (!link_info) {
+ LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list\n",
+ parse_ctx->tlli);
+ link_info = gbproxy_link_info_alloc(peer);
+ gbproxy_attach_link_info(peer, now, link_info);
+
+ /* Setup TLLIs */
+ sgsn_tlli = gbproxy_make_sgsn_tlli(peer, link_info,
+ parse_ctx->tlli);
+ link_info->sgsn_tlli.current = sgsn_tlli;
+ link_info->tlli.current = parse_ctx->tlli;
+ } else if (!tlli_is_valid) {
+ /* New TLLI (info found by IMSI or P-TMSI) */
+ link_info->tlli.current = parse_ctx->tlli;
+ link_info->tlli.assigned = 0;
+ link_info->sgsn_tlli.current =
+ gbproxy_make_sgsn_tlli(peer, link_info,
+ parse_ctx->tlli);
+ link_info->sgsn_tlli.assigned = 0;
+ gbproxy_touch_link_info(peer, link_info, now);
+ } else {
+ sgsn_tlli = gbproxy_map_tlli(parse_ctx->tlli, link_info, 0);
+ if (!sgsn_tlli)
+ sgsn_tlli = gbproxy_make_sgsn_tlli(peer, link_info,
+ parse_ctx->tlli);
+
+ gbproxy_validate_tlli(&link_info->tlli,
+ parse_ctx->tlli, 0);
+ gbproxy_validate_tlli(&link_info->sgsn_tlli,
+ sgsn_tlli, 0);
+ gbproxy_touch_link_info(peer, link_info, now);
+ }
+ } else if (link_info) {
+ gbproxy_touch_link_info(peer, link_info, now);
+ }
+
+ if (parse_ctx->imsi && link_info && link_info->imsi_len == 0)
+ gbproxy_assign_imsi(peer, link_info, parse_ctx);
+
+ return link_info;
+}
+
+static struct gbproxy_link_info *gbproxy_get_link_info_dl(
+ struct gbproxy_peer *peer,
+ struct gprs_gb_parse_context *parse_ctx)
+{
+ struct gbproxy_link_info *link_info = NULL;
+
+ /* Which key to use depends on its availability only, if that fails, do
+ * not retry it with another key (e.g. IMSI). */
+ if (parse_ctx->tlli_enc)
+ link_info = gbproxy_link_info_by_sgsn_tlli(peer, parse_ctx->tlli,
+ parse_ctx->peer_nsei);
+
+ /* TODO: Get link_info by (SGSN) P-TMSI if that is available (see
+ * GSM 08.18, 7.2) instead of using the IMSI as key. */
+ else if (parse_ctx->imsi)
+ link_info = gbproxy_link_info_by_imsi(
+ peer, parse_ctx->imsi, parse_ctx->imsi_len);
+
+ if (link_info)
+ link_info->is_deregistered = false;
+
+ return link_info;
+}
+
+struct gbproxy_link_info *gbproxy_update_link_state_dl(
+ struct gbproxy_peer *peer,
+ time_t now,
+ struct gprs_gb_parse_context *parse_ctx)
+{
+ struct gbproxy_link_info *link_info = NULL;
+
+ link_info = gbproxy_get_link_info_dl(peer, parse_ctx);
+
+ if (parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc && link_info) {
+ /* A new P-TMSI has been signalled in the message,
+ * register new TLLI */
+ uint32_t new_sgsn_ptmsi;
+ uint32_t new_bss_ptmsi = GSM_RESERVED_TMSI;
+ gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_sgsn_ptmsi);
+
+ if (link_info->sgsn_tlli.ptmsi == new_sgsn_ptmsi)
+ new_bss_ptmsi = link_info->tlli.ptmsi;
+
+ if (new_bss_ptmsi == GSM_RESERVED_TMSI)
+ new_bss_ptmsi = gbproxy_make_bss_ptmsi(peer, new_sgsn_ptmsi);
+
+ LOGP(DGPRS, LOGL_INFO,
+ "Got new PTMSI %08x from SGSN, using %08x for BSS\n",
+ new_sgsn_ptmsi, new_bss_ptmsi);
+ /* Setup PTMSIs */
+ 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) {
+ /* 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 */
+ uint32_t new_ptmsi;
+ gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi);
+
+ LOGP(DGPRS, LOGL_INFO,
+ "Adding TLLI %08x to list (SGSN, new P-TMSI is %08x)\n",
+ parse_ctx->tlli, new_ptmsi);
+
+ link_info = gbproxy_link_info_alloc(peer);
+ link_info->sgsn_tlli.current = parse_ctx->tlli;
+ link_info->tlli.current = parse_ctx->tlli;
+ link_info->sgsn_tlli.ptmsi = new_ptmsi;
+ 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) {
+ /* Unknown SGSN TLLI, create a new link_info */
+ uint32_t new_ptmsi;
+ link_info = gbproxy_link_info_alloc(peer);
+ LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list (SGSN)\n",
+ parse_ctx->tlli);
+
+ gbproxy_attach_link_info(peer, now, link_info);
+
+ /* Setup TLLIs */
+ link_info->sgsn_tlli.current = parse_ctx->tlli;
+ link_info->tlli.current = parse_ctx->tlli;
+
+ if (!parse_ctx->new_ptmsi_enc)
+ return link_info;
+ /* A new P-TMSI has been signalled in the message */
+
+ gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi);
+ LOGP(DGPRS, LOGL_INFO,
+ "Assigning new P-TMSI %08x\n", new_ptmsi);
+ /* Setup P-TMSIs */
+ link_info->sgsn_tlli.ptmsi = new_ptmsi;
+ link_info->tlli.ptmsi = new_ptmsi;
+ } else if (parse_ctx->tlli_enc && parse_ctx->llc && link_info) {
+ uint32_t bss_tlli = gbproxy_map_tlli(parse_ctx->tlli,
+ link_info, 1);
+ gbproxy_validate_tlli(&link_info->sgsn_tlli, parse_ctx->tlli, 1);
+ gbproxy_validate_tlli(&link_info->tlli, bss_tlli, 1);
+ gbproxy_touch_link_info(peer, link_info, now);
+ } else if (link_info) {
+ gbproxy_touch_link_info(peer, link_info, now);
+ }
+
+ if (parse_ctx->imsi && link_info && link_info->imsi_len == 0)
+ gbproxy_assign_imsi(peer, link_info, parse_ctx);
+
+ return link_info;
+}
+
+int gbproxy_update_link_state_after(
+ struct gbproxy_peer *peer,
+ struct gbproxy_link_info *link_info,
+ time_t now,
+ struct gprs_gb_parse_context *parse_ctx)
+{
+ int rc = 0;
+ 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 &&
+ parse_ctx->await_reattach) ||
+ (peer->cfg->keep_link_infos == GBPROX_KEEP_IDENTIFIED &&
+ link_info->imsi_len > 0);
+ if (keep_info) {
+ LOGP(DGPRS, LOGL_INFO, "Unregistering TLLI %08x\n",
+ link_info->tlli.current);
+ rc = gbproxy_unregister_link_info(peer, link_info);
+ } else {
+ LOGP(DGPRS, LOGL_INFO, "Removing TLLI %08x from list\n",
+ link_info->tlli.current);
+ gbproxy_delete_link_info(peer, link_info);
+ rc = 1;
+ }
+ } else if (parse_ctx->to_bss && parse_ctx->tlli_enc &&
+ parse_ctx->new_ptmsi_enc && link_info) {
+ /* A new PTMSI has been signaled in the message,
+ * register new TLLI */
+ uint32_t new_sgsn_ptmsi = link_info->sgsn_tlli.ptmsi;
+ uint32_t new_bss_ptmsi = link_info->tlli.ptmsi;
+ uint32_t new_sgsn_tlli;
+ uint32_t new_bss_tlli = 0;
+
+ new_sgsn_tlli = gprs_tmsi2tlli(new_sgsn_ptmsi, TLLI_LOCAL);
+ if (new_bss_ptmsi != GSM_RESERVED_TMSI)
+ new_bss_tlli = gprs_tmsi2tlli(new_bss_ptmsi, TLLI_LOCAL);
+ LOGP(DGPRS, LOGL_INFO,
+ "Assigning new TLLI %08x to SGSN, %08x to BSS\n",
+ new_sgsn_tlli, new_bss_tlli);
+
+ gbproxy_reassign_tlli(&link_info->sgsn_tlli,
+ peer, new_sgsn_tlli);
+ gbproxy_reassign_tlli(&link_info->tlli,
+ peer, new_bss_tlli);
+ gbproxy_remove_matching_link_infos(peer, link_info);
+ }
+
+ gbproxy_remove_stale_link_infos(peer, now);
+
+ return rc;
+}
+
+