diff options
author | Jacob Erlbeck <jerlbeck@sysmocom.de> | 2014-08-06 12:41:31 +0200 |
---|---|---|
committer | Holger Hans Peter Freyther <holger@moiji-mobile.com> | 2014-08-12 19:32:26 +0200 |
commit | 2db2512f4de4bc88c934293eb260e4888c240b3e (patch) | |
tree | 10e3fd5c51af193f812afd48bc4cf35f6fccf941 | |
parent | 2bdd253d542db2c7fa66c289d94acdc03c7b928d (diff) |
gbproxy: Separate BSSGP parsing from patching
This adds a gbprox_parse_bssgp_message() function that contains the
parsing part of the former gbprox_patch_bssgp_message(). This
includes a call to gbprox_parse_llc().
The calls to gbprox_patch_llc(), gbprox_update_state() and
gbprox_update_state_after() have therefore been moved to
gbprox_patch_bssgp_message().
Sponsored-by: On-Waves ehf
-rw-r--r-- | openbsc/src/gprs/gb_proxy.c | 306 |
1 files changed, 183 insertions, 123 deletions
diff --git a/openbsc/src/gprs/gb_proxy.c b/openbsc/src/gprs/gb_proxy.c index 1b3654fb0..003534903 100644 --- a/openbsc/src/gprs/gb_proxy.c +++ b/openbsc/src/gprs/gb_proxy.c @@ -375,23 +375,33 @@ static int parse_mi_tmsi(uint8_t *value, size_t value_len, uint32_t *tmsi) struct gbproxy_parse_context { /* Pointer to protocol specific parts */ struct gsm48_hdr *g48_hdr; - struct gprs_llc_hdr_parsed *llc_hdr_parsed; - struct tlv_parsed *bssgp_tp; + struct bssgp_normal_hdr *bgp_hdr; struct bssgp_ud_hdr *bud_hdr; + uint8_t *bssgp_data; + size_t bssgp_data_len; + uint8_t *llc; + size_t llc_len; /* Extracted information */ + struct gprs_llc_hdr_parsed llc_hdr_parsed; + struct tlv_parsed bssgp_tp; int to_bss; - uint32_t tlli; - const uint8_t *imsi; + uint8_t *tlli_enc; + uint8_t *imsi; size_t imsi_len; uint8_t *apn_ie; size_t apn_ie_len; uint8_t *ptmsi_enc; uint8_t *raid_enc; + uint8_t *bssgp_raid_enc; + uint8_t *bssgp_ptimsi; /* General info */ const char *llc_msg_name; int invalidate_tlli; + int need_decryption; + uint32_t tlli; + int pdu_type; }; static struct gbproxy_tlli_info *gbprox_find_tlli(struct gbproxy_peer *peer, @@ -1087,122 +1097,100 @@ static int allow_message_patching(struct gbproxy_peer *peer, int msg_type) } } -static void gbprox_update_state(struct gbproxy_peer *peer, - struct gbproxy_parse_context *parse_ctx); -static void gbprox_update_state_after(struct gbproxy_peer *peer, - struct gbproxy_parse_context *parse_ctx); - -static void gbprox_patch_llc(struct msgb *msg, uint8_t *llc, size_t llc_len, - struct gbproxy_peer *peer, int *len_change, - struct gbproxy_parse_context *parse_ctx) __attribute__((nonnull)); +static int gbprox_parse_llc(uint8_t *llc, size_t llc_len, + struct gbproxy_parse_context *parse_ctx) __attribute__((nonnull)); -static void gbprox_patch_llc(struct msgb *msg, uint8_t *llc, size_t llc_len, - struct gbproxy_peer *peer, int *len_change, - struct gbproxy_parse_context *parse_ctx) +static int gbprox_parse_llc(uint8_t *llc, size_t llc_len, + struct gbproxy_parse_context *parse_ctx) { - struct gprs_llc_hdr_parsed ghp = {0}; + struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed; int rc; - uint8_t *data; - size_t data_len; int fcs; - int have_patched = 0; /* parse LLC */ - rc = gprs_llc_hdr_parse(&ghp, llc, llc_len); - gprs_llc_hdr_dump(&ghp); + rc = gprs_llc_hdr_parse(ghp, llc, llc_len); + gprs_llc_hdr_dump(ghp); if (rc != 0) { LOGP(DLLC, LOGL_NOTICE, "Error during LLC header parsing\n"); - return; + return 0; } - fcs = gprs_llc_fcs(llc, ghp.crc_length); + fcs = gprs_llc_fcs(llc, ghp->crc_length); LOGP(DLLC, LOGL_DEBUG, "Got LLC message, CRC: %06x (computed %06x)\n", - ghp.fcs, fcs); - - if (!ghp.data) - return; + ghp->fcs, fcs); - if (ghp.sapi != GPRS_SAPI_GMM) - return; + if (!ghp->data) + return 0; - if (ghp.cmd != GPRS_LLC_UI) - goto update_tlli_mapping; + if (ghp->sapi != GPRS_SAPI_GMM) + return 0; - if (ghp.is_encrypted) { - if (patching_is_required(peer, GBPROX_PATCH_LLC_ATTACH)) { - /* Patching LLC messages has been requested explicitly, - * but the message (including the type) is encrypted, - * so we possibly fail to patch the LLC part of the - * message. */ + if (ghp->cmd != GPRS_LLC_UI) + return 0; - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_PATCH_CRYPT_ERR]); - LOGP(DGPRS, LOGL_ERROR, - "Failed to patch BSSGP/GMM message as requested: " - "GMM message is encrypted\n"); - } - goto update_tlli_mapping; + if (ghp->is_encrypted) { + parse_ctx->need_decryption = 1; + return 0; } - /* fix DTAP GMM/GSM */ - data = ghp.data; - data_len = ghp.data_len; - - parse_ctx->llc_hdr_parsed = &ghp; + return gbprox_parse_dtap(ghp->data, ghp->data_len, parse_ctx); +} - rc = gbprox_parse_dtap(data, data_len, parse_ctx); +static int gbprox_patch_llc(struct msgb *msg, uint8_t *llc, size_t llc_len, + struct gbproxy_peer *peer, int *len_change, + struct gbproxy_parse_context *parse_ctx) __attribute__((nonnull)); -update_tlli_mapping: +static int gbprox_patch_llc(struct msgb *msg, uint8_t *llc, size_t llc_len, + struct gbproxy_peer *peer, int *len_change, + struct gbproxy_parse_context *parse_ctx) +{ + struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed; + int have_patched = 0; + int fcs; - gbprox_update_state(peer, parse_ctx); + if (!allow_message_patching(peer, parse_ctx->g48_hdr->msg_type)) + return have_patched; - if (parse_ctx->g48_hdr && - allow_message_patching(peer, parse_ctx->g48_hdr->msg_type)) { - if (parse_ctx->raid_enc) { - gbprox_patch_raid(parse_ctx->raid_enc, peer, - parse_ctx->to_bss, - parse_ctx->llc_msg_name); - have_patched = 1; - } + if (parse_ctx->raid_enc) { + gbprox_patch_raid(parse_ctx->raid_enc, peer, parse_ctx->to_bss, + parse_ctx->llc_msg_name); + have_patched = 1; + } - if (parse_ctx->apn_ie && - peer->cfg->core_apn && - !parse_ctx->to_bss && - gbprox_check_tlli(peer, parse_ctx->tlli)) { - size_t new_len; - gbprox_patch_apn_ie(msg, - parse_ctx->apn_ie, - parse_ctx->apn_ie_len, peer, - &new_len, parse_ctx->llc_msg_name); - *len_change += (int)new_len - (int)parse_ctx->apn_ie_len; - - have_patched = 1; - } + if (parse_ctx->apn_ie && + peer->cfg->core_apn && + !parse_ctx->to_bss && + gbprox_check_tlli(peer, parse_ctx->tlli)) { + size_t new_len; + gbprox_patch_apn_ie(msg, + parse_ctx->apn_ie, parse_ctx->apn_ie_len, + peer, &new_len, parse_ctx->llc_msg_name); + *len_change += (int)new_len - (int)parse_ctx->apn_ie_len; + + have_patched = 1; + } - if (have_patched) { - llc_len += *len_change; - ghp.crc_length += *len_change; + if (have_patched) { + llc_len += *len_change; + ghp->crc_length += *len_change; - /* Fix FCS */ - fcs = gprs_llc_fcs(llc, ghp.crc_length); - LOGP(DLLC, LOGL_DEBUG, - "Updated LLC message, CRC: %06x -> %06x\n", - ghp.fcs, fcs); + /* Fix FCS */ + fcs = gprs_llc_fcs(llc, ghp->crc_length); + LOGP(DLLC, LOGL_DEBUG, "Updated LLC message, CRC: %06x -> %06x\n", + ghp->fcs, fcs); - llc[llc_len - 3] = fcs & 0xff; - llc[llc_len - 2] = (fcs >> 8) & 0xff; - llc[llc_len - 1] = (fcs >> 16) & 0xff; - } + llc[llc_len - 3] = fcs & 0xff; + llc[llc_len - 2] = (fcs >> 8) & 0xff; + llc[llc_len - 1] = (fcs >> 16) & 0xff; } - gbprox_update_state_after(peer, parse_ctx); - - return; + return have_patched; } static void gbprox_update_state(struct gbproxy_peer *peer, struct gbproxy_parse_context *parse_ctx) { - const char *msg_name = "LLC"; + const char *msg_name = "BSSGP"; if (parse_ctx->llc_msg_name) msg_name = parse_ctx->llc_msg_name; @@ -1222,7 +1210,7 @@ static void gbprox_update_state(struct gbproxy_peer *peer, } } - if (parse_ctx->tlli) { + if (parse_ctx->tlli_enc) { LOGP(DGPRS, LOGL_DEBUG, "%s: Got TLLI %08x\n", msg_name, parse_ctx->tlli); } @@ -1275,37 +1263,104 @@ static void gbprox_update_state_after(struct gbproxy_peer *peer, gbprox_unregister_tlli(peer, parse_ctx->tlli); } -/* patch BSSGP message to use core_mcc/mnc on the SGSN side */ -static void gbprox_patch_bssgp_message(struct gbproxy_config *cfg, struct msgb *msg, - struct gbproxy_peer *peer, int to_bss) +static int gbprox_parse_bssgp_message(uint8_t *bssgp, size_t bssgp_len, + struct gbproxy_parse_context *parse_ctx) { struct bssgp_normal_hdr *bgph; struct bssgp_ud_hdr *budh = NULL; - struct tlv_parsed tp; + struct tlv_parsed *tp = &parse_ctx->bssgp_tp; uint8_t pdu_type; uint8_t *data; size_t data_len; - const char *err_info = NULL; - int err_ctr = -1; + int rc; - if (!cfg->core_mcc && !cfg->core_mnc && !cfg->core_apn) - return; + if (bssgp_len < sizeof(struct bssgp_normal_hdr)) + return 0; - bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg); + bgph = (struct bssgp_normal_hdr *)bssgp; pdu_type = bgph->pdu_type; if (pdu_type == BSSGP_PDUT_UL_UNITDATA || pdu_type == BSSGP_PDUT_DL_UNITDATA) { - budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg); + if (bssgp_len < sizeof(struct bssgp_ud_hdr)) + return 0; + budh = (struct bssgp_ud_hdr *)bssgp; bgph = NULL; data = budh->data; - data_len = msgb_bssgp_len(msg) - sizeof(*budh); + data_len = bssgp_len - sizeof(*budh); } else { data = bgph->data; - data_len = msgb_bssgp_len(msg) - sizeof(*bgph); + data_len = bssgp_len - sizeof(*bgph); + } + + if (bssgp_tlv_parse(tp, data, data_len) < 0) + return 0; + + parse_ctx->pdu_type = pdu_type; + parse_ctx->bud_hdr = budh; + parse_ctx->bgp_hdr = bgph; + parse_ctx->bssgp_data = data; + parse_ctx->bssgp_data_len = data_len; + + if (budh) + parse_ctx->tlli_enc = (uint8_t *)&budh->tlli; + + if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) + parse_ctx->bssgp_raid_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA); + + if (TLVP_PRESENT(tp, BSSGP_IE_CELL_ID)) + parse_ctx->bssgp_raid_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_CELL_ID); + + if (TLVP_PRESENT(tp, BSSGP_IE_IMSI)) { + parse_ctx->imsi = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_IMSI); + parse_ctx->imsi_len = TLVP_LEN(tp, BSSGP_IE_IMSI); } - bssgp_tlv_parse(&tp, data, data_len); + if (TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU)) { + uint8_t *llc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_LLC_PDU); + size_t llc_len = TLVP_LEN(tp, BSSGP_IE_LLC_PDU); + + rc = gbprox_parse_llc(llc, llc_len, parse_ctx); + if (!rc) + return 0; + + parse_ctx->llc = llc; + parse_ctx->llc_len = llc_len; + } + + if (parse_ctx->tlli_enc) { + uint32_t tmp_tlli; + memcpy(&tmp_tlli, parse_ctx->tlli_enc, sizeof(tmp_tlli)); + parse_ctx->tlli = ntohl(tmp_tlli); + } + + return 1; +} + +/* patch BSSGP message to use core_mcc/mnc on the SGSN side */ +static void gbprox_patch_bssgp_message(struct gbproxy_config *cfg, struct msgb *msg, + struct gbproxy_peer *peer, int to_bss) +{ + struct gbproxy_parse_context parse_ctx = {0}; + const char *err_info = NULL; + int err_ctr = -1; + int rc; + + if (!cfg->core_mcc && !cfg->core_mnc && !cfg->core_apn) + return; + + parse_ctx.to_bss = to_bss; + + rc = gbprox_parse_bssgp_message(msgb_bssgph(msg), msgb_bssgp_len(msg), + &parse_ctx); + + if (!rc) { + if (!parse_ctx.need_decryption) { + LOGP(DGPRS, LOGL_ERROR, + "Failed to parse BSSGP/GMM message\n"); + return; + } + } if (!peer && msgb_bvci(msg) >= 2) peer = peer_by_bvci(cfg, msgb_bvci(msg)); @@ -1314,44 +1369,45 @@ static void gbprox_patch_bssgp_message(struct gbproxy_config *cfg, struct msgb * peer = gbprox_peer_by_nsei(cfg, msgb_nsei(msg)); if (!peer) - peer = peer_by_bssgp_tlv(cfg, &tp); + peer = 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), to_bss ? "SGSN" : "BSS", pdu_type); + msgb_nsei(msg), to_bss ? "SGSN" : "BSS", parse_ctx.pdu_type); /* Increment counter */ rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_PATCH_PEER_ERR]); return; } - if (TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA)) { - gbprox_patch_raid((uint8_t *)TLVP_VAL(&tp, BSSGP_IE_ROUTEING_AREA), - peer, to_bss, "ROUTING_AREA"); + if (parse_ctx.need_decryption && + patching_is_required(peer, GBPROX_PATCH_LLC_ATTACH)) { + /* Patching LLC messages has been requested + * explicitly, but the message (including the + * type) is encrypted, so we possibly fail to + * patch the LLC part of the message. */ + err_ctr = GBPROX_PEER_CTR_PATCH_CRYPT_ERR; + err_info = "GMM message is encrypted"; + goto patch_error; } - if (TLVP_PRESENT(&tp, BSSGP_IE_CELL_ID)) - gbprox_patch_raid((uint8_t *)TLVP_VAL(&tp, BSSGP_IE_CELL_ID), - peer, to_bss, "CELL_ID"); + gbprox_update_state(peer, &parse_ctx); + + if (parse_ctx.bssgp_raid_enc) + gbprox_patch_raid(parse_ctx.bssgp_raid_enc, peer, to_bss, "BSSGP"); - if (TLVP_PRESENT(&tp, BSSGP_IE_LLC_PDU) && + if (parse_ctx.llc && patching_is_enabled(peer, GBPROX_PATCH_LLC_ATTACH_REQ)) { - uint8_t *llc = (uint8_t *)TLVP_VAL(&tp, BSSGP_IE_LLC_PDU); - size_t llc_len = TLVP_LEN(&tp, BSSGP_IE_LLC_PDU); - struct gbproxy_parse_context parse_ctx = {0}; + uint8_t *llc = parse_ctx.llc; + size_t llc_len = parse_ctx.llc_len; int len_change = 0; - parse_ctx.bssgp_tp = &tp; - parse_ctx.bud_hdr = budh; - parse_ctx.tlli = budh ? ntohl(budh->tlli) : 0; - parse_ctx.to_bss = to_bss; - - if (TLVP_PRESENT(&tp, BSSGP_IE_IMSI)) { - parse_ctx.imsi = TLVP_VAL(&tp, BSSGP_IE_IMSI); - parse_ctx.imsi_len = TLVP_LEN(&tp, BSSGP_IE_IMSI); - } gbprox_patch_llc(msg, llc, llc_len, peer, &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 (len_change) { llc_len += len_change; @@ -1374,7 +1430,11 @@ static void gbprox_patch_bssgp_message(struct gbproxy_config *cfg, struct msgb * } /* Note that the tp struct might contain invalid pointers here * if the LLC field has changed its size */ + parse_ctx.llc_len = llc_len; } + + gbprox_update_state_after(peer, &parse_ctx); + return; patch_error: |