From 9114bee2424fa5a5e30261054573f9f78b5c5477 Mon Sep 17 00:00:00 2001 From: Jacob Erlbeck Date: Tue, 19 Aug 2014 12:21:01 +0200 Subject: [PATCH] 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/include/openbsc/gb_proxy.h | 120 +- openbsc/include/openbsc/gprs_gb_parse.h | 52 + openbsc/include/openbsc/gprs_utils.h | 3 + openbsc/src/gprs/Makefile.am | 3 +- openbsc/src/gprs/gb_proxy.c | 1786 +---------------------- 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 + openbsc/tests/gbproxy/Makefile.am | 3 + openbsc/tests/gbproxy/gbproxy_test.c | 126 +- 12 files changed, 1977 insertions(+), 1826 deletions(-) create mode 100644 openbsc/include/openbsc/gprs_gb_parse.h 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 diff --git a/openbsc/include/openbsc/gb_proxy.h b/openbsc/include/openbsc/gb_proxy.h index e7d1b1cbe..345a317e8 100644 --- a/openbsc/include/openbsc/gb_proxy.h +++ b/openbsc/include/openbsc/gb_proxy.h @@ -11,6 +11,7 @@ #include struct rate_ctr_group; +struct gprs_gb_parse_context; enum gbproxy_patch_mode { GBPROX_PATCH_DEFAULT, @@ -22,6 +23,42 @@ enum gbproxy_patch_mode { GBPROX_PATCH_LLC, /*!< BSSGP and all supported LLC msgs */ }; +enum gbproxy_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, +}; + +enum gbproxy_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, +}; + struct gbproxy_config { /* parsed from config file */ uint16_t nsip_sgsn_nsei; @@ -130,31 +167,74 @@ int gbprox_reset_persistent_nsvcs(struct gprs_ns_inst *nsi); void gbprox_reset(struct gbproxy_config *cfg); -int gbprox_set_patch_filter(struct gbproxy_config *cfg, const char *filter, - const char **err_msg); -void gbprox_clear_patch_filter(struct gbproxy_config *cfg); - -void gbprox_delete_tlli(struct gbproxy_peer *peer, - struct gbproxy_tlli_info *tlli_info); -int gbprox_remove_stale_tllis(struct gbproxy_peer *peer, time_t now); int gbprox_cleanup_peers(struct gbproxy_config *cfg, uint16_t nsei, uint16_t bvci); struct gbproxy_peer *gbprox_peer_by_nsei(struct gbproxy_config *cfg, uint16_t nsei); -struct gbproxy_tlli_info *gbprox_find_tlli(struct gbproxy_peer *peer, - uint32_t tlli); -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 *gbprox_find_tlli_by_sgsn_tlli( - struct gbproxy_peer *peer, - uint32_t tlli); -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_peer *gbproxy_peer_alloc(struct gbproxy_config *cfg, uint16_t bvci); void gbproxy_peer_free(struct gbproxy_peer *peer); -int gbprox_check_imsi(struct gbproxy_peer *peer, - const uint8_t *imsi, size_t imsi_len); +/* TLLI state handling */ +void gbproxy_delete_tllis(struct gbproxy_peer *peer); +int gbproxy_check_tlli(struct gbproxy_peer *peer, uint32_t tlli); +struct gbproxy_tlli_info *gbprox_find_tlli_by_ptmsi( + struct gbproxy_peer *peer, + uint32_t ptmsi); +uint32_t gbproxy_map_tlli( + uint32_t other_tlli, struct gbproxy_tlli_info *tlli_info, int to_bss); +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 *gbproxy_update_tlli_state_dl( + struct gbproxy_peer *peer, time_t now, + struct gprs_gb_parse_context *parse_ctx); +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); +int gbproxy_remove_stale_tllis(struct gbproxy_peer *peer, time_t now); +void gbproxy_delete_tlli(struct gbproxy_peer *peer, + struct gbproxy_tlli_info *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 *gbproxy_find_tlli( + struct gbproxy_peer *peer, uint32_t tlli); +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 *gbproxy_find_tlli_by_sgsn_tlli( + struct gbproxy_peer *peer, + uint32_t tlli); +struct gbproxy_tlli_info *gbproxy_find_tlli_by_ptmsi( + struct gbproxy_peer *peer, + uint32_t ptmsi); + +/* needed by gb_proxy_tlli.h */ +uint32_t gbproxy_make_bss_ptmsi(struct gbproxy_peer *peer, uint32_t sgsn_ptmsi); +uint32_t gbproxy_make_sgsn_tlli( + struct gbproxy_peer *peer, struct gbproxy_tlli_info *tlli_info, + uint32_t bss_tlli); +int gbproxy_check_imsi( + struct gbproxy_peer *peer, const uint8_t *imsi, size_t imsi_len); + +/* Message patching */ +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) + __attribute__((nonnull)); + +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) + __attribute__((nonnull)); + +int gbproxy_set_patch_filter( + struct gbproxy_config *cfg, const char *filter, const char **err_msg); +void gbproxy_clear_patch_filter(struct gbproxy_config *cfg); +int gbproxy_check_imsi( + struct gbproxy_peer *peer, const uint8_t *imsi, size_t imsi_len); + #endif diff --git a/openbsc/include/openbsc/gprs_gb_parse.h b/openbsc/include/openbsc/gprs_gb_parse.h new file mode 100644 index 000000000..e5ef4ef43 --- /dev/null +++ b/openbsc/include/openbsc/gprs_gb_parse.h @@ -0,0 +1,52 @@ +#pragma once + +#include + +#include + +struct gprs_gb_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; +}; + +int gprs_gb_parse_dtap(uint8_t *data, size_t data_len, + struct gprs_gb_parse_context *parse_ctx) __attribute__((nonnull)); + +int gprs_gb_parse_llc(uint8_t *llc, size_t llc_len, + struct gprs_gb_parse_context *parse_ctx) __attribute__((nonnull)); + +int gprs_gb_parse_bssgp(uint8_t *bssgp, size_t bssgp_len, + struct gprs_gb_parse_context *parse_ctx) __attribute__((nonnull)); + +void gprs_gb_log_parse_context(struct gprs_gb_parse_context *parse_ctx, + const char *default_msg_name) __attribute__((nonnull(1))); diff --git a/openbsc/include/openbsc/gprs_utils.h b/openbsc/include/openbsc/gprs_utils.h index 2ad5fe490..e610fdec9 100644 --- a/openbsc/include/openbsc/gprs_utils.h +++ b/openbsc/include/openbsc/gprs_utils.h @@ -31,3 +31,6 @@ int gprs_msgb_resize_area(struct msgb *msg, uint8_t *area, size_t old_size, size_t new_size); char *gprs_apn_to_str(char *out_str, const uint8_t *apn_enc, size_t rest_chars); int gprs_str_to_apn(uint8_t *apn_enc, size_t max_len, const char *str); +int gprs_is_mi_tmsi(const uint8_t *value, size_t value_len); +int gprs_is_mi_imsi(const uint8_t *value, size_t value_len); +int gprs_parse_mi_tmsi(const uint8_t *value, size_t value_len, uint32_t *tmsi); 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,629 +208,6 @@ 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) -{ - 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; -} - -/* GSM 04.08, 10.5.1.4 */ -static int 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 */ -static int 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; -} - -static int parse_mi_tmsi(const uint8_t *value, size_t value_len, uint32_t *tmsi) -{ - 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 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; - - 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; -} - -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; - - 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 *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; - - 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 *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 (!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 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; -} - -static void gbprox_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) - gbprox_delete_tlli(peer, tlli_info); - - OSMO_ASSERT(state->enabled_tllis_count == 0); - OSMO_ASSERT(llist_empty(&state->enabled_tllis)); -} - -void gbprox_clear_patch_filter(struct gbproxy_config *cfg) -{ - if (cfg->check_imsi) { - regfree(&cfg->imsi_re_comp); - cfg->check_imsi = 0; - } -} - -int gbprox_set_patch_filter(struct gbproxy_config *cfg, const char *filter, - const char **err_msg) -{ - static char err_buf[300]; - int rc; - - gbprox_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 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; - } - - 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; -} - -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; - - 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 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, @@ -907,412 +248,6 @@ static void gbprox_update_current_raid(uint8_t *raid_enc, 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)) { @@ -1339,285 +274,8 @@ struct gbproxy_peer *peer_by_bssgp_tlv(struct gbproxy_config *cfg, struct tlv_pa 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 gbproxy_make_bss_ptmsi(struct gbproxy_peer *peer, + uint32_t sgsn_ptmsi) { uint32_t bss_ptmsi; if (!peer->cfg->patch_ptmsi) { @@ -1627,7 +285,7 @@ static uint32_t gbprox_make_bss_ptmsi(struct gbproxy_peer *peer, bss_ptmsi = rand_r(&peer->cfg->bss_ptmsi_state); bss_ptmsi = bss_ptmsi | 0xC0000000; - if (gbprox_find_tlli_by_ptmsi(peer, bss_ptmsi)) + if (gbproxy_find_tlli_by_ptmsi(peer, bss_ptmsi)) bss_ptmsi = GSM_RESERVED_TMSI; } while (bss_ptmsi == GSM_RESERVED_TMSI); } @@ -1635,9 +293,9 @@ static uint32_t gbprox_make_bss_ptmsi(struct gbproxy_peer *peer, 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 gbproxy_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) { @@ -1651,391 +309,19 @@ static uint32_t gbprox_make_sgsn_tlli(struct gbproxy_peer *peer, 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)) + if (gbproxy_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}; + struct gprs_gb_parse_context parse_ctx = {0}; int rc; int len_change = 0; time_t now; @@ -2047,8 +333,8 @@ static void gbprox_process_bssgp_ul(struct gbproxy_config *cfg, parse_ctx.to_bss = 0; /* Parse BSSGP/LLC */ - rc = gbprox_parse_bssgp(msgb_bssgph(msg), msgb_bssgp_len(msg), - &parse_ctx); + rc = gprs_gb_parse_bssgp(msgb_bssgph(msg), msgb_bssgp_len(msg), + &parse_ctx); if (!rc) { if (!parse_ctx.need_decryption) { @@ -2089,12 +375,25 @@ static void gbprox_process_bssgp_ul(struct gbproxy_config *cfg, 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); + 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; - gbprox_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg), - peer, tlli_info, &len_change, &parse_ctx); + default: + break; + } + } - gbprox_update_state_after(peer, tlli_info, now, &parse_ctx); + gprs_gb_log_parse_context(&parse_ctx, "BSSGP"); + + tlli_info = gbproxy_update_tlli_state_ul(peer, now, &parse_ctx); + + gbproxy_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg), + peer, tlli_info, &len_change, &parse_ctx); + + gbproxy_update_tlli_state_after(peer, tlli_info, now, &parse_ctx); return; } @@ -2104,7 +403,7 @@ static void gbprox_process_bssgp_dl(struct gbproxy_config *cfg, struct msgb *msg, struct gbproxy_peer *peer) { - struct gbproxy_parse_context parse_ctx = {0}; + struct gprs_gb_parse_context parse_ctx = {0}; int rc; int len_change = 0; time_t now; @@ -2112,8 +411,8 @@ static void gbprox_process_bssgp_dl(struct gbproxy_config *cfg, parse_ctx.to_bss = 1; - rc = gbprox_parse_bssgp(msgb_bssgph(msg), msgb_bssgp_len(msg), - &parse_ctx); + rc = gprs_gb_parse_bssgp(msgb_bssgph(msg), msgb_bssgp_len(msg), + &parse_ctx); if (!rc) { if (!parse_ctx.need_decryption) { @@ -2143,12 +442,25 @@ static void gbprox_process_bssgp_dl(struct gbproxy_config *cfg, now = time(NULL); - tlli_info = gbprox_update_state_dl(peer, now, &parse_ctx); + 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; - gbprox_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg), - peer, tlli_info, &len_change, &parse_ctx); + default: + break; + } + } - gbprox_update_state_after(peer, tlli_info, now, &parse_ctx); + gprs_gb_log_parse_context(&parse_ctx, "BSSGP"); + + tlli_info = gbproxy_update_tlli_state_dl(peer, now, &parse_ctx); + + gbproxy_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg), + peer, tlli_info, &len_change, &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; +} + diff --git a/openbsc/tests/gbproxy/Makefile.am b/openbsc/tests/gbproxy/Makefile.am index 409d79125..5f5e58248 100644 --- a/openbsc/tests/gbproxy/Makefile.am +++ b/openbsc/tests/gbproxy/Makefile.am @@ -9,6 +9,9 @@ noinst_PROGRAMS = gbproxy_test gbproxy_test_SOURCES = gbproxy_test.c gbproxy_test_LDADD = \ $(top_builddir)/src/gprs/gb_proxy.o \ + $(top_builddir)/src/gprs/gb_proxy_patch.o \ + $(top_builddir)/src/gprs/gb_proxy_tlli.o \ + $(top_builddir)/src/gprs/gprs_gb_parse.o \ $(top_builddir)/src/gprs/gprs_llc_parse.o \ $(top_builddir)/src/gprs/crc24.o \ $(top_builddir)/src/gprs/gprs_utils.o \ diff --git a/openbsc/tests/gbproxy/gbproxy_test.c b/openbsc/tests/gbproxy/gbproxy_test.c index d7ddafd80..1d89ef6f2 100644 --- a/openbsc/tests/gbproxy/gbproxy_test.c +++ b/openbsc/tests/gbproxy/gbproxy_test.c @@ -1183,7 +1183,7 @@ static void test_gbproxy_ra_patching() configure_bss_peers(bss_peer, ARRAY_SIZE(bss_peer)); gbcfg.match_re = talloc_strdup(NULL, "^9898|^121314"); - if (gbprox_set_patch_filter(&gbcfg, gbcfg.match_re, &err_msg) != 0) { + if (gbproxy_set_patch_filter(&gbcfg, gbcfg.match_re, &err_msg) != 0) { fprintf(stderr, "Failed to compile RE '%s': %s\n", gbcfg.match_re, err_msg); exit(1); @@ -1236,7 +1236,7 @@ static void test_gbproxy_ra_patching() GPRS_SAPI_GMM, 1, dtap_attach_acc, sizeof(dtap_attach_acc)); - tlli_info = gbprox_find_tlli_by_sgsn_tlli(peer, local_tlli); + tlli_info = gbproxy_find_tlli_by_sgsn_tlli(peer, local_tlli); OSMO_ASSERT(tlli_info); OSMO_ASSERT(tlli_info->tlli.assigned == local_tlli); OSMO_ASSERT(tlli_info->tlli.current != local_tlli); @@ -1252,7 +1252,7 @@ static void test_gbproxy_ra_patching() GPRS_SAPI_GMM, 4, dtap_attach_complete, sizeof(dtap_attach_complete)); - tlli_info = gbprox_find_tlli_by_sgsn_tlli(peer, local_tlli); + tlli_info = gbproxy_find_tlli_by_sgsn_tlli(peer, local_tlli); OSMO_ASSERT(tlli_info); OSMO_ASSERT(tlli_info->tlli.assigned == local_tlli); OSMO_ASSERT(tlli_info->tlli.current != local_tlli); @@ -1269,7 +1269,7 @@ static void test_gbproxy_ra_patching() GPRS_SAPI_GMM, 3, dtap_act_pdp_ctx_req, sizeof(dtap_act_pdp_ctx_req)); - tlli_info = gbprox_find_tlli_by_sgsn_tlli(peer, local_tlli); + tlli_info = gbproxy_find_tlli_by_sgsn_tlli(peer, local_tlli); OSMO_ASSERT(tlli_info); OSMO_ASSERT(tlli_info->tlli.assigned == local_tlli); OSMO_ASSERT(tlli_info->tlli.current != local_tlli); @@ -1285,7 +1285,7 @@ static void test_gbproxy_ra_patching() GPRS_SAPI_GMM, 2, dtap_gmm_information, sizeof(dtap_gmm_information)); - tlli_info = gbprox_find_tlli_by_sgsn_tlli(peer, local_tlli); + tlli_info = gbproxy_find_tlli_by_sgsn_tlli(peer, local_tlli); OSMO_ASSERT(tlli_info); OSMO_ASSERT(tlli_info->tlli.assigned == 0); OSMO_ASSERT(tlli_info->tlli.current == local_tlli); @@ -1465,7 +1465,7 @@ static void test_gbproxy_ptmsi_patching() dump_peers(stdout, 0, 0, &gbcfg); - tlli_info = gbprox_find_tlli_by_sgsn_tlli(peer, random_sgsn_tlli); + tlli_info = gbproxy_find_tlli_by_sgsn_tlli(peer, random_sgsn_tlli); OSMO_ASSERT(tlli_info); OSMO_ASSERT(tlli_info->tlli.assigned == local_bss_tlli); OSMO_ASSERT(tlli_info->tlli.current == foreign_bss_tlli); @@ -1485,7 +1485,7 @@ static void test_gbproxy_ptmsi_patching() dump_peers(stdout, 0, 0, &gbcfg); - tlli_info = gbprox_find_tlli_by_sgsn_tlli(peer, local_sgsn_tlli); + tlli_info = gbproxy_find_tlli_by_sgsn_tlli(peer, local_sgsn_tlli); OSMO_ASSERT(tlli_info); OSMO_ASSERT(tlli_info->tlli.assigned == local_bss_tlli); OSMO_ASSERT(tlli_info->tlli.current == foreign_bss_tlli); @@ -1503,7 +1503,7 @@ static void test_gbproxy_ptmsi_patching() dump_peers(stdout, 0, 0, &gbcfg); - tlli_info = gbprox_find_tlli_by_sgsn_tlli(peer, local_sgsn_tlli); + tlli_info = gbproxy_find_tlli_by_sgsn_tlli(peer, local_sgsn_tlli); OSMO_ASSERT(tlli_info); OSMO_ASSERT(tlli_info->tlli.current == local_bss_tlli); OSMO_ASSERT(tlli_info->tlli.assigned == 0); @@ -1805,7 +1805,7 @@ static void test_gbproxy_tlli_expire(void) gbproxy_init_config(&cfg); - if (gbprox_set_patch_filter(&cfg, filter_re, &err_msg) != 0) { + if (gbproxy_set_patch_filter(&cfg, filter_re, &err_msg) != 0) { fprintf(stderr, "gbprox_set_patch_filter: got error: %s\n", err_msg); OSMO_ASSERT(err_msg == NULL); @@ -1822,16 +1822,16 @@ static void test_gbproxy_tlli_expire(void) OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 0); printf(" Add TLLI 1, IMSI 1\n"); - tlli_info = gbprox_register_tlli(peer, tlli1, - imsi1, ARRAY_SIZE(imsi1), now); + tlli_info = gbproxy_register_tlli(peer, tlli1, + imsi1, ARRAY_SIZE(imsi1), now); OSMO_ASSERT(tlli_info); OSMO_ASSERT(tlli_info->tlli.current == tlli1); OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 1); /* replace the old entry */ printf(" Add TLLI 2, IMSI 1 (should replace TLLI 1)\n"); - tlli_info = gbprox_register_tlli(peer, tlli2, - imsi1, ARRAY_SIZE(imsi1), now); + tlli_info = gbproxy_register_tlli(peer, tlli2, + imsi1, ARRAY_SIZE(imsi1), now); OSMO_ASSERT(tlli_info); OSMO_ASSERT(tlli_info->tlli.current == tlli2); OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 1); @@ -1839,10 +1839,10 @@ static void test_gbproxy_tlli_expire(void) dump_peers(stdout, 2, now, &cfg); /* verify that 5678 has survived */ - tlli_info = gbprox_find_tlli_by_mi(peer, imsi1, ARRAY_SIZE(imsi1)); + tlli_info = gbproxy_find_tlli_by_mi(peer, imsi1, ARRAY_SIZE(imsi1)); OSMO_ASSERT(tlli_info); OSMO_ASSERT(tlli_info->tlli.current == tlli2); - tlli_info = gbprox_find_tlli_by_mi(peer, imsi2, ARRAY_SIZE(imsi2)); + tlli_info = gbproxy_find_tlli_by_mi(peer, imsi2, ARRAY_SIZE(imsi2)); OSMO_ASSERT(!tlli_info); printf("\n"); @@ -1861,16 +1861,16 @@ static void test_gbproxy_tlli_expire(void) OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 0); printf(" Add TLLI 1, IMSI 1\n"); - tlli_info = gbprox_register_tlli(peer, tlli1, - imsi1, ARRAY_SIZE(imsi1), now); + tlli_info = gbproxy_register_tlli(peer, tlli1, + imsi1, ARRAY_SIZE(imsi1), now); OSMO_ASSERT(tlli_info); OSMO_ASSERT(tlli_info->tlli.current == tlli1); OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 1); /* try to replace the old entry */ printf(" Add TLLI 1, IMSI 2 (should replace IMSI 1)\n"); - tlli_info = gbprox_register_tlli(peer, tlli1, - imsi2, ARRAY_SIZE(imsi2), now); + tlli_info = gbproxy_register_tlli(peer, tlli1, + imsi2, ARRAY_SIZE(imsi2), now); OSMO_ASSERT(tlli_info); OSMO_ASSERT(tlli_info->tlli.current == tlli1); OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 1); @@ -1878,9 +1878,9 @@ static void test_gbproxy_tlli_expire(void) dump_peers(stdout, 2, now, &cfg); /* verify that 5678 has survived */ - tlli_info = gbprox_find_tlli_by_mi(peer, imsi1, ARRAY_SIZE(imsi1)); + tlli_info = gbproxy_find_tlli_by_mi(peer, imsi1, ARRAY_SIZE(imsi1)); OSMO_ASSERT(!tlli_info); - tlli_info = gbprox_find_tlli_by_mi(peer, imsi2, ARRAY_SIZE(imsi2)); + tlli_info = gbproxy_find_tlli_by_mi(peer, imsi2, ARRAY_SIZE(imsi2)); OSMO_ASSERT(tlli_info); OSMO_ASSERT(tlli_info->tlli.current == tlli1); @@ -1901,24 +1901,24 @@ static void test_gbproxy_tlli_expire(void) OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 0); printf(" Add TLLI 1, IMSI 1\n"); - gbprox_register_tlli(peer, tlli1, imsi1, ARRAY_SIZE(imsi1), now); + gbproxy_register_tlli(peer, tlli1, imsi1, ARRAY_SIZE(imsi1), now); OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 1); /* replace the old entry */ printf(" Add TLLI 2, IMSI 2 (should replace IMSI 1)\n"); - gbprox_register_tlli(peer, tlli2, imsi2, ARRAY_SIZE(imsi2), now); + gbproxy_register_tlli(peer, tlli2, imsi2, ARRAY_SIZE(imsi2), now); OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 2); - num_removed = gbprox_remove_stale_tllis(peer, time(NULL) + 2); + num_removed = gbproxy_remove_stale_tllis(peer, time(NULL) + 2); OSMO_ASSERT(num_removed == 1); OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 1); dump_peers(stdout, 2, now, &cfg); /* verify that 5678 has survived */ - tlli_info = gbprox_find_tlli_by_mi(peer, imsi1, ARRAY_SIZE(imsi1)); + tlli_info = gbproxy_find_tlli_by_mi(peer, imsi1, ARRAY_SIZE(imsi1)); OSMO_ASSERT(!tlli_info); - tlli_info = gbprox_find_tlli_by_mi(peer, imsi2, ARRAY_SIZE(imsi2)); + tlli_info = gbproxy_find_tlli_by_mi(peer, imsi2, ARRAY_SIZE(imsi2)); OSMO_ASSERT(tlli_info); OSMO_ASSERT(tlli_info->tlli.current == tlli2); @@ -1939,24 +1939,24 @@ static void test_gbproxy_tlli_expire(void) OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 0); printf(" Add TLLI 1, IMSI 1 (should expire after timeout)\n"); - gbprox_register_tlli(peer, tlli1, imsi1, ARRAY_SIZE(imsi1), now); + gbproxy_register_tlli(peer, tlli1, imsi1, ARRAY_SIZE(imsi1), now); OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 1); printf(" Add TLLI 2, IMSI 2 (should not expire after timeout)\n"); - gbprox_register_tlli(peer, tlli2, imsi2, ARRAY_SIZE(imsi2), + gbproxy_register_tlli(peer, tlli2, imsi2, ARRAY_SIZE(imsi2), now + 1); OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 2); - num_removed = gbprox_remove_stale_tllis(peer, now + 2); + num_removed = gbproxy_remove_stale_tllis(peer, now + 2); OSMO_ASSERT(num_removed == 1); OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 1); dump_peers(stdout, 2, now + 2, &cfg); /* verify that 5678 has survived */ - tlli_info = gbprox_find_tlli_by_mi(peer, imsi1, ARRAY_SIZE(imsi1)); + tlli_info = gbproxy_find_tlli_by_mi(peer, imsi1, ARRAY_SIZE(imsi1)); OSMO_ASSERT(!tlli_info); - tlli_info = gbprox_find_tlli_by_mi(peer, imsi2, ARRAY_SIZE(imsi2)); + tlli_info = gbproxy_find_tlli_by_mi(peer, imsi2, ARRAY_SIZE(imsi2)); OSMO_ASSERT(tlli_info); OSMO_ASSERT(tlli_info->tlli.current == tlli2); @@ -1977,34 +1977,34 @@ static void test_gbproxy_tlli_expire(void) OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 0); printf(" Add TLLI 1, IMSI 1 (should expire)\n"); - gbprox_register_tlli(peer, tlli1, imsi1, ARRAY_SIZE(imsi1), now); + gbproxy_register_tlli(peer, tlli1, imsi1, ARRAY_SIZE(imsi1), now); OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 1); printf(" Add TLLI 2, IMSI 2 (should expire after timeout)\n"); - gbprox_register_tlli(peer, tlli2, imsi2, ARRAY_SIZE(imsi2), + gbproxy_register_tlli(peer, tlli2, imsi2, ARRAY_SIZE(imsi2), now + 1); OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 2); printf(" Add TLLI 3, IMSI 3 (should not expire after timeout)\n"); - gbprox_register_tlli(peer, tlli3, imsi3, ARRAY_SIZE(imsi3), - now + 2); + gbproxy_register_tlli(peer, tlli3, imsi3, ARRAY_SIZE(imsi3), + now + 2); OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 3); dump_peers(stdout, 2, now + 2, &cfg); printf(" Remove stale TLLIs\n"); - num_removed = gbprox_remove_stale_tllis(peer, now + 3); + num_removed = gbproxy_remove_stale_tllis(peer, now + 3); OSMO_ASSERT(num_removed == 2); OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 1); dump_peers(stdout, 2, now + 2, &cfg); /* verify that tlli3 has survived */ - tlli_info = gbprox_find_tlli_by_mi(peer, imsi1, ARRAY_SIZE(imsi1)); + tlli_info = gbproxy_find_tlli_by_mi(peer, imsi1, ARRAY_SIZE(imsi1)); OSMO_ASSERT(!tlli_info); - tlli_info = gbprox_find_tlli_by_mi(peer, imsi2, ARRAY_SIZE(imsi2)); + tlli_info = gbproxy_find_tlli_by_mi(peer, imsi2, ARRAY_SIZE(imsi2)); OSMO_ASSERT(!tlli_info); - tlli_info = gbprox_find_tlli_by_mi(peer, imsi3, ARRAY_SIZE(imsi3)); + tlli_info = gbproxy_find_tlli_by_mi(peer, imsi3, ARRAY_SIZE(imsi3)); OSMO_ASSERT(tlli_info); OSMO_ASSERT(tlli_info->tlli.current == tlli3); @@ -2036,55 +2036,55 @@ static void test_gbproxy_imsi_matching(void) gbproxy_init_config(&cfg); OSMO_ASSERT(cfg.check_imsi == 0); - OSMO_ASSERT(gbprox_set_patch_filter(&cfg, filter_re1, &err_msg) == 0); + OSMO_ASSERT(gbproxy_set_patch_filter(&cfg, filter_re1, &err_msg) == 0); OSMO_ASSERT(cfg.check_imsi == 1); - OSMO_ASSERT(gbprox_set_patch_filter(&cfg, filter_re2, &err_msg) == 0); + OSMO_ASSERT(gbproxy_set_patch_filter(&cfg, filter_re2, &err_msg) == 0); OSMO_ASSERT(cfg.check_imsi == 1); err_msg = NULL; - OSMO_ASSERT(gbprox_set_patch_filter(&cfg, filter_re4_bad, &err_msg) == -1); + OSMO_ASSERT(gbproxy_set_patch_filter(&cfg, filter_re4_bad, &err_msg) == -1); OSMO_ASSERT(err_msg != NULL); OSMO_ASSERT(cfg.check_imsi == 0); - OSMO_ASSERT(gbprox_set_patch_filter(&cfg, filter_re2, &err_msg) == 0); + OSMO_ASSERT(gbproxy_set_patch_filter(&cfg, filter_re2, &err_msg) == 0); OSMO_ASSERT(cfg.check_imsi == 1); - OSMO_ASSERT(gbprox_set_patch_filter(&cfg, NULL, &err_msg) == 0); + OSMO_ASSERT(gbproxy_set_patch_filter(&cfg, NULL, &err_msg) == 0); OSMO_ASSERT(cfg.check_imsi == 0); - OSMO_ASSERT(gbprox_set_patch_filter(&cfg, filter_re2, &err_msg) == 0); + OSMO_ASSERT(gbproxy_set_patch_filter(&cfg, filter_re2, &err_msg) == 0); OSMO_ASSERT(cfg.check_imsi == 1); - gbprox_clear_patch_filter(&cfg); + gbproxy_clear_patch_filter(&cfg); OSMO_ASSERT(cfg.check_imsi == 0); peer = gbproxy_peer_alloc(&cfg, 20); - OSMO_ASSERT(gbprox_set_patch_filter(&cfg, filter_re2, &err_msg) == 0); + OSMO_ASSERT(gbproxy_set_patch_filter(&cfg, filter_re2, &err_msg) == 0); OSMO_ASSERT(cfg.check_imsi == 1); - OSMO_ASSERT(gbprox_check_imsi(peer, imsi1, ARRAY_SIZE(imsi1)) == 1); - OSMO_ASSERT(gbprox_check_imsi(peer, imsi2, ARRAY_SIZE(imsi2)) == 1); + OSMO_ASSERT(gbproxy_check_imsi(peer, imsi1, ARRAY_SIZE(imsi1)) == 1); + OSMO_ASSERT(gbproxy_check_imsi(peer, imsi2, ARRAY_SIZE(imsi2)) == 1); /* imsi3_bad contains 0xE and 0xF digits, but the conversion function - * doesn't complain, so gbprox_check_imsi() doesn't return -1 in this + * doesn't complain, so gbproxy_check_imsi() doesn't return -1 in this * case. */ - OSMO_ASSERT(gbprox_check_imsi(peer, imsi3_bad, ARRAY_SIZE(imsi3_bad)) == 0); - OSMO_ASSERT(gbprox_check_imsi(peer, tmsi1, ARRAY_SIZE(tmsi1)) == -1); - OSMO_ASSERT(gbprox_check_imsi(peer, tmsi2_bad, ARRAY_SIZE(tmsi2_bad)) == -1); - OSMO_ASSERT(gbprox_check_imsi(peer, imei1, ARRAY_SIZE(imei1)) == -1); - OSMO_ASSERT(gbprox_check_imsi(peer, imei2, ARRAY_SIZE(imei2)) == -1); + OSMO_ASSERT(gbproxy_check_imsi(peer, imsi3_bad, ARRAY_SIZE(imsi3_bad)) == 0); + OSMO_ASSERT(gbproxy_check_imsi(peer, tmsi1, ARRAY_SIZE(tmsi1)) == -1); + OSMO_ASSERT(gbproxy_check_imsi(peer, tmsi2_bad, ARRAY_SIZE(tmsi2_bad)) == -1); + OSMO_ASSERT(gbproxy_check_imsi(peer, imei1, ARRAY_SIZE(imei1)) == -1); + OSMO_ASSERT(gbproxy_check_imsi(peer, imei2, ARRAY_SIZE(imei2)) == -1); - OSMO_ASSERT(gbprox_set_patch_filter(&cfg, filter_re3, &err_msg) == 0); + OSMO_ASSERT(gbproxy_set_patch_filter(&cfg, filter_re3, &err_msg) == 0); OSMO_ASSERT(cfg.check_imsi == 1); - OSMO_ASSERT(gbprox_check_imsi(peer, imsi1, ARRAY_SIZE(imsi1)) == 0); - OSMO_ASSERT(gbprox_check_imsi(peer, imsi2, ARRAY_SIZE(imsi2)) == 0); - OSMO_ASSERT(gbprox_check_imsi(peer, imsi3_bad, ARRAY_SIZE(imsi3_bad)) == 0); - OSMO_ASSERT(gbprox_check_imsi(peer, tmsi1, ARRAY_SIZE(tmsi1)) == -1); - OSMO_ASSERT(gbprox_check_imsi(peer, tmsi2_bad, ARRAY_SIZE(tmsi2_bad)) == -1); - OSMO_ASSERT(gbprox_check_imsi(peer, imei1, ARRAY_SIZE(imei1)) == -1); - OSMO_ASSERT(gbprox_check_imsi(peer, imei2, ARRAY_SIZE(imei2)) == -1); + OSMO_ASSERT(gbproxy_check_imsi(peer, imsi1, ARRAY_SIZE(imsi1)) == 0); + OSMO_ASSERT(gbproxy_check_imsi(peer, imsi2, ARRAY_SIZE(imsi2)) == 0); + OSMO_ASSERT(gbproxy_check_imsi(peer, imsi3_bad, ARRAY_SIZE(imsi3_bad)) == 0); + OSMO_ASSERT(gbproxy_check_imsi(peer, tmsi1, ARRAY_SIZE(tmsi1)) == -1); + OSMO_ASSERT(gbproxy_check_imsi(peer, tmsi2_bad, ARRAY_SIZE(tmsi2_bad)) == -1); + OSMO_ASSERT(gbproxy_check_imsi(peer, imei1, ARRAY_SIZE(imei1)) == -1); + OSMO_ASSERT(gbproxy_check_imsi(peer, imei2, ARRAY_SIZE(imei2)) == -1); /* TODO: Check correct length but wrong type with is_mi_tmsi */