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
This commit is contained in:
Jacob Erlbeck 2014-08-19 12:21:01 +02:00 committed by Holger Hans Peter Freyther
parent 6bd7ded71e
commit 9114bee242
12 changed files with 1977 additions and 1826 deletions

View File

@ -11,6 +11,7 @@
#include <regex.h>
struct rate_ctr_group;
struct gprs_gb_parse_context;
enum gbproxy_patch_mode {
GBPROX_PATCH_DEFAULT,
@ -22,6 +23,42 @@ enum gbproxy_patch_mode {
GBPROX_PATCH_LLC, /*!< BSSGP and all supported LLC msgs */
};
enum gbproxy_global_ctr {
GBPROX_GLOB_CTR_INV_BVCI,
GBPROX_GLOB_CTR_INV_LAI,
GBPROX_GLOB_CTR_INV_RAI,
GBPROX_GLOB_CTR_INV_NSEI,
GBPROX_GLOB_CTR_PROTO_ERR_BSS,
GBPROX_GLOB_CTR_PROTO_ERR_SGSN,
GBPROX_GLOB_CTR_NOT_SUPPORTED_BSS,
GBPROX_GLOB_CTR_NOT_SUPPORTED_SGSN,
GBPROX_GLOB_CTR_RESTART_RESET_SGSN,
GBPROX_GLOB_CTR_TX_ERR_SGSN,
GBPROX_GLOB_CTR_OTHER_ERR,
GBPROX_GLOB_CTR_PATCH_PEER_ERR,
};
enum gbproxy_peer_ctr {
GBPROX_PEER_CTR_BLOCKED,
GBPROX_PEER_CTR_UNBLOCKED,
GBPROX_PEER_CTR_DROPPED,
GBPROX_PEER_CTR_INV_NSEI,
GBPROX_PEER_CTR_TX_ERR,
GBPROX_PEER_CTR_RAID_PATCHED_BSS,
GBPROX_PEER_CTR_RAID_PATCHED_SGSN,
GBPROX_PEER_CTR_APN_PATCHED,
GBPROX_PEER_CTR_TLLI_PATCHED_BSS,
GBPROX_PEER_CTR_TLLI_PATCHED_SGSN,
GBPROX_PEER_CTR_PTMSI_PATCHED_BSS,
GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN,
GBPROX_PEER_CTR_PATCH_CRYPT_ERR,
GBPROX_PEER_CTR_PATCH_ERR,
GBPROX_PEER_CTR_ATTACH_REQS,
GBPROX_PEER_CTR_ATTACH_REJS,
GBPROX_PEER_CTR_TLLI_UNKNOWN,
GBPROX_PEER_CTR_TLLI_CACHE_SIZE,
};
struct gbproxy_config {
/* parsed from config file */
uint16_t nsip_sgsn_nsei;
@ -130,31 +167,74 @@ int gbprox_reset_persistent_nsvcs(struct gprs_ns_inst *nsi);
void gbprox_reset(struct gbproxy_config *cfg);
int gbprox_set_patch_filter(struct gbproxy_config *cfg, const char *filter,
const char **err_msg);
void gbprox_clear_patch_filter(struct gbproxy_config *cfg);
void gbprox_delete_tlli(struct gbproxy_peer *peer,
struct gbproxy_tlli_info *tlli_info);
int gbprox_remove_stale_tllis(struct gbproxy_peer *peer, time_t now);
int gbprox_cleanup_peers(struct gbproxy_config *cfg, uint16_t nsei, uint16_t bvci);
struct gbproxy_peer *gbprox_peer_by_nsei(struct gbproxy_config *cfg, uint16_t nsei);
struct gbproxy_tlli_info *gbprox_find_tlli(struct gbproxy_peer *peer,
uint32_t tlli);
struct gbproxy_tlli_info *gbprox_find_tlli_by_mi(struct gbproxy_peer *peer,
const uint8_t *mi_data,
size_t mi_data_len);
struct gbproxy_tlli_info *gbprox_find_tlli_by_sgsn_tlli(
struct gbproxy_peer *peer,
uint32_t tlli);
struct gbproxy_tlli_info *gbprox_register_tlli(
struct gbproxy_peer *peer, uint32_t tlli,
const uint8_t *imsi, size_t imsi_len, time_t now);
struct gbproxy_peer *gbproxy_peer_alloc(struct gbproxy_config *cfg, uint16_t bvci);
void gbproxy_peer_free(struct gbproxy_peer *peer);
int gbprox_check_imsi(struct gbproxy_peer *peer,
const uint8_t *imsi, size_t imsi_len);
/* TLLI state handling */
void gbproxy_delete_tllis(struct gbproxy_peer *peer);
int gbproxy_check_tlli(struct gbproxy_peer *peer, uint32_t tlli);
struct gbproxy_tlli_info *gbprox_find_tlli_by_ptmsi(
struct gbproxy_peer *peer,
uint32_t ptmsi);
uint32_t gbproxy_map_tlli(
uint32_t other_tlli, struct gbproxy_tlli_info *tlli_info, int to_bss);
struct gbproxy_tlli_info *gbproxy_update_tlli_state_ul(
struct gbproxy_peer *peer, time_t now,
struct gprs_gb_parse_context *parse_ctx);
struct gbproxy_tlli_info *gbproxy_update_tlli_state_dl(
struct gbproxy_peer *peer, time_t now,
struct gprs_gb_parse_context *parse_ctx);
void gbproxy_update_tlli_state_after(
struct gbproxy_peer *peer, struct gbproxy_tlli_info *tlli_info,
time_t now, struct gprs_gb_parse_context *parse_ctx);
int gbproxy_remove_stale_tllis(struct gbproxy_peer *peer, time_t now);
void gbproxy_delete_tlli(struct gbproxy_peer *peer,
struct gbproxy_tlli_info *tlli_info);
struct gbproxy_tlli_info *gbproxy_register_tlli(
struct gbproxy_peer *peer, uint32_t tlli,
const uint8_t *imsi, size_t imsi_len, time_t now);
struct gbproxy_tlli_info *gbproxy_find_tlli(
struct gbproxy_peer *peer, uint32_t tlli);
struct gbproxy_tlli_info *gbproxy_find_tlli_by_mi(
struct gbproxy_peer *peer, const uint8_t *mi_data, size_t mi_data_len);
struct gbproxy_tlli_info *gbproxy_find_tlli_by_sgsn_tlli(
struct gbproxy_peer *peer,
uint32_t tlli);
struct gbproxy_tlli_info *gbproxy_find_tlli_by_ptmsi(
struct gbproxy_peer *peer,
uint32_t ptmsi);
/* needed by gb_proxy_tlli.h */
uint32_t gbproxy_make_bss_ptmsi(struct gbproxy_peer *peer, uint32_t sgsn_ptmsi);
uint32_t gbproxy_make_sgsn_tlli(
struct gbproxy_peer *peer, struct gbproxy_tlli_info *tlli_info,
uint32_t bss_tlli);
int gbproxy_check_imsi(
struct gbproxy_peer *peer, const uint8_t *imsi, size_t imsi_len);
/* Message patching */
void gbproxy_patch_bssgp(
struct msgb *msg, uint8_t *bssgp, size_t bssgp_len,
struct gbproxy_peer *peer, struct gbproxy_tlli_info *tlli_info,
int *len_change, struct gprs_gb_parse_context *parse_ctx)
__attribute__((nonnull));
int gbproxy_patch_llc(
struct msgb *msg, uint8_t *llc, size_t llc_len,
struct gbproxy_peer *peer, struct gbproxy_tlli_info *tlli_info,
int *len_change, struct gprs_gb_parse_context *parse_ctx)
__attribute__((nonnull));
int gbproxy_set_patch_filter(
struct gbproxy_config *cfg, const char *filter, const char **err_msg);
void gbproxy_clear_patch_filter(struct gbproxy_config *cfg);
int gbproxy_check_imsi(
struct gbproxy_peer *peer, const uint8_t *imsi, size_t imsi_len);
#endif

View File

@ -0,0 +1,52 @@
#pragma once
#include <openbsc/gprs_llc.h>
#include <sys/types.h>
struct gprs_gb_parse_context {
/* Pointer to protocol specific parts */
struct gsm48_hdr *g48_hdr;
struct bssgp_normal_hdr *bgp_hdr;
struct bssgp_ud_hdr *bud_hdr;
uint8_t *bssgp_data;
size_t bssgp_data_len;
uint8_t *llc;
size_t llc_len;
/* Extracted information */
struct gprs_llc_hdr_parsed llc_hdr_parsed;
struct tlv_parsed bssgp_tp;
int to_bss;
uint8_t *tlli_enc;
uint8_t *imsi;
size_t imsi_len;
uint8_t *apn_ie;
size_t apn_ie_len;
uint8_t *ptmsi_enc;
uint8_t *new_ptmsi_enc;
uint8_t *raid_enc;
uint8_t *old_raid_enc;
uint8_t *bssgp_raid_enc;
uint8_t *bssgp_ptimsi;
/* General info */
const char *llc_msg_name;
int invalidate_tlli;
int need_decryption;
uint32_t tlli;
int pdu_type;
int old_raid_matches;
};
int gprs_gb_parse_dtap(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx) __attribute__((nonnull));
int gprs_gb_parse_llc(uint8_t *llc, size_t llc_len,
struct gprs_gb_parse_context *parse_ctx) __attribute__((nonnull));
int gprs_gb_parse_bssgp(uint8_t *bssgp, size_t bssgp_len,
struct gprs_gb_parse_context *parse_ctx) __attribute__((nonnull));
void gprs_gb_log_parse_context(struct gprs_gb_parse_context *parse_ctx,
const char *default_msg_name) __attribute__((nonnull(1)));

View File

@ -31,3 +31,6 @@ int gprs_msgb_resize_area(struct msgb *msg, uint8_t *area,
size_t old_size, size_t new_size);
char *gprs_apn_to_str(char *out_str, const uint8_t *apn_enc, size_t rest_chars);
int gprs_str_to_apn(uint8_t *apn_enc, size_t max_len, const char *str);
int gprs_is_mi_tmsi(const uint8_t *value, size_t value_len);
int gprs_is_mi_imsi(const uint8_t *value, size_t value_len);
int gprs_parse_mi_tmsi(const uint8_t *value, size_t value_len, uint32_t *tmsi);

View File

@ -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)

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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");
}

