diff options
Diffstat (limited to 'src/sgsn/gprs_llc.c')
-rw-r--r-- | src/sgsn/gprs_llc.c | 127 |
1 files changed, 84 insertions, 43 deletions
diff --git a/src/sgsn/gprs_llc.c b/src/sgsn/gprs_llc.c index 2a27da844..6f563851f 100644 --- a/src/sgsn/gprs_llc.c +++ b/src/sgsn/gprs_llc.c @@ -22,6 +22,7 @@ #include <errno.h> #include <stdint.h> #include <stdbool.h> +#include <inttypes.h> #include <osmocom/core/msgb.h> #include <osmocom/core/linuxlist.h> @@ -32,7 +33,7 @@ #include <osmocom/gsm/gsm_utils.h> #include <osmocom/sgsn/debug.h> -#include <osmocom/sgsn/gprs_sgsn.h> +#include <osmocom/sgsn/mmctx.h> #include <osmocom/sgsn/gprs_gmm.h> #include <osmocom/sgsn/gprs_llc.h> #include <osmocom/sgsn/crc24.h> @@ -41,16 +42,29 @@ #include <osmocom/sgsn/gprs_sndcp_comp.h> #include <osmocom/sgsn/gprs_sndcp.h> +#include <osmocom/crypt/kdf.h> + const struct value_string gprs_llc_llme_state_names[] = { { GPRS_LLMS_UNASSIGNED, "UNASSIGNED" }, { GPRS_LLMS_ASSIGNED, "ASSIGNED" }, { 0, NULL } }; +const struct value_string gprs_llc_lle_state_names[] = { + { GPRS_LLES_UNASSIGNED, "TLLI Unassigned" }, + { GPRS_LLES_ASSIGNED_ADM, "TLLI Assigned" }, + { GPRS_LLES_LOCAL_EST, "Local Establishment" }, + { GPRS_LLES_REMOTE_EST, "Remote Establishment" }, + { GPRS_LLES_ABM, "Asynchronous Balanced Mode" }, + { GPRS_LLES_LOCAL_REL, "Local Release" }, + { GPRS_LLES_TIMER_REC, "Timer Recovery" }, + { 0, NULL } +}; + static struct gprs_llc_llme *llme_alloc(uint32_t tlli); -static int gprs_llc_tx_xid(struct gprs_llc_lle *lle, struct msgb *msg, +static int gprs_llc_tx_xid(const struct gprs_llc_lle *lle, struct msgb *msg, int command); -static int gprs_llc_tx_dm(struct gprs_llc_lle *lle); +static int gprs_llc_tx_dm(const struct gprs_llc_lle *lle); static int gprs_llc_tx_u(struct msgb *msg, uint8_t sapi, int command, enum gprs_llc_u_cmd u_cmd, int pf_bit); @@ -68,17 +82,20 @@ static int gprs_llc_generate_xid(uint8_t *bytes, int bytes_len, struct gprs_llc_xid_field xid_version; struct gprs_llc_xid_field xid_n201u; struct gprs_llc_xid_field xid_n201i; + uint16_t n201_u, n201_i; xid_version.type = GPRS_LLC_XID_T_VERSION; xid_version.data = (uint8_t *) "\x00"; xid_version.data_len = 1; + n201_u = htons(lle->params.n201_u); xid_n201u.type = GPRS_LLC_XID_T_N201_U; - xid_n201u.data = (uint8_t *) "\x05\xf0"; + xid_n201u.data = (uint8_t *) &n201_u; xid_n201u.data_len = 2; + n201_i = htons(lle->params.n201_i); xid_n201i.type = GPRS_LLC_XID_T_N201_I; - xid_n201i.data = (uint8_t *) "\x05\xf0"; + xid_n201i.data = (uint8_t *) &n201_i; xid_n201i.data_len = 2; /* Add locally managed XID Fields */ @@ -206,7 +223,7 @@ static int gprs_llc_process_xid_ind(uint8_t *bytes_request, int bytes_request_len, uint8_t *bytes_response, int bytes_response_maxlen, - struct gprs_llc_lle *lle) + const struct gprs_llc_lle *lle) { /* Note: This function computes the response that is sent back to the * MS when a mobile originated XID is received. The function is @@ -282,7 +299,7 @@ static int gprs_llc_process_xid_ind(uint8_t *bytes_request, /* Dispatch XID indications and responses comming from the MS */ static void rx_llc_xid(struct gprs_llc_lle *lle, - struct gprs_llc_hdr_parsed *gph) + const struct gprs_llc_hdr_parsed *gph) { uint8_t response[1024]; int response_len; @@ -372,20 +389,24 @@ static int _bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx) * not yet have a MMC context (e.g. XID negotiation of primarly * LLC connection from GMM sapi). */ if (mmctx) { + /* In rare cases the LLME is NULL in those cases don't + * use the mm radio capabilities */ dup.imsi = mmctx->imsi; - dup.drx_parms = mmctx->drx_parms; - dup.ms_ra_cap.len = mmctx->ms_radio_access_capa.len; - dup.ms_ra_cap.v = mmctx->ms_radio_access_capa.buf; - - /* make sure we only send it to the right llme */ - if (!(msgb_tlli(msg) == mmctx->gb.llme->tlli - || msgb_tlli(msg) == mmctx->gb.llme->old_tlli)) { - LOGP(DLLC, LOGL_ERROR, - "_bssgp_tx_dl_ud(): Attempt to send Downlink Unitdata to wrong LLME:" - " msgb_tlli=0x%x mmctx->gb.llme->tlli=0x%x ->old_tlli=0x%x\n", - msgb_tlli(msg), mmctx->gb.llme->tlli, mmctx->gb.llme->old_tlli); - msgb_free(msg); - return -EINVAL; + if (mmctx->gb.llme) { + dup.drx_parms = mmctx->drx_parms; + dup.ms_ra_cap.len = mmctx->ms_radio_access_capa.len; + dup.ms_ra_cap.v = mmctx->ms_radio_access_capa.buf; + + /* make sure we only send it to the right llme */ + if (!(msgb_tlli(msg) == mmctx->gb.llme->tlli + || msgb_tlli(msg) == mmctx->gb.llme->old_tlli)) { + LOGP(DLLC, LOGL_ERROR, + "_bssgp_tx_dl_ud(): Attempt to send Downlink Unitdata to wrong LLME:" + " msgb_tlli=0x%x mmctx->gb.llme->tlli=0x%x ->old_tlli=0x%x\n", + msgb_tlli(msg), mmctx->gb.llme->tlli, mmctx->gb.llme->old_tlli); + msgb_free(msg); + return -EINVAL; + } } } memcpy(&dup.qos_profile, qos_profile_default, @@ -528,7 +549,7 @@ static struct gprs_llc_lle *lle_for_rx_by_tlli_sapi(const uint32_t tlli, struct gprs_llc_llme *llme; /* FIXME: don't use the TLLI but the 0xFFFF unassigned? */ llme = llme_alloc(tlli); - LOGP(DLLC, LOGL_NOTICE, "LLC RX: unknown TLLI 0x%08x, " + LOGGBP(llme, DLLC, LOGL_NOTICE, "LLC RX: unknown TLLI 0x%08x, " "creating LLME on the fly\n", tlli); lle = &llme->lle[sapi]; return lle; @@ -580,6 +601,7 @@ static struct gprs_llc_llme *llme_alloc(uint32_t tlli) static void llme_free(struct gprs_llc_llme *llme) { + gprs_sndcp_sm_deactivate_ind_by_llme(llme); gprs_sndcp_comp_free(llme->comp.proto); gprs_sndcp_comp_free(llme->comp.data); llist_del(&llme->list); @@ -662,15 +684,15 @@ int gprs_llc_tx_u(struct msgb *msg, uint8_t sapi, int command, /* Identifiers passed down: (BVCI, NSEI) */ - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_LLC_DL_PACKETS]); - rate_ctr_add(&sgsn->rate_ctrs->ctr[CTR_LLC_DL_BYTES], msg->len); + rate_ctr_inc(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_LLC_DL_PACKETS)); + rate_ctr_add(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_LLC_DL_BYTES), msg->len); /* Send BSSGP-DL-UNITDATA.req */ return _bssgp_tx_dl_ud(msg, NULL); } /* Send XID response to LLE */ -static int gprs_llc_tx_xid(struct gprs_llc_lle *lle, struct msgb *msg, +static int gprs_llc_tx_xid(const struct gprs_llc_lle *lle, struct msgb *msg, int command) { /* copy identifiers from LLE to ensure lower layers can route */ @@ -681,7 +703,7 @@ static int gprs_llc_tx_xid(struct gprs_llc_lle *lle, struct msgb *msg, return gprs_llc_tx_u(msg, lle->sapi, command, GPRS_LLC_U_XID, 1); } -static int gprs_llc_tx_dm(struct gprs_llc_lle *lle) +static int gprs_llc_tx_dm(const struct gprs_llc_lle *lle) { struct msgb *msg = msgb_alloc_headroom(4096, 1024, "LLC_DM"); @@ -694,7 +716,7 @@ static int gprs_llc_tx_dm(struct gprs_llc_lle *lle) } /* encrypt information field + FCS, if needed! */ -static int apply_gea(struct gprs_llc_lle *lle, uint16_t crypt_len, uint16_t nu, +static int apply_gea(const struct gprs_llc_lle *lle, uint16_t crypt_len, uint16_t nu, uint32_t oc, uint8_t sapi, uint8_t *fcs, uint8_t *data) { uint8_t cipher_out[GSM0464_CIPH_MAX_BLOCK]; @@ -806,8 +828,8 @@ int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command, } } - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_LLC_DL_PACKETS]); - rate_ctr_add(&sgsn->rate_ctrs->ctr[CTR_LLC_DL_BYTES], msg->len); + rate_ctr_inc(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_LLC_DL_PACKETS)); + rate_ctr_add(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_LLC_DL_BYTES), msg->len); /* Identifiers passed down: (BVCI, NSEI) */ @@ -954,9 +976,9 @@ int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv) llhp.data); if (rc < 0) return rc; - llhp.fcs = *(llhp.data + llhp.data_len); - llhp.fcs |= *(llhp.data + llhp.data_len + 1) << 8; - llhp.fcs |= *(llhp.data + llhp.data_len + 2) << 16; + llhp.fcs = *(llhp.data + llhp.data_len); + llhp.fcs |= *(llhp.data + llhp.data_len + 1) << 8; + llhp.fcs |= *(llhp.data + llhp.data_len + 2) << 16; } else { LOGP(DLLC, LOGL_NOTICE, "encrypted frame for LLC that " "has no KC/Algo! Dropping.\n"); @@ -992,8 +1014,8 @@ int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv) msgb_l3trim(msg, llhp.data_len); } - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_LLC_UL_PACKETS]); - rate_ctr_add(&sgsn->rate_ctrs->ctr[CTR_LLC_UL_BYTES], msg->len); + rate_ctr_inc(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_LLC_UL_PACKETS)); + rate_ctr_add(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_LLC_UL_BYTES), msg->len); /* llhp.data is only set when we need to send LL_[UNIT]DATA_IND up */ if (llhp.cmd == GPRS_LLC_UI && llhp.data && llhp.data_len) { @@ -1008,7 +1030,7 @@ int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv) case GPRS_SAPI_SNDCP9: case GPRS_SAPI_SNDCP11: /* send LL_DATA_IND/LL_UNITDATA_IND to SNDCP */ - rc = sndcp_llunitdata_ind(msg, lle, llhp.data, llhp.data_len); + rc = sndcp_ll_unitdata_ind(msg, lle, llhp.data, llhp.data_len); break; case GPRS_SAPI_SMS: /* FIXME */ @@ -1026,7 +1048,7 @@ int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv) } /* Propagate crypto parameters MM -> LLME */ -void gprs_llme_copy_key(struct sgsn_mm_ctx *mm, struct gprs_llc_llme *llme) +void gprs_llme_copy_key(const struct sgsn_mm_ctx *mm, struct gprs_llc_llme *llme) { if (!mm) return; @@ -1034,8 +1056,13 @@ void gprs_llme_copy_key(struct sgsn_mm_ctx *mm, struct gprs_llc_llme *llme) llme->algo = mm->ciph_algo; if (llme->cksn != mm->auth_triplet.key_seq && mm->auth_triplet.key_seq != GSM_KEY_SEQ_INVAL) { - memcpy(llme->kc, mm->auth_triplet.vec.kc, - gprs_cipher_key_length(mm->ciph_algo)); + + /* gea4 needs kc128 */ + if (mm->ciph_algo == GPRS_ALGO_GEA4) + osmo_kdf_kc128(mm->auth_triplet.vec.ck, mm->auth_triplet.vec.ik, llme->kc); + else + memcpy(llme->kc, mm->auth_triplet.vec.kc, gprs_cipher_key_length(mm->ciph_algo)); + llme->cksn = mm->auth_triplet.key_seq; } } else @@ -1047,6 +1074,10 @@ int gprs_llgmm_assign(struct gprs_llc_llme *llme, uint32_t old_tlli, uint32_t new_tlli) { unsigned int i; + bool free = false; + + LOGGBP(llme, DLLC, LOGL_NOTICE, "LLGM Assign pre (%08x => %08x)\n", + old_tlli, new_tlli); if (old_tlli == TLLI_UNASSIGNED && new_tlli != TLLI_UNASSIGNED) { /* TLLI Assignment 8.3.1 */ @@ -1087,10 +1118,16 @@ int gprs_llgmm_assign(struct gprs_llc_llme *llme, struct gprs_llc_lle *l = &llme->lle[i]; l->state = GPRS_LLES_UNASSIGNED; } - llme_free(llme); + free = true; } else return -EINVAL; + LOGGBP(llme, DLLC, LOGL_NOTICE, "LLGM Assign post (%08x => %08x)\n", + old_tlli, new_tlli); + + if (free) + llme_free(llme); + return 0; } @@ -1104,16 +1141,18 @@ int gprs_llgmm_unassign(struct gprs_llc_llme *llme) int gprs_llgmm_reset(struct gprs_llc_llme *llme) { struct msgb *msg = msgb_alloc_headroom(4096, 1024, "LLC_XID"); - struct gprs_llc_lle *lle = &llme->lle[1]; + struct gprs_llc_lle *lle = &llme->lle[GPRS_SAPI_GMM]; uint8_t xid_bytes[1024]; int xid_bytes_len, rc; uint8_t *xid; - LOGP(DLLC, LOGL_NOTICE, "LLGM Reset\n"); + LOGGBP(llme, DLLC, LOGL_NOTICE, "LLGM Reset\n"); rc = osmo_get_rand_id((uint8_t *) &llme->iov_ui, 4); if (rc < 0) { - LOGP(DLLC, LOGL_ERROR, "osmo_get_rand_id() failed for LLC XID reset: %s\n", strerror(-rc)); + LOGGBP(llme, DLLC, LOGL_ERROR, + "osmo_get_rand_id() failed for LLC XID reset: %s\n", + strerror(-rc)); return rc; } @@ -1144,11 +1183,13 @@ int gprs_llgmm_reset_oldmsg(struct msgb* oldmsg, uint8_t sapi, int xid_bytes_len, rc; uint8_t *xid; - LOGP(DLLC, LOGL_NOTICE, "LLGM Reset\n"); + LOGGBP(llme, DLLC, LOGL_NOTICE, "LLGM Reset (SAPI=%" PRIu8 ")\n", sapi); rc = osmo_get_rand_id((uint8_t *) &llme->iov_ui, 4); if (rc < 0) { - LOGP(DLLC, LOGL_ERROR, "osmo_get_rand_id() failed for LLC XID reset: %s\n", strerror(-rc)); + LOGGBP(llme, DLLC, LOGL_ERROR, + "osmo_get_rand_id() failed for LLC XID reset: %s\n", + strerror(-rc)); return rc; } |