/* 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 . * */ #include #include #include #include #include #include #include #include struct gbproxy_tlli_info *gbproxy_find_tlli(struct gbproxy_peer *peer, uint32_t tlli) { struct gbproxy_tlli_info *tlli_info; struct gbproxy_patch_state *state = &peer->patch_state; llist_for_each_entry(tlli_info, &state->enabled_tllis, list) if (tlli_info->tlli.current == tlli || tlli_info->tlli.assigned == tlli) return tlli_info; return NULL; } struct gbproxy_tlli_info *gbproxy_find_tlli_by_ptmsi( struct gbproxy_peer *peer, uint32_t ptmsi) { struct gbproxy_tlli_info *tlli_info; struct gbproxy_patch_state *state = &peer->patch_state; llist_for_each_entry(tlli_info, &state->enabled_tllis, list) if (tlli_info->tlli.ptmsi == ptmsi) return tlli_info; return NULL; } struct gbproxy_tlli_info *gbproxy_find_tlli_by_sgsn_tlli( struct gbproxy_peer *peer, uint32_t tlli) { struct gbproxy_tlli_info *tlli_info; struct gbproxy_patch_state *state = &peer->patch_state; llist_for_each_entry(tlli_info, &state->enabled_tllis, list) if (tlli_info->sgsn_tlli.current == tlli || tlli_info->sgsn_tlli.assigned == tlli) return tlli_info; return NULL; } struct gbproxy_tlli_info *gbproxy_find_tlli_by_imsi( struct gbproxy_peer *peer, const uint8_t *imsi, size_t imsi_len) { struct gbproxy_tlli_info *tlli_info; struct gbproxy_patch_state *state = &peer->patch_state; if (!gprs_is_mi_imsi(imsi, imsi_len)) return NULL; llist_for_each_entry(tlli_info, &state->enabled_tllis, list) { if (tlli_info->imsi_len != imsi_len) continue; if (memcmp(tlli_info->imsi, imsi, imsi_len) != 0) continue; return tlli_info; } return NULL; } void gbproxy_tlli_info_discard_messages(struct gbproxy_tlli_info *tlli_info) { struct msgb *msg, *nxt; llist_for_each_entry_safe(msg, nxt, &tlli_info->stored_msgs, list) { llist_del(&msg->list); msgb_free(msg); } } void gbproxy_delete_tlli(struct gbproxy_peer *peer, struct gbproxy_tlli_info *tlli_info) { struct gbproxy_patch_state *state = &peer->patch_state; gbproxy_tlli_info_discard_messages(tlli_info); llist_del(&tlli_info->list); talloc_free(tlli_info); state->enabled_tllis_count -= 1; peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current = state->enabled_tllis_count; } void gbproxy_delete_tllis(struct gbproxy_peer *peer) { struct gbproxy_tlli_info *tlli_info, *nxt; struct gbproxy_patch_state *state = &peer->patch_state; llist_for_each_entry_safe(tlli_info, nxt, &state->enabled_tllis, list) gbproxy_delete_tlli(peer, tlli_info); OSMO_ASSERT(state->enabled_tllis_count == 0); OSMO_ASSERT(llist_empty(&state->enabled_tllis)); } void gbproxy_attach_tlli_info(struct gbproxy_peer *peer, time_t now, struct gbproxy_tlli_info *tlli_info) { struct gbproxy_patch_state *state = &peer->patch_state; tlli_info->timestamp = now; llist_add(&tlli_info->list, &state->enabled_tllis); state->enabled_tllis_count += 1; peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current = state->enabled_tllis_count; } int gbproxy_remove_stale_tllis(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->enabled_tllis_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_tlli_info *tlli_info; OSMO_ASSERT(!llist_empty(&state->enabled_tllis)); tlli_info = llist_entry(state->enabled_tllis.prev, struct gbproxy_tlli_info, list); LOGP(DGPRS, LOGL_INFO, "Removing TLLI %08x from list " "(stale, length %d, max_len exceeded)\n", tlli_info->tlli.current, state->enabled_tllis_count); gbproxy_delete_tlli(peer, tlli_info); deleted_count += 1; } while (check_for_age && !llist_empty(&state->enabled_tllis)) { time_t age; struct gbproxy_tlli_info *tlli_info; tlli_info = llist_entry(state->enabled_tllis.prev, struct gbproxy_tlli_info, list); age = now - tlli_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", tlli_info->tlli.current, (int)age); gbproxy_delete_tlli(peer, tlli_info); deleted_count += 1; } return deleted_count; } struct gbproxy_tlli_info *gbproxy_tlli_info_alloc( struct gbproxy_peer *peer) { struct gbproxy_tlli_info *tlli_info; tlli_info = talloc_zero(peer, struct gbproxy_tlli_info); tlli_info->tlli.ptmsi = GSM_RESERVED_TMSI; tlli_info->sgsn_tlli.ptmsi = GSM_RESERVED_TMSI; INIT_LLIST_HEAD(&tlli_info->stored_msgs); return tlli_info; } void gbproxy_detach_tlli_info( struct gbproxy_peer *peer, struct gbproxy_tlli_info *tlli_info) { struct gbproxy_patch_state *state = &peer->patch_state; llist_del(&tlli_info->list); OSMO_ASSERT(state->enabled_tllis_count > 0); state->enabled_tllis_count -= 1; peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current = state->enabled_tllis_count; } void gbproxy_update_tlli_info(struct gbproxy_tlli_info *tlli_info, const uint8_t *imsi, size_t imsi_len) { if (!gprs_is_mi_imsi(imsi, imsi_len)) return; tlli_info->imsi_len = imsi_len; tlli_info->imsi = talloc_realloc_size(tlli_info, tlli_info->imsi, imsi_len); OSMO_ASSERT(tlli_info->imsi != NULL); memcpy(tlli_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_tlli_info *tlli_info, int to_bss) { uint32_t tlli = 0; struct gbproxy_tlli_state *src, *dst; if (to_bss) { src = &tlli_info->sgsn_tlli; dst = &tlli_info->tlli; } else { src = &tlli_info->tlli; dst = &tlli_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; } void gbproxy_touch_tlli(struct gbproxy_peer *peer, struct gbproxy_tlli_info *tlli_info, time_t now) { gbproxy_detach_tlli_info(peer, tlli_info); gbproxy_attach_tlli_info(peer, now, tlli_info); } static void gbproxy_unregister_tlli(struct gbproxy_peer *peer, struct gbproxy_tlli_info *tlli_info) { if (!tlli_info) return; if (tlli_info->tlli.ptmsi == GSM_RESERVED_TMSI && !tlli_info->imsi_len) { LOGP(DGPRS, LOGL_INFO, "Removing TLLI %08x from list (P-TMSI or IMSI are not set)\n", tlli_info->tlli.current); gbproxy_delete_tlli(peer, tlli_info); return; } tlli_info->tlli.current = 0; tlli_info->tlli.assigned = 0; tlli_info->sgsn_tlli.current = 0; tlli_info->sgsn_tlli.assigned = 0; tlli_info->is_deregistered = 1; return; } int gbproxy_check_tlli(struct gbproxy_peer *peer, struct gbproxy_tlli_info *tlli_info) { if (!peer->cfg->check_imsi) return 1; return tlli_info != NULL && tlli_info->enable_patching; } void gbproxy_assign_imsi(struct gbproxy_peer *peer, struct gbproxy_tlli_info *tlli_info, struct gprs_gb_parse_context *parse_ctx) { int enable_patching; struct gbproxy_tlli_info *other_tlli_info; /* Make sure that there is a second entry with the same IMSI */ other_tlli_info = gbproxy_find_tlli_by_imsi( peer, parse_ctx->imsi, parse_ctx->imsi_len); if (other_tlli_info && other_tlli_info != tlli_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_tlli_info->tlli.current, mi_buf); gbproxy_delete_tlli(peer, other_tlli_info); } /* Update the IMSI field */ gbproxy_update_tlli_info(tlli_info, parse_ctx->imsi, parse_ctx->imsi_len); /* Check, whether the IMSI matches */ enable_patching = gbproxy_check_imsi(peer, parse_ctx->imsi, parse_ctx->imsi_len); if (enable_patching >= 0) tlli_info->enable_patching = enable_patching; } 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_tllis(struct gbproxy_peer *peer, struct gbproxy_tlli_info *tlli_info) { struct gbproxy_tlli_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->enabled_tllis, list) { if (info == tlli_info) continue; if (!gbproxy_tlli_match(&tlli_info->tlli, &info->tlli) && !gbproxy_tlli_match(&tlli_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_tlli(peer, info); } } struct gbproxy_tlli_info *gbproxy_get_tlli_info_ul( struct gbproxy_peer *peer, struct gprs_gb_parse_context *parse_ctx) { struct gbproxy_tlli_info *tlli_info = NULL; if (parse_ctx->tlli_enc) tlli_info = gbproxy_find_tlli(peer, parse_ctx->tlli); if (!tlli_info && parse_ctx->imsi) tlli_info = gbproxy_find_tlli_by_imsi( peer, parse_ctx->imsi, parse_ctx->imsi_len); if (!tlli_info && parse_ctx->ptmsi_enc && !parse_ctx->old_raid_is_foreign) { uint32_t bss_ptmsi; if (!gprs_parse_mi_tmsi(parse_ctx->ptmsi_enc, GSM48_TMSI_LEN, &bss_ptmsi)) LOGP(DGPRS, LOGL_ERROR, "Failed to parse P-TMSI (TLLI is %08x)\n", parse_ctx->tlli); else tlli_info = gbproxy_find_tlli_by_ptmsi(peer, bss_ptmsi); } if (tlli_info) tlli_info->is_deregistered = 0; return tlli_info; } struct gbproxy_tlli_info *gbproxy_update_tlli_state_ul( struct gbproxy_peer *peer, time_t now, struct gprs_gb_parse_context *parse_ctx) { struct gbproxy_tlli_info *tlli_info; tlli_info = gbproxy_get_tlli_info_ul(peer, parse_ctx); if (parse_ctx->tlli_enc && parse_ctx->llc) { uint32_t sgsn_tlli; if (!tlli_info) { LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list\n", parse_ctx->tlli); tlli_info = gbproxy_tlli_info_alloc(peer); gbproxy_attach_tlli_info(peer, now, tlli_info); /* Setup TLLIs */ sgsn_tlli = gbproxy_make_sgsn_tlli(peer, tlli_info, parse_ctx->tlli); tlli_info->sgsn_tlli.current = sgsn_tlli; tlli_info->tlli.current = parse_ctx->tlli;; } else if (!tlli_info->tlli.current) { /* New TLLI (info found by IMSI or P-TMSI) */ tlli_info->tlli.current = parse_ctx->tlli; tlli_info->sgsn_tlli.current = gbproxy_make_sgsn_tlli(peer, tlli_info, parse_ctx->tlli); gbproxy_touch_tlli(peer, tlli_info, now); } else { sgsn_tlli = gbproxy_map_tlli(parse_ctx->tlli, tlli_info, 0); if (!sgsn_tlli) sgsn_tlli = gbproxy_make_sgsn_tlli(peer, tlli_info, parse_ctx->tlli); gbproxy_validate_tlli(&tlli_info->tlli, parse_ctx->tlli, 0); gbproxy_validate_tlli(&tlli_info->sgsn_tlli, sgsn_tlli, 0); gbproxy_touch_tlli(peer, tlli_info, now); } } else if (tlli_info) { gbproxy_touch_tlli(peer, tlli_info, now); } if (parse_ctx->imsi && tlli_info && tlli_info->imsi_len == 0) gbproxy_assign_imsi(peer, tlli_info, parse_ctx); return tlli_info; } struct gbproxy_tlli_info *gbproxy_update_tlli_state_dl( struct gbproxy_peer *peer, time_t now, struct gprs_gb_parse_context *parse_ctx) { struct gbproxy_tlli_info *tlli_info = NULL; if (parse_ctx->tlli_enc) tlli_info = gbproxy_find_tlli_by_sgsn_tlli(peer, parse_ctx->tlli); if (parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc && tlli_info) { /* A new P-TMSI has been signalled in the message, * register new TLLI */ uint32_t new_sgsn_ptmsi; uint32_t new_bss_ptmsi; if (!gprs_parse_mi_tmsi(parse_ctx->new_ptmsi_enc, GSM48_TMSI_LEN, &new_sgsn_ptmsi)) { LOGP(DGPRS, LOGL_ERROR, "Failed to parse new TLLI/PTMSI (current is %08x)\n", parse_ctx->tlli); return tlli_info; } 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 */ tlli_info->sgsn_tlli.ptmsi = new_sgsn_ptmsi; tlli_info->tlli.ptmsi = new_bss_ptmsi; } else if (parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc && !tlli_info && !peer->cfg->patch_ptmsi) { /* A new P-TMSI has been signalled in the message with an unknown * TLLI, create a new tlli_info */ /* TODO: Add a test case for this branch */ uint32_t new_ptmsi; if (!gprs_parse_mi_tmsi(parse_ctx->new_ptmsi_enc, GSM48_TMSI_LEN, &new_ptmsi)) { LOGP(DGPRS, LOGL_ERROR, "Failed to parse new PTMSI (TLLI is %08x)\n", parse_ctx->tlli); return tlli_info; } LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list (SGSN, new P-TMSI is %08x)\n", parse_ctx->tlli, new_ptmsi); tlli_info = gbproxy_tlli_info_alloc(peer); tlli_info->sgsn_tlli.current = parse_ctx->tlli;; tlli_info->tlli.current = parse_ctx->tlli;; tlli_info->sgsn_tlli.ptmsi = new_ptmsi; tlli_info->tlli.ptmsi = new_ptmsi; gbproxy_attach_tlli_info(peer, now, tlli_info); } else if (parse_ctx->tlli_enc && parse_ctx->llc && !tlli_info && !peer->cfg->patch_ptmsi) { /* Unknown SGSN TLLI, create a new tlli_info */ uint32_t new_ptmsi; tlli_info = gbproxy_tlli_info_alloc(peer); LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list (SGSN)\n", parse_ctx->tlli); gbproxy_attach_tlli_info(peer, now, tlli_info); /* Setup TLLIs */ tlli_info->sgsn_tlli.current = parse_ctx->tlli; tlli_info->tlli.current = parse_ctx->tlli; if (!parse_ctx->new_ptmsi_enc) return tlli_info; /* A new P-TMSI has been signalled in the message */ if (!gprs_parse_mi_tmsi(parse_ctx->new_ptmsi_enc, GSM48_TMSI_LEN, &new_ptmsi)) { LOGP(DGPRS, LOGL_ERROR, "Failed to parse new PTMSI (TLLI is %08x)\n", parse_ctx->tlli); return tlli_info; } LOGP(DGPRS, LOGL_INFO, "Assigning new P-TMSI %08x\n", new_ptmsi); /* Setup P-TMSIs */ tlli_info->sgsn_tlli.ptmsi = new_ptmsi; tlli_info->tlli.ptmsi = new_ptmsi; } else if (parse_ctx->tlli_enc && parse_ctx->llc && tlli_info) { uint32_t bss_tlli = gbproxy_map_tlli(parse_ctx->tlli, tlli_info, 1); gbproxy_validate_tlli(&tlli_info->sgsn_tlli, parse_ctx->tlli, 1); gbproxy_validate_tlli(&tlli_info->tlli, bss_tlli, 1); gbproxy_touch_tlli(peer, tlli_info, now); } else if (tlli_info) { gbproxy_touch_tlli(peer, tlli_info, now); } if (parse_ctx->imsi && tlli_info && tlli_info->imsi_len == 0) gbproxy_assign_imsi(peer, tlli_info, parse_ctx); return tlli_info; } void gbproxy_update_tlli_state_after( struct gbproxy_peer *peer, struct gbproxy_tlli_info *tlli_info, time_t now, struct gprs_gb_parse_context *parse_ctx) { if (parse_ctx->invalidate_tlli && tlli_info) { int keep_info = peer->cfg->keep_tlli_infos == GBPROX_KEEP_ALWAYS || (peer->cfg->keep_tlli_infos == GBPROX_KEEP_REATTACH && parse_ctx->await_reattach) || (peer->cfg->keep_tlli_infos == GBPROX_KEEP_IDENTIFIED && tlli_info->imsi_len > 0); if (keep_info) { LOGP(DGPRS, LOGL_INFO, "Unregistering TLLI %08x\n", tlli_info->tlli.current); gbproxy_unregister_tlli(peer, tlli_info); } else { LOGP(DGPRS, LOGL_INFO, "Removing TLLI %08x from list\n", tlli_info->tlli.current); gbproxy_delete_tlli(peer, tlli_info); } } else if (parse_ctx->to_bss && parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc && tlli_info) { /* A new PTMSI has been signaled in the message, * register new TLLI */ uint32_t new_sgsn_ptmsi = tlli_info->sgsn_tlli.ptmsi; uint32_t new_bss_ptmsi = tlli_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(&tlli_info->sgsn_tlli, peer, new_sgsn_tlli); gbproxy_reassign_tlli(&tlli_info->tlli, peer, new_bss_tlli); gbproxy_remove_matching_tllis(peer, tlli_info); } gbproxy_remove_stale_tllis(peer, now); }