View File

@ -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;
}

View File

@ -9,6 +9,9 @@ noinst_PROGRAMS = gbproxy_test
gbproxy_test_SOURCES = gbproxy_test.c
gbproxy_test_LDADD = \
$(top_builddir)/src/gprs/gb_proxy.o \
$(top_builddir)/src/gprs/gb_proxy_patch.o \
$(top_builddir)/src/gprs/gb_proxy_tlli.o \
$(top_builddir)/src/gprs/gprs_gb_parse.o \
$(top_builddir)/src/gprs/gprs_llc_parse.o \
$(top_builddir)/src/gprs/crc24.o \
$(top_builddir)/src/gprs/gprs_utils.o \

View File

@ -1183,7 +1183,7 @@ static void test_gbproxy_ra_patching()
configure_bss_peers(bss_peer, ARRAY_SIZE(bss_peer));
gbcfg.match_re = talloc_strdup(NULL, "^9898|^121314");
if (gbprox_set_patch_filter(&gbcfg, gbcfg.match_re, &err_msg) != 0) {
if (gbproxy_set_patch_filter(&gbcfg, gbcfg.match_re, &err_msg) != 0) {
fprintf(stderr, "Failed to compile RE '%s': %s\n",
gbcfg.match_re, err_msg);
exit(1);
@ -1236,7 +1236,7 @@ static void test_gbproxy_ra_patching()
GPRS_SAPI_GMM, 1,
dtap_attach_acc, sizeof(dtap_attach_acc));
tlli_info = gbprox_find_tlli_by_sgsn_tlli(peer, local_tlli);
tlli_info = gbproxy_find_tlli_by_sgsn_tlli(peer, local_tlli);
OSMO_ASSERT(tlli_info);
OSMO_ASSERT(tlli_info->tlli.assigned == local_tlli);
OSMO_ASSERT(tlli_info->tlli.current != local_tlli);
@ -1252,7 +1252,7 @@ static void test_gbproxy_ra_patching()
GPRS_SAPI_GMM, 4,
dtap_attach_complete, sizeof(dtap_attach_complete));
tlli_info = gbprox_find_tlli_by_sgsn_tlli(peer, local_tlli);
tlli_info = gbproxy_find_tlli_by_sgsn_tlli(peer, local_tlli);
OSMO_ASSERT(tlli_info);
OSMO_ASSERT(tlli_info->tlli.assigned == local_tlli);
OSMO_ASSERT(tlli_info->tlli.current != local_tlli);
@ -1269,7 +1269,7 @@ static void test_gbproxy_ra_patching()
GPRS_SAPI_GMM, 3,
dtap_act_pdp_ctx_req, sizeof(dtap_act_pdp_ctx_req));
tlli_info = gbprox_find_tlli_by_sgsn_tlli(peer, local_tlli);
tlli_info = gbproxy_find_tlli_by_sgsn_tlli(peer, local_tlli);
OSMO_ASSERT(tlli_info);
OSMO_ASSERT(tlli_info->tlli.assigned == local_tlli);
OSMO_ASSERT(tlli_info->tlli.current != local_tlli);
@ -1285,7 +1285,7 @@ static void test_gbproxy_ra_patching()
GPRS_SAPI_GMM, 2,
dtap_gmm_information, sizeof(dtap_gmm_information));
tlli_info = gbprox_find_tlli_by_sgsn_tlli(peer, local_tlli);
tlli_info = gbproxy_find_tlli_by_sgsn_tlli(peer, local_tlli);
OSMO_ASSERT(tlli_info);
OSMO_ASSERT(tlli_info->tlli.assigned == 0);
OSMO_ASSERT(tlli_info->tlli.current == local_tlli);
@ -1465,7 +1465,7 @@ static void test_gbproxy_ptmsi_patching()
dump_peers(stdout, 0, 0, &gbcfg);
tlli_info = gbprox_find_tlli_by_sgsn_tlli(peer, random_sgsn_tlli);
tlli_info = gbproxy_find_tlli_by_sgsn_tlli(peer, random_sgsn_tlli);
OSMO_ASSERT(tlli_info);
OSMO_ASSERT(tlli_info->tlli.assigned == local_bss_tlli);
OSMO_ASSERT(tlli_info->tlli.current == foreign_bss_tlli);
@ -1485,7 +1485,7 @@ static void test_gbproxy_ptmsi_patching()
dump_peers(stdout, 0, 0, &gbcfg);
tlli_info = gbprox_find_tlli_by_sgsn_tlli(peer, local_sgsn_tlli);
tlli_info = gbproxy_find_tlli_by_sgsn_tlli(peer, local_sgsn_tlli);
OSMO_ASSERT(tlli_info);
OSMO_ASSERT(tlli_info->tlli.assigned == local_bss_tlli);
OSMO_ASSERT(tlli_info->tlli.current == foreign_bss_tlli);
@ -1503,7 +1503,7 @@ static void test_gbproxy_ptmsi_patching()
dump_peers(stdout, 0, 0, &gbcfg);
tlli_info = gbprox_find_tlli_by_sgsn_tlli(peer, local_sgsn_tlli);
tlli_info = gbproxy_find_tlli_by_sgsn_tlli(peer, local_sgsn_tlli);
OSMO_ASSERT(tlli_info);
OSMO_ASSERT(tlli_info->tlli.current == local_bss_tlli);
OSMO_ASSERT(tlli_info->tlli.assigned == 0);
@ -1805,7 +1805,7 @@ static void test_gbproxy_tlli_expire(void)
gbproxy_init_config(&cfg);
if (gbprox_set_patch_filter(&cfg, filter_re, &err_msg) != 0) {
if (gbproxy_set_patch_filter(&cfg, filter_re, &err_msg) != 0) {
fprintf(stderr, "gbprox_set_patch_filter: got error: %s\n",
err_msg);
OSMO_ASSERT(err_msg == NULL);
@ -1822,16 +1822,16 @@ static void test_gbproxy_tlli_expire(void)
OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 0);
printf(" Add TLLI 1, IMSI 1\n");
tlli_info = gbprox_register_tlli(peer, tlli1,
imsi1, ARRAY_SIZE(imsi1), now);
tlli_info = gbproxy_register_tlli(peer, tlli1,
imsi1, ARRAY_SIZE(imsi1), now);
OSMO_ASSERT(tlli_info);
OSMO_ASSERT(tlli_info->tlli.current == tlli1);
OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 1);
/* replace the old entry */
printf(" Add TLLI 2, IMSI 1 (should replace TLLI 1)\n");
tlli_info = gbprox_register_tlli(peer, tlli2,
imsi1, ARRAY_SIZE(imsi1), now);
tlli_info = gbproxy_register_tlli(peer, tlli2,
imsi1, ARRAY_SIZE(imsi1), now);
OSMO_ASSERT(tlli_info);
OSMO_ASSERT(tlli_info->tlli.current == tlli2);
OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 1);
@ -1839,10 +1839,10 @@ static void test_gbproxy_tlli_expire(void)
dump_peers(stdout, 2, now, &cfg);
/* verify that 5678 has survived */
tlli_info = gbprox_find_tlli_by_mi(peer, imsi1, ARRAY_SIZE(imsi1));
tlli_info = gbproxy_find_tlli_by_mi(peer, imsi1, ARRAY_SIZE(imsi1));
OSMO_ASSERT(tlli_info);
OSMO_ASSERT(tlli_info->tlli.current == tlli2);
tlli_info = gbprox_find_tlli_by_mi(peer, imsi2, ARRAY_SIZE(imsi2));
tlli_info = gbproxy_find_tlli_by_mi(peer, imsi2, ARRAY_SIZE(imsi2));
OSMO_ASSERT(!tlli_info);
printf("\n");
@ -1861,16 +1861,16 @@ static void test_gbproxy_tlli_expire(void)
OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 0);
printf(" Add TLLI 1, IMSI 1\n");
tlli_info = gbprox_register_tlli(peer, tlli1,
imsi1, ARRAY_SIZE(imsi1), now);
tlli_info = gbproxy_register_tlli(peer, tlli1,
imsi1, ARRAY_SIZE(imsi1), now);
OSMO_ASSERT(tlli_info);
OSMO_ASSERT(tlli_info->tlli.current == tlli1);
OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 1);
/* try to replace the old entry */
printf(" Add TLLI 1, IMSI 2 (should replace IMSI 1)\n");
tlli_info = gbprox_register_tlli(peer, tlli1,
imsi2, ARRAY_SIZE(imsi2), now);
tlli_info = gbproxy_register_tlli(peer, tlli1,
imsi2, ARRAY_SIZE(imsi2), now);
OSMO_ASSERT(tlli_info);
OSMO_ASSERT(tlli_info->tlli.current == tlli1);
OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 1);
@ -1878,9 +1878,9 @@ static void test_gbproxy_tlli_expire(void)
dump_peers(stdout, 2, now, &cfg);
/* verify that 5678 has survived */
tlli_info = gbprox_find_tlli_by_mi(peer, imsi1, ARRAY_SIZE(imsi1));
tlli_info = gbproxy_find_tlli_by_mi(peer, imsi1, ARRAY_SIZE(imsi1));
OSMO_ASSERT(!tlli_info);
tlli_info = gbprox_find_tlli_by_mi(peer, imsi2, ARRAY_SIZE(imsi2));
tlli_info = gbproxy_find_tlli_by_mi(peer, imsi2, ARRAY_SIZE(imsi2));
OSMO_ASSERT(tlli_info);
OSMO_ASSERT(tlli_info->tlli.current == tlli1);
@ -1901,24 +1901,24 @@ static void test_gbproxy_tlli_expire(void)
OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 0);
printf(" Add TLLI 1, IMSI 1\n");
gbprox_register_tlli(peer, tlli1, imsi1, ARRAY_SIZE(imsi1), now);
gbproxy_register_tlli(peer, tlli1, imsi1, ARRAY_SIZE(imsi1), now);
OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 1);
/* replace the old entry */
printf(" Add TLLI 2, IMSI 2 (should replace IMSI 1)\n");
gbprox_register_tlli(peer, tlli2, imsi2, ARRAY_SIZE(imsi2), now);
gbproxy_register_tlli(peer, tlli2, imsi2, ARRAY_SIZE(imsi2), now);
OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 2);
num_removed = gbprox_remove_stale_tllis(peer, time(NULL) + 2);
num_removed = gbproxy_remove_stale_tllis(peer, time(NULL) + 2);
OSMO_ASSERT(num_removed == 1);
OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 1);
dump_peers(stdout, 2, now, &cfg);
/* verify that 5678 has survived */
tlli_info = gbprox_find_tlli_by_mi(peer, imsi1, ARRAY_SIZE(imsi1));
tlli_info = gbproxy_find_tlli_by_mi(peer, imsi1, ARRAY_SIZE(imsi1));
OSMO_ASSERT(!tlli_info);
tlli_info = gbprox_find_tlli_by_mi(peer, imsi2, ARRAY_SIZE(imsi2));
tlli_info = gbproxy_find_tlli_by_mi(peer, imsi2, ARRAY_SIZE(imsi2));
OSMO_ASSERT(tlli_info);
OSMO_ASSERT(tlli_info->tlli.current == tlli2);
@ -1939,24 +1939,24 @@ static void test_gbproxy_tlli_expire(void)
OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 0);
printf(" Add TLLI 1, IMSI 1 (should expire after timeout)\n");
gbprox_register_tlli(peer, tlli1, imsi1, ARRAY_SIZE(imsi1), now);
gbproxy_register_tlli(peer, tlli1, imsi1, ARRAY_SIZE(imsi1), now);
OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 1);
printf(" Add TLLI 2, IMSI 2 (should not expire after timeout)\n");
gbprox_register_tlli(peer, tlli2, imsi2, ARRAY_SIZE(imsi2),
gbproxy_register_tlli(peer, tlli2, imsi2, ARRAY_SIZE(imsi2),
now + 1);
OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 2);
num_removed = gbprox_remove_stale_tllis(peer, now + 2);
num_removed = gbproxy_remove_stale_tllis(peer, now + 2);
OSMO_ASSERT(num_removed == 1);
OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 1);
dump_peers(stdout, 2, now + 2, &cfg);
/* verify that 5678 has survived */
tlli_info = gbprox_find_tlli_by_mi(peer, imsi1, ARRAY_SIZE(imsi1));
tlli_info = gbproxy_find_tlli_by_mi(peer, imsi1, ARRAY_SIZE(imsi1));
OSMO_ASSERT(!tlli_info);
tlli_info = gbprox_find_tlli_by_mi(peer, imsi2, ARRAY_SIZE(imsi2));
tlli_info = gbproxy_find_tlli_by_mi(peer, imsi2, ARRAY_SIZE(imsi2));
OSMO_ASSERT(tlli_info);
OSMO_ASSERT(tlli_info->tlli.current == tlli2);
@ -1977,34 +1977,34 @@ static void test_gbproxy_tlli_expire(void)
OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 0);
printf(" Add TLLI 1, IMSI 1 (should expire)\n");
gbprox_register_tlli(peer, tlli1, imsi1, ARRAY_SIZE(imsi1), now);
gbproxy_register_tlli(peer, tlli1, imsi1, ARRAY_SIZE(imsi1), now);
OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 1);
printf(" Add TLLI 2, IMSI 2 (should expire after timeout)\n");
gbprox_register_tlli(peer, tlli2, imsi2, ARRAY_SIZE(imsi2),
gbproxy_register_tlli(peer, tlli2, imsi2, ARRAY_SIZE(imsi2),
now + 1);
OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 2);
printf(" Add TLLI 3, IMSI 3 (should not expire after timeout)\n");
gbprox_register_tlli(peer, tlli3, imsi3, ARRAY_SIZE(imsi3),
now + 2);
gbproxy_register_tlli(peer, tlli3, imsi3, ARRAY_SIZE(imsi3),
now + 2);
OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 3);
dump_peers(stdout, 2, now + 2, &cfg);
printf(" Remove stale TLLIs\n");
num_removed = gbprox_remove_stale_tllis(peer, now + 3);
num_removed = gbproxy_remove_stale_tllis(peer, now + 3);
OSMO_ASSERT(num_removed == 2);
OSMO_ASSERT(peer->patch_state.enabled_tllis_count == 1);
dump_peers(stdout, 2, now + 2, &cfg);
/* verify that tlli3 has survived */
tlli_info = gbprox_find_tlli_by_mi(peer, imsi1, ARRAY_SIZE(imsi1));
tlli_info = gbproxy_find_tlli_by_mi(peer, imsi1, ARRAY_SIZE(imsi1));
OSMO_ASSERT(!tlli_info);
tlli_info = gbprox_find_tlli_by_mi(peer, imsi2, ARRAY_SIZE(imsi2));
tlli_info = gbproxy_find_tlli_by_mi(peer, imsi2, ARRAY_SIZE(imsi2));
OSMO_ASSERT(!tlli_info);
tlli_info = gbprox_find_tlli_by_mi(peer, imsi3, ARRAY_SIZE(imsi3));
tlli_info = gbproxy_find_tlli_by_mi(peer, imsi3, ARRAY_SIZE(imsi3));
OSMO_ASSERT(tlli_info);
OSMO_ASSERT(tlli_info->tlli.current == tlli3);
@ -2036,55 +2036,55 @@ static void test_gbproxy_imsi_matching(void)
gbproxy_init_config(&cfg);
OSMO_ASSERT(cfg.check_imsi == 0);
OSMO_ASSERT(gbprox_set_patch_filter(&cfg, filter_re1, &err_msg) == 0);
OSMO_ASSERT(gbproxy_set_patch_filter(&cfg, filter_re1, &err_msg) == 0);
OSMO_ASSERT(cfg.check_imsi == 1);
OSMO_ASSERT(gbprox_set_patch_filter(&cfg, filter_re2, &err_msg) == 0);
OSMO_ASSERT(gbproxy_set_patch_filter(&cfg, filter_re2, &err_msg) == 0);
OSMO_ASSERT(cfg.check_imsi == 1);
err_msg = NULL;
OSMO_ASSERT(gbprox_set_patch_filter(&cfg, filter_re4_bad, &err_msg) == -1);
OSMO_ASSERT(gbproxy_set_patch_filter(&cfg, filter_re4_bad, &err_msg) == -1);
OSMO_ASSERT(err_msg != NULL);
OSMO_ASSERT(cfg.check_imsi == 0);
OSMO_ASSERT(gbprox_set_patch_filter(&cfg, filter_re2, &err_msg) == 0);
OSMO_ASSERT(gbproxy_set_patch_filter(&cfg, filter_re2, &err_msg) == 0);
OSMO_ASSERT(cfg.check_imsi == 1);
OSMO_ASSERT(gbprox_set_patch_filter(&cfg, NULL, &err_msg) == 0);
OSMO_ASSERT(gbproxy_set_patch_filter(&cfg, NULL, &err_msg) == 0);
OSMO_ASSERT(cfg.check_imsi == 0);
OSMO_ASSERT(gbprox_set_patch_filter(&cfg, filter_re2, &err_msg) == 0);
OSMO_ASSERT(gbproxy_set_patch_filter(&cfg, filter_re2, &err_msg) == 0);
OSMO_ASSERT(cfg.check_imsi == 1);
gbprox_clear_patch_filter(&cfg);
gbproxy_clear_patch_filter(&cfg);
OSMO_ASSERT(cfg.check_imsi == 0);
peer = gbproxy_peer_alloc(&cfg, 20);
OSMO_ASSERT(gbprox_set_patch_filter(&cfg, filter_re2, &err_msg) == 0);
OSMO_ASSERT(gbproxy_set_patch_filter(&cfg, filter_re2, &err_msg) == 0);
OSMO_ASSERT(cfg.check_imsi == 1);
OSMO_ASSERT(gbprox_check_imsi(peer, imsi1, ARRAY_SIZE(imsi1)) == 1);
OSMO_ASSERT(gbprox_check_imsi(peer, imsi2, ARRAY_SIZE(imsi2)) == 1);
OSMO_ASSERT(gbproxy_check_imsi(peer, imsi1, ARRAY_SIZE(imsi1)) == 1);
OSMO_ASSERT(gbproxy_check_imsi(peer, imsi2, ARRAY_SIZE(imsi2)) == 1);
/* imsi3_bad contains 0xE and 0xF digits, but the conversion function
* doesn't complain, so gbprox_check_imsi() doesn't return -1 in this
* doesn't complain, so gbproxy_check_imsi() doesn't return -1 in this
* case. */
OSMO_ASSERT(gbprox_check_imsi(peer, imsi3_bad, ARRAY_SIZE(imsi3_bad)) == 0);
OSMO_ASSERT(gbprox_check_imsi(peer, tmsi1, ARRAY_SIZE(tmsi1)) == -1);
OSMO_ASSERT(gbprox_check_imsi(peer, tmsi2_bad, ARRAY_SIZE(tmsi2_bad)) == -1);
OSMO_ASSERT(gbprox_check_imsi(peer, imei1, ARRAY_SIZE(imei1)) == -1);
OSMO_ASSERT(gbprox_check_imsi(peer, imei2, ARRAY_SIZE(imei2)) == -1);
OSMO_ASSERT(gbproxy_check_imsi(peer, imsi3_bad, ARRAY_SIZE(imsi3_bad)) == 0);
OSMO_ASSERT(gbproxy_check_imsi(peer, tmsi1, ARRAY_SIZE(tmsi1)) == -1);
OSMO_ASSERT(gbproxy_check_imsi(peer, tmsi2_bad, ARRAY_SIZE(tmsi2_bad)) == -1);
OSMO_ASSERT(gbproxy_check_imsi(peer, imei1, ARRAY_SIZE(imei1)) == -1);
OSMO_ASSERT(gbproxy_check_imsi(peer, imei2, ARRAY_SIZE(imei2)) == -1);
OSMO_ASSERT(gbprox_set_patch_filter(&cfg, filter_re3, &err_msg) == 0);
OSMO_ASSERT(gbproxy_set_patch_filter(&cfg, filter_re3, &err_msg) == 0);
OSMO_ASSERT(cfg.check_imsi == 1);
OSMO_ASSERT(gbprox_check_imsi(peer, imsi1, ARRAY_SIZE(imsi1)) == 0);
OSMO_ASSERT(gbprox_check_imsi(peer, imsi2, ARRAY_SIZE(imsi2)) == 0);
OSMO_ASSERT(gbprox_check_imsi(peer, imsi3_bad, ARRAY_SIZE(imsi3_bad)) == 0);
OSMO_ASSERT(gbprox_check_imsi(peer, tmsi1, ARRAY_SIZE(tmsi1)) == -1);
OSMO_ASSERT(gbprox_check_imsi(peer, tmsi2_bad, ARRAY_SIZE(tmsi2_bad)) == -1);
OSMO_ASSERT(gbprox_check_imsi(peer, imei1, ARRAY_SIZE(imei1)) == -1);
OSMO_ASSERT(gbprox_check_imsi(peer, imei2, ARRAY_SIZE(imei2)) == -1);
OSMO_ASSERT(gbproxy_check_imsi(peer, imsi1, ARRAY_SIZE(imsi1)) == 0);
OSMO_ASSERT(gbproxy_check_imsi(peer, imsi2, ARRAY_SIZE(imsi2)) == 0);
OSMO_ASSERT(gbproxy_check_imsi(peer, imsi3_bad, ARRAY_SIZE(imsi3_bad)) == 0);
OSMO_ASSERT(gbproxy_check_imsi(peer, tmsi1, ARRAY_SIZE(tmsi1)) == -1);
OSMO_ASSERT(gbproxy_check_imsi(peer, tmsi2_bad, ARRAY_SIZE(tmsi2_bad)) == -1);
OSMO_ASSERT(gbproxy_check_imsi(peer, imei1, ARRAY_SIZE(imei1)) == -1);
OSMO_ASSERT(gbproxy_check_imsi(peer, imei2, ARRAY_SIZE(imei2)) == -1);
/* TODO: Check correct length but wrong type with is_mi_tmsi */