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
diff --git a/openbsc/src/gprs/gb_proxy.c b/openbsc/src/gprs/gb_proxy.c
index 86ae0e8..7a1508e 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 @@
 	.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 @@
 	.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 @@
 {
 	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 @@
 	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 @@
 		     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 @@
 	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 @@
 			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 @@
 	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 @@
 			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 @@
 	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 @@
 	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 @@
 				    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 @@
 
 	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 @@
 
 	now = time(NULL);
 
-	tlli_info = gbprox_update_state_dl(peer, now, &parse_ctx);
+	if (parse_ctx.g48_hdr) {
+		switch (parse_ctx.g48_hdr->msg_type) {
+		case GSM48_MT_GMM_ATTACH_REJ:
+			rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_REJS]);
+			break;
 
-	gbprox_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg),
-			   peer, tlli_info, &len_change, &parse_ctx);
+		default:
+			break;
+		}
+	}
 
-	gbprox_update_state_after(peer, tlli_info, now, &parse_ctx);
+	gprs_gb_log_parse_context(&parse_ctx, "BSSGP");
+
+	tlli_info = gbproxy_update_tlli_state_dl(peer, now, &parse_ctx);
+
+	gbproxy_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg),
+			    peer, tlli_info, &len_change, &parse_ctx);
+
+	gbproxy_update_tlli_state_after(peer, tlli_info, now, &parse_ctx);
 
 	return;
 }