gbproxy: Remove patching, TLLI-tracking and SGSN2 support

Those features were introduced a long time ago for one specific use
case at one specific user, and they are not needed anymore.  They
complicate the code base significantly and are hard to maintain with
all the upcoming modifications regarding SGSN pool supoprt.

Change-Id: Id9cc2e1c63486491ac5bb68876088a615075fde6
This commit is contained in:
Harald Welte 2020-12-05 11:18:23 +01:00
parent 8b4c794679
commit 7a0c9ddab0
15 changed files with 4 additions and 12310 deletions

View File

@ -131,7 +131,6 @@ make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
%files -n osmo-gbproxy
%dir %{_docdir}/%{name}/examples
%dir %{_docdir}/%{name}/examples/osmo-gbproxy
%{_docdir}/%{name}/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg
%{_docdir}/%{name}/examples/osmo-gbproxy/osmo-gbproxy.cfg
%{_bindir}/osmo-gbproxy
%dir %{_sysconfdir}/osmocom

3
debian/copyright vendored
View File

@ -24,7 +24,6 @@ Files: .gitignore
contrib/twisted_ipa.py
doc/Makefile.am
doc/examples/Makefile.am
doc/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg
doc/examples/osmo-gbproxy/osmo-gbproxy.cfg
doc/examples/osmo-gtphub/gtphub-example.txt
doc/examples/osmo-gtphub/osmo-gtphub-1iface.cfg
@ -88,9 +87,7 @@ Files: include/osmocom/sgsn/a_reset.h
src/gprs/sgsn_ares.c
src/gbproxy/gb_proxy.c
src/gbproxy/gb_proxy_main.c
src/gbproxy/gb_proxy_patch.c
src/gbproxy/gb_proxy_peer.c
src/gbproxy/gb_proxy_tlli.c
src/gbproxy/gb_proxy_vty.c
src/gtphub/gtphub.c
src/gtphub/gtphub_main.c

View File

@ -1,5 +1,4 @@
etc/osmocom/osmo-gbproxy.cfg
lib/systemd/system/osmo-gbproxy.service
usr/bin/osmo-gbproxy
usr/share/doc/osmo-sgsn/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg usr/share/doc/osmo-gbproxy/examples
usr/share/doc/osmo-sgsn/examples/osmo-gbproxy/osmo-gbproxy.cfg usr/share/doc/osmo-gbproxy/examples

View File

