diff options
author | Jacob Erlbeck <jerlbeck@sysmocom.de> | 2014-08-19 12:21:01 +0200 |
---|---|---|
committer | Holger Hans Peter Freyther <holger@moiji-mobile.com> | 2014-08-24 16:16:40 +0200 |
commit | 9114bee2424fa5a5e30261054573f9f78b5c5477 (patch) | |
tree | d19928d839a625ec509dd1606c1786c397db7111 /openbsc/src/gprs/gb_proxy_tlli.c | |
parent | 6bd7ded71ea3a1de200ad1190e7f7cbee6cae5f9 (diff) |
gbproxy: Refactor gb_proxy.c into several files
This patch moves several functions and declarations out of gb_proxy.c
to make them reusable by other components and to separate them by
context and task.
Counter enums (prefix is changed to gbproxy_):
enum gbprox_global_ctr -> gprs/gb_proxy.h
enum gbprox_peer_ctr -> gprs/gb_proxy.h
Generic Gb parsing (prefix is changed to gprs_gb_):
struct gbproxy_parse_context -> openbsc/gprs_gb_parse.h
gbprox_parse_dtap() -> gprs/gprs_gb_parse.c
gbprox_parse_llc() -> gprs/gprs_gb_parse.c
gbprox_parse_bssgp() -> gprs/gprs_gb_parse.c
gbprox_log_parse_context() -> gprs/gprs_gb_parse.c
*_shift(), *_match() -> gprs/gprs_gb_parse.c (no prefix)
gbprox_parse_gmm_* -> gprs/gprs_gb_parse.c (static)
gbprox_parse_gsm_* -> gprs/gprs_gb_parse.c (static)
MI testing/parsing (prefix gprs_ added):
is_mi_tmsi() -> gprs/gprs_utils.c
is_mi_imsi() -> gprs/gprs_utils.c
parse_mi_tmsi() -> gprs/gprs_utils.c
TLLI state handling (prefix is changed to gbproxy_):
gbprox_*tlli* -> gprs/gb_proxy_tlli.c
(except gbprox_patch_tlli, gbproxy_make_sgsn_tlli)
Message patching (prefix is changed to gbproxy_):
gbprox_*patch* -> gprs/gb_proxy_patch.c
gbprox_check_imsi -> gprs/gb_proxy_patch.c
Sponsored-by: On-Waves ehf
Diffstat (limited to 'openbsc/src/gprs/gb_proxy_tlli.c')
-rw-r--r-- | openbsc/src/gprs/gb_proxy_tlli.c | 544 |
1 files changed, 544 insertions, 0 deletions
diff --git a/openbsc/src/gprs/gb_proxy_tlli.c b/openbsc/src/gprs/gb_proxy_tlli.c new file mode 100644 index 000000000..ccd118ed5 --- /dev/null +++ b/openbsc/src/gprs/gb_proxy_tlli.c @@ -0,0 +1,544 @@ +/* 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 <openbsc/gb_proxy.h> + +#include <openbsc/gprs_utils.h> +#include <openbsc/gprs_gb_parse.h> + +#include <openbsc/gsm_data_shared.h> +#include <openbsc/debug.h> + +#include <osmocom/gsm/gsm_utils.h> + +#include <osmocom/core/rate_ctr.h> +#include <osmocom/core/talloc.h> + +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_mi( + struct gbproxy_peer *peer, + const uint8_t *mi_data, + size_t mi_data_len) +{ + struct gbproxy_tlli_info *tlli_info; + struct gbproxy_patch_state *state = &peer->patch_state; + + if (!gprs_is_mi_imsi(mi_data, mi_data_len)) + return NULL; + + llist_for_each_entry(tlli_info, &state->enabled_tllis, list) { + if (tlli_info->mi_data_len != mi_data_len) + continue; + if (memcmp(tlli_info->mi_data, mi_data, mi_data_len) != 0) + continue; + + return tlli_info; + } + + return NULL; +} + +void gbproxy_delete_tlli(struct gbproxy_peer *peer, + struct gbproxy_tlli_info *tlli_info) +{ + struct gbproxy_patch_state *state = &peer->patch_state; + + 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)); +} + +static 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; +} + +static 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; + + return tlli_info; +} + +static 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; +} + +static 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->mi_data_len = imsi_len; + tlli_info->mi_data = + talloc_realloc_size(tlli_info, tlli_info->mi_data, imsi_len); + OSMO_ASSERT(tlli_info->mi_data != NULL); + memcpy(tlli_info->mi_data, 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); +} + +struct gbproxy_tlli_info *gbproxy_register_tlli( + struct gbproxy_peer *peer, uint32_t tlli, + const uint8_t *imsi, size_t imsi_len, time_t now) +{ + struct gbproxy_tlli_info *tlli_info; + int enable_patching = -1; + int tlli_already_known; + + /* Check, whether the IMSI matches */ + if (gprs_is_mi_imsi(imsi, imsi_len)) { + enable_patching = gbproxy_check_imsi(peer, imsi, imsi_len); + if (enable_patching < 0) + return NULL; + } + + tlli_info = gbproxy_find_tlli(peer, tlli); + + if (!tlli_info) { + tlli_info = gbproxy_find_tlli_by_mi(peer, imsi, imsi_len); + + if (tlli_info) { + /* TLLI has changed somehow, adjust it */ + LOGP(DGPRS, LOGL_INFO, + "The TLLI has changed from %08x to %08x\n", + tlli_info->tlli.current, tlli); + tlli_info->tlli.current = tlli; + } + } + + if (!tlli_info) { + tlli_info = gbproxy_tlli_info_alloc(peer); + tlli_info->tlli.current = tlli; + } else { + gbproxy_detach_tlli_info(peer, tlli_info); + tlli_already_known = 1; + } + + OSMO_ASSERT(tlli_info != NULL); + + if (!tlli_already_known) + LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list\n", tlli); + + gbproxy_attach_tlli_info(peer, now, tlli_info); + gbproxy_update_tlli_info(tlli_info, imsi, imsi_len); + + if (enable_patching >= 0) + tlli_info->enable_patching = enable_patching; + + return tlli_info; +} + +static void gbproxy_unregister_tlli(struct gbproxy_peer *peer, uint32_t tlli) +{ + struct gbproxy_tlli_info *tlli_info; + + tlli_info = gbproxy_find_tlli(peer, tlli); + if (tlli_info) { + LOGP(DGPRS, LOGL_INFO, + "Removing TLLI %08x from list\n", + tlli); + gbproxy_delete_tlli(peer, tlli_info); + } +} + +int gbproxy_check_tlli(struct gbproxy_peer *peer, uint32_t tlli) +{ + struct gbproxy_tlli_info *tlli_info; + + LOGP(DGPRS, LOGL_INFO, "Checking TLLI %08x, class: %d\n", + tlli, gprs_tlli_type(tlli)); + + if (!peer->cfg->check_imsi) + return 1; + + tlli_info = gbproxy_find_tlli(peer, tlli); + + return tlli_info != NULL && tlli_info->enable_patching; +} + +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 = NULL; + + if (parse_ctx->tlli_enc) + tlli_info = gbproxy_find_tlli(peer, parse_ctx->tlli); + + if (parse_ctx->tlli_enc && parse_ctx->llc) { + uint32_t sgsn_tlli; + if (!tlli_info) { + tlli_info = + gbproxy_register_tlli(peer, parse_ctx->tlli, + parse_ctx->imsi, + parse_ctx->imsi_len, now); + /* Setup TLLIs */ + sgsn_tlli = gbproxy_make_sgsn_tlli(peer, tlli_info, + parse_ctx->tlli); + tlli_info->sgsn_tlli.current = sgsn_tlli; + } 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->mi_data_len == 0) { + int enable_patching; + 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; + } + + 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) { + /* A new PTMSI has been signaled in the message, + * register new TLLI */ + uint32_t new_sgsn_ptmsi; + uint32_t new_sgsn_tlli; + uint32_t new_bss_ptmsi; + uint32_t new_bss_tlli = 0; + 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_sgsn_tlli = gprs_tmsi2tlli(new_sgsn_ptmsi, TLLI_LOCAL); + new_bss_ptmsi = gbproxy_make_bss_ptmsi(peer, new_sgsn_ptmsi); + if (new_bss_ptmsi != GSM_RESERVED_TMSI) + new_bss_tlli = gprs_tmsi2tlli(new_bss_ptmsi, TLLI_LOCAL); + LOGP(DGPRS, LOGL_INFO, + "Got new TLLI(PTMSI) %08x(%08x) from SGSN, using %08x(%08x)\n", + new_sgsn_tlli, new_sgsn_ptmsi, new_bss_tlli, new_bss_ptmsi); + if (tlli_info) { + gbproxy_reassign_tlli(&tlli_info->sgsn_tlli, + peer, new_sgsn_tlli); + gbproxy_reassign_tlli(&tlli_info->tlli, + peer, new_bss_tlli); + gbproxy_touch_tlli(peer, tlli_info, now); + } else { + tlli_info = gbproxy_tlli_info_alloc(peer); + LOGP(DGPRS, LOGL_INFO, + "Adding TLLI %08x to list (SGSN, new P-TMSI)\n", + new_sgsn_tlli); + + gbproxy_attach_tlli_info(peer, now, tlli_info); + /* Setup TLLIs */ + tlli_info->sgsn_tlli.current = new_sgsn_tlli; + } + /* 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->llc && !tlli_info) { + /* Unknown SGSN TLLI */ + 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; + if (peer->cfg->patch_ptmsi) { + /* TODO: We don't know the local TLLI here, perhaps add + * a workaround that derives a PTMSI from the SGSN TLLI + * and use that to get the missing values. This may + * only happen when the gbproxy has been restarted or a + * tlli_info has been discarded due to age or queue + * length. + */ + tlli_info->tlli.current = 0; + } else { + tlli_info->tlli.current = tlli_info->sgsn_tlli.current; + } + } 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->mi_data_len == 0) { + int enable_patching; + 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; + } + + 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) + gbproxy_unregister_tlli(peer, parse_ctx->tlli); + + gbproxy_remove_stale_tllis(peer, now); +} + + |