summaryrefslogtreecommitdiffstats
path: root/openbsc/src
diff options
context:
space:
mode:
authorJacob Erlbeck <jerlbeck@sysmocom.de>2014-08-19 12:21:01 +0200
committerHolger Hans Peter Freyther <holger@moiji-mobile.com>2014-08-24 16:16:40 +0200
commit9114bee2424fa5a5e30261054573f9f78b5c5477 (patch)
treed19928d839a625ec509dd1606c1786c397db7111 /openbsc/src
parent6bd7ded71ea3a1de200ad1190e7f7cbee6cae5f9 (diff)
gbproxy: Refactor gb_proxy.c into several files
This patch moves several functions and declarations out of gb_proxy.c to make them reusable by other components and to separate them by context and task. Counter enums (prefix is changed to gbproxy_): enum gbprox_global_ctr -> gprs/gb_proxy.h enum gbprox_peer_ctr -> gprs/gb_proxy.h Generic Gb parsing (prefix is changed to gprs_gb_): struct gbproxy_parse_context -> openbsc/gprs_gb_parse.h gbprox_parse_dtap() -> gprs/gprs_gb_parse.c gbprox_parse_llc() -> gprs/gprs_gb_parse.c gbprox_parse_bssgp() -> gprs/gprs_gb_parse.c gbprox_log_parse_context() -> gprs/gprs_gb_parse.c *_shift(), *_match() -> gprs/gprs_gb_parse.c (no prefix) gbprox_parse_gmm_* -> gprs/gprs_gb_parse.c (static) gbprox_parse_gsm_* -> gprs/gprs_gb_parse.c (static) MI testing/parsing (prefix gprs_ added): is_mi_tmsi() -> gprs/gprs_utils.c is_mi_imsi() -> gprs/gprs_utils.c parse_mi_tmsi() -> gprs/gprs_utils.c TLLI state handling (prefix is changed to gbproxy_): gbprox_*tlli* -> gprs/gb_proxy_tlli.c (except gbprox_patch_tlli, gbproxy_make_sgsn_tlli) Message patching (prefix is changed to gbproxy_): gbprox_*patch* -> gprs/gb_proxy_patch.c gbprox_check_imsi -> gprs/gb_proxy_patch.c Sponsored-by: On-Waves ehf
Diffstat (limited to 'openbsc/src')
-rw-r--r--openbsc/src/gprs/Makefile.am3
-rw-r--r--openbsc/src/gprs/gb_proxy.c1786
-rw-r--r--openbsc/src/gprs/gb_proxy_patch.c475
-rw-r--r--openbsc/src/gprs/gb_proxy_tlli.c544
-rw-r--r--openbsc/src/gprs/gb_proxy_vty.c10
-rw-r--r--openbsc/src/gprs/gprs_gb_parse.c642
-rw-r--r--openbsc/src/gprs/gprs_utils.c39
7 files changed, 1756 insertions, 1743 deletions
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 <openbsc/signal.h>
#include <openbsc/debug.h>
+#include <openbsc/gprs_gb_parse.h>
#include <openbsc/gb_proxy.h>
#include <openbsc/gprs_llc.h>
@@ -49,21 +50,6 @@
#include <openbsc/gsm_04_08_gprs.h>
#include <openbsc/gprs_utils.h>
-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;
+
+ default:
+ break;
+ }
+ }
+
+ gprs_gb_log_parse_context(&parse_ctx, "BSSGP");
+
+ tlli_info = gbproxy_update_tlli_state_dl(peer, now, &parse_ctx);
- gbprox_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg),
- peer, tlli_info, &len_change, &parse_ctx);
+ gbproxy_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg),
+ peer, tlli_info, &len_change, &parse_ctx);
- gbprox_update_state_after(peer, tlli_info, now, &parse_ctx);
+ gbproxy_update_tlli_state_after(peer, tlli_info, now, &parse_ctx);
return;
}
diff --git a/openbsc/src/gprs/gb_proxy_patch.c b/openbsc/src/gprs/gb_proxy_patch.c
new file mode 100644
index 000000000..59d0b2f2b
--- /dev/null
+++ b/openbsc/src/gprs/gb_proxy_patch.c
@@ -0,0 +1,475 @@
+/* Gb-proxy message patching */
+
+/* (C) 2014 by On-Waves
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <openbsc/gb_proxy.h>
+
+#include <openbsc/gprs_utils.h>
+#include <openbsc/gprs_gb_parse.h>
+
+#include <openbsc/gsm_data_shared.h>
+#include <openbsc/gsm_04_08_gprs.h>
+#include <openbsc/debug.h>
+
+#include <osmocom/gprs/protocol/gsm_08_18.h>
+#include <osmocom/core/rate_ctr.h>
+
+/* 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <openbsc/gb_proxy.h>
+
+#include <openbsc/gprs_utils.h>
+#include <openbsc/gprs_gb_parse.h>
+
+#include <openbsc/gsm_data_shared.h>
+#include <openbsc/debug.h>
+
+#include <osmocom/gsm/gsm_utils.h>
+
+#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/talloc.h>
+
+struct gbproxy_tlli_info *gbproxy_find_tlli(struct gbproxy_peer *peer,
+ uint32_t tlli)
+{
+ struct gbproxy_tlli_info *tlli_info;
+ struct gbproxy_patch_state *state = &peer->patch_state;
+
+ llist_for_each_entry(tlli_info, &state->enabled_tllis, list)
+ if (tlli_info->tlli.current == tlli ||
+ tlli_info->tlli.assigned == tlli)
+ return tlli_info;
+
+ return NULL;
+}
+
+struct gbproxy_tlli_info *gbproxy_find_tlli_by_ptmsi(
+ struct gbproxy_peer *peer,
+ uint32_t ptmsi)
+{
+ struct gbproxy_tlli_info *tlli_info;
+ struct gbproxy_patch_state *state = &peer->patch_state;
+
+ llist_for_each_entry(tlli_info, &state->enabled_tllis, list)
+ if (tlli_info->tlli.ptmsi == ptmsi)
+ return tlli_info;
+
+ return NULL;
+}
+
+struct gbproxy_tlli_info *gbproxy_find_tlli_by_sgsn_tlli(
+ struct gbproxy_peer *peer,
+ uint32_t tlli)
+{
+ struct gbproxy_tlli_info *tlli_info;
+ struct gbproxy_patch_state *state = &peer->patch_state;
+
+ llist_for_each_entry(tlli_info, &state->enabled_tllis, list)
+ if (tlli_info->sgsn_tlli.current == tlli ||
+ tlli_info->sgsn_tlli.assigned == tlli)
+ return tlli_info;
+
+ return NULL;
+}
+
+struct gbproxy_tlli_info *gbproxy_find_tlli_by_mi(
+ struct gbproxy_peer *peer,
+ const uint8_t *mi_data,
+ size_t mi_data_len)
+{
+ struct gbproxy_tlli_info *tlli_info;
+ struct gbproxy_patch_state *state = &peer->patch_state;
+
+ if (!gprs_is_mi_imsi(mi_data, mi_data_len))
+ return NULL;
+
+ llist_for_each_entry(tlli_info, &state->enabled_tllis, list) {
+ if (tlli_info->mi_data_len != mi_data_len)
+ continue;
+ if (memcmp(tlli_info->mi_data, mi_data, mi_data_len) != 0)
+ continue;
+
+ return tlli_info;
+ }
+
+ return NULL;
+}
+
+void gbproxy_delete_tlli(struct gbproxy_peer *peer,
+ struct gbproxy_tlli_info *tlli_info)
+{
+ struct gbproxy_patch_state *state = &peer->patch_state;
+
+ llist_del(&tlli_info->list);
+ talloc_free(tlli_info);
+ state->enabled_tllis_count -= 1;
+
+ peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current =
+ state->enabled_tllis_count;
+}
+
+void gbproxy_delete_tllis(struct gbproxy_peer *peer)
+{
+ struct gbproxy_tlli_info *tlli_info, *nxt;
+ struct gbproxy_patch_state *state = &peer->patch_state;
+
+ llist_for_each_entry_safe(tlli_info, nxt, &state->enabled_tllis, list)
+ gbproxy_delete_tlli(peer, tlli_info);
+
+ OSMO_ASSERT(state->enabled_tllis_count == 0);
+ OSMO_ASSERT(llist_empty(&state->enabled_tllis));
+}
+
+static void gbproxy_attach_tlli_info(struct gbproxy_peer *peer, time_t now,
+ struct gbproxy_tlli_info *tlli_info)
+{
+ struct gbproxy_patch_state *state = &peer->patch_state;
+
+ tlli_info->timestamp = now;
+ llist_add(&tlli_info->list, &state->enabled_tllis);
+ state->enabled_tllis_count += 1;
+
+ peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current =
+ state->enabled_tllis_count;
+}
+
+int gbproxy_remove_stale_tllis(struct gbproxy_peer *peer, time_t now)
+{
+ struct gbproxy_patch_state *state = &peer->patch_state;
+ int exceeded_max_len = 0;
+ int deleted_count = 0;
+ int check_for_age;
+
+ if (peer->cfg->tlli_max_len > 0)
+ exceeded_max_len =
+ state->enabled_tllis_count - peer->cfg->tlli_max_len;
+
+ check_for_age = peer->cfg->tlli_max_age > 0;
+
+ for (; exceeded_max_len > 0; exceeded_max_len--) {
+ struct gbproxy_tlli_info *tlli_info;
+ OSMO_ASSERT(!llist_empty(&state->enabled_tllis));
+ tlli_info = llist_entry(state->enabled_tllis.prev,
+ struct gbproxy_tlli_info,
+ list);
+ LOGP(DGPRS, LOGL_INFO,
+ "Removing TLLI %08x from list "
+ "(stale, length %d, max_len exceeded)\n",
+ tlli_info->tlli.current, state->enabled_tllis_count);
+
+ gbproxy_delete_tlli(peer, tlli_info);
+ deleted_count += 1;
+ }
+
+ while (check_for_age && !llist_empty(&state->enabled_tllis)) {
+ time_t age;
+ struct gbproxy_tlli_info *tlli_info;
+ tlli_info = llist_entry(state->enabled_tllis.prev,
+ struct gbproxy_tlli_info,
+ list);
+ age = now - tlli_info->timestamp;
+ /* age < 0 only happens after system time jumps, discard entry */
+ if (age <= peer->cfg->tlli_max_age && age >= 0) {
+ check_for_age = 0;
+ continue;
+ }
+
+ LOGP(DGPRS, LOGL_INFO,
+ "Removing TLLI %08x from list "
+ "(stale, age %d, max_age exceeded)\n",
+ tlli_info->tlli.current, (int)age);
+
+ gbproxy_delete_tlli(peer, tlli_info);
+ deleted_count += 1;
+ }
+
+ return deleted_count;
+}
+
+static struct gbproxy_tlli_info *gbproxy_tlli_info_alloc(
+ struct gbproxy_peer *peer)
+{
+ struct gbproxy_tlli_info *tlli_info;
+
+ tlli_info = talloc_zero(peer, struct gbproxy_tlli_info);
+ tlli_info->tlli.ptmsi = GSM_RESERVED_TMSI;
+ tlli_info->sgsn_tlli.ptmsi = GSM_RESERVED_TMSI;
+
+ return tlli_info;
+}
+
+static void gbproxy_detach_tlli_info(
+ struct gbproxy_peer *peer,
+ struct gbproxy_tlli_info *tlli_info)
+{
+ struct gbproxy_patch_state *state = &peer->patch_state;
+
+ llist_del(&tlli_info->list);
+ OSMO_ASSERT(state->enabled_tllis_count > 0);
+ state->enabled_tllis_count -= 1;
+
+ peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current =
+ state->enabled_tllis_count;
+}
+
+static void gbproxy_update_tlli_info(struct gbproxy_tlli_info *tlli_info,
+ const uint8_t *imsi, size_t imsi_len)
+{
+ if (!gprs_is_mi_imsi(imsi, imsi_len))
+ return;
+
+ tlli_info->mi_data_len = imsi_len;
+ tlli_info->mi_data =
+ talloc_realloc_size(tlli_info, tlli_info->mi_data, imsi_len);
+ OSMO_ASSERT(tlli_info->mi_data != NULL);
+ memcpy(tlli_info->mi_data, imsi, imsi_len);
+}
+
+void gbproxy_reassign_tlli(struct gbproxy_tlli_state *tlli_state,
+ struct gbproxy_peer *peer, uint32_t new_tlli)
+{
+ if (new_tlli == tlli_state->current)
+ return;
+
+ LOGP(DGPRS, LOGL_INFO,
+ "The TLLI has been reassigned from %08x to %08x\n",
+ tlli_state->current, new_tlli);
+
+ /* Remember assigned TLLI */
+ tlli_state->assigned = new_tlli;
+ tlli_state->bss_validated = 0;
+ tlli_state->net_validated = 0;
+}
+
+uint32_t gbproxy_map_tlli(uint32_t other_tlli,
+ struct gbproxy_tlli_info *tlli_info, int to_bss)
+{
+ uint32_t tlli = 0;
+ struct gbproxy_tlli_state *src, *dst;
+ if (to_bss) {
+ src = &tlli_info->sgsn_tlli;
+ dst = &tlli_info->tlli;
+ } else {
+ src = &tlli_info->tlli;
+ dst = &tlli_info->sgsn_tlli;
+ }
+ if (src->current == other_tlli)
+ tlli = dst->current;
+ else if (src->assigned == other_tlli)
+ tlli = dst->assigned;
+
+ return tlli;
+}
+
+static void gbproxy_validate_tlli(struct gbproxy_tlli_state *tlli_state,
+ uint32_t tlli, int to_bss)
+{
+ LOGP(DGPRS, LOGL_DEBUG,
+ "%s({current = %08x, assigned = %08x, net_vld = %d, bss_vld = %d}, %08x)\n",
+ __func__, tlli_state->current, tlli_state->assigned,
+ tlli_state->net_validated, tlli_state->bss_validated, tlli);
+
+ if (!tlli_state->assigned || tlli_state->assigned != tlli)
+ return;
+
+ /* TODO: Is this ok? Check spec */
+ if (gprs_tlli_type(tlli) != TLLI_LOCAL)
+ return;
+
+ /* See GSM 04.08, 4.7.1.5 */
+ if (to_bss)
+ tlli_state->net_validated = 1;
+ else
+ tlli_state->bss_validated = 1;
+
+ if (!tlli_state->bss_validated || !tlli_state->net_validated)
+ return;
+
+ LOGP(DGPRS, LOGL_INFO,
+ "The TLLI %08x has been validated (was %08x)\n",
+ tlli_state->assigned, tlli_state->current);
+
+ tlli_state->current = tlli;
+ tlli_state->assigned = 0;
+}
+
+void gbproxy_touch_tlli(struct gbproxy_peer *peer,
+ struct gbproxy_tlli_info *tlli_info, time_t now)
+{
+ gbproxy_detach_tlli_info(peer, tlli_info);
+ gbproxy_attach_tlli_info(peer, now, tlli_info);
+}
+
+struct gbproxy_tlli_info *gbproxy_register_tlli(
+ struct gbproxy_peer *peer, uint32_t tlli,
+ const uint8_t *imsi, size_t imsi_len, time_t now)
+{
+ struct gbproxy_tlli_info *tlli_info;
+ int enable_patching = -1;
+ int tlli_already_known;
+
+ /* Check, whether the IMSI matches */
+ if (gprs_is_mi_imsi(imsi, imsi_len)) {
+ enable_patching = gbproxy_check_imsi(peer, imsi, imsi_len);
+ if (enable_patching < 0)
+ return NULL;
+ }
+
+ tlli_info = gbproxy_find_tlli(peer, tlli);
+
+ if (!tlli_info) {
+ tlli_info = gbproxy_find_tlli_by_mi(peer, imsi, imsi_len);
+
+ if (tlli_info) {
+ /* TLLI has changed somehow, adjust it */
+ LOGP(DGPRS, LOGL_INFO,
+ "The TLLI has changed from %08x to %08x\n",
+ tlli_info->tlli.current, tlli);
+ tlli_info->tlli.current = tlli;
+ }
+ }
+
+ if (!tlli_info) {
+ tlli_info = gbproxy_tlli_info_alloc(peer);
+ tlli_info->tlli.current = tlli;
+ } else {
+ gbproxy_detach_tlli_info(peer, tlli_info);
+ tlli_already_known = 1;
+ }
+
+ OSMO_ASSERT(tlli_info != NULL);
+
+ if (!tlli_already_known)
+ LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list\n", tlli);
+
+ gbproxy_attach_tlli_info(peer, now, tlli_info);
+ gbproxy_update_tlli_info(tlli_info, imsi, imsi_len);
+
+ if (enable_patching >= 0)
+ tlli_info->enable_patching = enable_patching;
+
+ return tlli_info;
+}
+
+static void gbproxy_unregister_tlli(struct gbproxy_peer *peer, uint32_t tlli)
+{
+ struct gbproxy_tlli_info *tlli_info;
+
+ tlli_info = gbproxy_find_tlli(peer, tlli);
+ if (tlli_info) {
+ LOGP(DGPRS, LOGL_INFO,
+ "Removing TLLI %08x from list\n",
+ tlli);
+ gbproxy_delete_tlli(peer, tlli_info);
+ }
+}
+
+int gbproxy_check_tlli(struct gbproxy_peer *peer, uint32_t tlli)
+{
+ struct gbproxy_tlli_info *tlli_info;
+
+ LOGP(DGPRS, LOGL_INFO, "Checking TLLI %08x, class: %d\n",
+ tlli, gprs_tlli_type(tlli));
+
+ if (!peer->cfg->check_imsi)
+ return 1;
+
+ tlli_info = gbproxy_find_tlli(peer, tlli);
+
+ return tlli_info != NULL && tlli_info->enable_patching;
+}
+
+struct gbproxy_tlli_info *gbproxy_update_tlli_state_ul(
+ struct gbproxy_peer *peer,
+ time_t now,
+ struct gprs_gb_parse_context *parse_ctx)
+{
+ struct gbproxy_tlli_info *tlli_info = NULL;
+
+ if (parse_ctx->tlli_enc)
+ tlli_info = gbproxy_find_tlli(peer, parse_ctx->tlli);
+
+ if (parse_ctx->tlli_enc && parse_ctx->llc) {
+ uint32_t sgsn_tlli;
+ if (!tlli_info) {
+ tlli_info =
+ gbproxy_register_tlli(peer, parse_ctx->tlli,
+ parse_ctx->imsi,
+ parse_ctx->imsi_len, now);
+ /* Setup TLLIs */
+ sgsn_tlli = gbproxy_make_sgsn_tlli(peer, tlli_info,
+ parse_ctx->tlli);
+ tlli_info->sgsn_tlli.current = sgsn_tlli;
+ } else {
+ sgsn_tlli = gbproxy_map_tlli(parse_ctx->tlli, tlli_info, 0);
+ if (!sgsn_tlli)
+ sgsn_tlli = gbproxy_make_sgsn_tlli(peer, tlli_info,
+ parse_ctx->tlli);
+
+ gbproxy_validate_tlli(&tlli_info->tlli,
+ parse_ctx->tlli, 0);
+ gbproxy_validate_tlli(&tlli_info->sgsn_tlli,
+ sgsn_tlli, 0);
+ gbproxy_touch_tlli(peer, tlli_info, now);
+ }
+ } else if (tlli_info) {
+ gbproxy_touch_tlli(peer, tlli_info, now);
+ }
+
+ if (parse_ctx->imsi && tlli_info && tlli_info->mi_data_len == 0) {
+ int enable_patching;
+ gbproxy_update_tlli_info(tlli_info,
+ parse_ctx->imsi, parse_ctx->imsi_len);
+
+ /* Check, whether the IMSI matches */
+ enable_patching = gbproxy_check_imsi(peer, parse_ctx->imsi,
+ parse_ctx->imsi_len);
+ if (enable_patching >= 0)
+ tlli_info->enable_patching = enable_patching;
+ }
+
+ return tlli_info;
+}
+
+struct gbproxy_tlli_info *gbproxy_update_tlli_state_dl(
+ struct gbproxy_peer *peer,
+ time_t now,
+ struct gprs_gb_parse_context *parse_ctx)
+{
+ struct gbproxy_tlli_info *tlli_info = NULL;
+
+ if (parse_ctx->tlli_enc)
+ tlli_info = gbproxy_find_tlli_by_sgsn_tlli(peer, parse_ctx->tlli);
+
+ if (parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc) {
+ /* A new PTMSI has been signaled in the message,
+ * register new TLLI */
+ uint32_t new_sgsn_ptmsi;
+ uint32_t new_sgsn_tlli;
+ uint32_t new_bss_ptmsi;
+ uint32_t new_bss_tlli = 0;
+ if (!gprs_parse_mi_tmsi(parse_ctx->new_ptmsi_enc, GSM48_TMSI_LEN,
+ &new_sgsn_ptmsi)) {
+ LOGP(DGPRS, LOGL_ERROR,
+ "Failed to parse new TLLI/PTMSI (current is %08x)\n",
+ parse_ctx->tlli);
+ return tlli_info;
+ }
+ new_sgsn_tlli = gprs_tmsi2tlli(new_sgsn_ptmsi, TLLI_LOCAL);
+ new_bss_ptmsi = gbproxy_make_bss_ptmsi(peer, new_sgsn_ptmsi);
+ if (new_bss_ptmsi != GSM_RESERVED_TMSI)
+ new_bss_tlli = gprs_tmsi2tlli(new_bss_ptmsi, TLLI_LOCAL);
+ LOGP(DGPRS, LOGL_INFO,
+ "Got new TLLI(PTMSI) %08x(%08x) from SGSN, using %08x(%08x)\n",
+ new_sgsn_tlli, new_sgsn_ptmsi, new_bss_tlli, new_bss_ptmsi);
+ if (tlli_info) {
+ gbproxy_reassign_tlli(&tlli_info->sgsn_tlli,
+ peer, new_sgsn_tlli);
+ gbproxy_reassign_tlli(&tlli_info->tlli,
+ peer, new_bss_tlli);
+ gbproxy_touch_tlli(peer, tlli_info, now);
+ } else {
+ tlli_info = gbproxy_tlli_info_alloc(peer);
+ LOGP(DGPRS, LOGL_INFO,
+ "Adding TLLI %08x to list (SGSN, new P-TMSI)\n",
+ new_sgsn_tlli);
+
+ gbproxy_attach_tlli_info(peer, now, tlli_info);
+ /* Setup TLLIs */
+ tlli_info->sgsn_tlli.current = new_sgsn_tlli;
+ }
+ /* Setup PTMSIs */
+ tlli_info->sgsn_tlli.ptmsi = new_sgsn_ptmsi;
+ tlli_info->tlli.ptmsi = new_bss_ptmsi;
+ } else if (parse_ctx->tlli_enc && parse_ctx->llc && !tlli_info) {
+ /* Unknown SGSN TLLI */
+ tlli_info = gbproxy_tlli_info_alloc(peer);
+ LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list (SGSN)\n",
+ parse_ctx->tlli);
+
+ gbproxy_attach_tlli_info(peer, now, tlli_info);
+ /* Setup TLLIs */
+ tlli_info->sgsn_tlli.current = parse_ctx->tlli;
+ if (peer->cfg->patch_ptmsi) {
+ /* TODO: We don't know the local TLLI here, perhaps add
+ * a workaround that derives a PTMSI from the SGSN TLLI
+ * and use that to get the missing values. This may
+ * only happen when the gbproxy has been restarted or a
+ * tlli_info has been discarded due to age or queue
+ * length.
+ */
+ tlli_info->tlli.current = 0;
+ } else {
+ tlli_info->tlli.current = tlli_info->sgsn_tlli.current;
+ }
+ } else if (parse_ctx->tlli_enc && parse_ctx->llc && tlli_info) {
+ uint32_t bss_tlli = gbproxy_map_tlli(parse_ctx->tlli,
+ tlli_info, 1);
+ gbproxy_validate_tlli(&tlli_info->sgsn_tlli, parse_ctx->tlli, 1);
+ gbproxy_validate_tlli(&tlli_info->tlli, bss_tlli, 1);
+ gbproxy_touch_tlli(peer, tlli_info, now);
+ } else if (tlli_info) {
+ gbproxy_touch_tlli(peer, tlli_info, now);
+ }
+
+ if (parse_ctx->imsi && tlli_info && tlli_info->mi_data_len == 0) {
+ int enable_patching;
+ gbproxy_update_tlli_info(tlli_info,
+ parse_ctx->imsi, parse_ctx->imsi_len);
+
+ /* Check, whether the IMSI matches */
+ enable_patching = gbproxy_check_imsi(peer, parse_ctx->imsi,
+ parse_ctx->imsi_len);
+ if (enable_patching >= 0)
+ tlli_info->enable_patching = enable_patching;
+ }
+
+ return tlli_info;
+}
+
+void gbproxy_update_tlli_state_after(
+ struct gbproxy_peer *peer,
+ struct gbproxy_tlli_info *tlli_info,
+ time_t now,
+ struct gprs_gb_parse_context *parse_ctx)
+{
+ if (parse_ctx->invalidate_tlli)
+ gbproxy_unregister_tlli(peer, parse_ctx->tlli);
+
+ gbproxy_remove_stale_tllis(peer, now);
+}
+
+
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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <openbsc/gprs_gb_parse.h>
+
+#include <openbsc/gprs_utils.h>
+
+#include <openbsc/gsm_04_08_gprs.h>
+#include <openbsc/gsm_data_shared.h>
+#include <openbsc/debug.h>
+
+#include <osmocom/gprs/gprs_bssgp.h>
+
+/* 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 <osmocom/core/msgb.h>
#include <osmocom/gprs/gprs_ns.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+
#include <string.h>
/* 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;
+}
+