aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/gprs/gb_proxy.c
diff options
context:
space:
mode:
Diffstat (limited to 'openbsc/src/gprs/gb_proxy.c')
-rw-r--r--openbsc/src/gprs/gb_proxy.c193
1 files changed, 184 insertions, 9 deletions
diff --git a/openbsc/src/gprs/gb_proxy.c b/openbsc/src/gprs/gb_proxy.c
index 280c55707..965059bdb 100644
--- a/openbsc/src/gprs/gb_proxy.c
+++ b/openbsc/src/gprs/gb_proxy.c
@@ -72,6 +72,11 @@ static const struct rate_ctr_group_desc global_ctrg_desc = {
.ctr_desc = global_ctr_description,
};
+static int gbprox_relay2peer(struct msgb *old_msg, struct gbproxy_peer *peer,
+ uint16_t ns_bvci);
+static int gbprox_relay2sgsn(struct gbproxy_config *cfg, struct msgb *old_msg,
+ uint16_t ns_bvci);
+
static int check_peer_nsei(struct gbproxy_peer *peer, uint16_t nsei)
{
if (peer->nsei != nsei) {
@@ -92,6 +97,81 @@ 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;
+}
+
+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;
+ size_t msg_size = msgb_length(msg);
+ 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 peer according to the BSS message */
static void gbprox_update_current_raid(uint8_t *raid_enc,
struct gbproxy_peer *peer,
@@ -175,9 +255,9 @@ uint32_t gbproxy_make_sgsn_tlli(struct gbproxy_peer *peer,
}
/* patch BSSGP message */
-static void gbprox_process_bssgp_ul(struct gbproxy_config *cfg,
- struct msgb *msg,
- struct gbproxy_peer *peer)
+static int gbprox_process_bssgp_ul(struct gbproxy_config *cfg,
+ struct msgb *msg,
+ struct gbproxy_peer *peer)
{
struct gprs_gb_parse_context parse_ctx = {0};
int rc;
@@ -185,8 +265,9 @@ static void gbprox_process_bssgp_ul(struct gbproxy_config *cfg,
time_t now;
struct gbproxy_tlli_info *tlli_info = NULL;
- if (!cfg->core_mcc && !cfg->core_mnc && !cfg->core_apn)
- return;
+ if (!cfg->core_mcc && !cfg->core_mnc && !cfg->core_apn &&
+ !cfg->acquire_imsi)
+ return 1;
parse_ctx.to_bss = 0;
@@ -200,7 +281,7 @@ static void gbprox_process_bssgp_ul(struct gbproxy_config *cfg,
"NSEI=%u(BSS) patching: "
"failed to parse BSSGP/GMM message\n",
msgb_nsei(msg));
- return;
+ return 0;
}
}
@@ -221,7 +302,7 @@ static void gbprox_process_bssgp_ul(struct gbproxy_config *cfg,
msgb_nsei(msg), parse_ctx.pdu_type);
/* Increment counter */
rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_PATCH_PEER_ERR]);
- return;
+ return 0;
}
now = time(NULL);
@@ -248,12 +329,103 @@ static void gbprox_process_bssgp_ul(struct gbproxy_config *cfg,
tlli_info = gbproxy_update_tlli_state_ul(peer, now, &parse_ctx);
+ 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));
+
+ 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);
+ gprs_push_llc_ui(idreq_msg, 0, GPRS_SAPI_GMM,
+ /* TODO: use a real N(U) */0);
+ 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;
+ }
+
+
gbproxy_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg),
peer, tlli_info, &len_change, &parse_ctx);
gbproxy_update_tlli_state_after(peer, tlli_info, now, &parse_ctx);
- return;
+ return 1;
}
/* patch BSSGP message to use core_mcc/mnc on the SGSN side */
@@ -428,13 +600,16 @@ static int gbprox_rx_ptp_from_bss(struct gbproxy_config *cfg,
uint16_t nsvci, uint16_t ns_bvci)
{
struct gbproxy_peer *peer;
+ int rc;
peer = gbproxy_peer_by_bvci(cfg, ns_bvci);
if (peer)
check_peer_nsei(peer, nsei);
- gbprox_process_bssgp_ul(cfg, msg, peer);
+ rc = gbprox_process_bssgp_ul(cfg, msg, peer);
+ if (!rc)
+ return 0;
return gbprox_relay2sgsn(cfg, msg, ns_bvci);
}