From 9114bee2424fa5a5e30261054573f9f78b5c5477 Mon Sep 17 00:00:00 2001 From: Jacob Erlbeck Date: Tue, 19 Aug 2014 12:21:01 +0200 Subject: 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 --- openbsc/src/gprs/Makefile.am | 3 +- openbsc/src/gprs/gb_proxy.c | 2072 ++++--------------------------------- openbsc/src/gprs/gb_proxy_patch.c | 475 +++++++++ openbsc/src/gprs/gb_proxy_tlli.c | 544 ++++++++++ openbsc/src/gprs/gb_proxy_vty.c | 10 +- openbsc/src/gprs/gprs_gb_parse.c | 642 ++++++++++++ openbsc/src/gprs/gprs_utils.c | 39 + 7 files changed, 1899 insertions(+), 1886 deletions(-) create mode 100644 openbsc/src/gprs/gb_proxy_patch.c create mode 100644 openbsc/src/gprs/gb_proxy_tlli.c create mode 100644 openbsc/src/gprs/gprs_gb_parse.c (limited to 'openbsc/src/gprs') diff --git a/openbsc/src/gprs/Makefile.am b/openbsc/src/gprs/Makefile.am index bad38c8fc..4d0c9cdb2 100644 --- a/openbsc/src/gprs/Makefile.am +++ b/openbsc/src/gprs/Makefile.am @@ -14,7 +14,8 @@ bin_PROGRAMS = osmo-gbproxy endif osmo_gbproxy_SOURCES = gb_proxy.c gb_proxy_main.c gb_proxy_vty.c \ - gprs_llc_parse.c crc24.c gprs_utils.c + gb_proxy_patch.c gb_proxy_tlli.c \ + gprs_gb_parse.c gprs_llc_parse.c crc24.c gprs_utils.c osmo_gbproxy_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ $(OSMO_LIBS) diff --git a/openbsc/src/gprs/gb_proxy.c b/openbsc/src/gprs/gb_proxy.c index 86ae0e821..7a1508ee7 100644 --- a/openbsc/src/gprs/gb_proxy.c +++ b/openbsc/src/gprs/gb_proxy.c @@ -42,6 +42,7 @@ #include #include +#include #include #include @@ -49,21 +50,6 @@ #include #include -enum gbprox_global_ctr { - GBPROX_GLOB_CTR_INV_BVCI, - GBPROX_GLOB_CTR_INV_LAI, - GBPROX_GLOB_CTR_INV_RAI, - GBPROX_GLOB_CTR_INV_NSEI, - GBPROX_GLOB_CTR_PROTO_ERR_BSS, - GBPROX_GLOB_CTR_PROTO_ERR_SGSN, - GBPROX_GLOB_CTR_NOT_SUPPORTED_BSS, - GBPROX_GLOB_CTR_NOT_SUPPORTED_SGSN, - GBPROX_GLOB_CTR_RESTART_RESET_SGSN, - GBPROX_GLOB_CTR_TX_ERR_SGSN, - GBPROX_GLOB_CTR_OTHER_ERR, - GBPROX_GLOB_CTR_PATCH_PEER_ERR, -}; - static const struct rate_ctr_desc global_ctr_description[] = { { "inv-bvci", "Invalid BVC Identifier " }, { "inv-lai", "Invalid Location Area Identifier" }, @@ -86,27 +72,6 @@ static const struct rate_ctr_group_desc global_ctrg_desc = { .ctr_desc = global_ctr_description, }; -enum gbprox_peer_ctr { - GBPROX_PEER_CTR_BLOCKED, - GBPROX_PEER_CTR_UNBLOCKED, - GBPROX_PEER_CTR_DROPPED, - GBPROX_PEER_CTR_INV_NSEI, - GBPROX_PEER_CTR_TX_ERR, - GBPROX_PEER_CTR_RAID_PATCHED_BSS, - GBPROX_PEER_CTR_RAID_PATCHED_SGSN, - GBPROX_PEER_CTR_APN_PATCHED, - GBPROX_PEER_CTR_TLLI_PATCHED_BSS, - GBPROX_PEER_CTR_TLLI_PATCHED_SGSN, - GBPROX_PEER_CTR_PTMSI_PATCHED_BSS, - GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN, - GBPROX_PEER_CTR_PATCH_CRYPT_ERR, - GBPROX_PEER_CTR_PATCH_ERR, - GBPROX_PEER_CTR_ATTACH_REQS, - GBPROX_PEER_CTR_ATTACH_REJS, - GBPROX_PEER_CTR_TLLI_UNKNOWN, - GBPROX_PEER_CTR_TLLI_CACHE_SIZE, -}; - static const struct rate_ctr_desc peer_ctr_description[] = { { "blocked", "BVC Block " }, { "unblocked", "BVC Unblock " }, @@ -135,7 +100,6 @@ static const struct rate_ctr_group_desc peer_ctrg_desc = { .ctr_desc = peer_ctr_description, }; -static void gbprox_delete_tllis(struct gbproxy_peer *peer); /* Find the gbprox_peer by its BVCI */ static struct gbproxy_peer *peer_by_bvci(struct gbproxy_config *cfg, uint16_t bvci) @@ -229,7 +193,7 @@ void gbproxy_peer_free(struct gbproxy_peer *peer) { llist_del(&peer->list); - gbprox_delete_tllis(peer); + gbproxy_delete_tllis(peer); rate_ctr_group_free(peer->ctrg); peer->ctrg = NULL; @@ -244,1911 +208,259 @@ static void strip_ns_hdr(struct msgb *msg) msgb_pull(msg, strip_len); } -/* TODO: Move shift functions to libosmocore */ - -int v_fixed_shift(uint8_t **data, size_t *data_len, - size_t len, uint8_t **value) -{ - if (len > *data_len) - goto fail; - - if (value) - *value = *data; - - *data += len; - *data_len -= len; - - return len; - -fail: - *data += *data_len; - *data_len = 0; - return -1; -} - -int tv_fixed_match(uint8_t **data, size_t *data_len, - uint8_t tag, size_t len, - uint8_t **value) -{ - size_t ie_len; - - if (*data_len == 0) - goto fail; - - if ((*data)[0] != tag) - return 0; - - if (len > *data_len - 1) - goto fail; - - if (value) - *value = *data + 1; - - ie_len = len + 1; - *data += ie_len; - *data_len -= ie_len; - - return ie_len; - -fail: - *data += *data_len; - *data_len = 0; - return -1; -} - -int tlv_match(uint8_t **data, size_t *data_len, - uint8_t tag, uint8_t **value, size_t *value_len) +/* update peer according to the BSS message */ +static void gbprox_update_current_raid(uint8_t *raid_enc, + struct gbproxy_peer *peer, + const char *log_text) { - size_t len; - size_t ie_len; - - if (*data_len < 2) - goto fail; - - if ((*data)[0] != tag) - return 0; - - len = (*data)[1]; - if (len > *data_len - 2) - goto fail; + struct gbproxy_patch_state *state = &peer->patch_state; + const int old_local_mcc = state->local_mcc; + const int old_local_mnc = state->local_mnc; + struct gprs_ra_id raid; - if (value) - *value = *data + 2; - if (value_len) - *value_len = len; + if (!raid_enc) + return; - ie_len = len + 2; + gsm48_parse_ra(&raid, raid_enc); - *data += ie_len; - *data_len -= ie_len; + /* save source side MCC/MNC */ + if (!peer->cfg->core_mcc || raid.mcc == peer->cfg->core_mcc) { + state->local_mcc = 0; + } else { + state->local_mcc = raid.mcc; + } - return ie_len; + if (!peer->cfg->core_mnc || raid.mnc == peer->cfg->core_mnc) { + state->local_mnc = 0; + } else { + state->local_mnc = raid.mnc; + } -fail: - *data += *data_len; - *data_len = 0; - return -1; + if (old_local_mcc != state->local_mcc || + old_local_mnc != state->local_mnc) + LOGP(DGPRS, LOGL_NOTICE, + "Patching RAID %sactivated, msg: %s, " + "local: %d-%d, core: %d-%d\n", + state->local_mcc || state->local_mnc ? + "" : "de", + log_text, + state->local_mcc, state->local_mnc, + peer->cfg->core_mcc, peer->cfg->core_mnc); } -int lv_shift(uint8_t **data, size_t *data_len, - uint8_t **value, size_t *value_len) +struct gbproxy_peer *peer_by_bssgp_tlv(struct gbproxy_config *cfg, struct tlv_parsed *tp) { - size_t len; - size_t ie_len; - - if (*data_len < 1) - goto fail; - - len = (*data)[0]; - if (len > *data_len - 1) - goto fail; + if (TLVP_PRESENT(tp, BSSGP_IE_BVCI)) { + uint16_t bvci; - if (value) - *value = *data + 1; - if (value_len) - *value_len = len; + bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI)); + if (bvci >= 2) + return peer_by_bvci(cfg, bvci); + } - ie_len = len + 1; - *data += ie_len; - *data_len -= ie_len; + if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) { + uint8_t *rai = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA); + /* Only compare LAC part, since MCC/MNC are possibly patched. + * Since the LAC of different BSS must be different when + * MCC/MNC are patched, collisions shouldn't happen. */ + return peer_by_lac(cfg, rai); + } - return ie_len; + if (TLVP_PRESENT(tp, BSSGP_IE_LOCATION_AREA)) { + uint8_t *lai = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA); + return peer_by_lac(cfg, lai); + } -fail: - *data += *data_len; - *data_len = 0; - return -1; + return NULL; } -/* GSM 04.08, 10.5.1.4 */ -static int is_mi_tmsi(const uint8_t *value, size_t value_len) +uint32_t gbproxy_make_bss_ptmsi(struct gbproxy_peer *peer, + uint32_t sgsn_ptmsi) { - if (value_len != GSM48_TMSI_LEN) - return 0; + uint32_t bss_ptmsi; + if (!peer->cfg->patch_ptmsi) { + bss_ptmsi = sgsn_ptmsi; + } else { + do { + bss_ptmsi = rand_r(&peer->cfg->bss_ptmsi_state); + bss_ptmsi = bss_ptmsi | 0xC0000000; - if (!value || (value[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_TMSI) - return 0; + if (gbproxy_find_tlli_by_ptmsi(peer, bss_ptmsi)) + bss_ptmsi = GSM_RESERVED_TMSI; + } while (bss_ptmsi == GSM_RESERVED_TMSI); + } - return 1; + return bss_ptmsi; } -/* GSM 04.08, 10.5.1.4 */ -static int is_mi_imsi(const uint8_t *value, size_t value_len) +uint32_t gbproxy_make_sgsn_tlli(struct gbproxy_peer *peer, + struct gbproxy_tlli_info *tlli_info, + uint32_t bss_tlli) { - if (value_len == 0) - return 0; - - if (!value || (value[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_IMSI) - return 0; + uint32_t sgsn_tlli; + if (!peer->cfg->patch_ptmsi) { + sgsn_tlli = bss_tlli; + } else if (tlli_info->sgsn_tlli.ptmsi != GSM_RESERVED_TMSI) { + sgsn_tlli = gprs_tmsi2tlli(tlli_info->sgsn_tlli.ptmsi, + TLLI_FOREIGN); + } else { + do { + /* create random TLLI, 0b01111xxx... */ + sgsn_tlli = rand_r(&peer->cfg->sgsn_tlli_state); + sgsn_tlli = (sgsn_tlli & 0x7fffffff) | 0x78000000; - return 1; + if (gbproxy_find_tlli_by_sgsn_tlli(peer, sgsn_tlli)) + sgsn_tlli = 0; + } while (!sgsn_tlli); + } + return sgsn_tlli; } -static int parse_mi_tmsi(const uint8_t *value, size_t value_len, uint32_t *tmsi) +/* patch BSSGP message */ +static void gbprox_process_bssgp_ul(struct gbproxy_config *cfg, + struct msgb *msg, + struct gbproxy_peer *peer) { - uint32_t tmsi_be; - - if (!is_mi_tmsi(value, value_len)) - return 0; - - memcpy(&tmsi_be, value + 1, sizeof(tmsi_be)); - - *tmsi = ntohl(tmsi_be); - return 1; -} - -struct gbproxy_parse_context { - /* Pointer to protocol specific parts */ - struct gsm48_hdr *g48_hdr; - struct bssgp_normal_hdr *bgp_hdr; - struct bssgp_ud_hdr *bud_hdr; - uint8_t *bssgp_data; - size_t bssgp_data_len; - uint8_t *llc; - size_t llc_len; - - /* Extracted information */ - struct gprs_llc_hdr_parsed llc_hdr_parsed; - struct tlv_parsed bssgp_tp; - int to_bss; - uint8_t *tlli_enc; - uint8_t *imsi; - size_t imsi_len; - uint8_t *apn_ie; - size_t apn_ie_len; - uint8_t *ptmsi_enc; - uint8_t *new_ptmsi_enc; - uint8_t *raid_enc; - uint8_t *old_raid_enc; - uint8_t *bssgp_raid_enc; - uint8_t *bssgp_ptimsi; - - /* General info */ - const char *llc_msg_name; - int invalidate_tlli; - int need_decryption; - uint32_t tlli; - int pdu_type; - int old_raid_matches; -}; + struct gprs_gb_parse_context parse_ctx = {0}; + int rc; + int len_change = 0; + time_t now; + struct gbproxy_tlli_info *tlli_info = NULL; -struct gbproxy_tlli_info *gbprox_find_tlli(struct gbproxy_peer *peer, - uint32_t tlli) -{ - struct gbproxy_tlli_info *tlli_info; - struct gbproxy_patch_state *state = &peer->patch_state; + if (!cfg->core_mcc && !cfg->core_mnc && !cfg->core_apn) + return; - llist_for_each_entry(tlli_info, &state->enabled_tllis, list) - if (tlli_info->tlli.current == tlli || - tlli_info->tlli.assigned == tlli) - return tlli_info; + parse_ctx.to_bss = 0; - return NULL; -} + /* Parse BSSGP/LLC */ + rc = gprs_gb_parse_bssgp(msgb_bssgph(msg), msgb_bssgp_len(msg), + &parse_ctx); -static struct gbproxy_tlli_info *gbprox_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; + if (!rc) { + if (!parse_ctx.need_decryption) { + LOGP(DGPRS, LOGL_ERROR, + "NSEI=%u(BSS) patching: " + "failed to parse BSSGP/GMM message\n", + msgb_nsei(msg)); + return; + } + } - llist_for_each_entry(tlli_info, &state->enabled_tllis, list) - if (tlli_info->tlli.ptmsi == ptmsi) - return tlli_info; + /* Get peer */ + if (!peer && msgb_bvci(msg) >= 2) + peer = peer_by_bvci(cfg, msgb_bvci(msg)); - return NULL; -} + if (!peer) + peer = gbprox_peer_by_nsei(cfg, msgb_nsei(msg)); -struct gbproxy_tlli_info *gbprox_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; + if (!peer) + peer = peer_by_bssgp_tlv(cfg, &parse_ctx.bssgp_tp); - 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; + if (!peer) { + LOGP(DLLC, LOGL_INFO, + "NSEI=%d(BSS) patching: didn't find peer for message, " + "PDU %d\n", + msgb_nsei(msg), parse_ctx.pdu_type); + /* Increment counter */ + rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_PATCH_PEER_ERR]); + return; + } - return NULL; -} + now = time(NULL); -struct gbproxy_tlli_info *gbprox_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 (parse_ctx.bssgp_raid_enc && parse_ctx.old_raid_enc && + memcmp(parse_ctx.bssgp_raid_enc, parse_ctx.old_raid_enc, 6) == 0) + parse_ctx.old_raid_matches = 1; - if (!is_mi_imsi(mi_data, mi_data_len)) - return NULL; + gbprox_update_current_raid(parse_ctx.bssgp_raid_enc, peer, + parse_ctx.llc_msg_name); - 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; + if (parse_ctx.g48_hdr) { + switch (parse_ctx.g48_hdr->msg_type) { + case GSM48_MT_GMM_ATTACH_REQ: + rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_REQS]); + break; - return tlli_info; + default: + break; + } } - return NULL; -} - -void gbprox_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; -} + gprs_gb_log_parse_context(&parse_ctx, "BSSGP"); -static void gbprox_delete_tllis(struct gbproxy_peer *peer) -{ - struct gbproxy_tlli_info *tlli_info, *nxt; - struct gbproxy_patch_state *state = &peer->patch_state; + tlli_info = gbproxy_update_tlli_state_ul(peer, now, &parse_ctx); - llist_for_each_entry_safe(tlli_info, nxt, &state->enabled_tllis, list) - gbprox_delete_tlli(peer, tlli_info); + gbproxy_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg), + peer, tlli_info, &len_change, &parse_ctx); - OSMO_ASSERT(state->enabled_tllis_count == 0); - OSMO_ASSERT(llist_empty(&state->enabled_tllis)); -} + gbproxy_update_tlli_state_after(peer, tlli_info, now, &parse_ctx); -void gbprox_clear_patch_filter(struct gbproxy_config *cfg) -{ - if (cfg->check_imsi) { - regfree(&cfg->imsi_re_comp); - cfg->check_imsi = 0; - } + return; } -int gbprox_set_patch_filter(struct gbproxy_config *cfg, const char *filter, - const char **err_msg) +/* patch BSSGP message to use core_mcc/mnc on the SGSN side */ +static void gbprox_process_bssgp_dl(struct gbproxy_config *cfg, + struct msgb *msg, + struct gbproxy_peer *peer) { - static char err_buf[300]; + struct gprs_gb_parse_context parse_ctx = {0}; int rc; + int len_change = 0; + time_t now; + struct gbproxy_tlli_info *tlli_info = NULL; - gbprox_clear_patch_filter(cfg); - - if (!filter) - return 0; - - rc = regcomp(&cfg->imsi_re_comp, filter, - REG_EXTENDED | REG_NOSUB | REG_ICASE); + parse_ctx.to_bss = 1; - if (rc == 0) { - cfg->check_imsi = 1; - return 0; - } + rc = gprs_gb_parse_bssgp(msgb_bssgph(msg), msgb_bssgp_len(msg), + &parse_ctx); - if (err_msg) { - regerror(rc, &cfg->imsi_re_comp, - err_buf, sizeof(err_buf)); - *err_msg = err_buf; + if (!rc) { + if (!parse_ctx.need_decryption) { + LOGP(DGPRS, LOGL_ERROR, + "NSEI=%u(SGSN) patching: " + "failed to parse BSSGP/GMM message\n", + msgb_nsei(msg)); + return; + } } - return -1; -} - -int gbprox_check_imsi(struct gbproxy_peer *peer, - const uint8_t *imsi, size_t imsi_len) -{ - char mi_buf[200]; - int rc; - - if (!peer->cfg->check_imsi) - return 1; - - rc = is_mi_imsi(imsi, imsi_len); - if (rc > 0) - rc = gsm48_mi_to_string(mi_buf, sizeof(mi_buf), imsi, imsi_len); - if (rc <= 0) { - LOGP(DGPRS, LOGL_NOTICE, "Invalid IMSI %s\n", - osmo_hexdump(imsi, imsi_len)); - return -1; - } + if (!peer && msgb_bvci(msg) >= 2) + peer = peer_by_bvci(cfg, msgb_bvci(msg)); - LOGP(DGPRS, LOGL_DEBUG, "Checking IMSI '%s' (%d)\n", mi_buf, rc); + if (!peer) + peer = peer_by_bssgp_tlv(cfg, &parse_ctx.bssgp_tp); - rc = regexec(&peer->cfg->imsi_re_comp, mi_buf, 0, NULL, 0); - if (rc == REG_NOMATCH) { - LOGP(DGPRS, LOGL_INFO, - "IMSI '%s' doesn't match pattern '%s'\n", - mi_buf, peer->cfg->match_re); - return 0; + if (!peer) { + LOGP(DLLC, LOGL_INFO, + "NSEI=%d(SGSN) patching: didn't find peer for message, " + "PDU %d\n", + msgb_nsei(msg), parse_ctx.pdu_type); + /* Increment counter */ + rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_PATCH_PEER_ERR]); + return; } - return 1; -} - -static void gbprox_attach_tlli_info(struct gbproxy_peer *peer, time_t now, - struct gbproxy_tlli_info *tlli_info) -{ - struct gbproxy_patch_state *state = &peer->patch_state; + now = time(NULL); - tlli_info->timestamp = now; - llist_add(&tlli_info->list, &state->enabled_tllis); - state->enabled_tllis_count += 1; + if (parse_ctx.g48_hdr) { + switch (parse_ctx.g48_hdr->msg_type) { + case GSM48_MT_GMM_ATTACH_REJ: + rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_REJS]); + break; - peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current = - state->enabled_tllis_count; -} + default: + break; + } + } -int gbprox_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); - - gbprox_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); - - gbprox_delete_tlli(peer, tlli_info); - deleted_count += 1; - } - - return deleted_count; -} - -static struct gbproxy_tlli_info *gbprox_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 gbprox_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 gbprox_update_tlli_info(struct gbproxy_tlli_info *tlli_info, - const uint8_t *imsi, size_t imsi_len) -{ - if (!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 gbprox_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; -} - -static uint32_t gbprox_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 gbprox_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 gbprox_touch_tlli(struct gbproxy_peer *peer, - struct gbproxy_tlli_info *tlli_info, time_t now) -{ - gbprox_detach_tlli_info(peer, tlli_info); - gbprox_attach_tlli_info(peer, now, tlli_info); -} - -struct gbproxy_tlli_info *gbprox_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 (is_mi_imsi(imsi, imsi_len)) { - enable_patching = gbprox_check_imsi(peer, imsi, imsi_len); - if (enable_patching < 0) - return NULL; - } - - tlli_info = gbprox_find_tlli(peer, tlli); - - if (!tlli_info) { - tlli_info = gbprox_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 = gbprox_tlli_info_alloc(peer); - tlli_info->tlli.current = tlli; - } else { - gbprox_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); - - gbprox_attach_tlli_info(peer, now, tlli_info); - gbprox_update_tlli_info(tlli_info, imsi, imsi_len); - - if (enable_patching >= 0) - tlli_info->enable_patching = enable_patching; - - return tlli_info; -} - -static void gbprox_unregister_tlli(struct gbproxy_peer *peer, uint32_t tlli) -{ - struct gbproxy_tlli_info *tlli_info; - - tlli_info = gbprox_find_tlli(peer, tlli); - if (tlli_info) { - LOGP(DGPRS, LOGL_INFO, - "Removing TLLI %08x from list\n", - tlli); - gbprox_delete_tlli(peer, tlli_info); - } -} - -static int gbprox_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 = gbprox_find_tlli(peer, tlli); - - return tlli_info != NULL && tlli_info->enable_patching; -} - -/* check whether patching is enabled at this level */ -static int patching_is_enabled(struct gbproxy_peer *peer, - enum gbproxy_patch_mode need_at_least) -{ - enum gbproxy_patch_mode patch_mode = peer->cfg->patch_mode; - if (patch_mode == GBPROX_PATCH_DEFAULT) - patch_mode = GBPROX_PATCH_LLC; - - return need_at_least <= patch_mode; -} - -/* check whether patching is enabled at this level */ -static int patching_is_required(struct gbproxy_peer *peer, - enum gbproxy_patch_mode need_at_least) -{ - return need_at_least <= peer->cfg->patch_mode; -} - -/* update peer according to the BSS message */ -static void gbprox_update_current_raid(uint8_t *raid_enc, - struct gbproxy_peer *peer, - const char *log_text) -{ - struct gbproxy_patch_state *state = &peer->patch_state; - const int old_local_mcc = state->local_mcc; - const int old_local_mnc = state->local_mnc; - struct gprs_ra_id raid; - - if (!raid_enc) - return; - - gsm48_parse_ra(&raid, raid_enc); - - /* save source side MCC/MNC */ - if (!peer->cfg->core_mcc || raid.mcc == peer->cfg->core_mcc) { - state->local_mcc = 0; - } else { - state->local_mcc = raid.mcc; - } - - if (!peer->cfg->core_mnc || raid.mnc == peer->cfg->core_mnc) { - state->local_mnc = 0; - } else { - state->local_mnc = raid.mnc; - } - - if (old_local_mcc != state->local_mcc || - old_local_mnc != state->local_mnc) - LOGP(DGPRS, LOGL_NOTICE, - "Patching RAID %sactivated, msg: %s, " - "local: %d-%d, core: %d-%d\n", - state->local_mcc || state->local_mnc ? - "" : "de", - log_text, - state->local_mcc, state->local_mnc, - peer->cfg->core_mcc, peer->cfg->core_mnc); -} - -/* patch RA identifier in place */ -static void gbprox_patch_raid(uint8_t *raid_enc, struct gbproxy_peer *peer, - int to_bss, const char *log_text) -{ - struct gbproxy_patch_state *state = &peer->patch_state; - int old_mcc; - int old_mnc; - struct gprs_ra_id raid; - - gsm48_parse_ra(&raid, raid_enc); - - old_mcc = raid.mcc; - old_mnc = raid.mnc; - - if (!to_bss) { - /* BSS -> SGSN */ - if (state->local_mcc) - raid.mcc = peer->cfg->core_mcc; - - if (state->local_mnc) - raid.mnc = peer->cfg->core_mnc; - } else { - /* SGSN -> BSS */ - if (state->local_mcc) - raid.mcc = state->local_mcc; - - if (state->local_mnc) - raid.mnc = state->local_mnc; - } - - if (state->local_mcc || state->local_mnc) { - enum gbprox_peer_ctr counter = - to_bss ? - GBPROX_PEER_CTR_RAID_PATCHED_SGSN : - GBPROX_PEER_CTR_RAID_PATCHED_BSS; - - LOGP(DGPRS, LOGL_DEBUG, - "Patching %s to %s: " - "%d-%d-%d-%d -> %d-%d-%d-%d\n", - log_text, - to_bss ? "BSS" : "SGSN", - old_mcc, old_mnc, raid.lac, raid.rac, - raid.mcc, raid.mnc, raid.lac, raid.rac); - - gsm48_construct_ra(raid_enc, &raid); - rate_ctr_inc(&peer->ctrg->ctr[counter]); - } -} - -static void gbprox_patch_apn_ie(struct msgb *msg, - uint8_t *apn_ie, size_t apn_ie_len, - struct gbproxy_peer *peer, - size_t *new_apn_ie_len, const char *log_text) -{ - struct apn_ie_hdr { - uint8_t iei; - uint8_t apn_len; - uint8_t apn[0]; - } *hdr = (void *)apn_ie; - - size_t apn_len = hdr->apn_len; - uint8_t *apn = hdr->apn; - - 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) { - char str1[110]; - /* Remove the IE */ - LOGP(DGPRS, LOGL_DEBUG, - "Patching %s to SGSN: Removing APN '%s'\n", - log_text, - gprs_apn_to_str(str1, apn, apn_len)); - - *new_apn_ie_len = 0; - gprs_msgb_resize_area(msg, apn_ie, apn_ie_len, 0); - } else { - /* Resize the IE */ - char str1[110]; - char str2[110]; - - OSMO_ASSERT(peer->cfg->core_apn_size <= 100); - - LOGP(DGPRS, LOGL_DEBUG, - "Patching %s to SGSN: " - "Replacing APN '%s' -> '%s'\n", - log_text, - gprs_apn_to_str(str1, apn, apn_len), - gprs_apn_to_str(str2, peer->cfg->core_apn, - peer->cfg->core_apn_size)); - - *new_apn_ie_len = peer->cfg->core_apn_size + 2; - gprs_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; - } - - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_APN_PATCHED]); -} - -static int gbprox_patch_tlli(uint8_t *tlli_enc, - struct gbproxy_peer *peer, - uint32_t new_tlli, - int to_bss, const char *log_text) -{ - uint32_t tlli_be; - uint32_t tlli; - enum gbprox_peer_ctr counter = - to_bss ? - GBPROX_PEER_CTR_TLLI_PATCHED_SGSN : - GBPROX_PEER_CTR_TLLI_PATCHED_BSS; - - memcpy(&tlli_be, tlli_enc, sizeof(tlli_be)); - tlli = ntohl(tlli_be); - - if (tlli == new_tlli) - return 0; - - LOGP(DGPRS, LOGL_DEBUG, - "Patching %ss: " - "Replacing %08x -> %08x\n", - log_text, tlli, new_tlli); - - tlli_be = htonl(new_tlli); - memcpy(tlli_enc, &tlli_be, sizeof(tlli_be)); - - rate_ctr_inc(&peer->ctrg->ctr[counter]); - - return 1; -} - -static int gbprox_patch_ptmsi(uint8_t *ptmsi_enc, - struct gbproxy_peer *peer, - uint32_t new_ptmsi, - int to_bss, const char *log_text) -{ - uint32_t ptmsi_be; - uint32_t ptmsi; - enum gbprox_peer_ctr counter = - to_bss ? - GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN : - GBPROX_PEER_CTR_PTMSI_PATCHED_BSS; - memcpy(&ptmsi_be, ptmsi_enc + 1, sizeof(ptmsi_be)); - ptmsi = ntohl(ptmsi_be); - - if (ptmsi == new_ptmsi) - return 0; - - LOGP(DGPRS, LOGL_DEBUG, - "Patching %ss: " - "Replacing %08x -> %08x\n", - log_text, ptmsi, new_ptmsi); - - ptmsi_be = htonl(new_ptmsi); - memcpy(ptmsi_enc + 1, &ptmsi_be, sizeof(ptmsi_be)); - - rate_ctr_inc(&peer->ctrg->ctr[counter]); - - return 1; -} - -static int gbprox_parse_gmm_attach_req(uint8_t *data, size_t data_len, - struct gbproxy_parse_context *parse_ctx) -{ - uint8_t *value; - size_t value_len; - - parse_ctx->llc_msg_name = "ATTACH_REQ"; - - /* Skip MS network capability */ - if (lv_shift(&data, &data_len, NULL, &value_len) <= 0 || - value_len < 1 || value_len > 2) - /* invalid */ - return 0;; - - /* Skip Attach type */ - /* Skip Ciphering key sequence number */ - /* Skip DRX parameter */ - v_fixed_shift(&data, &data_len, 3, NULL); - - /* Get Mobile identity */ - if (lv_shift(&data, &data_len, &value, &value_len) <= 0 || - value_len < 5 || value_len > 8) - /* invalid */ - return 0; - - if (is_mi_tmsi(value, value_len)) { - parse_ctx->ptmsi_enc = value; - } else if (is_mi_imsi(value, value_len)) { - parse_ctx->imsi = value; - parse_ctx->imsi_len = value_len; - } - - if (v_fixed_shift(&data, &data_len, 6, &value) <= 0) - return 0; - - parse_ctx->old_raid_enc = value; - - return 1; -} - -static int gbprox_parse_gmm_attach_ack(uint8_t *data, size_t data_len, - struct gbproxy_parse_context *parse_ctx) -{ - uint8_t *value; - size_t value_len; - - parse_ctx->llc_msg_name = "ATTACH_ACK"; - - /* Skip Attach result */ - /* Skip Force to standby */ - /* Skip Periodic RA update timer */ - /* Skip Radio priority for SMS */ - /* Skip Spare half octet */ - v_fixed_shift(&data, &data_len, 3, NULL); - - if (v_fixed_shift(&data, &data_len, 6, &value) <= 0) - return 0; - - parse_ctx->raid_enc = value; - - /* Skip P-TMSI signature (P-TMSI signature, opt, TV, length 4) */ - tv_fixed_match(&data, &data_len, GSM48_IE_GMM_PTMSI_SIG, 3, NULL); - - /* Skip Negotiated READY timer value (GPRS timer, opt, TV, length 2) */ - tv_fixed_match(&data, &data_len, GSM48_IE_GMM_TIMER_READY, 1, NULL); - - /* Allocated P-TMSI (Mobile identity, opt, TLV, length 7) */ - if (tlv_match(&data, &data_len, GSM48_IE_GMM_ALLOC_PTMSI, - &value, &value_len) > 0 && - is_mi_tmsi(value, value_len)) - parse_ctx->new_ptmsi_enc = value; - return 1; -} - -static int gbprox_parse_gmm_detach_req(uint8_t *data, size_t data_len, - struct gbproxy_parse_context *parse_ctx) -{ - uint8_t *value; - size_t value_len; - int detach_type; - int power_off; - - parse_ctx->llc_msg_name = "DETACH_REQ"; - - /* Skip spare half octet */ - /* Get Detach type */ - if (v_fixed_shift(&data, &data_len, 1, &value) <= 0) - /* invalid */ - return 0; - - detach_type = *value & 0x07; - power_off = *value & 0x08 ? 1 : 0; - - if (!parse_ctx->to_bss) { - /* Mobile originated */ - - if (power_off) - parse_ctx->invalidate_tlli = 1; - - /* Get P-TMSI (Mobile identity), see GSM 24.008, 9.4.5.2 */ - if (tlv_match(&data, &data_len, - GSM48_IE_GMM_ALLOC_PTMSI, &value, &value_len) > 0) - { - if (is_mi_tmsi(value, value_len)) - parse_ctx->ptmsi_enc = value; - } - } - - return 1; -} - -static int gbprox_parse_gmm_ra_upd_req(uint8_t *data, size_t data_len, - struct gbproxy_parse_context *parse_ctx) -{ - uint8_t *value; - - parse_ctx->llc_msg_name = "RA_UPD_REQ"; - - /* Skip Update type */ - /* Skip GPRS ciphering key sequence number */ - v_fixed_shift(&data, &data_len, 1, NULL); - - if (v_fixed_shift(&data, &data_len, 6, &value) <= 0) - return 0; - - parse_ctx->old_raid_enc = value; - - return 1; -} - -static int gbprox_parse_gmm_ra_upd_ack(uint8_t *data, size_t data_len, - struct gbproxy_parse_context *parse_ctx) -{ - uint8_t *value; - size_t value_len; - - parse_ctx->llc_msg_name = "RA_UPD_ACK"; - - /* Skip Force to standby */ - /* Skip Update result */ - /* Skip Periodic RA update timer */ - v_fixed_shift(&data, &data_len, 2, NULL); - - if (v_fixed_shift(&data, &data_len, 6, &value) <= 0) - return 0; - - parse_ctx->raid_enc = value; - - /* Skip P-TMSI signature (P-TMSI signature, opt, TV, length 4) */ - tv_fixed_match(&data, &data_len, GSM48_IE_GMM_PTMSI_SIG, 3, NULL); - - /* Allocated P-TMSI (Mobile identity, opt, TLV, length 7) */ - if (tlv_match(&data, &data_len, GSM48_IE_GMM_ALLOC_PTMSI, - &value, &value_len) > 0 && - is_mi_tmsi(value, value_len)) - parse_ctx->new_ptmsi_enc = value; - - return 1; -} - -static int gbprox_parse_gmm_ptmsi_reall_cmd(uint8_t *data, size_t data_len, - struct gbproxy_parse_context *parse_ctx) -{ - uint8_t *value; - size_t value_len; - - parse_ctx->llc_msg_name = "PTMSI_REALL_CMD"; - - LOGP(DLLC, LOGL_NOTICE, - "Got P-TMSI Reallocation Command which is not covered by unit tests yet.\n"); - - /* Allocated P-TMSI */ - if (lv_shift(&data, &data_len, &value, &value_len) > 0 && - is_mi_tmsi(value, value_len)) - parse_ctx->new_ptmsi_enc = value; - - if (v_fixed_shift(&data, &data_len, 6, &value) <= 0) - return 0; - - parse_ctx->raid_enc = value; - - return 1; -} - -static int gbprox_parse_gmm_id_resp(uint8_t *data, size_t data_len, - struct gbproxy_parse_context *parse_ctx) -{ - uint8_t *value; - size_t value_len; - - parse_ctx->llc_msg_name = "ID_RESP"; - - /* Mobile identity, Mobile identity 10.5.1.4, M LV 2-10 */ - if (lv_shift(&data, &data_len, &value, &value_len) <= 0 || - value_len < 1 || value_len > 9) - /* invalid */ - return 0; - - if (is_mi_tmsi(value, value_len)) { - parse_ctx->ptmsi_enc = value; - } else if (is_mi_imsi(value, value_len)) { - parse_ctx->imsi = value; - parse_ctx->imsi_len = value_len; - } - - return 1; -} - -static int gbprox_parse_gsm_act_pdp_req(uint8_t *data, size_t data_len, - struct gbproxy_parse_context *parse_ctx) -{ - ssize_t old_len; - uint8_t *value; - size_t value_len; - - parse_ctx->llc_msg_name = "ACT_PDP_REQ"; - - /* Skip Requested NSAPI */ - /* Skip Requested LLC SAPI */ - v_fixed_shift(&data, &data_len, 2, NULL); - - /* Skip Requested QoS (support 04.08 and 24.008) */ - if (lv_shift(&data, &data_len, NULL, &value_len) <= 0 || - value_len < 4 || value_len > 14) - /* invalid */ - return 0;; - - /* Skip Requested PDP address */ - if (lv_shift(&data, &data_len, NULL, &value_len) <= 0 || - value_len < 2 || value_len > 18) - /* invalid */ - return 0; - - /* Access point name */ - old_len = tlv_match(&data, &data_len, - GSM48_IE_GSM_APN, &value, &value_len); - - if (old_len > 0 && value_len >=1 && value_len <= 100) { - parse_ctx->apn_ie = data - old_len; - parse_ctx->apn_ie_len = old_len; - } - - return 1; -} - -struct gbproxy_peer *peer_by_bssgp_tlv(struct gbproxy_config *cfg, struct tlv_parsed *tp) -{ - if (TLVP_PRESENT(tp, BSSGP_IE_BVCI)) { - uint16_t bvci; - - bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI)); - if (bvci >= 2) - return peer_by_bvci(cfg, bvci); - } - - if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) { - uint8_t *rai = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA); - /* Only compare LAC part, since MCC/MNC are possibly patched. - * Since the LAC of different BSS must be different when - * MCC/MNC are patched, collisions shouldn't happen. */ - return peer_by_lac(cfg, rai); - } - - if (TLVP_PRESENT(tp, BSSGP_IE_LOCATION_AREA)) { - uint8_t *lai = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA); - return peer_by_lac(cfg, lai); - } - - return NULL; -} - -static int gbprox_parse_dtap(uint8_t *data, size_t data_len, - struct gbproxy_parse_context *parse_ctx) __attribute__((nonnull)); - -static int gbprox_parse_dtap(uint8_t *data, size_t data_len, - struct gbproxy_parse_context *parse_ctx) -{ - struct gsm48_hdr *g48h; - - if (v_fixed_shift(&data, &data_len, sizeof(*g48h), (uint8_t **)&g48h) <= 0) - return 0; - - parse_ctx->g48_hdr = g48h; - - if ((g48h->proto_discr & 0x0f) != GSM48_PDISC_MM_GPRS && - (g48h->proto_discr & 0x0f) != GSM48_PDISC_SM_GPRS) - return 1; - - switch (g48h->msg_type) { - case GSM48_MT_GMM_ATTACH_REQ: - return gbprox_parse_gmm_attach_req(data, data_len, parse_ctx); - - case GSM48_MT_GMM_ATTACH_ACK: - return gbprox_parse_gmm_attach_ack(data, data_len, parse_ctx); - - case GSM48_MT_GMM_RA_UPD_REQ: - return gbprox_parse_gmm_ra_upd_req(data, data_len, parse_ctx); - - case GSM48_MT_GMM_RA_UPD_ACK: - return gbprox_parse_gmm_ra_upd_ack(data, data_len, parse_ctx); - - case GSM48_MT_GMM_PTMSI_REALL_CMD: - return gbprox_parse_gmm_ptmsi_reall_cmd(data, data_len, parse_ctx); - - case GSM48_MT_GSM_ACT_PDP_REQ: - return gbprox_parse_gsm_act_pdp_req(data, data_len, parse_ctx); - - case GSM48_MT_GMM_ID_RESP: - return gbprox_parse_gmm_id_resp(data, data_len, parse_ctx); - - case GSM48_MT_GMM_DETACH_REQ: - return gbprox_parse_gmm_detach_req(data, data_len, parse_ctx); - - case GSM48_MT_GMM_DETACH_ACK: - parse_ctx->llc_msg_name = "DETACH_ACK"; - parse_ctx->invalidate_tlli = 1; - break; - - default: - break; - }; - - return 1; -} - -static int allow_message_patching(struct gbproxy_peer *peer, int msg_type) -{ - if (msg_type >= GSM48_MT_GSM_ACT_PDP_REQ) { - return patching_is_enabled(peer, GBPROX_PATCH_LLC_GSM); - } else if (msg_type > GSM48_MT_GMM_ATTACH_REJ) { - return patching_is_enabled(peer, GBPROX_PATCH_LLC); - } else if (msg_type > GSM48_MT_GMM_ATTACH_REQ) { - return patching_is_enabled(peer, GBPROX_PATCH_LLC_ATTACH); - } else { - return patching_is_enabled(peer, GBPROX_PATCH_LLC_ATTACH_REQ); - } -} - -static int gbprox_parse_llc(uint8_t *llc, size_t llc_len, - struct gbproxy_parse_context *parse_ctx) __attribute__((nonnull)); - -static int gbprox_parse_llc(uint8_t *llc, size_t llc_len, - struct gbproxy_parse_context *parse_ctx) -{ - struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed; - int rc; - int fcs; - - /* parse LLC */ - rc = gprs_llc_hdr_parse(ghp, llc, llc_len); - gprs_llc_hdr_dump(ghp); - if (rc != 0) { - LOGP(DLLC, LOGL_NOTICE, "Error during LLC header parsing\n"); - return 0; - } - - fcs = gprs_llc_fcs(llc, ghp->crc_length); - LOGP(DLLC, LOGL_DEBUG, "Got LLC message, CRC: %06x (computed %06x)\n", - ghp->fcs, fcs); - - if (!ghp->data) - return 0; - - if (ghp->sapi != GPRS_SAPI_GMM) - return 1; - - if (ghp->cmd != GPRS_LLC_UI) - return 1; - - if (ghp->is_encrypted) { - parse_ctx->need_decryption = 1; - return 0; - } - - return gbprox_parse_dtap(ghp->data, ghp->data_len, parse_ctx); -} - -static int gbprox_patch_llc(struct msgb *msg, uint8_t *llc, size_t llc_len, - struct gbproxy_peer *peer, - struct gbproxy_tlli_info *tlli_info, int *len_change, - struct gbproxy_parse_context *parse_ctx) __attribute__((nonnull)); - -static int gbprox_patch_llc(struct msgb *msg, uint8_t *llc, size_t llc_len, - struct gbproxy_peer *peer, - struct gbproxy_tlli_info *tlli_info, int *len_change, - struct gbproxy_parse_context *parse_ctx) -{ - struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed; - int have_patched = 0; - int fcs; - - if (parse_ctx->g48_hdr && !allow_message_patching(peer, parse_ctx->g48_hdr->msg_type)) - return have_patched; - - if (parse_ctx->ptmsi_enc && tlli_info) { - uint32_t ptmsi; - if (parse_ctx->to_bss) - ptmsi = tlli_info->tlli.ptmsi; - else - ptmsi = tlli_info->sgsn_tlli.ptmsi; - - if (ptmsi != GSM_RESERVED_TMSI) { - if (gbprox_patch_ptmsi(parse_ctx->ptmsi_enc, peer, - ptmsi, parse_ctx->to_bss, "P-TMSI")) - have_patched = 1; - } else { - /* TODO: invalidate old RAI if present (see below) */ - } - } - - if (parse_ctx->new_ptmsi_enc && tlli_info) { - uint32_t ptmsi; - if (parse_ctx->to_bss) - ptmsi = tlli_info->tlli.ptmsi; - else - ptmsi = tlli_info->sgsn_tlli.ptmsi; - - OSMO_ASSERT(ptmsi); - if (gbprox_patch_ptmsi(parse_ctx->new_ptmsi_enc, peer, - ptmsi, parse_ctx->to_bss, "new P-TMSI")) - have_patched = 1; - } - - if (parse_ctx->raid_enc) { - gbprox_patch_raid(parse_ctx->raid_enc, peer, parse_ctx->to_bss, - parse_ctx->llc_msg_name); - have_patched = 1; - } - - if (parse_ctx->old_raid_enc && parse_ctx->old_raid_matches) { - /* TODO: Patch to invalid if P-TMSI unknown. */ - gbprox_patch_raid(parse_ctx->old_raid_enc, peer, parse_ctx->to_bss, - parse_ctx->llc_msg_name); - have_patched = 1; - } - - if (parse_ctx->apn_ie && - peer->cfg->core_apn && - !parse_ctx->to_bss && - gbprox_check_tlli(peer, parse_ctx->tlli)) { - size_t new_len; - gbprox_patch_apn_ie(msg, - parse_ctx->apn_ie, parse_ctx->apn_ie_len, - peer, &new_len, parse_ctx->llc_msg_name); - *len_change += (int)new_len - (int)parse_ctx->apn_ie_len; - - have_patched = 1; - } - - if (have_patched) { - llc_len += *len_change; - ghp->crc_length += *len_change; - - /* Fix FCS */ - fcs = gprs_llc_fcs(llc, ghp->crc_length); - LOGP(DLLC, LOGL_DEBUG, "Updated LLC message, CRC: %06x -> %06x\n", - ghp->fcs, fcs); - - llc[llc_len - 3] = fcs & 0xff; - llc[llc_len - 2] = (fcs >> 8) & 0xff; - llc[llc_len - 1] = (fcs >> 16) & 0xff; - } - - return have_patched; -} - -static void gbprox_log_parse_context(struct gbproxy_parse_context *parse_ctx, - const char *default_msg_name) -{ - const char *msg_name = default_msg_name; - const char *sep = ""; - - if (!parse_ctx->tlli_enc && - !parse_ctx->ptmsi_enc && - !parse_ctx->new_ptmsi_enc && - !parse_ctx->imsi) - return; - - if (parse_ctx->llc_msg_name) - msg_name = parse_ctx->llc_msg_name; - - LOGP(DGPRS, LOGL_DEBUG, "%s: Got", msg_name); - - if (parse_ctx->tlli_enc) { - LOGP(DGPRS, LOGL_DEBUG, "%s TLLI %08x", sep, parse_ctx->tlli); - sep = ","; - } - - if (parse_ctx->bssgp_raid_enc) { - struct gprs_ra_id raid; - gsm48_parse_ra(&raid, parse_ctx->bssgp_raid_enc); - LOGP(DGPRS, LOGL_DEBUG, "%s BSSGP RAID %u-%u-%u-%u", sep, - raid.mcc, raid.mnc, raid.lac, raid.rac); - sep = ","; - } - - if (parse_ctx->raid_enc) { - struct gprs_ra_id raid; - gsm48_parse_ra(&raid, parse_ctx->raid_enc); - LOGP(DGPRS, LOGL_DEBUG, "%s RAID %u-%u-%u-%u", sep, - raid.mcc, raid.mnc, raid.lac, raid.rac); - sep = ","; - } - - if (parse_ctx->old_raid_enc) { - struct gprs_ra_id raid; - gsm48_parse_ra(&raid, parse_ctx->old_raid_enc); - LOGP(DGPRS, LOGL_DEBUG, "%s old RAID %u-%u-%u-%u", sep, - raid.mcc, raid.mnc, raid.lac, raid.rac); - sep = ","; - } - - if (parse_ctx->ptmsi_enc) { - uint32_t ptmsi = GSM_RESERVED_TMSI; - int ok; - ok = parse_mi_tmsi(parse_ctx->ptmsi_enc, GSM48_TMSI_LEN, &ptmsi); - LOGP(DGPRS, LOGL_DEBUG, "%s PTMSI %08x%s", - sep, ptmsi, ok ? "" : " (parse error)"); - sep = ","; - } - - if (parse_ctx->new_ptmsi_enc) { - uint32_t new_ptmsi = GSM_RESERVED_TMSI; - int ok; - ok = parse_mi_tmsi(parse_ctx->new_ptmsi_enc, GSM48_TMSI_LEN, - &new_ptmsi); - LOGP(DGPRS, LOGL_DEBUG, "%s new PTMSI %08x%s", - sep, new_ptmsi, ok ? "" : " (parse error)"); - sep = ","; - } - - if (parse_ctx->imsi) { - 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_DEBUG, "%s IMSI %s", - sep, mi_buf); - sep = ","; - } - if (parse_ctx->invalidate_tlli) { - LOGP(DGPRS, LOGL_DEBUG, "%s invalidate", sep); - sep = ","; - } - - LOGP(DGPRS, LOGL_DEBUG, "\n"); -} - -static uint32_t gbprox_make_bss_ptmsi(struct gbproxy_peer *peer, - uint32_t sgsn_ptmsi) -{ - uint32_t bss_ptmsi; - if (!peer->cfg->patch_ptmsi) { - bss_ptmsi = sgsn_ptmsi; - } else { - do { - bss_ptmsi = rand_r(&peer->cfg->bss_ptmsi_state); - bss_ptmsi = bss_ptmsi | 0xC0000000; - - if (gbprox_find_tlli_by_ptmsi(peer, bss_ptmsi)) - bss_ptmsi = GSM_RESERVED_TMSI; - } while (bss_ptmsi == GSM_RESERVED_TMSI); - } - - return bss_ptmsi; -} - -static uint32_t gbprox_make_sgsn_tlli(struct gbproxy_peer *peer, - struct gbproxy_tlli_info *tlli_info, - uint32_t bss_tlli) -{ - uint32_t sgsn_tlli; - if (!peer->cfg->patch_ptmsi) { - sgsn_tlli = bss_tlli; - } else if (tlli_info->sgsn_tlli.ptmsi != GSM_RESERVED_TMSI) { - sgsn_tlli = gprs_tmsi2tlli(tlli_info->sgsn_tlli.ptmsi, - TLLI_FOREIGN); - } else { - do { - /* create random TLLI, 0b01111xxx... */ - sgsn_tlli = rand_r(&peer->cfg->sgsn_tlli_state); - sgsn_tlli = (sgsn_tlli & 0x7fffffff) | 0x78000000; - - if (gbprox_find_tlli_by_sgsn_tlli(peer, sgsn_tlli)) - sgsn_tlli = 0; - } while (!sgsn_tlli); - } - return sgsn_tlli; -} - -static struct gbproxy_tlli_info *gbprox_update_state_ul( - struct gbproxy_peer *peer, - time_t now, - struct gbproxy_parse_context *parse_ctx) -{ - struct gbproxy_tlli_info *tlli_info = NULL; - - if (parse_ctx->tlli_enc) - tlli_info = gbprox_find_tlli(peer, parse_ctx->tlli); - - if (parse_ctx->g48_hdr) { - switch (parse_ctx->g48_hdr->msg_type) { - case GSM48_MT_GMM_ATTACH_REQ: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_REQS]); - break; - - default: - break; - } - } - - gbprox_log_parse_context(parse_ctx, "BSSGP"); - - if (parse_ctx->tlli_enc && parse_ctx->llc) { - uint32_t sgsn_tlli; - if (!tlli_info) { - tlli_info = - gbprox_register_tlli(peer, parse_ctx->tlli, - parse_ctx->imsi, - parse_ctx->imsi_len, now); - /* Setup TLLIs */ - sgsn_tlli = gbprox_make_sgsn_tlli(peer, tlli_info, - parse_ctx->tlli); - tlli_info->sgsn_tlli.current = sgsn_tlli; - } else { - sgsn_tlli = gbprox_map_tlli(parse_ctx->tlli, tlli_info, 0); - if (!sgsn_tlli) - sgsn_tlli = gbprox_make_sgsn_tlli(peer, tlli_info, - parse_ctx->tlli); - - gbprox_validate_tlli(&tlli_info->tlli, - parse_ctx->tlli, 0); - gbprox_validate_tlli(&tlli_info->sgsn_tlli, - sgsn_tlli, 0); - gbprox_touch_tlli(peer, tlli_info, now); - } - } else if (tlli_info) { - gbprox_touch_tlli(peer, tlli_info, now); - } - - if (parse_ctx->imsi && tlli_info && tlli_info->mi_data_len == 0) { - int enable_patching; - gbprox_update_tlli_info(tlli_info, - parse_ctx->imsi, parse_ctx->imsi_len); - - /* Check, whether the IMSI matches */ - enable_patching = gbprox_check_imsi(peer, parse_ctx->imsi, - parse_ctx->imsi_len); - if (enable_patching >= 0) - tlli_info->enable_patching = enable_patching; - } - - return tlli_info; -} - -static struct gbproxy_tlli_info *gbprox_update_state_dl( - struct gbproxy_peer *peer, - time_t now, - struct gbproxy_parse_context *parse_ctx) -{ - struct gbproxy_tlli_info *tlli_info = NULL; - - if (parse_ctx->tlli_enc) - tlli_info = gbprox_find_tlli_by_sgsn_tlli(peer, parse_ctx->tlli); - - if (parse_ctx->g48_hdr) { - switch (parse_ctx->g48_hdr->msg_type) { - case GSM48_MT_GMM_ATTACH_REJ: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_REJS]); - break; - - default: - break; - } - } - - gbprox_log_parse_context(parse_ctx, "BSSGP"); - - 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 (!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 = gbprox_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) { - gbprox_reassign_tlli(&tlli_info->sgsn_tlli, - peer, new_sgsn_tlli); - gbprox_reassign_tlli(&tlli_info->tlli, - peer, new_bss_tlli); - gbprox_touch_tlli(peer, tlli_info, now); - } else { - tlli_info = gbprox_tlli_info_alloc(peer); - LOGP(DGPRS, LOGL_INFO, - "Adding TLLI %08x to list (SGSN, new P-TMSI)\n", - new_sgsn_tlli); - - gbprox_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 = gbprox_tlli_info_alloc(peer); - LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list (SGSN)\n", - parse_ctx->tlli); - - gbprox_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 = gbprox_map_tlli(parse_ctx->tlli, - tlli_info, 1); - gbprox_validate_tlli(&tlli_info->sgsn_tlli, parse_ctx->tlli, 1); - gbprox_validate_tlli(&tlli_info->tlli, bss_tlli, 1); - gbprox_touch_tlli(peer, tlli_info, now); - } else if (tlli_info) { - gbprox_touch_tlli(peer, tlli_info, now); - } - - if (parse_ctx->imsi && tlli_info && tlli_info->mi_data_len == 0) { - int enable_patching; - gbprox_update_tlli_info(tlli_info, - parse_ctx->imsi, parse_ctx->imsi_len); - - /* Check, whether the IMSI matches */ - enable_patching = gbprox_check_imsi(peer, parse_ctx->imsi, - parse_ctx->imsi_len); - if (enable_patching >= 0) - tlli_info->enable_patching = enable_patching; - } - - return tlli_info; -} - -static void gbprox_update_state_after(struct gbproxy_peer *peer, - struct gbproxy_tlli_info *tlli_info, - time_t now, - struct gbproxy_parse_context *parse_ctx) -{ - if (parse_ctx->invalidate_tlli) - gbprox_unregister_tlli(peer, parse_ctx->tlli); - - gbprox_remove_stale_tllis(peer, now); -} - -static int gbprox_parse_bssgp(uint8_t *bssgp, size_t bssgp_len, - struct gbproxy_parse_context *parse_ctx) -{ - struct bssgp_normal_hdr *bgph; - struct bssgp_ud_hdr *budh = NULL; - struct tlv_parsed *tp = &parse_ctx->bssgp_tp; - uint8_t pdu_type; - uint8_t *data; - size_t data_len; - int rc; - - if (bssgp_len < sizeof(struct bssgp_normal_hdr)) - return 0; - - bgph = (struct bssgp_normal_hdr *)bssgp; - pdu_type = bgph->pdu_type; - - if (pdu_type == BSSGP_PDUT_UL_UNITDATA || - pdu_type == BSSGP_PDUT_DL_UNITDATA) { - if (bssgp_len < sizeof(struct bssgp_ud_hdr)) - return 0; - budh = (struct bssgp_ud_hdr *)bssgp; - bgph = NULL; - data = budh->data; - data_len = bssgp_len - sizeof(*budh); - } else { - data = bgph->data; - data_len = bssgp_len - sizeof(*bgph); - } - - if (bssgp_tlv_parse(tp, data, data_len) < 0) - return 0; - - parse_ctx->pdu_type = pdu_type; - parse_ctx->bud_hdr = budh; - parse_ctx->bgp_hdr = bgph; - parse_ctx->bssgp_data = data; - parse_ctx->bssgp_data_len = data_len; - - if (budh) - parse_ctx->tlli_enc = (uint8_t *)&budh->tlli; - - if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) - parse_ctx->bssgp_raid_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA); - - if (TLVP_PRESENT(tp, BSSGP_IE_CELL_ID)) - parse_ctx->bssgp_raid_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_CELL_ID); - - if (TLVP_PRESENT(tp, BSSGP_IE_IMSI)) { - parse_ctx->imsi = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_IMSI); - parse_ctx->imsi_len = TLVP_LEN(tp, BSSGP_IE_IMSI); - } - - /* TODO: This is TLLI old, don't confuse with TLLI current, add - * and use tlli_old_enc instead */ - if (0 && TLVP_PRESENT(tp, BSSGP_IE_TLLI)) - parse_ctx->tlli_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_TLLI); - - if (TLVP_PRESENT(tp, BSSGP_IE_TMSI) && pdu_type == BSSGP_PDUT_PAGING_PS) - parse_ctx->ptmsi_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_TMSI); - - if (TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU)) { - uint8_t *llc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_LLC_PDU); - size_t llc_len = TLVP_LEN(tp, BSSGP_IE_LLC_PDU); - - rc = gbprox_parse_llc(llc, llc_len, parse_ctx); - if (!rc) - return 0; - - parse_ctx->llc = llc; - parse_ctx->llc_len = llc_len; - } - - if (parse_ctx->tlli_enc) { - uint32_t tmp_tlli; - memcpy(&tmp_tlli, parse_ctx->tlli_enc, sizeof(tmp_tlli)); - parse_ctx->tlli = ntohl(tmp_tlli); - } - - return 1; -} - -/* patch BSSGP message to use core_mcc/mnc on the SGSN side */ -static void gbprox_patch_bssgp(struct msgb *msg, uint8_t *bssgp, size_t bssgp_len, - struct gbproxy_peer *peer, - struct gbproxy_tlli_info *tlli_info, int *len_change, - struct gbproxy_parse_context *parse_ctx) - __attribute__((nonnull)); -static void gbprox_patch_bssgp(struct msgb *msg, uint8_t *bssgp, size_t bssgp_len, - struct gbproxy_peer *peer, - struct gbproxy_tlli_info *tlli_info, int *len_change, - struct gbproxy_parse_context *parse_ctx) -{ - const char *err_info = NULL; - int err_ctr = -1; - - if (!patching_is_enabled(peer, GBPROX_PATCH_BSSGP)) - return; - - if (parse_ctx->bssgp_raid_enc) - gbprox_patch_raid(parse_ctx->bssgp_raid_enc, peer, - parse_ctx->to_bss, "BSSGP"); - - if (!patching_is_enabled(peer, GBPROX_PATCH_LLC_ATTACH_REQ)) - return; - - if (parse_ctx->need_decryption && - patching_is_required(peer, GBPROX_PATCH_LLC_ATTACH)) { - /* Patching LLC messages has been requested - * explicitly, but the message (including the - * type) is encrypted, so we possibly fail to - * patch the LLC part of the message. */ - err_ctr = GBPROX_PEER_CTR_PATCH_CRYPT_ERR; - err_info = "GMM message is encrypted"; - goto patch_error; - } - - if (parse_ctx->tlli_enc && tlli_info) { - uint32_t tlli = gbprox_map_tlli(parse_ctx->tlli, - tlli_info, parse_ctx->to_bss); - - if (tlli) { - gbprox_patch_tlli(parse_ctx->tlli_enc, peer, tlli, - parse_ctx->to_bss, "TLLI"); - parse_ctx->tlli = tlli; - } else if (parse_ctx->to_bss) { - /* Happens with unknown (not cached) TLLI coming from - * the SGSN */ - /* TODO: What shall be done with the message in this case? */ - err_ctr = GBPROX_PEER_CTR_TLLI_UNKNOWN; - err_info = "TLLI sent by the SGSN is unknown"; - goto patch_error; - } else { - /* Internal error */ - err_ctr = GBPROX_PEER_CTR_PATCH_ERR; - err_info = "Replacement TLLI is 0"; - goto patch_error; - } - } - - if (parse_ctx->llc) { - uint8_t *llc = parse_ctx->llc; - size_t llc_len = parse_ctx->llc_len; - int llc_len_change = 0; - - gbprox_patch_llc(msg, llc, llc_len, peer, tlli_info, - &llc_len_change, parse_ctx); - /* Note that the APN might have been resized here, but no - * pointer int the parse_ctx will refer to an adress after the - * APN. So it's possible to patch first and do the TLLI - * handling afterwards. */ - - if (llc_len_change) { - llc_len += llc_len_change; - - /* Fix LLC IE len */ - /* TODO: This is a kludge, but the a pointer to the - * start of the IE is not available here */ - if (llc[-2] == BSSGP_IE_LLC_PDU && llc[-1] & 0x80) { - /* most probably a one byte length */ - if (llc_len > 127) { - err_info = "Cannot increase size"; - err_ctr = GBPROX_PEER_CTR_PATCH_ERR; - goto patch_error; - } - llc[-1] = llc_len | 0x80; - } else { - llc[-2] = (llc_len >> 8) & 0x7f; - llc[-1] = llc_len & 0xff; - } - *len_change += llc_len_change; - } - /* Note that the tp struct might contain invalid pointers here - * if the LLC field has changed its size */ - parse_ctx->llc_len = llc_len; - } - return; - -patch_error: - OSMO_ASSERT(err_ctr >= 0); - rate_ctr_inc(&peer->ctrg->ctr[err_ctr]); - LOGP(DGPRS, LOGL_ERROR, - "NSEI=%u(%s) failed to patch BSSGP message as requested: %s.\n", - msgb_nsei(msg), parse_ctx->to_bss ? "SGSN" : "BSS", - err_info); -} - -/* patch BSSGP message */ -static void gbprox_process_bssgp_ul(struct gbproxy_config *cfg, - struct msgb *msg, - struct gbproxy_peer *peer) -{ - struct gbproxy_parse_context parse_ctx = {0}; - int rc; - int len_change = 0; - time_t now; - struct gbproxy_tlli_info *tlli_info = NULL; - - if (!cfg->core_mcc && !cfg->core_mnc && !cfg->core_apn) - return; - - parse_ctx.to_bss = 0; - - /* Parse BSSGP/LLC */ - rc = gbprox_parse_bssgp(msgb_bssgph(msg), msgb_bssgp_len(msg), - &parse_ctx); - - if (!rc) { - if (!parse_ctx.need_decryption) { - LOGP(DGPRS, LOGL_ERROR, - "NSEI=%u(BSS) patching: " - "failed to parse BSSGP/GMM message\n", - msgb_nsei(msg)); - return; - } - } - - /* Get peer */ - if (!peer && msgb_bvci(msg) >= 2) - peer = peer_by_bvci(cfg, msgb_bvci(msg)); - - if (!peer) - peer = gbprox_peer_by_nsei(cfg, msgb_nsei(msg)); - - if (!peer) - peer = peer_by_bssgp_tlv(cfg, &parse_ctx.bssgp_tp); - - if (!peer) { - LOGP(DLLC, LOGL_INFO, - "NSEI=%d(BSS) patching: didn't find peer for message, " - "PDU %d\n", - msgb_nsei(msg), parse_ctx.pdu_type); - /* Increment counter */ - rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_PATCH_PEER_ERR]); - return; - } - - now = time(NULL); - - if (parse_ctx.bssgp_raid_enc && parse_ctx.old_raid_enc && - memcmp(parse_ctx.bssgp_raid_enc, parse_ctx.old_raid_enc, 6) == 0) - parse_ctx.old_raid_matches = 1; - - gbprox_update_current_raid(parse_ctx.bssgp_raid_enc, peer, - parse_ctx.llc_msg_name); - - tlli_info = gbprox_update_state_ul(peer, now, &parse_ctx); - - gbprox_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg), - peer, tlli_info, &len_change, &parse_ctx); - - gbprox_update_state_after(peer, tlli_info, now, &parse_ctx); - - return; -} - -/* patch BSSGP message to use core_mcc/mnc on the SGSN side */ -static void gbprox_process_bssgp_dl(struct gbproxy_config *cfg, - struct msgb *msg, - struct gbproxy_peer *peer) -{ - struct gbproxy_parse_context parse_ctx = {0}; - int rc; - int len_change = 0; - time_t now; - struct gbproxy_tlli_info *tlli_info = NULL; - - parse_ctx.to_bss = 1; - - rc = gbprox_parse_bssgp(msgb_bssgph(msg), msgb_bssgp_len(msg), - &parse_ctx); - - if (!rc) { - if (!parse_ctx.need_decryption) { - LOGP(DGPRS, LOGL_ERROR, - "NSEI=%u(SGSN) patching: " - "failed to parse BSSGP/GMM message\n", - msgb_nsei(msg)); - return; - } - } - - if (!peer && msgb_bvci(msg) >= 2) - peer = peer_by_bvci(cfg, msgb_bvci(msg)); - - if (!peer) - peer = peer_by_bssgp_tlv(cfg, &parse_ctx.bssgp_tp); - - if (!peer) { - LOGP(DLLC, LOGL_INFO, - "NSEI=%d(SGSN) patching: didn't find peer for message, " - "PDU %d\n", - msgb_nsei(msg), parse_ctx.pdu_type); - /* Increment counter */ - rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_PATCH_PEER_ERR]); - return; - } - - now = time(NULL); + gprs_gb_log_parse_context(&parse_ctx, "BSSGP"); - tlli_info = gbprox_update_state_dl(peer, now, &parse_ctx); + tlli_info = gbproxy_update_tlli_state_dl(peer, now, &parse_ctx); - gbprox_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg), - peer, tlli_info, &len_change, &parse_ctx); + gbproxy_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg), + peer, tlli_info, &len_change, &parse_ctx); - gbprox_update_state_after(peer, tlli_info, now, &parse_ctx); + gbproxy_update_tlli_state_after(peer, tlli_info, now, &parse_ctx); return; } diff --git a/openbsc/src/gprs/gb_proxy_patch.c b/openbsc/src/gprs/gb_proxy_patch.c new file mode 100644 index 000000000..59d0b2f2b --- /dev/null +++ b/openbsc/src/gprs/gb_proxy_patch.c @@ -0,0 +1,475 @@ +/* Gb-proxy message patching */ + +/* (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 + +/* check whether patching is enabled at this level */ +static int patching_is_enabled(struct gbproxy_peer *peer, + enum gbproxy_patch_mode need_at_least) +{ + enum gbproxy_patch_mode patch_mode = peer->cfg->patch_mode; + if (patch_mode == GBPROX_PATCH_DEFAULT) + patch_mode = GBPROX_PATCH_LLC; + + return need_at_least <= patch_mode; +} + +/* check whether patching is enabled at this level */ +static int patching_is_required(struct gbproxy_peer *peer, + enum gbproxy_patch_mode need_at_least) +{ + return need_at_least <= peer->cfg->patch_mode; +} + +static int allow_message_patching(struct gbproxy_peer *peer, int msg_type) +{ + if (msg_type >= GSM48_MT_GSM_ACT_PDP_REQ) { + return patching_is_enabled(peer, GBPROX_PATCH_LLC_GSM); + } else if (msg_type > GSM48_MT_GMM_ATTACH_REJ) { + return patching_is_enabled(peer, GBPROX_PATCH_LLC); + } else if (msg_type > GSM48_MT_GMM_ATTACH_REQ) { + return patching_is_enabled(peer, GBPROX_PATCH_LLC_ATTACH); + } else { + return patching_is_enabled(peer, GBPROX_PATCH_LLC_ATTACH_REQ); + } +} + +/* patch RA identifier in place */ +static void gbproxy_patch_raid(uint8_t *raid_enc, struct gbproxy_peer *peer, + int to_bss, const char *log_text) +{ + struct gbproxy_patch_state *state = &peer->patch_state; + int old_mcc; + int old_mnc; + struct gprs_ra_id raid; + + gsm48_parse_ra(&raid, raid_enc); + + old_mcc = raid.mcc; + old_mnc = raid.mnc; + + if (!to_bss) { + /* BSS -> SGSN */ + if (state->local_mcc) + raid.mcc = peer->cfg->core_mcc; + + if (state->local_mnc) + raid.mnc = peer->cfg->core_mnc; + } else { + /* SGSN -> BSS */ + if (state->local_mcc) + raid.mcc = state->local_mcc; + + if (state->local_mnc) + raid.mnc = state->local_mnc; + } + + if (state->local_mcc || state->local_mnc) { + enum gbproxy_peer_ctr counter = + to_bss ? + GBPROX_PEER_CTR_RAID_PATCHED_SGSN : + GBPROX_PEER_CTR_RAID_PATCHED_BSS; + + LOGP(DGPRS, LOGL_DEBUG, + "Patching %s to %s: " + "%d-%d-%d-%d -> %d-%d-%d-%d\n", + log_text, + to_bss ? "BSS" : "SGSN", + old_mcc, old_mnc, raid.lac, raid.rac, + raid.mcc, raid.mnc, raid.lac, raid.rac); + + gsm48_construct_ra(raid_enc, &raid); + rate_ctr_inc(&peer->ctrg->ctr[counter]); + } +} + +static void gbproxy_patch_apn_ie(struct msgb *msg, + uint8_t *apn_ie, size_t apn_ie_len, + struct gbproxy_peer *peer, + size_t *new_apn_ie_len, const char *log_text) +{ + struct apn_ie_hdr { + uint8_t iei; + uint8_t apn_len; + uint8_t apn[0]; + } *hdr = (void *)apn_ie; + + size_t apn_len = hdr->apn_len; + uint8_t *apn = hdr->apn; + + 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) { + char str1[110]; + /* Remove the IE */ + LOGP(DGPRS, LOGL_DEBUG, + "Patching %s to SGSN: Removing APN '%s'\n", + log_text, + gprs_apn_to_str(str1, apn, apn_len)); + + *new_apn_ie_len = 0; + gprs_msgb_resize_area(msg, apn_ie, apn_ie_len, 0); + } else { + /* Resize the IE */ + char str1[110]; + char str2[110]; + + OSMO_ASSERT(peer->cfg->core_apn_size <= 100); + + LOGP(DGPRS, LOGL_DEBUG, + "Patching %s to SGSN: " + "Replacing APN '%s' -> '%s'\n", + log_text, + gprs_apn_to_str(str1, apn, apn_len), + gprs_apn_to_str(str2, peer->cfg->core_apn, + peer->cfg->core_apn_size)); + + *new_apn_ie_len = peer->cfg->core_apn_size + 2; + gprs_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; + } + + rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_APN_PATCHED]); +} + +static int gbproxy_patch_tlli(uint8_t *tlli_enc, + struct gbproxy_peer *peer, + uint32_t new_tlli, + int to_bss, const char *log_text) +{ + uint32_t tlli_be; + uint32_t tlli; + enum gbproxy_peer_ctr counter = + to_bss ? + GBPROX_PEER_CTR_TLLI_PATCHED_SGSN : + GBPROX_PEER_CTR_TLLI_PATCHED_BSS; + + memcpy(&tlli_be, tlli_enc, sizeof(tlli_be)); + tlli = ntohl(tlli_be); + + if (tlli == new_tlli) + return 0; + + LOGP(DGPRS, LOGL_DEBUG, + "Patching %ss: " + "Replacing %08x -> %08x\n", + log_text, tlli, new_tlli); + + tlli_be = htonl(new_tlli); + memcpy(tlli_enc, &tlli_be, sizeof(tlli_be)); + + rate_ctr_inc(&peer->ctrg->ctr[counter]); + + return 1; +} + +static int gbproxy_patch_ptmsi(uint8_t *ptmsi_enc, + struct gbproxy_peer *peer, + uint32_t new_ptmsi, + int to_bss, const char *log_text) +{ + uint32_t ptmsi_be; + uint32_t ptmsi; + enum gbproxy_peer_ctr counter = + to_bss ? + GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN : + GBPROX_PEER_CTR_PTMSI_PATCHED_BSS; + memcpy(&ptmsi_be, ptmsi_enc + 1, sizeof(ptmsi_be)); + ptmsi = ntohl(ptmsi_be); + + if (ptmsi == new_ptmsi) + return 0; + + LOGP(DGPRS, LOGL_DEBUG, + "Patching %ss: " + "Replacing %08x -> %08x\n", + log_text, ptmsi, new_ptmsi); + + ptmsi_be = htonl(new_ptmsi); + memcpy(ptmsi_enc + 1, &ptmsi_be, sizeof(ptmsi_be)); + + rate_ctr_inc(&peer->ctrg->ctr[counter]); + + return 1; +} + +int gbproxy_patch_llc(struct msgb *msg, uint8_t *llc, size_t llc_len, + struct gbproxy_peer *peer, + struct gbproxy_tlli_info *tlli_info, int *len_change, + struct gprs_gb_parse_context *parse_ctx) +{ + struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed; + int have_patched = 0; + int fcs; + + if (parse_ctx->g48_hdr && !allow_message_patching(peer, parse_ctx->g48_hdr->msg_type)) + return have_patched; + + if (parse_ctx->ptmsi_enc && tlli_info) { + uint32_t ptmsi; + if (parse_ctx->to_bss) + ptmsi = tlli_info->tlli.ptmsi; + else + ptmsi = tlli_info->sgsn_tlli.ptmsi; + + if (ptmsi != GSM_RESERVED_TMSI) { + if (gbproxy_patch_ptmsi(parse_ctx->ptmsi_enc, peer, + ptmsi, parse_ctx->to_bss, "P-TMSI")) + have_patched = 1; + } else { + /* TODO: invalidate old RAI if present (see below) */ + } + } + + if (parse_ctx->new_ptmsi_enc && tlli_info) { + uint32_t ptmsi; + if (parse_ctx->to_bss) + ptmsi = tlli_info->tlli.ptmsi; + else + ptmsi = tlli_info->sgsn_tlli.ptmsi; + + OSMO_ASSERT(ptmsi); + if (gbproxy_patch_ptmsi(parse_ctx->new_ptmsi_enc, peer, + ptmsi, parse_ctx->to_bss, "new P-TMSI")) + have_patched = 1; + } + + if (parse_ctx->raid_enc) { + gbproxy_patch_raid(parse_ctx->raid_enc, peer, parse_ctx->to_bss, + parse_ctx->llc_msg_name); + have_patched = 1; + } + + if (parse_ctx->old_raid_enc && parse_ctx->old_raid_matches) { + /* TODO: Patch to invalid if P-TMSI unknown. */ + gbproxy_patch_raid(parse_ctx->old_raid_enc, peer, parse_ctx->to_bss, + parse_ctx->llc_msg_name); + have_patched = 1; + } + + if (parse_ctx->apn_ie && + peer->cfg->core_apn && + !parse_ctx->to_bss && + gbproxy_check_tlli(peer, parse_ctx->tlli)) { + size_t new_len; + gbproxy_patch_apn_ie(msg, + parse_ctx->apn_ie, parse_ctx->apn_ie_len, + peer, &new_len, parse_ctx->llc_msg_name); + *len_change += (int)new_len - (int)parse_ctx->apn_ie_len; + + have_patched = 1; + } + + if (have_patched) { + llc_len += *len_change; + ghp->crc_length += *len_change; + + /* Fix FCS */ + fcs = gprs_llc_fcs(llc, ghp->crc_length); + LOGP(DLLC, LOGL_DEBUG, "Updated LLC message, CRC: %06x -> %06x\n", + ghp->fcs, fcs); + + llc[llc_len - 3] = fcs & 0xff; + llc[llc_len - 2] = (fcs >> 8) & 0xff; + llc[llc_len - 1] = (fcs >> 16) & 0xff; + } + + return have_patched; +} + +/* patch BSSGP message to use core_mcc/mnc on the SGSN side */ +void gbproxy_patch_bssgp(struct msgb *msg, uint8_t *bssgp, size_t bssgp_len, + struct gbproxy_peer *peer, + struct gbproxy_tlli_info *tlli_info, int *len_change, + struct gprs_gb_parse_context *parse_ctx) +{ + const char *err_info = NULL; + int err_ctr = -1; + + if (!patching_is_enabled(peer, GBPROX_PATCH_BSSGP)) + return; + + if (parse_ctx->bssgp_raid_enc) + gbproxy_patch_raid(parse_ctx->bssgp_raid_enc, peer, + parse_ctx->to_bss, "BSSGP"); + + if (!patching_is_enabled(peer, GBPROX_PATCH_LLC_ATTACH_REQ)) + return; + + if (parse_ctx->need_decryption && + patching_is_required(peer, GBPROX_PATCH_LLC_ATTACH)) { + /* Patching LLC messages has been requested + * explicitly, but the message (including the + * type) is encrypted, so we possibly fail to + * patch the LLC part of the message. */ + err_ctr = GBPROX_PEER_CTR_PATCH_CRYPT_ERR; + err_info = "GMM message is encrypted"; + goto patch_error; + } + + if (parse_ctx->tlli_enc && tlli_info) { + uint32_t tlli = gbproxy_map_tlli(parse_ctx->tlli, + tlli_info, parse_ctx->to_bss); + + if (tlli) { + gbproxy_patch_tlli(parse_ctx->tlli_enc, peer, tlli, + parse_ctx->to_bss, "TLLI"); + parse_ctx->tlli = tlli; + } else if (parse_ctx->to_bss) { + /* Happens with unknown (not cached) TLLI coming from + * the SGSN */ + /* TODO: What shall be done with the message in this case? */ + err_ctr = GBPROX_PEER_CTR_TLLI_UNKNOWN; + err_info = "TLLI sent by the SGSN is unknown"; + goto patch_error; + } else { + /* Internal error */ + err_ctr = GBPROX_PEER_CTR_PATCH_ERR; + err_info = "Replacement TLLI is 0"; + goto patch_error; + } + } + + if (parse_ctx->llc) { + uint8_t *llc = parse_ctx->llc; + size_t llc_len = parse_ctx->llc_len; + int llc_len_change = 0; + + gbproxy_patch_llc(msg, llc, llc_len, peer, tlli_info, + &llc_len_change, parse_ctx); + /* Note that the APN might have been resized here, but no + * pointer int the parse_ctx will refer to an adress after the + * APN. So it's possible to patch first and do the TLLI + * handling afterwards. */ + + if (llc_len_change) { + llc_len += llc_len_change; + + /* Fix LLC IE len */ + /* TODO: This is a kludge, but the a pointer to the + * start of the IE is not available here */ + if (llc[-2] == BSSGP_IE_LLC_PDU && llc[-1] & 0x80) { + /* most probably a one byte length */ + if (llc_len > 127) { + err_info = "Cannot increase size"; + err_ctr = GBPROX_PEER_CTR_PATCH_ERR; + goto patch_error; + } + llc[-1] = llc_len | 0x80; + } else { + llc[-2] = (llc_len >> 8) & 0x7f; + llc[-1] = llc_len & 0xff; + } + *len_change += llc_len_change; + } + /* Note that the tp struct might contain invalid pointers here + * if the LLC field has changed its size */ + parse_ctx->llc_len = llc_len; + } + return; + +patch_error: + OSMO_ASSERT(err_ctr >= 0); + rate_ctr_inc(&peer->ctrg->ctr[err_ctr]); + LOGP(DGPRS, LOGL_ERROR, + "NSEI=%u(%s) failed to patch BSSGP message as requested: %s.\n", + msgb_nsei(msg), parse_ctx->to_bss ? "SGSN" : "BSS", + err_info); +} + +void gbproxy_clear_patch_filter(struct gbproxy_config *cfg) +{ + if (cfg->check_imsi) { + regfree(&cfg->imsi_re_comp); + cfg->check_imsi = 0; + } +} + +int gbproxy_set_patch_filter(struct gbproxy_config *cfg, const char *filter, + const char **err_msg) +{ + static char err_buf[300]; + int rc; + + gbproxy_clear_patch_filter(cfg); + + if (!filter) + return 0; + + rc = regcomp(&cfg->imsi_re_comp, filter, + REG_EXTENDED | REG_NOSUB | REG_ICASE); + + if (rc == 0) { + cfg->check_imsi = 1; + return 0; + } + + if (err_msg) { + regerror(rc, &cfg->imsi_re_comp, + err_buf, sizeof(err_buf)); + *err_msg = err_buf; + } + + return -1; +} + +int gbproxy_check_imsi(struct gbproxy_peer *peer, + const uint8_t *imsi, size_t imsi_len) +{ + char mi_buf[200]; + int rc; + + if (!peer->cfg->check_imsi) + return 1; + + rc = gprs_is_mi_imsi(imsi, imsi_len); + if (rc > 0) + rc = gsm48_mi_to_string(mi_buf, sizeof(mi_buf), imsi, imsi_len); + if (rc <= 0) { + LOGP(DGPRS, LOGL_NOTICE, "Invalid IMSI %s\n", + osmo_hexdump(imsi, imsi_len)); + return -1; + } + + LOGP(DGPRS, LOGL_DEBUG, "Checking IMSI '%s' (%d)\n", mi_buf, rc); + + rc = regexec(&peer->cfg->imsi_re_comp, mi_buf, 0, NULL, 0); + if (rc == REG_NOMATCH) { + LOGP(DGPRS, LOGL_INFO, + "IMSI '%s' doesn't match pattern '%s'\n", + mi_buf, peer->cfg->match_re); + return 0; + } + + return 1; +} + 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 . + * + */ + +#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_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); +} + + diff --git a/openbsc/src/gprs/gb_proxy_vty.c b/openbsc/src/gprs/gb_proxy_vty.c index a85f43969..b2f25a3fd 100644 --- a/openbsc/src/gprs/gb_proxy_vty.c +++ b/openbsc/src/gprs/gb_proxy_vty.c @@ -198,7 +198,7 @@ static int set_core_apn(struct vty *vty, const char *apn, const char *filter) talloc_free(g_cfg->core_apn); g_cfg->core_apn = NULL; g_cfg->core_apn_size = 0; - gbprox_clear_patch_filter(g_cfg); + gbproxy_clear_patch_filter(g_cfg); return CMD_SUCCESS; } @@ -211,8 +211,8 @@ static int set_core_apn(struct vty *vty, const char *apn, const char *filter) } if (!filter) { - gbprox_clear_patch_filter(g_cfg); - } else if (gbprox_set_patch_filter(g_cfg, filter, &err_msg) != 0) { + gbproxy_clear_patch_filter(g_cfg); + } else if (gbproxy_set_patch_filter(g_cfg, filter, &err_msg) != 0) { vty_out(vty, "Match expression invalid: %s%s", err_msg, VTY_NEWLINE); return CMD_WARNING; @@ -551,7 +551,7 @@ DEFUN(delete_gb_tlli, delete_gb_tlli_cmd, state = &peer->patch_state; if (match == MATCH_STALE) { - found = gbprox_remove_stale_tllis(peer, time(NULL)); + found = gbproxy_remove_stale_tllis(peer, time(NULL)); if (found) vty_out(vty, "Deleted %d stale TLLI%s%s", found, found == 1 ? "" : "s", VTY_NEWLINE); @@ -573,7 +573,7 @@ DEFUN(delete_gb_tlli, delete_gb_tlli_cmd, } vty_out(vty, "Deleting TLLI %08x%s", tlli_info->tlli.current, VTY_NEWLINE); - gbprox_delete_tlli(peer, tlli_info); + gbproxy_delete_tlli(peer, tlli_info); found += 1; } diff --git a/openbsc/src/gprs/gprs_gb_parse.c b/openbsc/src/gprs/gprs_gb_parse.c new file mode 100644 index 000000000..72c0d57e8 --- /dev/null +++ b/openbsc/src/gprs/gprs_gb_parse.c @@ -0,0 +1,642 @@ +/* GPRS Gb message parser */ + +/* (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 + +/* TODO: Move shift functions to libosmocore */ + +int v_fixed_shift(uint8_t **data, size_t *data_len, + size_t len, uint8_t **value) +{ + if (len > *data_len) + goto fail; + + if (value) + *value = *data; + + *data += len; + *data_len -= len; + + return len; + +fail: + *data += *data_len; + *data_len = 0; + return -1; +} + +int tv_fixed_match(uint8_t **data, size_t *data_len, + uint8_t tag, size_t len, + uint8_t **value) +{ + size_t ie_len; + + if (*data_len == 0) + goto fail; + + if ((*data)[0] != tag) + return 0; + + if (len > *data_len - 1) + goto fail; + + if (value) + *value = *data + 1; + + ie_len = len + 1; + *data += ie_len; + *data_len -= ie_len; + + return ie_len; + +fail: + *data += *data_len; + *data_len = 0; + return -1; +} + +int tlv_match(uint8_t **data, size_t *data_len, + uint8_t tag, uint8_t **value, size_t *value_len) +{ + size_t len; + size_t ie_len; + + if (*data_len < 2) + goto fail; + + if ((*data)[0] != tag) + return 0; + + len = (*data)[1]; + if (len > *data_len - 2) + goto fail; + + if (value) + *value = *data + 2; + if (value_len) + *value_len = len; + + ie_len = len + 2; + + *data += ie_len; + *data_len -= ie_len; + + return ie_len; + +fail: + *data += *data_len; + *data_len = 0; + return -1; +} + +int lv_shift(uint8_t **data, size_t *data_len, + uint8_t **value, size_t *value_len) +{ + size_t len; + size_t ie_len; + + if (*data_len < 1) + goto fail; + + len = (*data)[0]; + if (len > *data_len - 1) + goto fail; + + if (value) + *value = *data + 1; + if (value_len) + *value_len = len; + + ie_len = len + 1; + *data += ie_len; + *data_len -= ie_len; + + return ie_len; + +fail: + *data += *data_len; + *data_len = 0; + return -1; +} + +static int gprs_gb_parse_gmm_attach_req(uint8_t *data, size_t data_len, + struct gprs_gb_parse_context *parse_ctx) +{ + uint8_t *value; + size_t value_len; + + parse_ctx->llc_msg_name = "ATTACH_REQ"; + + /* Skip MS network capability */ + if (lv_shift(&data, &data_len, NULL, &value_len) <= 0 || + value_len < 1 || value_len > 2) + /* invalid */ + return 0;; + + /* Skip Attach type */ + /* Skip Ciphering key sequence number */ + /* Skip DRX parameter */ + v_fixed_shift(&data, &data_len, 3, NULL); + + /* Get Mobile identity */ + if (lv_shift(&data, &data_len, &value, &value_len) <= 0 || + value_len < 5 || value_len > 8) + /* invalid */ + return 0; + + if (gprs_is_mi_tmsi(value, value_len)) { + parse_ctx->ptmsi_enc = value; + } else if (gprs_is_mi_imsi(value, value_len)) { + parse_ctx->imsi = value; + parse_ctx->imsi_len = value_len; + } + + if (v_fixed_shift(&data, &data_len, 6, &value) <= 0) + return 0; + + parse_ctx->old_raid_enc = value; + + return 1; +} + +static int gprs_gb_parse_gmm_attach_ack(uint8_t *data, size_t data_len, + struct gprs_gb_parse_context *parse_ctx) +{ + uint8_t *value; + size_t value_len; + + parse_ctx->llc_msg_name = "ATTACH_ACK"; + + /* Skip Attach result */ + /* Skip Force to standby */ + /* Skip Periodic RA update timer */ + /* Skip Radio priority for SMS */ + /* Skip Spare half octet */ + v_fixed_shift(&data, &data_len, 3, NULL); + + if (v_fixed_shift(&data, &data_len, 6, &value) <= 0) + return 0; + + parse_ctx->raid_enc = value; + + /* Skip P-TMSI signature (P-TMSI signature, opt, TV, length 4) */ + tv_fixed_match(&data, &data_len, GSM48_IE_GMM_PTMSI_SIG, 3, NULL); + + /* Skip Negotiated READY timer value (GPRS timer, opt, TV, length 2) */ + tv_fixed_match(&data, &data_len, GSM48_IE_GMM_TIMER_READY, 1, NULL); + + /* Allocated P-TMSI (Mobile identity, opt, TLV, length 7) */ + if (tlv_match(&data, &data_len, GSM48_IE_GMM_ALLOC_PTMSI, + &value, &value_len) > 0 && + gprs_is_mi_tmsi(value, value_len)) + parse_ctx->new_ptmsi_enc = value; + return 1; +} + +static int gprs_gb_parse_gmm_detach_req(uint8_t *data, size_t data_len, + struct gprs_gb_parse_context *parse_ctx) +{ + uint8_t *value; + size_t value_len; + int detach_type; + int power_off; + + parse_ctx->llc_msg_name = "DETACH_REQ"; + + /* Skip spare half octet */ + /* Get Detach type */ + if (v_fixed_shift(&data, &data_len, 1, &value) <= 0) + /* invalid */ + return 0; + + detach_type = *value & 0x07; + power_off = *value & 0x08 ? 1 : 0; + + if (!parse_ctx->to_bss) { + /* Mobile originated */ + + if (power_off) + parse_ctx->invalidate_tlli = 1; + + /* Get P-TMSI (Mobile identity), see GSM 24.008, 9.4.5.2 */ + if (tlv_match(&data, &data_len, + GSM48_IE_GMM_ALLOC_PTMSI, &value, &value_len) > 0) + { + if (gprs_is_mi_tmsi(value, value_len)) + parse_ctx->ptmsi_enc = value; + } + } + + return 1; +} + +static int gprs_gb_parse_gmm_ra_upd_req(uint8_t *data, size_t data_len, + struct gprs_gb_parse_context *parse_ctx) +{ + uint8_t *value; + + parse_ctx->llc_msg_name = "RA_UPD_REQ"; + + /* Skip Update type */ + /* Skip GPRS ciphering key sequence number */ + v_fixed_shift(&data, &data_len, 1, NULL); + + if (v_fixed_shift(&data, &data_len, 6, &value) <= 0) + return 0; + + parse_ctx->old_raid_enc = value; + + return 1; +} + +static int gprs_gb_parse_gmm_ra_upd_ack(uint8_t *data, size_t data_len, + struct gprs_gb_parse_context *parse_ctx) +{ + uint8_t *value; + size_t value_len; + + parse_ctx->llc_msg_name = "RA_UPD_ACK"; + + /* Skip Force to standby */ + /* Skip Update result */ + /* Skip Periodic RA update timer */ + v_fixed_shift(&data, &data_len, 2, NULL); + + if (v_fixed_shift(&data, &data_len, 6, &value) <= 0) + return 0; + + parse_ctx->raid_enc = value; + + /* Skip P-TMSI signature (P-TMSI signature, opt, TV, length 4) */ + tv_fixed_match(&data, &data_len, GSM48_IE_GMM_PTMSI_SIG, 3, NULL); + + /* Allocated P-TMSI (Mobile identity, opt, TLV, length 7) */ + if (tlv_match(&data, &data_len, GSM48_IE_GMM_ALLOC_PTMSI, + &value, &value_len) > 0 && + gprs_is_mi_tmsi(value, value_len)) + parse_ctx->new_ptmsi_enc = value; + + return 1; +} + +static int gprs_gb_parse_gmm_ptmsi_reall_cmd(uint8_t *data, size_t data_len, + struct gprs_gb_parse_context *parse_ctx) +{ + uint8_t *value; + size_t value_len; + + parse_ctx->llc_msg_name = "PTMSI_REALL_CMD"; + + LOGP(DLLC, LOGL_NOTICE, + "Got P-TMSI Reallocation Command which is not covered by unit tests yet.\n"); + + /* Allocated P-TMSI */ + if (lv_shift(&data, &data_len, &value, &value_len) > 0 && + gprs_is_mi_tmsi(value, value_len)) + parse_ctx->new_ptmsi_enc = value; + + if (v_fixed_shift(&data, &data_len, 6, &value) <= 0) + return 0; + + parse_ctx->raid_enc = value; + + return 1; +} + +static int gprs_gb_parse_gmm_id_resp(uint8_t *data, size_t data_len, + struct gprs_gb_parse_context *parse_ctx) +{ + uint8_t *value; + size_t value_len; + + parse_ctx->llc_msg_name = "ID_RESP"; + + /* Mobile identity, Mobile identity 10.5.1.4, M LV 2-10 */ + if (lv_shift(&data, &data_len, &value, &value_len) <= 0 || + value_len < 1 || value_len > 9) + /* invalid */ + return 0; + + if (gprs_is_mi_tmsi(value, value_len)) { + parse_ctx->ptmsi_enc = value; + } else if (gprs_is_mi_imsi(value, value_len)) { + parse_ctx->imsi = value; + parse_ctx->imsi_len = value_len; + } + + return 1; +} + +static int gprs_gb_parse_gsm_act_pdp_req(uint8_t *data, size_t data_len, + struct gprs_gb_parse_context *parse_ctx) +{ + ssize_t old_len; + uint8_t *value; + size_t value_len; + + parse_ctx->llc_msg_name = "ACT_PDP_REQ"; + + /* Skip Requested NSAPI */ + /* Skip Requested LLC SAPI */ + v_fixed_shift(&data, &data_len, 2, NULL); + + /* Skip Requested QoS (support 04.08 and 24.008) */ + if (lv_shift(&data, &data_len, NULL, &value_len) <= 0 || + value_len < 4 || value_len > 14) + /* invalid */ + return 0;; + + /* Skip Requested PDP address */ + if (lv_shift(&data, &data_len, NULL, &value_len) <= 0 || + value_len < 2 || value_len > 18) + /* invalid */ + return 0; + + /* Access point name */ + old_len = tlv_match(&data, &data_len, + GSM48_IE_GSM_APN, &value, &value_len); + + if (old_len > 0 && value_len >=1 && value_len <= 100) { + parse_ctx->apn_ie = data - old_len; + parse_ctx->apn_ie_len = old_len; + } + + return 1; +} + +int gprs_gb_parse_dtap(uint8_t *data, size_t data_len, + struct gprs_gb_parse_context *parse_ctx) +{ + struct gsm48_hdr *g48h; + + if (v_fixed_shift(&data, &data_len, sizeof(*g48h), (uint8_t **)&g48h) <= 0) + return 0; + + parse_ctx->g48_hdr = g48h; + + if ((g48h->proto_discr & 0x0f) != GSM48_PDISC_MM_GPRS && + (g48h->proto_discr & 0x0f) != GSM48_PDISC_SM_GPRS) + return 1; + + switch (g48h->msg_type) { + case GSM48_MT_GMM_ATTACH_REQ: + return gprs_gb_parse_gmm_attach_req(data, data_len, parse_ctx); + + case GSM48_MT_GMM_ATTACH_ACK: + return gprs_gb_parse_gmm_attach_ack(data, data_len, parse_ctx); + + case GSM48_MT_GMM_RA_UPD_REQ: + return gprs_gb_parse_gmm_ra_upd_req(data, data_len, parse_ctx); + + case GSM48_MT_GMM_RA_UPD_ACK: + return gprs_gb_parse_gmm_ra_upd_ack(data, data_len, parse_ctx); + + case GSM48_MT_GMM_PTMSI_REALL_CMD: + return gprs_gb_parse_gmm_ptmsi_reall_cmd(data, data_len, parse_ctx); + + case GSM48_MT_GSM_ACT_PDP_REQ: + return gprs_gb_parse_gsm_act_pdp_req(data, data_len, parse_ctx); + + case GSM48_MT_GMM_ID_RESP: + return gprs_gb_parse_gmm_id_resp(data, data_len, parse_ctx); + + case GSM48_MT_GMM_DETACH_REQ: + return gprs_gb_parse_gmm_detach_req(data, data_len, parse_ctx); + + case GSM48_MT_GMM_DETACH_ACK: + parse_ctx->llc_msg_name = "DETACH_ACK"; + parse_ctx->invalidate_tlli = 1; + break; + + default: + break; + }; + + return 1; +} + +int gprs_gb_parse_llc(uint8_t *llc, size_t llc_len, + struct gprs_gb_parse_context *parse_ctx) +{ + struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed; + int rc; + int fcs; + + /* parse LLC */ + rc = gprs_llc_hdr_parse(ghp, llc, llc_len); + gprs_llc_hdr_dump(ghp); + if (rc != 0) { + LOGP(DLLC, LOGL_NOTICE, "Error during LLC header parsing\n"); + return 0; + } + + fcs = gprs_llc_fcs(llc, ghp->crc_length); + LOGP(DLLC, LOGL_DEBUG, "Got LLC message, CRC: %06x (computed %06x)\n", + ghp->fcs, fcs); + + if (!ghp->data) + return 0; + + if (ghp->sapi != GPRS_SAPI_GMM) + return 1; + + if (ghp->cmd != GPRS_LLC_UI) + return 1; + + if (ghp->is_encrypted) { + parse_ctx->need_decryption = 1; + return 0; + } + + return gprs_gb_parse_dtap(ghp->data, ghp->data_len, parse_ctx); +} + +int gprs_gb_parse_bssgp(uint8_t *bssgp, size_t bssgp_len, + struct gprs_gb_parse_context *parse_ctx) +{ + struct bssgp_normal_hdr *bgph; + struct bssgp_ud_hdr *budh = NULL; + struct tlv_parsed *tp = &parse_ctx->bssgp_tp; + uint8_t pdu_type; + uint8_t *data; + size_t data_len; + int rc; + + if (bssgp_len < sizeof(struct bssgp_normal_hdr)) + return 0; + + bgph = (struct bssgp_normal_hdr *)bssgp; + pdu_type = bgph->pdu_type; + + if (pdu_type == BSSGP_PDUT_UL_UNITDATA || + pdu_type == BSSGP_PDUT_DL_UNITDATA) { + if (bssgp_len < sizeof(struct bssgp_ud_hdr)) + return 0; + budh = (struct bssgp_ud_hdr *)bssgp; + bgph = NULL; + data = budh->data; + data_len = bssgp_len - sizeof(*budh); + } else { + data = bgph->data; + data_len = bssgp_len - sizeof(*bgph); + } + + if (bssgp_tlv_parse(tp, data, data_len) < 0) + return 0; + + parse_ctx->pdu_type = pdu_type; + parse_ctx->bud_hdr = budh; + parse_ctx->bgp_hdr = bgph; + parse_ctx->bssgp_data = data; + parse_ctx->bssgp_data_len = data_len; + + if (budh) + parse_ctx->tlli_enc = (uint8_t *)&budh->tlli; + + if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) + parse_ctx->bssgp_raid_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA); + + if (TLVP_PRESENT(tp, BSSGP_IE_CELL_ID)) + parse_ctx->bssgp_raid_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_CELL_ID); + + if (TLVP_PRESENT(tp, BSSGP_IE_IMSI)) { + parse_ctx->imsi = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_IMSI); + parse_ctx->imsi_len = TLVP_LEN(tp, BSSGP_IE_IMSI); + } + + /* TODO: This is TLLI old, don't confuse with TLLI current, add + * and use tlli_old_enc instead */ + if (0 && TLVP_PRESENT(tp, BSSGP_IE_TLLI)) + parse_ctx->tlli_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_TLLI); + + if (TLVP_PRESENT(tp, BSSGP_IE_TMSI) && pdu_type == BSSGP_PDUT_PAGING_PS) + parse_ctx->ptmsi_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_TMSI); + + if (TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU)) { + uint8_t *llc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_LLC_PDU); + size_t llc_len = TLVP_LEN(tp, BSSGP_IE_LLC_PDU); + + rc = gprs_gb_parse_llc(llc, llc_len, parse_ctx); + if (!rc) + return 0; + + parse_ctx->llc = llc; + parse_ctx->llc_len = llc_len; + } + + if (parse_ctx->tlli_enc) { + uint32_t tmp_tlli; + memcpy(&tmp_tlli, parse_ctx->tlli_enc, sizeof(tmp_tlli)); + parse_ctx->tlli = ntohl(tmp_tlli); + } + + return 1; +} + +void gprs_gb_log_parse_context(struct gprs_gb_parse_context *parse_ctx, + const char *default_msg_name) +{ + const char *msg_name = default_msg_name; + const char *sep = ""; + + if (!parse_ctx->tlli_enc && + !parse_ctx->ptmsi_enc && + !parse_ctx->new_ptmsi_enc && + !parse_ctx->imsi) + return; + + if (parse_ctx->llc_msg_name) + msg_name = parse_ctx->llc_msg_name; + + LOGP(DGPRS, LOGL_DEBUG, "%s: Got", msg_name); + + if (parse_ctx->tlli_enc) { + LOGP(DGPRS, LOGL_DEBUG, "%s TLLI %08x", sep, parse_ctx->tlli); + sep = ","; + } + + if (parse_ctx->bssgp_raid_enc) { + struct gprs_ra_id raid; + gsm48_parse_ra(&raid, parse_ctx->bssgp_raid_enc); + LOGP(DGPRS, LOGL_DEBUG, "%s BSSGP RAID %u-%u-%u-%u", sep, + raid.mcc, raid.mnc, raid.lac, raid.rac); + sep = ","; + } + + if (parse_ctx->raid_enc) { + struct gprs_ra_id raid; + gsm48_parse_ra(&raid, parse_ctx->raid_enc); + LOGP(DGPRS, LOGL_DEBUG, "%s RAID %u-%u-%u-%u", sep, + raid.mcc, raid.mnc, raid.lac, raid.rac); + sep = ","; + } + + if (parse_ctx->old_raid_enc) { + struct gprs_ra_id raid; + gsm48_parse_ra(&raid, parse_ctx->old_raid_enc); + LOGP(DGPRS, LOGL_DEBUG, "%s old RAID %u-%u-%u-%u", sep, + raid.mcc, raid.mnc, raid.lac, raid.rac); + sep = ","; + } + + if (parse_ctx->ptmsi_enc) { + uint32_t ptmsi = GSM_RESERVED_TMSI; + int ok; + ok = gprs_parse_mi_tmsi(parse_ctx->ptmsi_enc, GSM48_TMSI_LEN, &ptmsi); + LOGP(DGPRS, LOGL_DEBUG, "%s PTMSI %08x%s", + sep, ptmsi, ok ? "" : " (parse error)"); + sep = ","; + } + + if (parse_ctx->new_ptmsi_enc) { + uint32_t new_ptmsi = GSM_RESERVED_TMSI; + int ok; + ok = gprs_parse_mi_tmsi(parse_ctx->new_ptmsi_enc, GSM48_TMSI_LEN, + &new_ptmsi); + LOGP(DGPRS, LOGL_DEBUG, "%s new PTMSI %08x%s", + sep, new_ptmsi, ok ? "" : " (parse error)"); + sep = ","; + } + + if (parse_ctx->imsi) { + 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_DEBUG, "%s IMSI %s", + sep, mi_buf); + sep = ","; + } + if (parse_ctx->invalidate_tlli) { + LOGP(DGPRS, LOGL_DEBUG, "%s invalidate", sep); + sep = ","; + } + + LOGP(DGPRS, LOGL_DEBUG, "\n"); +} + diff --git a/openbsc/src/gprs/gprs_utils.c b/openbsc/src/gprs/gprs_utils.c index 1a8fa4316..55d4efda1 100644 --- a/openbsc/src/gprs/gprs_utils.c +++ b/openbsc/src/gprs/gprs_utils.c @@ -24,6 +24,8 @@ #include #include +#include + #include /* FIXME: this needs to go to libosmocore/msgb.c */ @@ -162,3 +164,40 @@ int gprs_str_to_apn(uint8_t *apn_enc, size_t max_len, const char *str) return len; } +/* GSM 04.08, 10.5.1.4 */ +int gprs_is_mi_tmsi(const uint8_t *value, size_t value_len) +{ + if (value_len != GSM48_TMSI_LEN) + return 0; + + if (!value || (value[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_TMSI) + return 0; + + return 1; +} + +/* GSM 04.08, 10.5.1.4 */ +int gprs_is_mi_imsi(const uint8_t *value, size_t value_len) +{ + if (value_len == 0) + return 0; + + if (!value || (value[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_IMSI) + return 0; + + return 1; +} + +int gprs_parse_mi_tmsi(const uint8_t *value, size_t value_len, uint32_t *tmsi) +{ + uint32_t tmsi_be; + + if (!gprs_is_mi_tmsi(value, value_len)) + return 0; + + memcpy(&tmsi_be, value + 1, sizeof(tmsi_be)); + + *tmsi = ntohl(tmsi_be); + return 1; +} + -- cgit v1.2.3