| /* 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 <openbsc/gb_proxy.h> |
| |
| #include <openbsc/gprs_utils.h> |
| #include <openbsc/gprs_gb_parse.h> |
| |
| #include <openbsc/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 = 0; |
| tlli_state->net_validated = 0; |
| } |
| |
| 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 = 1; |
| else |
| tlli_state->bss_validated = 1; |
| |
| 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 void gbproxy_unregister_link_info(struct gbproxy_peer *peer, |
| struct gbproxy_link_info *link_info) |
| { |
| if (!link_info) |
| return; |
| |
| 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; |
| } |
| |
| 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 = 1; |
| |
| gbproxy_reset_link(link_info); |
| |
| return; |
| } |
| |
| 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]; |
| } |
| |
| 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; |
| } |
| } |
| |
| 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); |
| } |
| } |
| |
| struct gbproxy_link_info *gbproxy_get_link_info_ul( |
| struct gbproxy_peer *peer, |
| 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 && 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) |
| link_info->is_deregistered = 0; |
| |
| 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; |
| |
| link_info = gbproxy_get_link_info_ul(peer, 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 (!link_info->tlli.current) { |
| /* New TLLI (info found by IMSI or P-TMSI) */ |
| link_info->tlli.current = parse_ctx->tlli; |
| link_info->sgsn_tlli.current = |
| gbproxy_make_sgsn_tlli(peer, link_info, |
| parse_ctx->tlli); |
| 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; |
| } |
| |
| 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; |
| |
| if (parse_ctx->tlli_enc) |
| link_info = gbproxy_link_info_by_sgsn_tlli( |
| peer, parse_ctx->tlli, parse_ctx->peer_nsei); |
| |
| 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; |
| gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_sgsn_ptmsi); |
| 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; |
| } |
| |
| void 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) |
| { |
| 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); |
| 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); |
| } |
| } 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); |
| } |
| |
| |