@ -35,7 +35,6 @@ enum gbproxy_global_ctr {
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_bvc_ctr {
@ -44,56 +43,9 @@ enum gbproxy_bvc_ctr {
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_ATTACH_ACKS,
GBPROX_PEER_CTR_ATTACH_COMPLS,
GBPROX_PEER_CTR_RA_UPD_REQS,
GBPROX_PEER_CTR_RA_UPD_REJS,
GBPROX_PEER_CTR_RA_UPD_ACKS,
GBPROX_PEER_CTR_RA_UPD_COMPLS,
GBPROX_PEER_CTR_GMM_STATUS_BSS,
GBPROX_PEER_CTR_GMM_STATUS_SGSN,
GBPROX_PEER_CTR_DETACH_REQS,
GBPROX_PEER_CTR_DETACH_ACKS,
GBPROX_PEER_CTR_PDP_ACT_REQS,
GBPROX_PEER_CTR_PDP_ACT_REJS,
GBPROX_PEER_CTR_PDP_ACT_ACKS,
GBPROX_PEER_CTR_PDP_DEACT_REQS,
GBPROX_PEER_CTR_PDP_DEACT_ACKS,
GBPROX_PEER_CTR_TLLI_UNKNOWN,
GBPROX_PEER_CTR_TLLI_CACHE_SIZE,
GBPROX_PEER_CTR_LAST,
};
enum gbproxy_keep_mode {
GBPROX_KEEP_NEVER, /* don't ever keep TLLI/IMSI state of de-registered subscribers */
GBPROX_KEEP_REATTACH, /* keep if re-attach has been requested by SGSN */
GBPROX_KEEP_IDENTIFIED, /* keep if we had resolved an IMSI */
GBPROX_KEEP_ALWAYS, /* always keep */
};
enum gbproxy_match_id {
GBPROX_MATCH_PATCHING, /* match rule on whether or not we should patch */
GBPROX_MATCH_ROUTING, /* match rule on whether or not we should route (2-SGSN) */
GBPROX_MATCH_LAST
};
struct gbproxy_match {
bool enable; /* is this match enabled? */
char *re_str; /* regular expression (for IMSI) in string format */
regex_t re_comp; /* compiled regular expression (for IMSI) */
};
/* global gb-proxy configuration */
struct gbproxy_config {
/* parsed from config file */
@ -107,44 +59,6 @@ struct gbproxy_config {
/* Counter */
struct rate_ctr_group *ctrg;
/* MCC/MNC to be patched into RA-ID on the way from BSS to SGSN? */
struct osmo_plmn_id core_plmn;
/* APN to be patched into PDP CTX ACT REQ on the way from BSS to SGSN */
uint8_t* core_apn;
size_t core_apn_size;
/* Frequency (sec) at which timer to clean stale links is fired (0 disabled) */
unsigned int clean_stale_timer_freq;
/* If !0, Max age to consider a struct gbproxy_link_info as stale */
int tlli_max_age;
/* If !0, Max len of gbproxy_bvc->list (list of struct gbproxy_link_info) */
int tlli_max_len;
/* If !0, Max len of gbproxy_link_info->stored_msgs (list of msgb) */
uint32_t stored_msgs_max_len;
/* Should the P-TMSI be patched on the fly (required for 2-SGSN config) */
bool patch_ptmsi;
/* Should the IMSI be acquired by the proxy (required for 2-SGSN config) */
bool acquire_imsi;
/* Should we route subscribers to two different SGSNs? */
bool route_to_sgsn2;
/* NSEI of the second SGSN */
uint16_t nsip_sgsn2_nsei;
/* should we keep a cache of per-subscriber state even after de-registration? */
enum gbproxy_keep_mode keep_link_infos;
/* IMSI checking/matching for 2-SGSN routing and patching */
struct gbproxy_match matches[GBPROX_MATCH_LAST];
};
struct gbproxy_patch_state {
struct osmo_plmn_id local_plmn;
/* List of TLLIs for which patching is enabled */
struct llist_head logical_links;
int logical_link_count;
};
/* One BVC inside an NSE */
@ -166,12 +80,6 @@ struct gbproxy_bvc {
/* Counter */
struct rate_ctr_group *ctrg;
/* State related to on-the-fly patching of certain messages */
struct gbproxy_patch_state patch_state;
/* Fired periodically to clean up stale links from list */
struct osmo_timer_list clean_stale_timer;
};
/* one NS Entity that we interact with (BSS/PCU) */
@ -189,58 +97,6 @@ struct gbproxy_nse {
DECLARE_HASHTABLE(bvcs, 10);
};
struct gbproxy_tlli_state {
/* currently active TLLI */
uint32_t current;
/* newly-assigned TLLI (e.g. during P-TMSI allocation procedure) */
uint32_t assigned;
/* has the BSS side validated (confirmed) the new TLLI? */
bool bss_validated;
/* has the SGSN side validated (confirmed) the new TLLI? */
bool net_validated;
/* NOTE: once both are validated, we set current = assigned and assigned = 0 */
/* The P-TMSI for this subscriber */
uint32_t ptmsi;
};
/* One TLLI (= UE, = Subscriber) served via this proxy */
struct gbproxy_link_info {
/* link to gbproxy_bvc.patch_state.logical_links */
struct llist_head list;
/* TLLI on the BSS/PCU side */
struct gbproxy_tlli_state tlli;
/* TLLI on the SGSN side (can be different in case of P-TMSI patching) */
struct gbproxy_tlli_state sgsn_tlli;
/* NSEI of the SGSN serving this link */
uint32_t sgsn_nsei;
/* timestamp when we last had any contact with this UE */
time_t timestamp;
/* IMSI of the subscriber (if/once known) */
uint8_t *imsi;
size_t imsi_len;
/* is the IMSI acquisition still pending? */
bool imsi_acq_pending;
/* queue of stored UL messages (until IMSI acquisition completes and we can
* determine which of the SGSNs we should route this to */
struct llist_head stored_msgs;
uint32_t stored_msgs_len;
/* generated N(U) we use (required due to IMSI acquisition */
unsigned vu_gen_tx_bss;
/* is this subscriber deregistered (TLLI invalidated)? */
bool is_deregistered;
/* does this link match either the (2-SGSN) routing or the patching rule? */
bool is_matching[GBPROX_MATCH_LAST];
};
/* Convenience logging macros for NSE/BVC */
#define LOGPNSE_CAT(NSE, SUBSYS, LEVEL, FMT, ARGS...) \
LOGP(SUBSYS, LEVEL, "NSE(%05u/BSS) " FMT, (NSE)->nsei, ## ARGS)
@ -276,74 +132,6 @@ int gprs_ns2_prim_cb(struct osmo_prim_hdr *oph, void *ctx);
void gbprox_reset(struct gbproxy_config *cfg);
/* TLLI info handling */
void gbproxy_delete_link_infos(struct gbproxy_bvc *bvc);
struct gbproxy_link_info *gbproxy_update_link_state_ul(
struct gbproxy_bvc *bvc, time_t now,
struct gprs_gb_parse_context *parse_ctx);
struct gbproxy_link_info *gbproxy_update_link_state_dl(
struct gbproxy_bvc *bvc, time_t now,
struct gprs_gb_parse_context *parse_ctx);
int gbproxy_update_link_state_after(
struct gbproxy_bvc *bvc, struct gbproxy_link_info *link_info,
time_t now, struct gprs_gb_parse_context *parse_ctx);
int gbproxy_remove_stale_link_infos(struct gbproxy_bvc *bvc, time_t now);
void gbproxy_delete_link_info(struct gbproxy_bvc *bvc,
struct gbproxy_link_info *link_info);
void gbproxy_link_info_discard_messages(struct gbproxy_link_info *link_info);
void gbproxy_attach_link_info(struct gbproxy_bvc *bvc, time_t now,
struct gbproxy_link_info *link_info);
void gbproxy_update_link_info(struct gbproxy_link_info *link_info,
const uint8_t *imsi, size_t imsi_len);
void gbproxy_detach_link_info(struct gbproxy_bvc *bvc,
struct gbproxy_link_info *link_info);
struct gbproxy_link_info *gbproxy_link_info_alloc( struct gbproxy_bvc *bvc);
struct gbproxy_link_info *gbproxy_link_info_by_tlli(
struct gbproxy_bvc *bvc, uint32_t tlli);
struct gbproxy_link_info *gbproxy_link_info_by_imsi(
struct gbproxy_bvc *bvc, const uint8_t *imsi, size_t imsi_len);
struct gbproxy_link_info *gbproxy_link_info_by_any_sgsn_tlli(
struct gbproxy_bvc *bvc, uint32_t tlli);
struct gbproxy_link_info *gbproxy_link_info_by_sgsn_tlli(
struct gbproxy_bvc *bvc,
uint32_t tlli, uint32_t sgsn_nsei);
struct gbproxy_link_info *gbproxy_link_info_by_ptmsi(
struct gbproxy_bvc *bvc,
uint32_t ptmsi);
int gbproxy_imsi_matches(
struct gbproxy_config *cfg,
enum gbproxy_match_id match_id,
struct gbproxy_link_info *link_info);
uint32_t gbproxy_map_tlli(
uint32_t other_tlli, struct gbproxy_link_info *link_info, int to_bss);
/* needed by gb_proxy_tlli.h */
uint32_t gbproxy_make_bss_ptmsi(struct gbproxy_bvc *bvc, uint32_t sgsn_ptmsi);
uint32_t gbproxy_make_sgsn_tlli(
struct gbproxy_bvc *bvc, struct gbproxy_link_info *link_info,
uint32_t bss_tlli);
void gbproxy_reset_link(struct gbproxy_link_info *link_info);
int gbproxy_check_imsi(
struct gbproxy_match *match, 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_bvc *bvc, struct gbproxy_link_info *link_info,
int *len_change, struct gprs_gb_parse_context *parse_ctx);
int gbproxy_patch_llc(
struct msgb *msg, uint8_t *llc, size_t llc_len,
struct gbproxy_bvc *bvc, struct gbproxy_link_info *link_info,
int *len_change, struct gprs_gb_parse_context *parse_ctx);
int gbproxy_set_patch_filter(
struct gbproxy_match *match, const char *filter, const char **err_msg);
void gbproxy_clear_patch_filter(struct gbproxy_match *match);
/* Peer handling */
struct gbproxy_bvc *gbproxy_bvc_by_bvci(
struct gbproxy_config *cfg, uint16_t bvci);
@ -351,12 +139,6 @@ struct gbproxy_bvc *gbproxy_bvc_by_nsei(
struct gbproxy_config *cfg, uint16_t nsei);
struct gbproxy_bvc *gbproxy_bvc_by_rai(
struct gbproxy_config *cfg, const uint8_t *ra);
struct gbproxy_bvc *gbproxy_bvc_by_lai(
struct gbproxy_config *cfg, const uint8_t *la);
struct gbproxy_bvc *gbproxy_bvc_by_lac(
struct gbproxy_config *cfg, const uint8_t *la);
struct gbproxy_bvc *gbproxy_bvc_by_bssgp_tlv(
struct gbproxy_config *cfg, struct tlv_parsed *tp);
struct gbproxy_bvc *gbproxy_bvc_alloc(struct gbproxy_nse *nse, uint16_t bvci);
void gbproxy_bvc_free(struct gbproxy_bvc *bvc);
void gbproxy_bvc_move(struct gbproxy_bvc *bvc, struct gbproxy_nse *nse);

View File

@ -16,8 +16,7 @@
app_configs = {
"gbproxy": ["doc/examples/osmo-gbproxy/osmo-gbproxy.cfg",
"doc/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg"],
"gbproxy": ["doc/examples/osmo-gbproxy/osmo-gbproxy.cfg"],
"sgsn": ["doc/examples/osmo-sgsn/osmo-sgsn.cfg"],
"gtphub": ["doc/examples/osmo-gtphub/osmo-gtphub-1iface.cfg"]
}

View File

@ -27,8 +27,6 @@ osmo_gbproxy_SOURCES = \
gb_proxy_main.c \
gb_proxy_vty.c \
gb_proxy_ctrl.c \
gb_proxy_patch.c \
gb_proxy_tlli.c \
gb_proxy_peer.c \
$(NULL)
osmo_gbproxy_LDADD = \

View File

@ -82,7 +82,6 @@ static int gbprox_relay2peer(struct msgb *old_msg, struct gbproxy_bvc *bvc,
uint16_t ns_bvci);
static int gbprox_relay2sgsn(struct gbproxy_config *cfg, struct msgb *old_msg,
uint16_t ns_bvci, uint16_t sgsn_nsei);
static void gbproxy_reset_imsi_acquisition(struct gbproxy_link_info* link_info);
static int check_bvc_nsei(struct gbproxy_bvc *bvc, uint16_t nsei)
{
@ -106,685 +105,6 @@ static void strip_ns_hdr(struct msgb *msg)
msgb_pull(msg, strip_len);
}
/* Transmit Chapter 9.2.10 Identity Request */
static void gprs_put_identity_req(struct msgb *msg, uint8_t id_type)
{
struct gsm48_hdr *gh;
id_type &= GSM_MI_TYPE_MASK;
gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
gh->proto_discr = GSM48_PDISC_MM_GPRS;
gh->msg_type = GSM48_MT_GMM_ID_REQ;
gh->data[0] = id_type;
}
/* Transmit Chapter 9.4.6.2 Detach Accept (mobile originated detach) */
static void gprs_put_mo_detach_acc(struct msgb *msg)
{
struct gsm48_hdr *gh;
gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
gh->proto_discr = GSM48_PDISC_MM_GPRS;
gh->msg_type = GSM48_MT_GMM_DETACH_ACK;
gh->data[0] = 0; /* no force to standby */
}
static void gprs_push_llc_ui(struct msgb *msg,
int is_uplink, unsigned sapi, unsigned nu)
{
const uint8_t e_bit = 0;
const uint8_t pm_bit = 1;
const uint8_t cr_bit = is_uplink ? 0 : 1;
uint8_t *llc;
uint8_t *fcs_field;
uint32_t fcs;
nu &= 0x01ff; /* 9 Bit */
llc = msgb_push(msg, 3);
llc[0] = (cr_bit << 6) | (sapi & 0x0f);
llc[1] = 0xc0 | (nu >> 6); /* UI frame */
llc[2] = (nu << 2) | ((e_bit & 1) << 1) | (pm_bit & 1);
fcs = gprs_llc_fcs(llc, msgb_length(msg));
fcs_field = msgb_put(msg, 3);
fcs_field[0] = (uint8_t)(fcs >> 0);
fcs_field[1] = (uint8_t)(fcs >> 8);
fcs_field[2] = (uint8_t)(fcs >> 16);
}
static void gprs_push_bssgp_dl_unitdata(struct msgb *msg,
uint32_t tlli)
{
struct bssgp_ud_hdr *budh;
uint8_t *llc = msgb_data(msg);
size_t llc_size = msgb_length(msg);
const size_t llc_ie_hdr_size = 3;
const uint8_t qos_profile[] = {0x00, 0x50, 0x20}; /* hard-coded */
const uint8_t lifetime[] = {0x02, 0x58}; /* 6s hard-coded */
const size_t bssgp_overhead = sizeof(*budh) +
TVLV_GROSS_LEN(sizeof(lifetime)) + llc_ie_hdr_size;
uint8_t *ie;
uint32_t tlli_be = htonl(tlli);
budh = (struct bssgp_ud_hdr *)msgb_push(msg, bssgp_overhead);
budh->pdu_type = BSSGP_PDUT_DL_UNITDATA;
memcpy(&budh->tlli, &tlli_be, sizeof(budh->tlli));
memcpy(&budh->qos_profile, qos_profile, sizeof(budh->qos_profile));
ie = budh->data;
tvlv_put(ie, BSSGP_IE_PDU_LIFETIME, sizeof(lifetime), lifetime);
ie += TVLV_GROSS_LEN(sizeof(lifetime));
/* Note: Add alignment before the LLC IE if inserting other IE */
*(ie++) = BSSGP_IE_LLC_PDU;
*(ie++) = llc_size / 256;
*(ie++) = llc_size % 256;
OSMO_ASSERT(ie == llc);
msgb_bssgph(msg) = (uint8_t *)budh;
msgb_tlli(msg) = tlli;
}
/* update bvc according to the BSS message */
static void gbprox_update_current_raid(uint8_t *raid_enc,
struct gbproxy_bvc *bvc,
const char *log_text)
{
struct gbproxy_patch_state *state = &bvc->patch_state;
const struct osmo_plmn_id old_plmn = state->local_plmn;
struct gprs_ra_id raid;
OSMO_ASSERT(bvc->nse);
struct gbproxy_config *cfg = bvc->nse->cfg;
OSMO_ASSERT(cfg);
if (!raid_enc)
return;
gsm48_parse_ra(&raid, raid_enc);
/* save source side MCC/MNC */
if (!cfg->core_plmn.mcc || raid.mcc == cfg->core_plmn.mcc) {
state->local_plmn.mcc = 0;
} else {
state->local_plmn.mcc = raid.mcc;
}
if (!cfg->core_plmn.mnc
|| !osmo_mnc_cmp(raid.mnc, raid.mnc_3_digits,
cfg->core_plmn.mnc, cfg->core_plmn.mnc_3_digits)) {
state->local_plmn.mnc = 0;
state->local_plmn.mnc_3_digits = false;
} else {
state->local_plmn.mnc = raid.mnc;
state->local_plmn.mnc_3_digits = raid.mnc_3_digits;
}
if (osmo_plmn_cmp(&old_plmn, &state->local_plmn))
LOGPBVC(bvc, LOGL_NOTICE,
"Patching RAID %sactivated, msg: %s, "
"local: %s, core: %s\n",
state->local_plmn.mcc || state->local_plmn.mnc ?
"" : "de",
log_text,
osmo_plmn_name(&state->local_plmn),
osmo_plmn_name2(&cfg->core_plmn));
}
uint32_t gbproxy_make_bss_ptmsi(struct gbproxy_bvc *bvc,
uint32_t sgsn_ptmsi)
{
uint32_t bss_ptmsi;
int max_retries = 23, rc = 0;
if (!bvc->nse->cfg->patch_ptmsi) {
bss_ptmsi = sgsn_ptmsi;
} else {
do {
rc = osmo_get_rand_id((uint8_t *) &bss_ptmsi, sizeof(bss_ptmsi));
if (rc < 0) {
bss_ptmsi = GSM_RESERVED_TMSI;
break;
}
bss_ptmsi = bss_ptmsi | GSM23003_TMSI_SGSN_MASK;
if (gbproxy_link_info_by_ptmsi(bvc, bss_ptmsi))
bss_ptmsi = GSM_RESERVED_TMSI;
} while (bss_ptmsi == GSM_RESERVED_TMSI && max_retries--);
}
if (bss_ptmsi == GSM_RESERVED_TMSI)
LOGPBVC(bvc, LOGL_ERROR, "Failed to allocate a BSS P-TMSI: %d (%s)\n", rc, strerror(-rc));
return bss_ptmsi;
}
uint32_t gbproxy_make_sgsn_tlli(struct gbproxy_bvc *bvc,
struct gbproxy_link_info *link_info,
uint32_t bss_tlli)
{
uint32_t sgsn_tlli;
int max_retries = 23, rc = 0;
if (!bvc->nse->cfg->patch_ptmsi) {
sgsn_tlli = bss_tlli;
} else if (link_info->sgsn_tlli.ptmsi != GSM_RESERVED_TMSI &&
gprs_tlli_type(bss_tlli) == TLLI_FOREIGN) {
sgsn_tlli = gprs_tmsi2tlli(link_info->sgsn_tlli.ptmsi,
TLLI_FOREIGN);
} else if (link_info->sgsn_tlli.ptmsi != GSM_RESERVED_TMSI &&
gprs_tlli_type(bss_tlli) == TLLI_LOCAL) {
sgsn_tlli = gprs_tmsi2tlli(link_info->sgsn_tlli.ptmsi,
TLLI_LOCAL);
} else {
do {
/* create random TLLI, 0b01111xxx... */
rc = osmo_get_rand_id((uint8_t *) &sgsn_tlli, sizeof(sgsn_tlli));
if (rc < 0) {
sgsn_tlli = 0;
break;
}
sgsn_tlli = (sgsn_tlli & 0x7fffffff) | 0x78000000;
if (gbproxy_link_info_by_any_sgsn_tlli(bvc, sgsn_tlli))
sgsn_tlli = 0;
} while (!sgsn_tlli && max_retries--);
}
if (!sgsn_tlli)
LOGPBVC(bvc, LOGL_ERROR, "Failed to allocate an SGSN TLLI: %d (%s)\n", rc, strerror(-rc));
return sgsn_tlli;
}
void gbproxy_reset_link(struct gbproxy_link_info *link_info)
{
gbproxy_reset_imsi_acquisition(link_info);
}
/* Returns != 0 iff IMSI acquisition was in progress */
static int gbproxy_restart_imsi_acquisition(struct gbproxy_link_info* link_info)
{
int in_progress = 0;
if (!link_info)
return 0;
if (link_info->imsi_acq_pending)
in_progress = 1;
gbproxy_link_info_discard_messages(link_info);
link_info->imsi_acq_pending = false;
return in_progress;
}
static void gbproxy_reset_imsi_acquisition(struct gbproxy_link_info* link_info)
{
gbproxy_restart_imsi_acquisition(link_info);
link_info->vu_gen_tx_bss = GBPROXY_INIT_VU_GEN_TX;
}
/* Got identity response with IMSI, assuming the request had
* been generated by the gbproxy */
static int gbproxy_flush_stored_messages(struct gbproxy_bvc *bvc,
time_t now,
struct gbproxy_link_info* link_info)
{
int rc;
struct msgb *stored_msg;
OSMO_ASSERT(bvc);
OSMO_ASSERT(bvc->nse);
struct gbproxy_config *cfg = bvc->nse->cfg;
OSMO_ASSERT(cfg);
/* Patch and flush stored messages towards the SGSN */
while ((stored_msg = msgb_dequeue_count(&link_info->stored_msgs,
&link_info->stored_msgs_len))) {
struct gprs_gb_parse_context tmp_parse_ctx = {0};
tmp_parse_ctx.to_bss = 0;
tmp_parse_ctx.peer_nsei = msgb_nsei(stored_msg);
int len_change = 0;
gprs_gb_parse_bssgp(msgb_bssgph(stored_msg),
msgb_bssgp_len(stored_msg),
&tmp_parse_ctx);
gbproxy_patch_bssgp(stored_msg, msgb_bssgph(stored_msg),
msgb_bssgp_len(stored_msg),
bvc, link_info, &len_change,
&tmp_parse_ctx);
rc = gbproxy_update_link_state_after(bvc, link_info, now,
&tmp_parse_ctx);
if (rc == 1) {
LOGPBVC_CAT(bvc, DLLC, LOGL_NOTICE, "link_info deleted while flushing stored messages\n");
msgb_free(stored_msg);
return -1;
}
rc = gbprox_relay2sgsn(cfg, stored_msg,
msgb_bvci(stored_msg), link_info->sgsn_nsei);
if (rc < 0)
LOGPBVC_CAT(bvc, DLLC, LOGL_ERROR,
"failed to send stored message "
"(%s)\n",
tmp_parse_ctx.llc_msg_name ?
tmp_parse_ctx.llc_msg_name : "BSSGP");
msgb_free(stored_msg);
}
return 0;
}
static int gbproxy_gsm48_to_bvc(struct gbproxy_bvc *bvc,
struct gbproxy_link_info* link_info,
uint16_t bvci,
struct msgb *msg /* Takes msg ownership */)
{
int rc;
/* Workaround to avoid N(U) collisions and to enable a restart
* of the IMSI acquisition procedure. This will work unless the
* SGSN has an initial V(UT) within [256-32, 256+n_retries]
* (see GSM 04.64, 8.4.2). */
gprs_push_llc_ui(msg, 0, GPRS_SAPI_GMM, link_info->vu_gen_tx_bss);
link_info->vu_gen_tx_bss = (link_info->vu_gen_tx_bss + 1) % 512;
gprs_push_bssgp_dl_unitdata(msg, link_info->tlli.current);
msg->l3h = msg->data;
rc = gbprox_relay2peer(msg, bvc, bvci);
msgb_free(msg);
return rc;
}
static void gbproxy_acquire_imsi(struct gbproxy_bvc *bvc,
struct gbproxy_link_info* link_info,
uint16_t bvci)
{
struct msgb *idreq_msg;
/* Send IDENT REQ */
idreq_msg = gsm48_msgb_alloc_name("GSM 04.08 ACQ IMSI");
gprs_put_identity_req(idreq_msg, GSM_MI_TYPE_IMSI);
gbproxy_gsm48_to_bvc(bvc, link_info, bvci, idreq_msg);
}
static void gbproxy_tx_detach_acc(struct gbproxy_bvc *bvc,
struct gbproxy_link_info* link_info,
uint16_t bvci)
{
struct msgb *detacc_msg;
/* Send DETACH ACC */
detacc_msg = gsm48_msgb_alloc_name("GSM 04.08 DET ACC");
gprs_put_mo_detach_acc(detacc_msg);
gbproxy_gsm48_to_bvc(bvc, link_info, bvci, detacc_msg);
}
/* Return != 0 iff msg still needs to be processed */
static int gbproxy_imsi_acquisition(struct gbproxy_bvc *bvc,
struct msgb *msg,
time_t now,
struct gbproxy_link_info* link_info,
struct gprs_gb_parse_context *parse_ctx)
{
struct msgb *stored_msg;
OSMO_ASSERT(bvc);
OSMO_ASSERT(bvc->nse);
struct gbproxy_config *cfg = bvc->nse->cfg;
OSMO_ASSERT(cfg);
if (!link_info)
return 1;
if (!link_info->imsi_acq_pending && link_info->imsi_len > 0)
return 1;
if (parse_ctx->g48_hdr)
switch (parse_ctx->g48_hdr->msg_type)
{
case GSM48_MT_GMM_RA_UPD_REQ:
case GSM48_MT_GMM_ATTACH_REQ:
if (gbproxy_restart_imsi_acquisition(link_info)) {
LOGPBVC_CAT(bvc, DLLC, LOGL_INFO,
" IMSI acquisition was in progress "
"when receiving an %s.\n",
parse_ctx->llc_msg_name);
}
break;
case GSM48_MT_GMM_DETACH_REQ:
/* Nothing has been sent to the SGSN yet */
if (link_info->imsi_acq_pending) {
LOGPBVC_CAT(bvc, DLLC, LOGL_INFO,
"IMSI acquisition was in progress "
"when receiving a DETACH_REQ.\n");
}
if (!parse_ctx->invalidate_tlli) {
LOGPBVC_CAT(bvc, DLLC, LOGL_INFO,
"IMSI not yet acquired, "
"faking a DETACH_ACC.\n");
gbproxy_tx_detach_acc(bvc, link_info, msgb_bvci(msg));
parse_ctx->invalidate_tlli = 1;
}
gbproxy_reset_imsi_acquisition(link_info);
gbproxy_update_link_state_after(bvc, link_info, now,
parse_ctx);
return 0;
}
if (link_info->imsi_acq_pending && link_info->imsi_len > 0) {
int is_ident_resp =
parse_ctx->g48_hdr &&
gsm48_hdr_pdisc(parse_ctx->g48_hdr) == GSM48_PDISC_MM_GPRS &&
gsm48_hdr_msg_type(parse_ctx->g48_hdr) == GSM48_MT_GMM_ID_RESP;
LOGPBVC_CAT(bvc, DLLC, LOGL_DEBUG,
"IMSI acquisition succeeded, "
"flushing stored messages\n");
/* The IMSI is now available. If flushing the messages fails,
* then link_info has been deleted and we should return
* immediately. */
if (gbproxy_flush_stored_messages(bvc, now, link_info) < 0)
return 0;
gbproxy_reset_imsi_acquisition(link_info);
/* This message is most probably the response to the ident
* request sent by gbproxy_acquire_imsi(). Don't forward it to
* the SGSN. */
return !is_ident_resp;
}
/* The message cannot be processed since the IMSI is still missing */
/* If queue is getting too large, drop oldest msgb before adding new one */
if (cfg->stored_msgs_max_len > 0) {
int exceeded_max_len = link_info->stored_msgs_len
+ 1 - cfg->stored_msgs_max_len;
for (; exceeded_max_len > 0; exceeded_max_len--) {
struct msgb *msgb_drop;
msgb_drop = msgb_dequeue_count(&link_info->stored_msgs,
&link_info->stored_msgs_len);
LOGPBVC_CAT(bvc, DLLC, LOGL_INFO,
"Dropping stored msgb from list "
"(!acq imsi, length %d, max_len exceeded)\n",
link_info->stored_msgs_len);
msgb_free(msgb_drop);
}
}
/* Enqueue unpatched messages */
LOGPBVC_CAT(bvc, DLLC, LOGL_INFO,
"IMSI acquisition in progress, "
"storing message (%s)\n",
parse_ctx->llc_msg_name ? parse_ctx->llc_msg_name : "BSSGP");
stored_msg = bssgp_msgb_copy(msg, "process_bssgp_ul");
msgb_enqueue_count(&link_info->stored_msgs, stored_msg,
&link_info->stored_msgs_len);
if (!link_info->imsi_acq_pending) {
LOGPBVC_CAT(bvc, DLLC, LOGL_INFO,
"IMSI is required but not available, "
"initiating identification procedure (%s)\n",
parse_ctx->llc_msg_name ? parse_ctx->llc_msg_name : "BSSGP");
gbproxy_acquire_imsi(bvc, link_info, msgb_bvci(msg));
/* There is no explicit retransmission handling, the
* implementation relies on the MS doing proper retransmissions
* of the triggering message instead */
link_info->imsi_acq_pending = true;
}
return 0;
}
struct gbproxy_bvc *gbproxy_find_bvc(struct gbproxy_config *cfg,
struct msgb *msg,
struct gprs_gb_parse_context *parse_ctx)
{
struct gbproxy_bvc *bvc = NULL;
if (msgb_bvci(msg) >= 2)
bvc = gbproxy_bvc_by_bvci(cfg, msgb_bvci(msg));
if (!bvc && !parse_ctx->to_bss)
bvc = gbproxy_bvc_by_nsei(cfg, msgb_nsei(msg));
if (!bvc)
bvc = gbproxy_bvc_by_bssgp_tlv(cfg, &parse_ctx->bssgp_tp);
if (!bvc) {
LOGP(DLLC, LOGL_INFO,
"NSE(%05u/%s) patching: didn't find bvc for message, "
"PDU %d\n",
msgb_nsei(msg), parse_ctx->to_bss ? "BSS" : "SGSN",
parse_ctx->pdu_type);
/* Increment counter */
rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_PATCH_PEER_ERR]);
}
return bvc;
}
/* patch BSSGP message */
static int gbprox_process_bssgp_ul(struct gbproxy_config *cfg,
struct msgb *msg,
struct gbproxy_bvc *bvc)
{
struct gprs_gb_parse_context parse_ctx = {0};
int rc;
int len_change = 0;
time_t now;
struct timespec ts = {0,};
struct gbproxy_link_info *link_info = NULL;
uint32_t sgsn_nsei = cfg->nsip_sgsn_nsei;
if (!cfg->core_plmn.mcc && !cfg->core_plmn.mnc && !cfg->core_apn &&
!cfg->acquire_imsi && !cfg->patch_ptmsi && !cfg->route_to_sgsn2)
return 1;
parse_ctx.to_bss = 0;
parse_ctx.peer_nsei = msgb_nsei(msg);
/* Parse BSSGP/LLC */
rc = gprs_gb_parse_bssgp(msgb_bssgph(msg), msgb_bssgp_len(msg),
&parse_ctx);
if (!rc && !parse_ctx.need_decryption) {
LOGP(DGPRS, LOGL_ERROR,
"NSE(%05u/BSS) patching: failed to parse invalid %s message\n",
msgb_nsei(msg), gprs_gb_message_name(&parse_ctx, "NS_UNITDATA"));
gprs_gb_log_parse_context(LOGL_NOTICE, &parse_ctx, "NS_UNITDATA");
LOGP(DGPRS, LOGL_NOTICE,
"NSE(%05u/BSS) invalid message was: %s\n",
msgb_nsei(msg), msgb_hexdump(msg));
return 0;
}
/* Get bvc */
if (!bvc)
bvc = gbproxy_find_bvc(cfg, msg, &parse_ctx);
if (!bvc)
return 0;
osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
now = ts.tv_sec;
gbprox_update_current_raid(parse_ctx.bssgp_raid_enc, bvc,
parse_ctx.llc_msg_name);
gprs_gb_log_parse_context(LOGL_DEBUG, &parse_ctx, "NS_UNITDATA");
link_info = gbproxy_update_link_state_ul(bvc, now, &parse_ctx);
if (parse_ctx.g48_hdr) {
switch (parse_ctx.g48_hdr->msg_type) {
case GSM48_MT_GMM_ATTACH_REQ:
rate_ctr_inc(&bvc->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_REQS]);
break;
case GSM48_MT_GMM_DETACH_REQ:
rate_ctr_inc(&bvc->ctrg->ctr[GBPROX_PEER_CTR_DETACH_REQS]);
break;
case GSM48_MT_GMM_ATTACH_COMPL:
rate_ctr_inc(&bvc->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_COMPLS]);
break;
case GSM48_MT_GMM_RA_UPD_REQ:
rate_ctr_inc(&bvc->ctrg->ctr[GBPROX_PEER_CTR_RA_UPD_REQS]);
break;
case GSM48_MT_GMM_RA_UPD_COMPL:
rate_ctr_inc(&bvc->ctrg->ctr[GBPROX_PEER_CTR_RA_UPD_COMPLS]);
break;
case GSM48_MT_GMM_STATUS:
rate_ctr_inc(&bvc->ctrg->ctr[GBPROX_PEER_CTR_GMM_STATUS_BSS]);
break;
case GSM48_MT_GSM_ACT_PDP_REQ:
rate_ctr_inc(&bvc->ctrg->ctr[GBPROX_PEER_CTR_PDP_ACT_REQS]);
break;
case GSM48_MT_GSM_DEACT_PDP_REQ:
rate_ctr_inc(&bvc->ctrg->ctr[GBPROX_PEER_CTR_PDP_DEACT_REQS]);
break;
default:
break;
}
}
if (link_info && cfg->route_to_sgsn2) {
if (cfg->acquire_imsi && link_info->imsi_len == 0)
sgsn_nsei = 0xffff;
else if (gbproxy_imsi_matches(cfg, GBPROX_MATCH_ROUTING,
link_info))
sgsn_nsei = cfg->nsip_sgsn2_nsei;
}
if (link_info)
link_info->sgsn_nsei = sgsn_nsei;
/* Handle IMSI acquisition */
if (cfg->acquire_imsi) {
rc = gbproxy_imsi_acquisition(bvc, msg, now, link_info,
&parse_ctx);
if (rc <= 0)
return rc;
}
gbproxy_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg),
bvc, link_info, &len_change, &parse_ctx);
gbproxy_update_link_state_after(bvc, link_info, now, &parse_ctx);
if (sgsn_nsei != cfg->nsip_sgsn_nsei) {
/* Send message directly to the selected SGSN */
rc = gbprox_relay2sgsn(cfg, msg, msgb_bvci(msg), sgsn_nsei);
/* Don't let the calling code handle the transmission */
return 0;
}
return 1;
}
/* patch BSSGP message to use core_plmn.mcc/mnc on the SGSN side */
static void gbprox_process_bssgp_dl(struct gbproxy_config *cfg,
struct msgb *msg,
struct gbproxy_bvc *bvc)
{
struct gprs_gb_parse_context parse_ctx = {0};
int rc;
int len_change = 0;
time_t now;
struct timespec ts = {0,};
struct gbproxy_link_info *link_info = NULL;
if (!cfg->core_plmn.mcc && !cfg->core_plmn.mnc && !cfg->core_apn &&
!cfg->acquire_imsi && !cfg->patch_ptmsi && !cfg->route_to_sgsn2)
return;
parse_ctx.to_bss = 1;
parse_ctx.peer_nsei = msgb_nsei(msg);
rc = gprs_gb_parse_bssgp(msgb_bssgph(msg), msgb_bssgp_len(msg),
&parse_ctx);
if (!rc && !parse_ctx.need_decryption) {
LOGP(DGPRS, LOGL_ERROR,
"NSE(%05u/SGSN) patching: failed to parse invalid %s message\n",
msgb_nsei(msg), gprs_gb_message_name(&parse_ctx, "NS_UNITDATA"));
gprs_gb_log_parse_context(LOGL_NOTICE, &parse_ctx, "NS_UNITDATA");
LOGP(DGPRS, LOGL_NOTICE,
"NSE(%05u/SGSN) invalid message was: %s\n",
msgb_nsei(msg), msgb_hexdump(msg));
return;
}
/* Get bvc */
if (!bvc)
bvc = gbproxy_find_bvc(cfg, msg, &parse_ctx);
if (!bvc)
return;
osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
now = ts.tv_sec;
if (parse_ctx.g48_hdr) {
switch (parse_ctx.g48_hdr->msg_type) {
case GSM48_MT_GMM_ATTACH_ACK:
rate_ctr_inc(&bvc->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_ACKS]);
break;
case GSM48_MT_GMM_ATTACH_REJ:
rate_ctr_inc(&bvc->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_REJS]);
break;
case GSM48_MT_GMM_DETACH_ACK:
rate_ctr_inc(&bvc->ctrg->ctr[GBPROX_PEER_CTR_DETACH_ACKS]);
break;
case GSM48_MT_GMM_RA_UPD_ACK:
rate_ctr_inc(&bvc->ctrg->ctr[GBPROX_PEER_CTR_RA_UPD_ACKS]);
break;
case GSM48_MT_GMM_RA_UPD_REJ:
rate_ctr_inc(&bvc->ctrg->ctr[GBPROX_PEER_CTR_RA_UPD_REJS]);
break;
case GSM48_MT_GMM_STATUS:
rate_ctr_inc(&bvc->ctrg->ctr[GBPROX_PEER_CTR_GMM_STATUS_SGSN]);
break;
case GSM48_MT_GSM_ACT_PDP_ACK:
rate_ctr_inc(&bvc->ctrg->ctr[GBPROX_PEER_CTR_PDP_ACT_ACKS]);
break;
case GSM48_MT_GSM_ACT_PDP_REJ:
rate_ctr_inc(&bvc->ctrg->ctr[GBPROX_PEER_CTR_PDP_ACT_REJS]);
break;
case GSM48_MT_GSM_DEACT_PDP_ACK:
rate_ctr_inc(&bvc->ctrg->ctr[GBPROX_PEER_CTR_PDP_DEACT_ACKS]);
break;
default:
break;
}
}
gprs_gb_log_parse_context(LOGL_DEBUG, &parse_ctx, "NS_UNITDATA");
link_info = gbproxy_update_link_state_dl(bvc, now, &parse_ctx);
gbproxy_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg),
bvc, link_info, &len_change, &parse_ctx);
gbproxy_update_link_state_after(bvc, link_info, now, &parse_ctx);
return;
}
/* feed a message down the NS-VC associated with the specified bvc */
static int gbprox_relay2sgsn(struct gbproxy_config *cfg, struct msgb *old_msg,
uint16_t ns_bvci, uint16_t sgsn_nsei)
@ -924,9 +244,6 @@ static int gbprox_rx_ptp_from_bss(struct gbproxy_config *cfg,
uint16_t ns_bvci)
{
struct gbproxy_bvc *bvc;
struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
uint8_t pdu_type = bgph->pdu_type;
int rc;
bvc = gbproxy_bvc_by_bvci(cfg, ns_bvci);
if (!bvc) {
@ -941,23 +258,6 @@ static int gbprox_rx_ptp_from_bss(struct gbproxy_config *cfg,
/* TODO: Should we discard this message if the check fails */
check_bvc_nsei(bvc, nsei);
rc = gbprox_process_bssgp_ul(cfg, msg, bvc);
if (!rc)
return 0;
switch (pdu_type) {
case BSSGP_PDUT_FLOW_CONTROL_BVC:
if (!cfg->route_to_sgsn2)
break;
/* Send a copy to the secondary SGSN */
gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn2_nsei);
break;
default:
break;
}
return gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn_nsei);
}
@ -967,8 +267,6 @@ static int gbprox_rx_ptp_from_sgsn(struct gbproxy_config *cfg,
uint16_t ns_bvci)
{
struct gbproxy_bvc *bvc;
struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
uint8_t pdu_type = bgph->pdu_type;
bvc = gbproxy_bvc_by_bvci(cfg, ns_bvci);
@ -991,29 +289,12 @@ static int gbprox_rx_ptp_from_sgsn(struct gbproxy_config *cfg,
return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, &ns_bvci, msg);
}
switch (pdu_type) {
case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK:
case BSSGP_PDUT_BVC_BLOCK_ACK:
case BSSGP_PDUT_BVC_UNBLOCK_ACK:
if (cfg->route_to_sgsn2 && nsei == cfg->nsip_sgsn2_nsei)
/* Hide ACKs from the secondary SGSN, the primary SGSN
* is responsible to send them. */
return 0;
break;
default:
break;
}
/* Optionally patch the message */
gbprox_process_bssgp_dl(cfg, msg, bvc);
return gbprox_relay2peer(msg, bvc, ns_bvci);
}
/* process a BVC-RESET message from the BSS side */
static int gbprox_rx_bvc_reset_from_bss(struct gbproxy_config *cfg, struct msgb *msg,
uint16_t nsei, struct tlv_parsed *tp,
int *copy_to_sgsn2)
uint16_t nsei, struct tlv_parsed *tp)
{
struct gbproxy_bvc *from_bvc = NULL;
uint16_t bvci;
@ -1087,8 +368,6 @@ static int gbprox_rx_bvc_reset_from_bss(struct gbproxy_config *cfg, struct msgb
gsm48_parse_ra(&raid, from_bvc->ra);
LOGPBVC(from_bvc, LOGL_INFO, "Cell ID %s\n", osmo_rai_name(&raid));
}
if (cfg->route_to_sgsn2)
*copy_to_sgsn2 = 1;
}
/* continue processing / relaying to SGSN[s] */
return 1;
@ -1105,7 +384,6 @@ static int gbprox_rx_sig_from_bss(struct gbproxy_config *cfg,
int data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
struct gbproxy_bvc *from_bvc = NULL;
struct gprs_ra_id raid;
int copy_to_sgsn2 = 0;
int rc;
if (ns_bvci != 0 && ns_bvci != 1) {
@ -1147,22 +425,13 @@ static int gbprox_rx_sig_from_bss(struct gbproxy_config *cfg,
/* FIXME: This only supports one BSS per RA */
break;
case BSSGP_PDUT_BVC_RESET:
rc = gbprox_rx_bvc_reset_from_bss(cfg, msg, nsei, &tp, &copy_to_sgsn2);
rc = gbprox_rx_bvc_reset_from_bss(cfg, msg, nsei, &tp);
/* if function retruns 0, we terminate processing here */
if (rc == 0)
return 0;
break;
}
/* Normally, we can simply pass on all signalling messages from BSS to
* SGSN */
rc = gbprox_process_bssgp_ul(cfg, msg, from_bvc);
if (!rc)
return 0;
if (copy_to_sgsn2)
gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn2_nsei);
return gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn_nsei);
err_no_bvc:
LOGP(DGPRS, LOGL_ERROR, "NSE(%05u/BSS) cannot find bvc based on NSEI\n",
@ -1337,7 +606,6 @@ static int gbprox_rx_sig_from_sgsn(struct gbproxy_config *cfg,
}
msg = bssgp_msgb_copy(orig_msg, "rx_sig_from_sgsn");
gbprox_process_bssgp_dl(cfg, msg, NULL);
/* Update message info */
bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
data_len = msgb_bssgp_len(orig_msg) - sizeof(*bgph);
@ -1348,8 +616,6 @@ static int gbprox_rx_sig_from_sgsn(struct gbproxy_config *cfg,
rc = rx_reset_from_sgsn(cfg, msg, orig_msg, &tp, nsei, ns_bvci);
break;
case BSSGP_PDUT_BVC_RESET_ACK:
if (cfg->route_to_sgsn2 && nsei == cfg->nsip_sgsn2_nsei)
break;
/* simple case: BVCI IE is mandatory */
if (!TLVP_PRES_LEN(&tp, BSSGP_IE_BVCI, 2))
goto err_mand_ie;
@ -1462,8 +728,7 @@ err_no_bvc:
static int gbproxy_is_sgsn_nsei(struct gbproxy_config *cfg, uint16_t nsei)
{
return nsei == cfg->nsip_sgsn_nsei ||
(cfg->route_to_sgsn2 && nsei == cfg->nsip_sgsn2_nsei);
return nsei == cfg->nsip_sgsn_nsei;
}
int gbprox_bssgp_send_cb(void *ctx, struct msgb *msg)

View File

@ -62,10 +62,6 @@ static int get_nsvc_state(struct ctrl_cmd *cmd, void *data)
/* NS-VCs for SGSN */
nse = gprs_ns2_nse_by_nsei(nsi, cfg->nsip_sgsn_nsei);
if (nse)
gprs_ns2_nse_foreach_nsvc(nse, &ctrl_nsvc_state_cb, cmd);
/* NS-VCs for SGSN2 */
nse = gprs_ns2_nse_by_nsei(nsi, cfg->nsip_sgsn2_nsei);
if (nse)
gprs_ns2_nse_foreach_nsvc(nse, &ctrl_nsvc_state_cb, cmd);

View File

@ -1,482 +0,0 @@
/* 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 <osmocom/gprs/gprs_msgb.h>
#include <osmocom/sgsn/gb_proxy.h>
#include <osmocom/sgsn/gprs_utils.h>
#include <osmocom/sgsn/gprs_gb_parse.h>
#include <osmocom/sgsn/debug.h>
#include <osmocom/gprs/protocol/gsm_08_18.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/gsm/apn.h>
extern void *tall_sgsn_ctx;
/* patch RA identifier in place */
static void gbproxy_patch_raid(struct gsm48_ra_id *raid_enc, struct gbproxy_bvc *bvc,
int to_bss, const char *log_text)
{
OSMO_ASSERT(bvc);
struct gbproxy_patch_state *state = &bvc->patch_state;
struct osmo_plmn_id old_plmn;
struct gprs_ra_id raid;
enum gbproxy_bvc_ctr counter =
to_bss ?
GBPROX_PEER_CTR_RAID_PATCHED_SGSN :
GBPROX_PEER_CTR_RAID_PATCHED_BSS;
OSMO_ASSERT(bvc->nse);
struct gbproxy_config *cfg = bvc->nse->cfg;
OSMO_ASSERT(cfg);
if (!state->local_plmn.mcc || !state->local_plmn.mnc)
return;
gsm48_parse_ra(&raid, (uint8_t *)raid_enc);
old_plmn = (struct osmo_plmn_id){
.mcc = raid.mcc,
.mnc = raid.mnc,
.mnc_3_digits = raid.mnc_3_digits,
};
if (!to_bss) {
/* BSS -> SGSN */
if (state->local_plmn.mcc)
raid.mcc = cfg->core_plmn.mcc;
if (state->local_plmn.mnc) {
raid.mnc = cfg->core_plmn.mnc;
raid.mnc_3_digits = cfg->core_plmn.mnc_3_digits;
}
} else {
/* SGSN -> BSS */
if (state->local_plmn.mcc)
raid.mcc = state->local_plmn.mcc;
if (state->local_plmn.mnc) {
raid.mnc = state->local_plmn.mnc;
raid.mnc_3_digits = state->local_plmn.mnc_3_digits;
}
}
LOGPBVC(bvc, LOGL_DEBUG,
"Patching %s to %s: "
"%s-%d-%d -> %s\n",
log_text,
to_bss ? "BSS" : "SGSN",
osmo_plmn_name(&old_plmn), raid.lac, raid.rac,
osmo_rai_name(&raid));
gsm48_encode_ra(raid_enc, &raid);
rate_ctr_inc(&bvc->ctrg->ctr[counter]);
}
static void gbproxy_patch_apn_ie(struct msgb *msg,
uint8_t *apn_ie, size_t apn_ie_len,
struct gbproxy_bvc *bvc,
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(bvc);
OSMO_ASSERT(bvc->nse);
struct gbproxy_config *cfg = bvc->nse->cfg;
OSMO_ASSERT(cfg);
OSMO_ASSERT(apn_ie_len == apn_len + sizeof(struct apn_ie_hdr));
OSMO_ASSERT(apn_ie_len > 2 && apn_ie_len <= 102);
if (cfg->core_apn_size == 0) {
char str1[110];
/* Remove the IE */
LOGPBVC(bvc, LOGL_DEBUG,
"Patching %s to SGSN: Removing APN '%s'\n",
log_text,
osmo_apn_to_str(str1, apn, apn_len));
*new_apn_ie_len = 0;
msgb_resize_area(msg, apn_ie, apn_ie_len, 0);
} else {
/* Resize the IE */
char str1[110];
char str2[110];
OSMO_ASSERT(cfg->core_apn_size <= 100);
LOGPBVC(bvc, LOGL_DEBUG,
"Patching %s to SGSN: "
"Replacing APN '%s' -> '%s'\n",
log_text,
osmo_apn_to_str(str1, apn, apn_len),
osmo_apn_to_str(str2, cfg->core_apn,
cfg->core_apn_size));
*new_apn_ie_len = cfg->core_apn_size + 2;
msgb_resize_area(msg, apn, apn_len, cfg->core_apn_size);
memcpy(apn, cfg->core_apn, cfg->core_apn_size);
hdr->apn_len = cfg->core_apn_size;
}
rate_ctr_inc(&bvc->ctrg->ctr[GBPROX_PEER_CTR_APN_PATCHED]);
}
static int gbproxy_patch_tlli(uint8_t *tlli_enc,
struct gbproxy_bvc *bvc,
uint32_t new_tlli,
int to_bss, const char *log_text)
{
uint32_t tlli_be;
uint32_t tlli;
enum gbproxy_bvc_ctr counter =
to_bss ?
GBPROX_PEER_CTR_TLLI_PATCHED_SGSN :
GBPROX_PEER_CTR_TLLI_PATCHED_BSS;
OSMO_ASSERT(bvc);
memcpy(&tlli_be, tlli_enc, sizeof(tlli_be));
tlli = ntohl(tlli_be);
if (tlli == new_tlli)
return 0;
LOGPBVC(bvc, 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(&bvc->ctrg->ctr[counter]);
return 1;
}
static int gbproxy_patch_ptmsi(uint8_t *ptmsi_enc,
struct gbproxy_bvc *bvc,
uint32_t new_ptmsi,
int to_bss, const char *log_text)
{
uint32_t ptmsi_be;
uint32_t ptmsi;
enum gbproxy_bvc_ctr counter =
to_bss ?
GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN :
GBPROX_PEER_CTR_PTMSI_PATCHED_BSS;
OSMO_ASSERT(bvc);
memcpy(&ptmsi_be, ptmsi_enc, sizeof(ptmsi_be));
ptmsi = ntohl(ptmsi_be);
if (ptmsi == new_ptmsi)
return 0;
LOGPBVC(bvc, LOGL_DEBUG,
"Patching %ss: "
"Replacing %08x -> %08x\n",
log_text, ptmsi, new_ptmsi);
ptmsi_be = htonl(new_ptmsi);
memcpy(ptmsi_enc, &ptmsi_be, sizeof(ptmsi_be));
rate_ctr_inc(&bvc->ctrg->ctr[counter]);
return 1;
}
int gbproxy_patch_llc(struct msgb *msg, uint8_t *llc, size_t llc_len,
struct gbproxy_bvc *bvc,
struct gbproxy_link_info *link_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;
OSMO_ASSERT(bvc);
OSMO_ASSERT(bvc->nse);
struct gbproxy_config *cfg = bvc->nse->cfg;
OSMO_ASSERT(cfg);
if (parse_ctx->ptmsi_enc && link_info &&
!parse_ctx->old_raid_is_foreign && cfg->patch_ptmsi) {
uint32_t ptmsi;
if (parse_ctx->to_bss)
ptmsi = link_info->tlli.ptmsi;
else
ptmsi = link_info->sgsn_tlli.ptmsi;
if (ptmsi != GSM_RESERVED_TMSI) {
if (gbproxy_patch_ptmsi(parse_ctx->ptmsi_enc, bvc,
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 && link_info && cfg->patch_ptmsi) {
uint32_t ptmsi;
if (parse_ctx->to_bss)
ptmsi = link_info->tlli.ptmsi;
else
ptmsi = link_info->sgsn_tlli.ptmsi;
OSMO_ASSERT(ptmsi);
if (gbproxy_patch_ptmsi(parse_ctx->new_ptmsi_enc, bvc,
ptmsi, parse_ctx->to_bss, "new P-TMSI"))
have_patched = 1;
}
if (parse_ctx->raid_enc) {
gbproxy_patch_raid((struct gsm48_ra_id *)parse_ctx->raid_enc, bvc, parse_ctx->to_bss,
parse_ctx->llc_msg_name);
have_patched = 1;
}
if (parse_ctx->old_raid_enc && !parse_ctx->old_raid_is_foreign) {
/* TODO: Patch to invalid if P-TMSI unknown. */
gbproxy_patch_raid((struct gsm48_ra_id *)parse_ctx->old_raid_enc, bvc, parse_ctx->to_bss,
parse_ctx->llc_msg_name);
have_patched = 1;
}
if (parse_ctx->apn_ie &&
cfg->core_apn &&
!parse_ctx->to_bss &&
gbproxy_imsi_matches(cfg, GBPROX_MATCH_PATCHING, link_info) &&
cfg->core_apn) {
size_t new_len;
gbproxy_patch_apn_ie(msg,
parse_ctx->apn_ie, parse_ctx->apn_ie_len,
bvc, &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);
LOGPBVC_CAT(bvc, 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_plmn.mcc/mnc on the SGSN side */
void gbproxy_patch_bssgp(struct msgb *msg, uint8_t *bssgp, size_t bssgp_len,
struct gbproxy_bvc *bvc,
struct gbproxy_link_info *link_info, int *len_change,
struct gprs_gb_parse_context *parse_ctx)
{
const char *err_info = NULL;
int err_ctr = -1;
OSMO_ASSERT(bvc->nse);
struct gbproxy_config *cfg = bvc->nse->cfg;
OSMO_ASSERT(cfg);
if (parse_ctx->bssgp_raid_enc)
gbproxy_patch_raid((struct gsm48_ra_id *)parse_ctx->bssgp_raid_enc, bvc,
parse_ctx->to_bss, "BSSGP");
if (parse_ctx->need_decryption &&
(cfg->patch_ptmsi || cfg->core_apn)) {
/* 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 (!link_info && parse_ctx->tlli_enc && 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;
}
if (!link_info)
return;
if (parse_ctx->tlli_enc && cfg->patch_ptmsi) {
uint32_t tlli = gbproxy_map_tlli(parse_ctx->tlli,
link_info, parse_ctx->to_bss);
if (tlli) {
gbproxy_patch_tlli(parse_ctx->tlli_enc, bvc, tlli,
parse_ctx->to_bss, "TLLI");
parse_ctx->tlli = tlli;
} else {
/* Internal error */
err_ctr = GBPROX_PEER_CTR_PATCH_ERR;
err_info = "Replacement TLLI is 0";
goto patch_error;
}
}
if (parse_ctx->bssgp_ptmsi_enc && cfg->patch_ptmsi) {
uint32_t ptmsi;
if (parse_ctx->to_bss)
ptmsi = link_info->tlli.ptmsi;
else
ptmsi = link_info->sgsn_tlli.ptmsi;
if (ptmsi != GSM_RESERVED_TMSI)
gbproxy_patch_ptmsi(
parse_ctx->bssgp_ptmsi_enc, bvc,
ptmsi, parse_ctx->to_bss, "BSSGP P-TMSI");
}
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, bvc, link_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(&bvc->ctrg->ctr[err_ctr]);
LOGPBVC(bvc, LOGL_ERROR,
"NSE(%05u/%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_match *match)
{
if (match->enable) {
regfree(&match->re_comp);
match->enable = false;
}
talloc_free(match->re_str);
match->re_str = NULL;
}
int gbproxy_set_patch_filter(struct gbproxy_match *match, const char *filter,
const char **err_msg)
{
static char err_buf[300];
int rc;
gbproxy_clear_patch_filter(match);
if (!filter)
return 0;
rc = regcomp(&match->re_comp, filter,
REG_EXTENDED | REG_NOSUB | REG_ICASE);
if (rc == 0) {
match->enable = true;
match->re_str = talloc_strdup(tall_sgsn_ctx, filter);
return 0;
}
if (err_msg) {
regerror(rc, &match->re_comp,
err_buf, sizeof(err_buf));
*err_msg = err_buf;
}
return -1;
}
int gbproxy_check_imsi(struct gbproxy_match *match,
const uint8_t *imsi, size_t imsi_len)
{
int rc;
struct osmo_mobile_identity mi;
if (!match->enable)
return 1;
rc = osmo_mobile_identity_decode(&mi, imsi, imsi_len, false);
if (rc || mi.type != GSM_MI_TYPE_IMSI) {
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.imsi, rc);
rc = regexec(&match->re_comp, mi.imsi, 0, NULL, 0);
if (rc == REG_NOMATCH) {
LOGP(DGPRS, LOGL_INFO,
"IMSI '%s' doesn't match pattern '%s'\n",
mi.imsi, match->re_str);
return 0;
}
return 1;
}

View File

@ -41,34 +41,6 @@ static const struct rate_ctr_desc bvc_ctr_description[] = {
{ "dropped", "BVC blocked, dropped packet " },
{ "inv-nsei", "NSEI mismatch " },
{ "tx-err", "NS Transmission error " },
{ "raid-mod:bss", "RAID patched (BSS )" },
{ "raid-mod:sgsn", "RAID patched (SGSN)" },
{ "apn-mod:sgsn", "APN patched " },
{ "tlli-mod:bss", "TLLI patched (BSS )" },
{ "tlli-mod:sgsn", "TLLI patched (SGSN)" },
{ "ptmsi-mod:bss", "P-TMSI patched (BSS )" },
{ "ptmsi-mod:sgsn","P-TMSI patched (SGSN)" },
{ "mod-crypt-err", "Patch error: encrypted " },
{ "mod-err", "Patch error: other " },
{ "attach-reqs", "Attach Request count " },
{ "attach-rejs", "Attach Reject count " },
{ "attach-acks", "Attach Accept count " },
{ "attach-cpls", "Attach Completed count " },
{ "ra-upd-reqs", "RoutingArea Update Request count" },
{ "ra-upd-rejs", "RoutingArea Update Reject count " },
{ "ra-upd-acks", "RoutingArea Update Accept count " },
{ "ra-upd-cpls", "RoutingArea Update Compltd count" },
{ "gmm-status", "GMM Status count (BSS)" },
{ "gmm-status", "GMM Status count (SGSN)" },
{ "detach-reqs", "Detach Request count " },
{ "detach-acks", "Detach Accept count " },
{ "pdp-act-reqs", "PDP Activation Request count " },
{ "pdp-act-rejs", "PDP Activation Reject count " },
{ "pdp-act-acks", "PDP Activation Accept count " },
{ "pdp-deact-reqs","PDP Deactivation Request count " },
{ "pdp-deact-acks","PDP Deactivation Accept count " },
{ "tlli-unknown", "TLLI from SGSN unknown " },
{ "tlli-cache", "TLLI cache size " },
};
osmo_static_assert(ARRAY_SIZE(bvc_ctr_description) == GBPROX_PEER_CTR_LAST, everything_described);
@ -136,89 +108,6 @@ struct gbproxy_bvc *gbproxy_bvc_by_rai(struct gbproxy_config *cfg,
return NULL;
}
/* look-up a bvc by its Location Area Identification (LAI) */
/* FIXME: this doesn't make sense, as LA can span multiple bvcs! */
struct gbproxy_bvc *gbproxy_bvc_by_lai(struct gbproxy_config *cfg,
const uint8_t *la)
{
struct gbproxy_nse *nse;
int i, j;
hash_for_each(cfg->bss_nses, i, nse, list) {
struct gbproxy_bvc *bvc;
hash_for_each(nse->bvcs, j, bvc, list) {
if (!memcmp(bvc->ra, la, 5))
return bvc;
}
}
return NULL;
}
/* look-up a bvc by its Location Area Code (LAC) */
/* FIXME: this doesn't make sense, as LAC can span multiple bvcs! */
struct gbproxy_bvc *gbproxy_bvc_by_lac(struct gbproxy_config *cfg,
const uint8_t *la)
{
struct gbproxy_nse *nse;
int i, j;
hash_for_each(cfg->bss_nses, i, nse, list) {
struct gbproxy_bvc *bvc;
hash_for_each(nse->bvcs, j, bvc, list) {
if (!memcmp(bvc->ra + 3, la + 3, 2))
return bvc;
}
}
return NULL;
}
struct gbproxy_bvc *gbproxy_bvc_by_bssgp_tlv(struct gbproxy_config *cfg,
struct tlv_parsed *tp)
{
if (TLVP_PRES_LEN(tp, BSSGP_IE_BVCI, 2)) {
uint16_t bvci;
bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI));
if (bvci >= 2)
return gbproxy_bvc_by_bvci(cfg, bvci);
}
/* FIXME: this doesn't make sense, as RA can span multiple bvcs! */
if (TLVP_PRES_LEN(tp, BSSGP_IE_ROUTEING_AREA, 6)) {
uint8_t *rai = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA);
/* Only compare LAC part, since MCC/MNC are possibly patched.
* Since the LAC of different BSS must be different when
* MCC/MNC are patched, collisions shouldn't happen. */
return gbproxy_bvc_by_lac(cfg, rai);
}
/* FIXME: this doesn't make sense, as LA can span multiple bvcs! */
if (TLVP_PRES_LEN(tp, BSSGP_IE_LOCATION_AREA, 5)) {
uint8_t *lai = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA);
return gbproxy_bvc_by_lac(cfg, lai);
}
return NULL;
}
static void clean_stale_timer_cb(void *data)
{
time_t now;
struct timespec ts = {0,};
struct gbproxy_bvc *bvc = (struct gbproxy_bvc *) data;
OSMO_ASSERT(bvc);
OSMO_ASSERT(bvc->nse);
struct gbproxy_config *cfg = bvc->nse->cfg;
OSMO_ASSERT(cfg);
osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
now = ts.tv_sec;
gbproxy_remove_stale_link_infos(bvc, now);
if (cfg->clean_stale_timer_freq != 0)
osmo_timer_schedule(&bvc->clean_stale_timer,
cfg->clean_stale_timer_freq, 0);
}
struct gbproxy_bvc *gbproxy_bvc_alloc(struct gbproxy_nse *nse, uint16_t bvci)
{
struct gbproxy_bvc *bvc;
@ -240,13 +129,6 @@ struct gbproxy_bvc *gbproxy_bvc_alloc(struct gbproxy_nse *nse, uint16_t bvci)
hash_add(nse->bvcs, &bvc->list, bvc->bvci);
INIT_LLIST_HEAD(&bvc->patch_state.logical_links);
osmo_timer_setup(&bvc->clean_stale_timer, clean_stale_timer_cb, bvc);
if (cfg->clean_stale_timer_freq != 0)
osmo_timer_schedule(&bvc->clean_stale_timer,
cfg->clean_stale_timer_freq, 0);
return bvc;
}
@ -256,8 +138,6 @@ void gbproxy_bvc_free(struct gbproxy_bvc *bvc)
return;
hash_del(&bvc->list);
osmo_timer_del(&bvc->clean_stale_timer);
gbproxy_delete_link_infos(bvc);
rate_ctr_group_free(bvc->ctrg);
bvc->ctrg = NULL;

View File

@ -1,746 +0,0 @@
/* 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 <osmocom/gsm/gsm48.h>
#include <osmocom/sgsn/gb_proxy.h>
#include <osmocom/sgsn/gprs_utils.h>
#include <osmocom/sgsn/gprs_gb_parse.h>
#include <osmocom/sgsn/debug.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/talloc.h>
struct gbproxy_link_info *gbproxy_link_info_by_tlli(struct gbproxy_bvc *bvc,
uint32_t tlli)
{
struct gbproxy_link_info *link_info;
struct gbproxy_patch_state *state = &bvc->patch_state;
if (!tlli)
return NULL;
llist_for_each_entry(link_info, &state->logical_links, list)
if (link_info->tlli.current == tlli ||
link_info->tlli.assigned == tlli)
return link_info;
return NULL;
}
struct gbproxy_link_info *gbproxy_link_info_by_ptmsi(
struct gbproxy_bvc *bvc,
uint32_t ptmsi)
{
struct gbproxy_link_info *link_info;
struct gbproxy_patch_state *state = &bvc->patch_state;
if (ptmsi == GSM_RESERVED_TMSI)
return NULL;
llist_for_each_entry(link_info, &state->logical_links, list)
if (link_info->tlli.ptmsi == ptmsi)
return link_info;
return NULL;
}
struct gbproxy_link_info *gbproxy_link_info_by_any_sgsn_tlli(
struct gbproxy_bvc *bvc,
uint32_t tlli)
{
struct gbproxy_link_info *link_info;
struct gbproxy_patch_state *state = &bvc->patch_state;
if (!tlli)
return NULL;
/* Don't care about the NSEI */
llist_for_each_entry(link_info, &state->logical_links, list)
if (link_info->sgsn_tlli.current == tlli ||
link_info->sgsn_tlli.assigned == tlli)
return link_info;
return NULL;
}
struct gbproxy_link_info *gbproxy_link_info_by_sgsn_tlli(
struct gbproxy_bvc *bvc,
uint32_t tlli, uint32_t sgsn_nsei)
{
struct gbproxy_link_info *link_info;
struct gbproxy_patch_state *state = &bvc->patch_state;
if (!tlli)
return NULL;
llist_for_each_entry(link_info, &state->logical_links, list)
if ((link_info->sgsn_tlli.current == tlli ||
link_info->sgsn_tlli.assigned == tlli) &&
link_info->sgsn_nsei == sgsn_nsei)
return link_info;
return NULL;
}
struct gbproxy_link_info *gbproxy_link_info_by_imsi(
struct gbproxy_bvc *bvc,
const uint8_t *imsi,
size_t imsi_len)
{
struct gbproxy_link_info *link_info;
struct gbproxy_patch_state *state = &bvc->patch_state;
if (!gprs_is_mi_imsi(imsi, imsi_len))
return NULL;
llist_for_each_entry(link_info, &state->logical_links, list) {
if (link_info->imsi_len != imsi_len)
continue;
if (memcmp(link_info->imsi, imsi, imsi_len) != 0)
continue;
return link_info;
}
return NULL;
}
void gbproxy_link_info_discard_messages(struct gbproxy_link_info *link_info)
{
struct msgb *msg, *nxt;
llist_for_each_entry_safe(msg, nxt, &link_info->stored_msgs, list) {
llist_del(&msg->list);
msgb_free(msg);
}
}
void gbproxy_delete_link_info(struct gbproxy_bvc *bvc,
struct gbproxy_link_info *link_info)
{
struct gbproxy_patch_state *state = &bvc->patch_state;
gbproxy_link_info_discard_messages(link_info);
llist_del(&link_info->list);
talloc_free(link_info);
state->logical_link_count -= 1;
bvc->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current =
state->logical_link_count;
}
void gbproxy_delete_link_infos(struct gbproxy_bvc *bvc)
{
struct gbproxy_link_info *link_info, *nxt;
struct gbproxy_patch_state *state = &bvc->patch_state;
llist_for_each_entry_safe(link_info, nxt, &state->logical_links, list)
gbproxy_delete_link_info(bvc, link_info);
OSMO_ASSERT(state->logical_link_count == 0);
OSMO_ASSERT(llist_empty(&state->logical_links));
}
void gbproxy_attach_link_info(struct gbproxy_bvc *bvc, time_t now,
struct gbproxy_link_info *link_info)
{
struct gbproxy_patch_state *state = &bvc->patch_state;
link_info->timestamp = now;
llist_add(&link_info->list, &state->logical_links);
state->logical_link_count += 1;
bvc->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current =
state->logical_link_count;
}
int gbproxy_remove_stale_link_infos(struct gbproxy_bvc *bvc, time_t now)
{
OSMO_ASSERT(bvc);
struct gbproxy_patch_state *state = &bvc->patch_state;
int exceeded_max_len = 0;
int deleted_count = 0;
int check_for_age;
OSMO_ASSERT(bvc->nse);
struct gbproxy_config *cfg = bvc->nse->cfg;
OSMO_ASSERT(cfg);
if (cfg->tlli_max_len > 0)
exceeded_max_len =
state->logical_link_count - cfg->tlli_max_len;
check_for_age = cfg->tlli_max_age > 0;
for (; exceeded_max_len > 0; exceeded_max_len--) {
struct gbproxy_link_info *link_info;
OSMO_ASSERT(!llist_empty(&state->logical_links));
link_info = llist_entry(state->logical_links.prev,
struct gbproxy_link_info,
list);
LOGPBVC(bvc, LOGL_INFO,
"Removing TLLI %08x from list "
"(stale, length %d, max_len exceeded)\n",
link_info->tlli.current, state->logical_link_count);
gbproxy_delete_link_info(bvc, link_info);
deleted_count += 1;
}
while (check_for_age && !llist_empty(&state->logical_links)) {
time_t age;
struct gbproxy_link_info *link_info;
link_info = llist_entry(state->logical_links.prev,
struct gbproxy_link_info,
list);
age = now - link_info->timestamp;
/* age < 0 only happens after system time jumps, discard entry */
if (age <= cfg->tlli_max_age && age >= 0) {
check_for_age = 0;
continue;
}
LOGPBVC(bvc, LOGL_INFO,
"Removing TLLI %08x from list "
"(stale, age %d, max_age exceeded)\n",
link_info->tlli.current, (int)age);
gbproxy_delete_link_info(bvc, link_info);
deleted_count += 1;
}
return deleted_count;
}
struct gbproxy_link_info *gbproxy_link_info_alloc( struct gbproxy_bvc *bvc)
{
struct gbproxy_link_info *link_info;
link_info = talloc_zero(bvc, struct gbproxy_link_info);
link_info->tlli.ptmsi = GSM_RESERVED_TMSI;
link_info->sgsn_tlli.ptmsi = GSM_RESERVED_TMSI;
link_info->vu_gen_tx_bss = GBPROXY_INIT_VU_GEN_TX;
INIT_LLIST_HEAD(&link_info->stored_msgs);
return link_info;
}
void gbproxy_detach_link_info(
struct gbproxy_bvc *bvc,
struct gbproxy_link_info *link_info)
{
struct gbproxy_patch_state *state = &bvc->patch_state;
llist_del(&link_info->list);
OSMO_ASSERT(state->logical_link_count > 0);
state->logical_link_count -= 1;
bvc->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current =
state->logical_link_count;
}
void gbproxy_update_link_info(struct gbproxy_link_info *link_info,
const uint8_t *imsi, size_t imsi_len)
{
if (!gprs_is_mi_imsi(imsi, imsi_len))
return;
link_info->imsi_len = imsi_len;
link_info->imsi =
talloc_realloc_size(link_info, link_info->imsi, imsi_len);
OSMO_ASSERT(link_info->imsi != NULL);
memcpy(link_info->imsi, imsi, imsi_len);
}
void gbproxy_reassign_tlli(struct gbproxy_tlli_state *tlli_state,
struct gbproxy_bvc *bvc, uint32_t new_tlli)
{
OSMO_ASSERT(bvc);
if (new_tlli == tlli_state->current)
return;
LOGPBVC(bvc, 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 = false;
tlli_state->net_validated = false;
}
uint32_t gbproxy_map_tlli(uint32_t other_tlli,
struct gbproxy_link_info *link_info, int to_bss)
{
uint32_t tlli = 0;
struct gbproxy_tlli_state *src, *dst;
if (to_bss) {
src = &link_info->sgsn_tlli;
dst = &link_info->tlli;
} else {
src = &link_info->tlli;
dst = &link_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 = true;
else
tlli_state->bss_validated = true;
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;
}
static void gbproxy_touch_link_info(struct gbproxy_bvc *bvc,
struct gbproxy_link_info *link_info,
time_t now)
{
gbproxy_detach_link_info(bvc, link_info);
gbproxy_attach_link_info(bvc, now, link_info);
}
static int gbproxy_unregister_link_info(struct gbproxy_bvc *bvc,
struct gbproxy_link_info *link_info)
{
OSMO_ASSERT(bvc);
if (!link_info)
return 1;
if (link_info->tlli.ptmsi == GSM_RESERVED_TMSI && !link_info->imsi_len) {
LOGPBVC(bvc, LOGL_INFO,
"Removing TLLI %08x from list (P-TMSI or IMSI are not set)\n",
link_info->tlli.current);
gbproxy_delete_link_info(bvc, link_info);
return 1;
}
link_info->tlli.current = 0;
link_info->tlli.assigned = 0;
link_info->sgsn_tlli.current = 0;
link_info->sgsn_tlli.assigned = 0;
link_info->is_deregistered = true;
gbproxy_reset_link(link_info);
return 0;
}
int gbproxy_imsi_matches(struct gbproxy_config *cfg,
enum gbproxy_match_id match_id,
struct gbproxy_link_info *link_info)
{
struct gbproxy_match *match;
OSMO_ASSERT(match_id >= 0 && match_id < ARRAY_SIZE(cfg->matches));
match = &cfg->matches[match_id];
if (!match->enable)
return 1;
return link_info != NULL && link_info->is_matching[match_id];
}
static void gbproxy_assign_imsi(struct gbproxy_bvc *bvc,
struct gbproxy_link_info *link_info,
struct gprs_gb_parse_context *parse_ctx)
{
int imsi_matches;
struct gbproxy_link_info *other_link_info;
enum gbproxy_match_id match_id;
OSMO_ASSERT(bvc);
OSMO_ASSERT(bvc->nse);
struct gbproxy_config *cfg = bvc->nse->cfg;
OSMO_ASSERT(cfg);
/* Make sure that there is a second entry with the same IMSI */
other_link_info = gbproxy_link_info_by_imsi(
bvc, parse_ctx->imsi, parse_ctx->imsi_len);
if (other_link_info && other_link_info != link_info) {
struct osmo_mobile_identity mi;
if (osmo_mobile_identity_decode(&mi, parse_ctx->imsi, parse_ctx->imsi_len, false)
|| mi.type != GSM_MI_TYPE_IMSI) {
LOGPBVC(bvc, LOGL_ERROR, "Failed to decode Mobile Identity\n");
} else {
LOGPBVC(bvc, LOGL_INFO,
"Removing TLLI %08x from list (IMSI %s re-used)\n",
other_link_info->tlli.current, mi.imsi);
gbproxy_delete_link_info(bvc, other_link_info);
}
}
/* Update the IMSI field */
gbproxy_update_link_info(link_info,
parse_ctx->imsi, parse_ctx->imsi_len);
/* Check, whether the IMSI matches */
OSMO_ASSERT(ARRAY_SIZE(link_info->is_matching) ==
ARRAY_SIZE(cfg->matches));
for (match_id = 0; match_id < ARRAY_SIZE(link_info->is_matching);
++match_id) {
imsi_matches = gbproxy_check_imsi(
&cfg->matches[match_id],
parse_ctx->imsi, parse_ctx->imsi_len);
if (imsi_matches >= 0)
link_info->is_matching[match_id] = imsi_matches ? true : false;
}
}
static int gbproxy_tlli_match(const struct gbproxy_tlli_state *a,
const struct gbproxy_tlli_state *b)
{
if (a->current && a->current == b->current)
return 1;
if (a->assigned && a->assigned == b->assigned)
return 1;
if (a->ptmsi != GSM_RESERVED_TMSI && a->ptmsi == b->ptmsi)
return 1;
return 0;
}
static void gbproxy_remove_matching_link_infos(
struct gbproxy_bvc *bvc, struct gbproxy_link_info *link_info)
{
OSMO_ASSERT(bvc);
struct gbproxy_link_info *info, *nxt;
struct gbproxy_patch_state *state = &bvc->patch_state;
/* Make sure that there is no second entry with the same P-TMSI or TLLI */
llist_for_each_entry_safe(info, nxt, &state->logical_links, list) {
if (info == link_info)
continue;
if (!gbproxy_tlli_match(&link_info->tlli, &info->tlli) &&
(link_info->sgsn_nsei != info->sgsn_nsei ||
!gbproxy_tlli_match(&link_info->sgsn_tlli, &info->sgsn_tlli)))
continue;
LOGPBVC(bvc, LOGL_INFO,
"Removing TLLI %08x from list (P-TMSI/TLLI re-used)\n",
info->tlli.current);
gbproxy_delete_link_info(bvc, info);
}
}
static struct gbproxy_link_info *gbproxy_get_link_info_ul(
struct gbproxy_bvc *bvc,
int *tlli_is_valid,
struct gprs_gb_parse_context *parse_ctx)
{
struct gbproxy_link_info *link_info = NULL;
if (parse_ctx->tlli_enc) {
link_info = gbproxy_link_info_by_tlli(bvc, parse_ctx->tlli);
if (link_info) {
*tlli_is_valid = 1;
return link_info;
}
}
*tlli_is_valid = 0;
if (!link_info && parse_ctx->imsi) {
link_info = gbproxy_link_info_by_imsi(
bvc, parse_ctx->imsi, parse_ctx->imsi_len);
}
if (!link_info && parse_ctx->ptmsi_enc && !parse_ctx->old_raid_is_foreign) {
uint32_t bss_ptmsi;
gprs_parse_tmsi(parse_ctx->ptmsi_enc, &bss_ptmsi);
link_info = gbproxy_link_info_by_ptmsi(bvc, bss_ptmsi);
}
if (!link_info)
return NULL;
link_info->is_deregistered = false;
return link_info;
}
struct gbproxy_link_info *gbproxy_update_link_state_ul(
struct gbproxy_bvc *bvc,
time_t now,
struct gprs_gb_parse_context *parse_ctx)
{
struct gbproxy_link_info *link_info;
int tlli_is_valid;
OSMO_ASSERT(bvc);
link_info = gbproxy_get_link_info_ul(bvc, &tlli_is_valid, parse_ctx);
if (parse_ctx->tlli_enc && parse_ctx->llc) {
uint32_t sgsn_tlli;
if (!link_info) {
LOGPBVC(bvc, LOGL_INFO, "Adding TLLI %08x to list\n",
parse_ctx->tlli);
link_info = gbproxy_link_info_alloc(bvc);
gbproxy_attach_link_info(bvc, now, link_info);
/* Setup TLLIs */
sgsn_tlli = gbproxy_make_sgsn_tlli(bvc, link_info,
parse_ctx->tlli);
link_info->sgsn_tlli.current = sgsn_tlli;
link_info->tlli.current = parse_ctx->tlli;
} else if (!tlli_is_valid) {
/* New TLLI (info found by IMSI or P-TMSI) */
link_info->tlli.current = parse_ctx->tlli;
link_info->tlli.assigned = 0;
link_info->sgsn_tlli.current =
gbproxy_make_sgsn_tlli(bvc, link_info,
parse_ctx->tlli);
link_info->sgsn_tlli.assigned = 0;
gbproxy_touch_link_info(bvc, link_info, now);
} else {
sgsn_tlli = gbproxy_map_tlli(parse_ctx->tlli, link_info, 0);
if (!sgsn_tlli)
sgsn_tlli = gbproxy_make_sgsn_tlli(bvc, link_info,
parse_ctx->tlli);
gbproxy_validate_tlli(&link_info->tlli,
parse_ctx->tlli, 0);
gbproxy_validate_tlli(&link_info->sgsn_tlli,
sgsn_tlli, 0);
gbproxy_touch_link_info(bvc, link_info, now);
}
} else if (link_info) {
gbproxy_touch_link_info(bvc, link_info, now);
}
if (parse_ctx->imsi && link_info && link_info->imsi_len == 0)
gbproxy_assign_imsi(bvc, link_info, parse_ctx);
return link_info;
}
static struct gbproxy_link_info *gbproxy_get_link_info_dl(
struct gbproxy_bvc *bvc,
struct gprs_gb_parse_context *parse_ctx)
{
struct gbproxy_link_info *link_info = NULL;
/* Which key to use depends on its availability only, if that fails, do
* not retry it with another key (e.g. IMSI). */
if (parse_ctx->tlli_enc)
link_info = gbproxy_link_info_by_sgsn_tlli(bvc, parse_ctx->tlli,
parse_ctx->peer_nsei);
/* TODO: Get link_info by (SGSN) P-TMSI if that is available (see
* GSM 08.18, 7.2) instead of using the IMSI as key. */
else if (parse_ctx->imsi)
link_info = gbproxy_link_info_by_imsi(
bvc, parse_ctx->imsi, parse_ctx->imsi_len);
if (link_info)
link_info->is_deregistered = false;
return link_info;
}
struct gbproxy_link_info *gbproxy_update_link_state_dl(
struct gbproxy_bvc *bvc,
time_t now,
struct gprs_gb_parse_context *parse_ctx)
{
struct gbproxy_link_info *link_info = NULL;
OSMO_ASSERT(bvc);
OSMO_ASSERT(bvc->nse);
struct gbproxy_config *cfg = bvc->nse->cfg;
OSMO_ASSERT(cfg);
link_info = gbproxy_get_link_info_dl(bvc, parse_ctx);
if (parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc && link_info) {
/* A new P-TMSI has been signalled in the message,
* register new TLLI */
uint32_t new_sgsn_ptmsi;
uint32_t new_bss_ptmsi = GSM_RESERVED_TMSI;
gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_sgsn_ptmsi);
if (link_info->sgsn_tlli.ptmsi == new_sgsn_ptmsi)
new_bss_ptmsi = link_info->tlli.ptmsi;
if (new_bss_ptmsi == GSM_RESERVED_TMSI)
new_bss_ptmsi = gbproxy_make_bss_ptmsi(bvc, new_sgsn_ptmsi);
LOGPBVC(bvc, LOGL_INFO,
"Got new PTMSI %08x from SGSN, using %08x for BSS\n",
new_sgsn_ptmsi, new_bss_ptmsi);
/* Setup PTMSIs */
link_info->sgsn_tlli.ptmsi = new_sgsn_ptmsi;
link_info->tlli.ptmsi = new_bss_ptmsi;
} else if (parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc && !link_info &&
!cfg->patch_ptmsi) {
/* A new P-TMSI has been signalled in the message with an unknown
* TLLI, create a new link_info */
/* TODO: Add a test case for this branch */
uint32_t new_ptmsi;
gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi);
LOGPBVC(bvc, LOGL_INFO,
"Adding TLLI %08x to list (SGSN, new P-TMSI is %08x)\n",
parse_ctx->tlli, new_ptmsi);
link_info = gbproxy_link_info_alloc(bvc);
link_info->sgsn_tlli.current = parse_ctx->tlli;
link_info->tlli.current = parse_ctx->tlli;
link_info->sgsn_tlli.ptmsi = new_ptmsi;
link_info->tlli.ptmsi = new_ptmsi;
gbproxy_attach_link_info(bvc, now, link_info);
} else if (parse_ctx->tlli_enc && parse_ctx->llc && !link_info &&
!cfg->patch_ptmsi) {
/* Unknown SGSN TLLI, create a new link_info */
uint32_t new_ptmsi;
link_info = gbproxy_link_info_alloc(bvc);
LOGPBVC(bvc, LOGL_INFO, "Adding TLLI %08x to list (SGSN)\n",
parse_ctx->tlli);
gbproxy_attach_link_info(bvc, now, link_info);
/* Setup TLLIs */
link_info->sgsn_tlli.current = parse_ctx->tlli;
link_info->tlli.current = parse_ctx->tlli;
if (!parse_ctx->new_ptmsi_enc)
return link_info;
/* A new P-TMSI has been signalled in the message */
gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi);
LOGPBVC(bvc, LOGL_INFO,
"Assigning new P-TMSI %08x\n", new_ptmsi);
/* Setup P-TMSIs */
link_info->sgsn_tlli.ptmsi = new_ptmsi;
link_info->tlli.ptmsi = new_ptmsi;
} else if (parse_ctx->tlli_enc && parse_ctx->llc && link_info) {
uint32_t bss_tlli = gbproxy_map_tlli(parse_ctx->tlli,
link_info, 1);
gbproxy_validate_tlli(&link_info->sgsn_tlli, parse_ctx->tlli, 1);
gbproxy_validate_tlli(&link_info->tlli, bss_tlli, 1);
gbproxy_touch_link_info(bvc, link_info, now);
} else if (link_info) {
gbproxy_touch_link_info(bvc, link_info, now);
}
if (parse_ctx->imsi && link_info && link_info->imsi_len == 0)
gbproxy_assign_imsi(bvc, link_info, parse_ctx);
return link_info;
}
int gbproxy_update_link_state_after(
struct gbproxy_bvc *bvc,
struct gbproxy_link_info *link_info,
time_t now,
struct gprs_gb_parse_context *parse_ctx)
{
int rc = 0;
OSMO_ASSERT(bvc);
OSMO_ASSERT(bvc->nse);
struct gbproxy_config *cfg = bvc->nse->cfg;
OSMO_ASSERT(cfg);
if (parse_ctx->invalidate_tlli && link_info) {
int keep_info =
cfg->keep_link_infos == GBPROX_KEEP_ALWAYS ||
(cfg->keep_link_infos == GBPROX_KEEP_REATTACH &&
parse_ctx->await_reattach) ||
(cfg->keep_link_infos == GBPROX_KEEP_IDENTIFIED &&
link_info->imsi_len > 0);
if (keep_info) {
LOGPBVC(bvc, LOGL_INFO, "Unregistering TLLI %08x\n",
link_info->tlli.current);
rc = gbproxy_unregister_link_info(bvc, link_info);
} else {
LOGPBVC(bvc, LOGL_INFO, "Removing TLLI %08x from list\n",
link_info->tlli.current);
gbproxy_delete_link_info(bvc, link_info);
rc = 1;
}
} else if (parse_ctx->to_bss && parse_ctx->tlli_enc &&
parse_ctx->new_ptmsi_enc && link_info) {
/* A new PTMSI has been signaled in the message,
* register new TLLI */
uint32_t new_sgsn_ptmsi = link_info->sgsn_tlli.ptmsi;
uint32_t new_bss_ptmsi = link_info->tlli.ptmsi;
uint32_t new_sgsn_tlli;
uint32_t new_bss_tlli = 0;
new_sgsn_tlli = gprs_tmsi2tlli(new_sgsn_ptmsi, TLLI_LOCAL);
if (new_bss_ptmsi != GSM_RESERVED_TMSI)
new_bss_tlli = gprs_tmsi2tlli(new_bss_ptmsi, TLLI_LOCAL);
LOGPBVC(bvc, LOGL_INFO,
"Assigning new TLLI %08x to SGSN, %08x to BSS\n",
new_sgsn_tlli, new_bss_tlli);
gbproxy_reassign_tlli(&link_info->sgsn_tlli,
bvc, new_sgsn_tlli);
gbproxy_reassign_tlli(&link_info->tlli,
bvc, new_bss_tlli);
gbproxy_remove_matching_link_infos(bvc, link_info);
}
gbproxy_remove_stale_link_infos(bvc, now);
return rc;
}

View File

@ -54,20 +54,6 @@ static struct cmd_node gbproxy_node = {
1,
};
static const struct value_string keep_modes[] = {
{GBPROX_KEEP_NEVER, "never"},
{GBPROX_KEEP_REATTACH, "re-attach"},
{GBPROX_KEEP_IDENTIFIED, "identified"},
{GBPROX_KEEP_ALWAYS, "always"},
{0, NULL}
};
static const struct value_string match_ids[] = {
{GBPROX_MATCH_PATCHING, "patching"},
{GBPROX_MATCH_ROUTING, "routing"},
{0, NULL}
};
static void gbprox_vty_print_bvc(struct vty *vty, struct gbproxy_bvc *bvc)
{
struct gprs_ra_id raid;
@ -83,62 +69,11 @@ static void gbprox_vty_print_bvc(struct vty *vty, struct gbproxy_bvc *bvc)
static int config_write_gbproxy(struct vty *vty)
{
enum gbproxy_match_id match_id;
vty_out(vty, "gbproxy%s", VTY_NEWLINE);
vty_out(vty, " sgsn nsei %u%s", g_cfg->nsip_sgsn_nsei,
VTY_NEWLINE);
if (g_cfg->core_plmn.mcc > 0)
vty_out(vty, " core-mobile-country-code %s%s",
osmo_mcc_name(g_cfg->core_plmn.mcc), VTY_NEWLINE);
if (g_cfg->core_plmn.mnc > 0)
vty_out(vty, " core-mobile-network-code %s%s",
osmo_mnc_name(g_cfg->core_plmn.mnc, g_cfg->core_plmn.mnc_3_digits), VTY_NEWLINE);
for (match_id = 0; match_id < ARRAY_SIZE(g_cfg->matches); ++match_id) {
struct gbproxy_match *match = &g_cfg->matches[match_id];
if (match->re_str)
vty_out(vty, " match-imsi %s %s%s",
get_value_string(match_ids, match_id),
match->re_str, VTY_NEWLINE);
}
if (g_cfg->core_apn != NULL) {
if (g_cfg->core_apn_size > 0) {
char str[500] = {0};
vty_out(vty, " core-access-point-name %s%s",
osmo_apn_to_str(str, g_cfg->core_apn,
g_cfg->core_apn_size),
VTY_NEWLINE);
} else {
vty_out(vty, " core-access-point-name none%s",
VTY_NEWLINE);
}
}
if (g_cfg->route_to_sgsn2)
vty_out(vty, " secondary-sgsn nsei %u%s", g_cfg->nsip_sgsn2_nsei,
VTY_NEWLINE);
if (g_cfg->clean_stale_timer_freq > 0)
vty_out(vty, " link-list clean-stale-timer %u%s",
g_cfg->clean_stale_timer_freq, VTY_NEWLINE);
if (g_cfg->tlli_max_age > 0)
vty_out(vty, " link-list max-age %d%s",
g_cfg->tlli_max_age, VTY_NEWLINE);
if (g_cfg->tlli_max_len > 0)
vty_out(vty, " link-list max-length %d%s",
g_cfg->tlli_max_len, VTY_NEWLINE);
vty_out(vty, " link-list keep-mode %s%s",
get_value_string(keep_modes, g_cfg->keep_link_infos),
VTY_NEWLINE);
if (g_cfg->stored_msgs_max_len > 0)
vty_out(vty, " link stored-msgs-max-length %"PRIu32"%s",
g_cfg->stored_msgs_max_len, VTY_NEWLINE);
return CMD_SUCCESS;
}
@ -160,387 +95,10 @@ DEFUN(cfg_nsip_sgsn_nsei,
{
unsigned int nsei = atoi(argv[0]);
if (g_cfg->route_to_sgsn2 && g_cfg->nsip_sgsn2_nsei == nsei) {
vty_out(vty, "SGSN NSEI %d conflicts with secondary SGSN NSEI%s",
nsei, VTY_NEWLINE);
return CMD_WARNING;
}
g_cfg->nsip_sgsn_nsei = nsei;
return CMD_SUCCESS;
}
#define GBPROXY_CORE_MNC_STR "Use this network code for the core network\n"
DEFUN(cfg_gbproxy_core_mnc,
cfg_gbproxy_core_mnc_cmd,
"core-mobile-network-code <1-999>",
GBPROXY_CORE_MNC_STR "NCC value\n")
{
uint16_t mnc;
bool mnc_3_digits;
if (osmo_mnc_from_str(argv[0], &mnc, &mnc_3_digits)) {
vty_out(vty, "%% Invalid MNC: %s%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
g_cfg->core_plmn.mnc = mnc;
g_cfg->core_plmn.mnc_3_digits = mnc_3_digits;
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_no_core_mnc,
cfg_gbproxy_no_core_mnc_cmd,
"no core-mobile-network-code",
NO_STR GBPROXY_CORE_MNC_STR)
{
g_cfg->core_plmn.mnc = 0;
g_cfg->core_plmn.mnc_3_digits = false;
return CMD_SUCCESS;
}
#define GBPROXY_CORE_MCC_STR "Use this country code for the core network\n"
DEFUN(cfg_gbproxy_core_mcc,
cfg_gbproxy_core_mcc_cmd,
"core-mobile-country-code <1-999>",
GBPROXY_CORE_MCC_STR "MCC value\n")
{
g_cfg->core_plmn.mcc = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_no_core_mcc,
cfg_gbproxy_no_core_mcc_cmd,
"no core-mobile-country-code",
NO_STR GBPROXY_CORE_MCC_STR)
{
g_cfg->core_plmn.mcc = 0;
return CMD_SUCCESS;
}
#define GBPROXY_MATCH_IMSI_STR "Restrict actions to certain IMSIs\n"
DEFUN(cfg_gbproxy_match_imsi,
cfg_gbproxy_match_imsi_cmd,
"match-imsi (patching|routing) .REGEXP",
GBPROXY_MATCH_IMSI_STR
"Patch MS related information elements on match only\n"
"Route to the secondary SGSN on match only\n"
"Regular expression for the IMSI match\n")
{
const char *filter = argv[1];
const char *err_msg = NULL;
struct gbproxy_match *match;
enum gbproxy_match_id match_id = get_string_value(match_ids, argv[0]);
OSMO_ASSERT(match_id >= GBPROX_MATCH_PATCHING &&
match_id < GBPROX_MATCH_LAST);
match = &g_cfg->matches[match_id];
if (gbproxy_set_patch_filter(match, filter, &err_msg) != 0) {
vty_out(vty, "Match expression invalid: %s%s",
err_msg, VTY_NEWLINE);
return CMD_WARNING;
}
g_cfg->acquire_imsi = true;
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_no_match_imsi,
cfg_gbproxy_no_match_imsi_cmd,
"no match-imsi",
NO_STR GBPROXY_MATCH_IMSI_STR)
{
enum gbproxy_match_id match_id;
for (match_id = 0; match_id < ARRAY_SIZE(g_cfg->matches); ++match_id)
gbproxy_clear_patch_filter(&g_cfg->matches[match_id]);
g_cfg->acquire_imsi = false;
return CMD_SUCCESS;
}
#define GBPROXY_CORE_APN_STR "Use this access point name (APN) for the backbone\n"
#define GBPROXY_CORE_APN_ARG_STR "Replace APN by this string\n" "Remove APN\n"
static int set_core_apn(struct vty *vty, const char *apn)
{
int apn_len;
if (!apn) {
talloc_free(g_cfg->core_apn);
g_cfg->core_apn = NULL;
g_cfg->core_apn_size = 0;
return CMD_SUCCESS;
}
apn_len = strlen(apn);
if (apn_len >= 100) {
vty_out(vty, "APN string too long (max 99 chars)%s",
VTY_NEWLINE);
return CMD_WARNING;
}
if (apn_len == 0) {
talloc_free(g_cfg->core_apn);
/* TODO: replace NULL */
g_cfg->core_apn = talloc_zero_size(NULL, 2);
g_cfg->core_apn_size = 0;
} else {
/* TODO: replace NULL */
g_cfg->core_apn =
talloc_realloc_size(NULL, g_cfg->core_apn, apn_len + 1);
g_cfg->core_apn_size =
gprs_str_to_apn(g_cfg->core_apn, apn_len + 1, apn);
}
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_core_apn,
cfg_gbproxy_core_apn_cmd,
"core-access-point-name (APN|none)",
GBPROXY_CORE_APN_STR GBPROXY_CORE_APN_ARG_STR)
{
if (strcmp(argv[0], "none") == 0)
return set_core_apn(vty, "");
else
return set_core_apn(vty, argv[0]);
}
DEFUN(cfg_gbproxy_no_core_apn,
cfg_gbproxy_no_core_apn_cmd,
"no core-access-point-name",
NO_STR GBPROXY_CORE_APN_STR)
{
return set_core_apn(vty, NULL);
}
/* TODO: Remove the patch-ptmsi command, since P-TMSI patching is enabled
* automatically when needed. This command is only left for manual testing
* (e.g. doing P-TMSI patching without using a secondary SGSN)
*/
#define GBPROXY_PATCH_PTMSI_STR "Patch P-TMSI/TLLI\n"
DEFUN(cfg_gbproxy_patch_ptmsi,
cfg_gbproxy_patch_ptmsi_cmd,
"patch-ptmsi",
GBPROXY_PATCH_PTMSI_STR)
{
g_cfg->patch_ptmsi = true;
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_no_patch_ptmsi,
cfg_gbproxy_no_patch_ptmsi_cmd,
"no patch-ptmsi",
NO_STR GBPROXY_PATCH_PTMSI_STR)
{
g_cfg->patch_ptmsi = false;
return CMD_SUCCESS;
}
/* TODO: Remove the acquire-imsi command, since that feature is enabled
* automatically when IMSI matching is enabled. This command is only left for
* manual testing (e.g. doing IMSI acquisition without IMSI based patching)
*/
#define GBPROXY_ACQUIRE_IMSI_STR "Acquire the IMSI before establishing a LLC connection (Experimental)\n"
DEFUN(cfg_gbproxy_acquire_imsi,
cfg_gbproxy_acquire_imsi_cmd,
"acquire-imsi",
GBPROXY_ACQUIRE_IMSI_STR)
{
g_cfg->acquire_imsi = true;
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_no_acquire_imsi,
cfg_gbproxy_no_acquire_imsi_cmd,
"no acquire-imsi",
NO_STR GBPROXY_ACQUIRE_IMSI_STR)
{
g_cfg->acquire_imsi = false;
return CMD_SUCCESS;
}
#define GBPROXY_SECOND_SGSN_STR "Route matching LLC connections to a second SGSN (Experimental)\n"
DEFUN(cfg_gbproxy_secondary_sgsn,
cfg_gbproxy_secondary_sgsn_cmd,
"secondary-sgsn nsei <0-65534>",
GBPROXY_SECOND_SGSN_STR
"NSEI to be used in the connection with the SGSN\n"
"The NSEI\n")
{
unsigned int nsei = atoi(argv[0]);
if (g_cfg->nsip_sgsn_nsei == nsei) {
vty_out(vty, "Secondary SGSN NSEI %d conflicts with primary SGSN NSEI%s",
nsei, VTY_NEWLINE);
return CMD_WARNING;
}
g_cfg->route_to_sgsn2 = true;
g_cfg->nsip_sgsn2_nsei = nsei;
g_cfg->patch_ptmsi = true;
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_no_secondary_sgsn,
cfg_gbproxy_no_secondary_sgsn_cmd,
"no secondary-sgsn",
NO_STR GBPROXY_SECOND_SGSN_STR)
{
g_cfg->route_to_sgsn2 = false;
g_cfg->nsip_sgsn2_nsei = 0xFFFF;
g_cfg->patch_ptmsi = false;
return CMD_SUCCESS;
}
#define GBPROXY_LINK_LIST_STR "Set TLLI list parameters\n"
#define GBPROXY_LINK_STR "Set TLLI parameters\n"
#define GBPROXY_CLEAN_STALE_TIMER_STR "Periodic timer to clean stale links\n"
DEFUN(cfg_gbproxy_link_list_clean_stale_timer,
cfg_gbproxy_link_list_clean_stale_timer_cmd,
"link-list clean-stale-timer <1-999999>",
GBPROXY_LINK_LIST_STR GBPROXY_CLEAN_STALE_TIMER_STR
"Frequency at which the periodic timer is fired (in seconds)\n")
{
struct gbproxy_nse *nse;
int i, j;
g_cfg->clean_stale_timer_freq = (unsigned int) atoi(argv[0]);
/* Re-schedule running timers soon in case prev frequency was really big
and new frequency is desired to be lower. After initial run, periodic
time is used. Use random() to avoid firing timers for all bvcs at
the same time */
hash_for_each(g_cfg->bss_nses, i, nse, list) {
struct gbproxy_bvc *bvc;
hash_for_each(nse->bvcs, j, bvc, list)
osmo_timer_schedule(&bvc->clean_stale_timer,
random() % 5, random() % 1000000);
}
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_link_list_no_clean_stale_timer,
cfg_gbproxy_link_list_no_clean_stale_timer_cmd,
"no link-list clean-stale-timer",
NO_STR GBPROXY_LINK_LIST_STR GBPROXY_CLEAN_STALE_TIMER_STR)
{
struct gbproxy_nse *nse;
int i, j;
g_cfg->clean_stale_timer_freq = 0;
hash_for_each(g_cfg->bss_nses, i, nse, list) {
struct gbproxy_bvc *bvc;
hash_for_each(nse->bvcs, j, bvc, list)
osmo_timer_del(&bvc->clean_stale_timer);
}
return CMD_SUCCESS;
}
#define GBPROXY_MAX_AGE_STR "Limit maximum age\n"
DEFUN(cfg_gbproxy_link_list_max_age,
cfg_gbproxy_link_list_max_age_cmd,
"link-list max-age <1-999999>",
GBPROXY_LINK_LIST_STR GBPROXY_MAX_AGE_STR
"Maximum age in seconds\n")
{
g_cfg->tlli_max_age = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_link_list_no_max_age,
cfg_gbproxy_link_list_no_max_age_cmd,
"no link-list max-age",
NO_STR GBPROXY_LINK_LIST_STR GBPROXY_MAX_AGE_STR)
{
g_cfg->tlli_max_age = 0;
return CMD_SUCCESS;
}
#define GBPROXY_MAX_LEN_STR "Limit list length\n"
DEFUN(cfg_gbproxy_link_list_max_len,
cfg_gbproxy_link_list_max_len_cmd,
"link-list max-length <1-99999>",
GBPROXY_LINK_LIST_STR GBPROXY_MAX_LEN_STR
"Maximum number of logical links in the list\n")
{
g_cfg->tlli_max_len = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_link_list_no_max_len,
cfg_gbproxy_link_list_no_max_len_cmd,
"no link-list max-length",
NO_STR GBPROXY_LINK_LIST_STR GBPROXY_MAX_LEN_STR)
{
g_cfg->tlli_max_len = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_link_list_keep_mode,
cfg_gbproxy_link_list_keep_mode_cmd,
"link-list keep-mode (never|re-attach|identified|always)",
GBPROXY_LINK_LIST_STR "How to keep entries for detached logical links\n"
"Discard entry immediately after detachment\n"
"Keep entry if a re-attachment has be requested\n"
"Keep entry if it associated with an IMSI\n"
"Don't discard entries after detachment\n")
{
int val = get_string_value(keep_modes, argv[0]);
OSMO_ASSERT(val >= GBPROX_KEEP_NEVER && val <= GBPROX_KEEP_ALWAYS);
g_cfg->keep_link_infos = val;
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_link_stored_msgs_max_len,
cfg_gbproxy_link_stored_msgs_max_len_cmd,
"link stored-msgs-max-length <1-99999>",
GBPROXY_LINK_STR GBPROXY_MAX_LEN_STR
"Maximum number of msgb stored in the logical link waiting to acquire its IMSI\n")
{
g_cfg->stored_msgs_max_len = (uint32_t) atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_link_no_stored_msgs_max_len,
cfg_gbproxy_link_no_stored_msgs_max_len_cmd,
"no link stored-msgs-max-length",
NO_STR GBPROXY_LINK_STR GBPROXY_MAX_LEN_STR)
{
g_cfg->stored_msgs_max_len = 0;
return CMD_SUCCESS;
}
static void log_set_bvc_filter(struct log_target *target,
const uint16_t *bvci)
{
@ -603,52 +161,12 @@ DEFUN(show_gbproxy_links, show_gbproxy_links_cmd, "show gbproxy links",
SHOW_STR "Display information about the Gb proxy\n" "Show logical links\n")
{
struct gbproxy_nse *nse;
time_t now;
struct timespec ts = {0,};
int i, j;
osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
now = ts.tv_sec;
hash_for_each(g_cfg->bss_nses, i, nse, list) {
struct gbproxy_bvc *bvc;
hash_for_each(nse->bvcs, j, bvc, list) {
struct gbproxy_link_info *link_info;
struct gbproxy_patch_state *state = &bvc->patch_state;
gbprox_vty_print_bvc(vty, bvc);
llist_for_each_entry(link_info, &state->logical_links, list) {
time_t age = now - link_info->timestamp;
struct osmo_mobile_identity mi;
const char *imsi_str;
if (link_info->imsi > 0) {
if (osmo_mobile_identity_decode(&mi, link_info->imsi, link_info->imsi_len, false)
|| mi.type != GSM_MI_TYPE_IMSI)
imsi_str = "(invalid)";
else
imsi_str = mi.imsi;
} else {
imsi_str = "(none)";
}
vty_out(vty, " TLLI %08x, IMSI %s, AGE %d",
link_info->tlli.current, imsi_str, (int)age);
if (link_info->stored_msgs_len)
vty_out(vty, ", STORED %"PRIu32"/%"PRIu32,
link_info->stored_msgs_len,
g_cfg->stored_msgs_max_len);
if (g_cfg->route_to_sgsn2)
vty_out(vty, ", SGSN NSEI %d",
link_info->sgsn_nsei);
if (link_info->is_deregistered)
vty_out(vty, ", DE-REGISTERED");
vty_out(vty, "%s", VTY_NEWLINE);
}
}
}
return CMD_SUCCESS;
@ -744,182 +262,6 @@ DEFUN(delete_gb_nsei, delete_gb_nsei_cmd,
return CMD_SUCCESS;
}
#define GBPROXY_DELETE_LINK_STR \
"Delete a GBProxy logical link entry by NSEI and identification\nNSEI number\n"
DEFUN(delete_gb_link_by_id, delete_gb_link_by_id_cmd,
"delete-gbproxy-link <0-65534> (tlli|imsi|sgsn-nsei) IDENT",
GBPROXY_DELETE_LINK_STR
"Delete entries with a matching TLLI (hex)\n"
"Delete entries with a matching IMSI\n"
"Delete entries with a matching SGSN NSEI\n"
"Identification to match\n")
{
const uint16_t nsei = atoi(argv[0]);
enum {MATCH_TLLI = 't', MATCH_IMSI = 'i', MATCH_SGSN = 's'} match;
uint32_t ident = 0;
const char *imsi = NULL;
struct gbproxy_bvc *bvc = 0;
struct gbproxy_link_info *link_info, *nxt;
struct gbproxy_patch_state *state;
int found = 0;
match = argv[1][0];
switch (match) {
case MATCH_TLLI: ident = strtoll(argv[2], NULL, 16); break;
case MATCH_IMSI: imsi = argv[2]; break;
case MATCH_SGSN: ident = strtoll(argv[2], NULL, 0); break;
};
bvc = gbproxy_bvc_by_nsei(g_cfg, nsei);
if (!bvc) {
vty_out(vty, "Didn't find bvc with NSEI %d%s",
nsei, VTY_NEWLINE);
return CMD_WARNING;
}
state = &bvc->patch_state;
llist_for_each_entry_safe(link_info, nxt, &state->logical_links, list) {
struct osmo_mobile_identity mi;
switch (match) {
case MATCH_TLLI:
if (link_info->tlli.current != ident)
continue;
break;
case MATCH_SGSN:
if (link_info->sgsn_nsei != ident)
continue;
break;
case MATCH_IMSI:
if (!link_info->imsi)
continue;
if (osmo_mobile_identity_decode(&mi, link_info->imsi, link_info->imsi_len, false)
|| mi.type != GSM_MI_TYPE_IMSI)
continue;
if (strcmp(mi.imsi, imsi) != 0)
continue;
break;
}
vty_out(vty, "Deleting link with TLLI %08x%s", link_info->tlli.current,
VTY_NEWLINE);
gbproxy_delete_link_info(bvc, link_info);
found += 1;
}
if (!found && argc >= 2) {
vty_out(vty, "Didn't find link entry with %s %s%s",
argv[1], argv[2], VTY_NEWLINE);
}
return CMD_SUCCESS;
}
DEFUN(delete_gb_link, delete_gb_link_cmd,
"delete-gbproxy-link <0-65534> (stale|de-registered)",
GBPROXY_DELETE_LINK_STR
"Delete stale entries\n"
"Delete de-registered entries\n")
{
const uint16_t nsei = atoi(argv[0]);
enum {MATCH_STALE = 's', MATCH_DEREGISTERED = 'd'} match;
struct gbproxy_bvc *bvc = 0;
struct gbproxy_link_info *link_info, *nxt;
struct gbproxy_patch_state *state;
time_t now;
struct timespec ts = {0,};
int found = 0;
match = argv[1][0];
bvc = gbproxy_bvc_by_nsei(g_cfg, nsei);
if (!bvc) {
vty_out(vty, "Didn't find bvc with NSEI %d%s",
nsei, VTY_NEWLINE);
return CMD_WARNING;
}
state = &bvc->patch_state;
osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
now = ts.tv_sec;
if (match == MATCH_STALE) {
found = gbproxy_remove_stale_link_infos(bvc, now);
if (found)
vty_out(vty, "Deleted %d stale logical link%s%s",
found, found == 1 ? "" : "s", VTY_NEWLINE);
} else {
llist_for_each_entry_safe(link_info, nxt,
&state->logical_links, list) {
if (!link_info->is_deregistered)
continue;
gbproxy_delete_link_info(bvc, link_info);
found += 1;
}
}
if (found)
vty_out(vty, "Deleted %d %s logical link%s%s",
found, argv[1], found == 1 ? "" : "s", VTY_NEWLINE);
return CMD_SUCCESS;
}
/*
* legacy commands to provide an upgrade path from "broken" releases
* or pre-releases
*/
DEFUN_DEPRECATED(cfg_gbproxy_broken_apn_match,
cfg_gbproxy_broken_apn_match_cmd,
"core-access-point-name none match-imsi .REGEXP",
GBPROXY_CORE_APN_STR GBPROXY_MATCH_IMSI_STR "Remove APN\n"
"Patch MS related information elements on match only\n"
"Route to the secondary SGSN on match only\n"
"Regular expression for the IMSI match\n")
{
const char *filter = argv[0];
const char *err_msg = NULL;
struct gbproxy_match *match;
enum gbproxy_match_id match_id = get_string_value(match_ids, "patching");
/* apply APN none */
set_core_apn(vty, "");
/* do the matching... with copy and paste */
OSMO_ASSERT(match_id >= GBPROX_MATCH_PATCHING &&
match_id < GBPROX_MATCH_LAST);
match = &g_cfg->matches[match_id];
if (gbproxy_set_patch_filter(match, filter, &err_msg) != 0) {
vty_out(vty, "Match expression invalid: %s%s",
err_msg, VTY_NEWLINE);
return CMD_WARNING;
}
g_cfg->acquire_imsi = true;
return CMD_SUCCESS;
}
#define GBPROXY_TLLI_LIST_STR "Set TLLI list parameters\n"
#define GBPROXY_MAX_LEN_STR "Limit list length\n"
DEFUN_DEPRECATED(cfg_gbproxy_depr_tlli_list_max_len,
cfg_gbproxy_depr_tlli_list_max_len_cmd,
"tlli-list max-length <1-99999>",
GBPROXY_TLLI_LIST_STR GBPROXY_MAX_LEN_STR
"Maximum number of TLLIs in the list\n")
{
g_cfg->tlli_max_len = atoi(argv[0]);
return CMD_SUCCESS;
}
int gbproxy_vty_init(void)
{
install_element_ve(&show_gbproxy_cmd);
@ -928,39 +270,10 @@ int gbproxy_vty_init(void)
install_element(ENABLE_NODE, &delete_gb_bvci_cmd);
install_element(ENABLE_NODE, &delete_gb_nsei_cmd);
install_element(ENABLE_NODE, &delete_gb_link_by_id_cmd);
install_element(ENABLE_NODE, &delete_gb_link_cmd);
install_element(CONFIG_NODE, &cfg_gbproxy_cmd);
install_node(&gbproxy_node, config_write_gbproxy);
install_element(GBPROXY_NODE, &cfg_nsip_sgsn_nsei_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_core_mcc_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_core_mnc_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_match_imsi_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_core_apn_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_secondary_sgsn_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_patch_ptmsi_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_acquire_imsi_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_clean_stale_timer_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_max_age_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_max_len_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_keep_mode_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_link_stored_msgs_max_len_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mcc_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mnc_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_match_imsi_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_apn_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_secondary_sgsn_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_patch_ptmsi_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_acquire_imsi_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_clean_stale_timer_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_max_age_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_max_len_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_link_no_stored_msgs_max_len_cmd);
/* broken or deprecated to allow an upgrade path */
install_element(GBPROXY_NODE, &cfg_gbproxy_broken_apn_match_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_depr_tlli_list_max_len_cmd);
return 0;
}

View File

@ -33,9 +33,7 @@ gbproxy_test_LDFLAGS = \
gbproxy_test_LDADD = \
$(top_builddir)/src/gbproxy/gb_proxy.o \
$(top_builddir)/src/gbproxy/gb_proxy_patch.o \
$(top_builddir)/src/gbproxy/gb_proxy_peer.o \
$(top_builddir)/src/gbproxy/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 \

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff