diff options
-rw-r--r-- | openbsc/src/gprs/gb_proxy.c | 327 |
1 files changed, 195 insertions, 132 deletions
diff --git a/openbsc/src/gprs/gb_proxy.c b/openbsc/src/gprs/gb_proxy.c index f7afe947d..514898a59 100644 --- a/openbsc/src/gprs/gb_proxy.c +++ b/openbsc/src/gprs/gb_proxy.c @@ -254,14 +254,16 @@ uint32_t gbproxy_make_sgsn_tlli(struct gbproxy_peer *peer, } /* Returns != 0 iff IMSI acquisition was in progress */ -int gbproxy_reset_imsi_acquisition(struct gbproxy_tlli_info* tlli_info) +static int gbproxy_restart_imsi_acquisition(struct gbproxy_tlli_info* tlli_info) { int in_progress = 0; if (!tlli_info) return 0; - if (tlli_info->imsi_acq_pending) + if (tlli_info->imsi_acq_pending) { in_progress = 1; + tlli_info->imsi_acq_retries += 1; + } gbproxy_tlli_info_discard_messages(tlli_info); tlli_info->imsi_acq_pending = 0; @@ -269,6 +271,187 @@ int gbproxy_reset_imsi_acquisition(struct gbproxy_tlli_info* tlli_info) return in_progress; } +static void gbproxy_reset_imsi_acquisition(struct gbproxy_tlli_info* tlli_info) +{ + gbproxy_restart_imsi_acquisition(tlli_info); + tlli_info->imsi_acq_retries = 0; +} + +static void gbproxy_flush_stored_messages(struct gbproxy_peer *peer, + struct msgb *msg, + uint16_t sgsn_nsei, + time_t now, + struct gbproxy_tlli_info* tlli_info, + struct gprs_gb_parse_context *parse_ctx) +{ + int rc; + struct msgb *stored_msg; + /* Got identity response with IMSI, assuming the request had + * been generated by the gbproxy */ + + LOGP(DLLC, LOGL_DEBUG, + "NSEI=%d(BSS) IMSI acquisition succeeded, " + "flushing stored messages\n", + msgb_nsei(msg)); + + /* Patch and flush stored messages towards the SGSN */ + while ((stored_msg = msgb_dequeue(&tlli_info->stored_msgs))) { + struct gprs_gb_parse_context tmp_parse_ctx = {0}; + tmp_parse_ctx.to_bss = 0; + int len_change = 0; + + gprs_gb_parse_bssgp(msgb_bssgph(stored_msg), + msgb_bssgp_len(stored_msg), + &tmp_parse_ctx); + gbproxy_patch_bssgp(msg, msgb_bssgph(stored_msg), + msgb_bssgp_len(stored_msg), + peer, tlli_info, &len_change, + &tmp_parse_ctx); + + gbproxy_update_tlli_state_after(peer, tlli_info, now, + &tmp_parse_ctx); + + rc = gbprox_relay2sgsn(peer->cfg, stored_msg, + msgb_bvci(msg), sgsn_nsei); + + if (rc < 0) + LOGP(DLLC, LOGL_ERROR, + "NSEI=%d(BSS) failed to send stored message " + "(%s)\n", + msgb_nsei(msg), + parse_ctx->llc_msg_name ? + parse_ctx->llc_msg_name : "BSSGP"); + msgb_free(stored_msg); + } +} + +static void gbproxy_acquire_imsi(struct gbproxy_peer *peer, + struct gbproxy_tlli_info* tlli_info, + uint16_t bvci) +{ + struct msgb *idreq_msg; + + /* Send IDENT REQ */ + idreq_msg = gsm48_msgb_alloc(); + gprs_put_identity_req(idreq_msg, GSM_MI_TYPE_IMSI); + /* 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). + * TODO: Check whether it is sensible to rely on this, it might + * be an issue when P-TMSI patching is _not_ enabled and the SGSN + * doesn't reset V(UT) after a detach. */ + gprs_push_llc_ui(idreq_msg, 0, GPRS_SAPI_GMM, + (tlli_info->imsi_acq_retries + 256) % 512); + gprs_push_bssgp_dl_unitdata(idreq_msg, tlli_info->tlli.current); + gbprox_relay2peer(idreq_msg, peer, bvci); + msgb_free(idreq_msg); +} + + +/* Return != 0 iff msg still needs to be processed */ +static int gbproxy_imsi_acquisition(struct gbproxy_peer *peer, + struct msgb *msg, + uint16_t sgsn_nsei, + time_t now, + struct gbproxy_tlli_info* tlli_info, + struct gprs_gb_parse_context *parse_ctx) +{ + if (!tlli_info) + return 1; + + if (parse_ctx->g48_hdr && + parse_ctx->g48_hdr->msg_type == GSM48_MT_GMM_ATTACH_REQ) + { + if (gbproxy_restart_imsi_acquisition(tlli_info)) { + LOGP(DLLC, LOGL_INFO, + "NSEI=%d(BSS) IMSI acquisition was in progress " + "when receiving an ATTACH_REQ.\n", + msgb_nsei(msg)); + } + } + + if (tlli_info->imsi_acq_pending && parse_ctx->g48_hdr && + parse_ctx->g48_hdr->proto_discr == GSM48_PDISC_MM_GPRS && + parse_ctx->g48_hdr->msg_type == GSM48_MT_GMM_ID_RESP && + parse_ctx->imsi_len > 0) { + /* The IMSI is now available */ + gbproxy_flush_stored_messages(peer, msg, sgsn_nsei, now, + tlli_info, parse_ctx); + + gbproxy_reset_imsi_acquisition(tlli_info); + return 0; + } + + if (tlli_info->imsi_acq_pending) { + struct msgb *stored_msg; + + LOGP(DLLC, LOGL_DEBUG, + "NSEI=%d(BSS) IMSI acquisition in progess, " + "storing message (%s)\n", + msgb_nsei(msg), + parse_ctx->llc_msg_name ? parse_ctx->llc_msg_name : "BSSGP"); + + /* Enqueue unpatched messages */ + stored_msg = gprs_msgb_copy(msg, "process_bssgp_ul"); + msgb_enqueue(&tlli_info->stored_msgs, stored_msg); + + return 0; + } + + if (tlli_info->mi_data_len == 0) { + struct msgb *stored_msg = gprs_msgb_copy(msg, "process_bssgp_ul"); + + LOGP(DLLC, LOGL_DEBUG, + "NSEI=%d(BSS) IMSI is required but not available, " + "storing message (%s)\n", + msgb_nsei(msg), + parse_ctx->llc_msg_name ? parse_ctx->llc_msg_name : "BSSGP"); + + /* Enqueue message */ + msgb_enqueue(&tlli_info->stored_msgs, stored_msg); + + gbproxy_acquire_imsi(peer, tlli_info, msgb_bvci(msg)); + + tlli_info->imsi_acq_pending = 1; + + /* There is no explicit retransmission handling, the + * implementation relies on the MS doing proper retransmissions + * of the triggering message instead */ + + return 0; + } + + return 1; +} + +struct gbproxy_peer *gbproxy_find_peer(struct gbproxy_config *cfg, + struct msgb *msg, + struct gprs_gb_parse_context *parse_ctx) +{ + struct gbproxy_peer *peer = NULL; + + if (msgb_bvci(msg) >= 2) + peer = gbproxy_peer_by_bvci(cfg, msgb_bvci(msg)); + + if (!peer && !parse_ctx->to_bss) + peer = gbproxy_peer_by_nsei(cfg, msgb_nsei(msg)); + + if (!peer) + peer = gbproxy_peer_by_bssgp_tlv(cfg, &parse_ctx->bssgp_tp); + + if (!peer) { + LOGP(DLLC, LOGL_INFO, + "NSEI=%d(%s) patching: didn't find peer 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 peer; +} + /* patch BSSGP message */ static int gbprox_process_bssgp_ul(struct gbproxy_config *cfg, struct msgb *msg, @@ -303,24 +486,11 @@ static int gbprox_process_bssgp_ul(struct gbproxy_config *cfg, } /* Get peer */ - if (!peer && msgb_bvci(msg) >= 2) - peer = gbproxy_peer_by_bvci(cfg, msgb_bvci(msg)); - if (!peer) - peer = gbproxy_peer_by_nsei(cfg, msgb_nsei(msg)); + peer = gbproxy_find_peer(cfg, msg, &parse_ctx); if (!peer) - peer = gbproxy_peer_by_bssgp_tlv(cfg, &parse_ctx.bssgp_tp); - - if (!peer) { - LOGP(DLLC, LOGL_INFO, - "NSEI=%d(BSS) patching: didn't find peer for message, " - "PDU %d\n", - msgb_nsei(msg), parse_ctx.pdu_type); - /* Increment counter */ - rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_PATCH_PEER_ERR]); return 0; - } now = time(NULL); @@ -339,13 +509,6 @@ static int gbprox_process_bssgp_ul(struct gbproxy_config *cfg, switch (parse_ctx.g48_hdr->msg_type) { case GSM48_MT_GMM_ATTACH_REQ: rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_REQS]); - if (gbproxy_reset_imsi_acquisition(tlli_info)) { - LOGP(DLLC, LOGL_INFO, - "NSEI=%d(BSS) IMSI acquisition was in progress " - "when receiving an ATTACH_REQ.\n", - msgb_nsei(msg)); - tlli_info->imsi_acq_retries += 1; - } break; default: @@ -358,105 +521,14 @@ static int gbprox_process_bssgp_ul(struct gbproxy_config *cfg, send_msg_directly = 1; } - if (tlli_info && tlli_info->imsi_acq_pending && parse_ctx.g48_hdr && - parse_ctx.g48_hdr->proto_discr == GSM48_PDISC_MM_GPRS && - parse_ctx.g48_hdr->msg_type == GSM48_MT_GMM_ID_RESP && - parse_ctx.imsi_len > 0) { - struct msgb *stored_msg; - /* Got identity response with IMSI, assuming the request had - * been generated by the gbproxy */ - - LOGP(DLLC, LOGL_DEBUG, - "NSEI=%d(BSS) IMSI acquisition succeeded, " - "flushing stored messages\n", - msgb_nsei(msg)); - - /* TODO: Patch and flush stored messages towards the SGSN */ - while ((stored_msg = msgb_dequeue(&tlli_info->stored_msgs))) { - struct gprs_gb_parse_context tmp_parse_ctx = {0}; - tmp_parse_ctx.to_bss = 0; - len_change = 0; - - gprs_gb_parse_bssgp(msgb_bssgph(stored_msg), - msgb_bssgp_len(stored_msg), - &tmp_parse_ctx); - gbproxy_patch_bssgp(msg, msgb_bssgph(stored_msg), - msgb_bssgp_len(stored_msg), - peer, tlli_info, &len_change, - &tmp_parse_ctx); - - gbproxy_update_tlli_state_after(peer, tlli_info, now, - &tmp_parse_ctx); - - rc = gbprox_relay2sgsn(cfg, stored_msg, msgb_bvci(msg), - sgsn_nsei); - - if (rc < 0) - LOGP(DLLC, LOGL_ERROR, - "NSEI=%d(BSS) failed to send stored message " - "(%s)\n", - msgb_nsei(msg), - parse_ctx.llc_msg_name ? parse_ctx.llc_msg_name : "BSSGP"); - msgb_free(stored_msg); - } - - tlli_info->imsi_acq_pending = 0; - - return 0; - } - - if (tlli_info && tlli_info->imsi_acq_pending) { - struct msgb *stored_msg; - - LOGP(DLLC, LOGL_DEBUG, - "NSEI=%d(BSS) IMSI acquisition in progess, " - "storing message (%s)\n", - msgb_nsei(msg), - parse_ctx.llc_msg_name ? parse_ctx.llc_msg_name : "BSSGP"); - - /* Enqueue unpatched messages */ - stored_msg = gprs_msgb_copy(msg, "process_bssgp_ul"); - msgb_enqueue(&tlli_info->stored_msgs, stored_msg); - - /* TODO: Timeout, retransmit IDENT REQ if expired */ - return 0; - } - - if (cfg->acquire_imsi && tlli_info && tlli_info->mi_data_len == 0) { - struct msgb *idreq_msg; - struct msgb *stored_msg = gprs_msgb_copy(msg, "process_bssgp_ul"); - - LOGP(DLLC, LOGL_DEBUG, - "NSEI=%d(BSS) IMSI is required but not available, " - "storing message (%s)\n", - msgb_nsei(msg), - parse_ctx.llc_msg_name ? parse_ctx.llc_msg_name : "BSSGP"); - - /* Enqueue message */ - msgb_enqueue(&tlli_info->stored_msgs, stored_msg); - - /* Send IDENT REQ */ - idreq_msg = gsm48_msgb_alloc(); - gprs_put_identity_req(idreq_msg, GSM_MI_TYPE_IMSI); - /* 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). - * TODO: Check whether it is sensible to rely on this, it might - * be an issue when P-TMSI patching is _not_ enabled and the SGSN - * doesn't reset V(UT) after a detach. */ - gprs_push_llc_ui(idreq_msg, 0, GPRS_SAPI_GMM, - (tlli_info->imsi_acq_retries + 256) % 512); - gprs_push_bssgp_dl_unitdata(idreq_msg, tlli_info->tlli.current); - gbprox_relay2peer(idreq_msg, peer, msgb_bvci(msg)); - msgb_free(idreq_msg); - - tlli_info->imsi_acq_pending = 1; - - return 0; + /* Handle IMSI acquisition */ + if (cfg->acquire_imsi) { + rc = gbproxy_imsi_acquisition(peer, msg, sgsn_nsei, now, + tlli_info, &parse_ctx); + if (rc <= 0) + return rc; } - gbproxy_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg), peer, tlli_info, &len_change, &parse_ctx); @@ -502,21 +574,12 @@ static void gbprox_process_bssgp_dl(struct gbproxy_config *cfg, } } - if (!peer && msgb_bvci(msg) >= 2) - peer = gbproxy_peer_by_bvci(cfg, msgb_bvci(msg)); - + /* Get peer */ if (!peer) - peer = gbproxy_peer_by_bssgp_tlv(cfg, &parse_ctx.bssgp_tp); + peer = gbproxy_find_peer(cfg, msg, &parse_ctx); - if (!peer) { - LOGP(DLLC, LOGL_INFO, - "NSEI=%d(SGSN) patching: didn't find peer for message, " - "PDU %d\n", - msgb_nsei(msg), parse_ctx.pdu_type); - /* Increment counter */ - rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_PATCH_PEER_ERR]); + if (!peer) return; - } now = time(NULL); |