diff --git a/src/bts.cpp b/src/bts.cpp index 5769c2f5..e9864e70 100644 --- a/src/bts.cpp +++ b/src/bts.cpp @@ -337,11 +337,44 @@ void bts_set_current_block_frame_number(struct gprs_rlcmac_bts *bts, int fn) bts_set_current_frame_number(bts, fn); } -int bts_add_paging(struct gprs_rlcmac_bts *bts, uint8_t chan_needed, const struct osmo_mobile_identity *mi) +/* Helper used by bts_add_paging() whenever the target MS is known */ +static void bts_add_paging_known_ms(struct GprsMs *ms, const struct osmo_mobile_identity *mi, uint8_t chan_needed) +{ + uint8_t ts; + + if (ms->ul_tbf) { + for (ts = 0; ts < ARRAY_SIZE(ms->ul_tbf->pdch); ts++) { + if (ms->ul_tbf->pdch[ts]) { + LOGPDCH(ms->ul_tbf->pdch[ts], DRLCMAC, LOGL_INFO, + "Paging on PACCH for %s\n", tbf_name(ms->ul_tbf)); + if (!ms->ul_tbf->pdch[ts]->add_paging(chan_needed, mi)) + continue; + return; + } + } + } + if (ms->dl_tbf) { + for (ts = 0; ts < ARRAY_SIZE(ms->dl_tbf->pdch); ts++) { + if (ms->dl_tbf->pdch[ts]) { + LOGPDCH(ms->dl_tbf->pdch[ts], DRLCMAC, LOGL_INFO, + "Paging on PACCH for %s\n", tbf_name(ms->ul_tbf)); + if (!ms->dl_tbf->pdch[ts]->add_paging(chan_needed, mi)) + continue; + return; + } + } + } + LOGPMS(ms, DRLCMAC, LOGL_INFO, "Unable to page on PACCH, no available TBFs\n"); + return; +} + +/* ms is NULL if no specific taget was found */ +int bts_add_paging(struct gprs_rlcmac_bts *bts, const struct paging_req_cs *req, struct GprsMs *ms) { uint8_t l, trx, ts, any_tbf = 0; struct gprs_rlcmac_tbf *tbf; struct llist_item *pos; + const struct osmo_mobile_identity *mi; uint8_t slot_mask[8]; int8_t first_ts; /* must be signed */ @@ -351,13 +384,31 @@ int bts_add_paging(struct gprs_rlcmac_bts *bts, uint8_t chan_needed, const struc NULL }; + /* First, build the MI used to page on PDCH from available subscriber info: */ + if (req->mi_tmsi_present) { + mi = &req->mi_tmsi; + } else if (req->mi_imsi_present) { + mi = &req->mi_imsi; + } else { + LOGPMS(ms, DRLCMAC, LOGL_ERROR, "Unable to page on PACCH, no TMSI nor IMSI in request\n"); + return -EINVAL; + } + if (log_check_level(DRLCMAC, LOGL_INFO)) { char str[64]; osmo_mobile_identity_to_str_buf(str, sizeof(str), mi); - LOGP(DRLCMAC, LOGL_INFO, "Add RR paging: chan-needed=%d MI=%s\n", chan_needed, str); + LOGP(DRLCMAC, LOGL_INFO, "Add RR paging: chan-needed=%d MI=%s\n", req->chan_needed, str); } - /* collect slots to page + /* We known the target MS for the paging req, send the req only on PDCH + * were that target MS is listening (first slot is enough), and we are done. */ + if (ms) { + bts_add_paging_known_ms(ms, mi, req->chan_needed); + return 0; + } + + /* We don't know the target MS. + * collect slots to page * Mark slots for every TBF, but only mark one of it. * Mark only the first slot found. * Don't mark, if TBF uses a different slot that is already marked. */ @@ -399,7 +450,7 @@ int bts_add_paging(struct gprs_rlcmac_bts *bts, uint8_t chan_needed, const struc for (ts = 0; ts < 8; ts++) { if ((slot_mask[trx] & (1 << ts))) { /* schedule */ - if (!bts->trx[trx].pdch[ts].add_paging(chan_needed, mi)) + if (!bts->trx[trx].pdch[ts].add_paging(req->chan_needed, mi)) return -ENOMEM; LOGPDCH(&bts->trx[trx].pdch[ts], DRLCMAC, LOGL_INFO, "Paging on PACCH\n"); diff --git a/src/bts.h b/src/bts.h index ea44fb70..11b5113b 100644 --- a/src/bts.h +++ b/src/bts.h @@ -265,8 +265,17 @@ struct gprs_rlcmac_bts { extern "C" { #endif +struct paging_req_cs { + uint8_t chan_needed; + uint32_t tlli; /* GSM_RESERVED_TMSI if not present */ + bool mi_tmsi_present; + struct osmo_mobile_identity mi_tmsi; + bool mi_imsi_present; + struct osmo_mobile_identity mi_imsi; +}; + struct GprsMs *bts_alloc_ms(struct gprs_rlcmac_bts *bts, uint8_t ms_class, uint8_t egprs_ms_class); -int bts_add_paging(struct gprs_rlcmac_bts *bts, uint8_t chan_needed, const struct osmo_mobile_identity *mi); +int bts_add_paging(struct gprs_rlcmac_bts *bts, const struct paging_req_cs *req, struct GprsMs *ms); uint32_t bts_rfn_to_fn(const struct gprs_rlcmac_bts *bts, int32_t rfn); diff --git a/src/gprs_bssgp_pcu.c b/src/gprs_bssgp_pcu.c index f6114dea..5d0a4891 100644 --- a/src/gprs_bssgp_pcu.c +++ b/src/gprs_bssgp_pcu.c @@ -171,8 +171,78 @@ static int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp) ms_class, egprs_ms_class, delay_csec, data, len); } +/* 3GPP TS 48.018 Table 10.3.2. Returns 0 on success, suggested BSSGP cause otherwise */ +static unsigned int get_paging_cs_mi(struct paging_req_cs *req, const struct tlv_parsed *tp) +{ + int rc; + + req->chan_needed = tlvp_val8(tp, BSSGP_IE_CHAN_NEEDED, 0); + + if (!TLVP_PRESENT(tp, BSSGP_IE_IMSI)) { + LOGP(DBSSGP, LOGL_ERROR, "IMSI Mobile Identity mandatory IE not found\n"); + return BSSGP_CAUSE_MISSING_MAND_IE; + } + + rc = osmo_mobile_identity_decode(&req->mi_imsi, TLVP_VAL(tp, BSSGP_IE_IMSI), + TLVP_LEN(tp, BSSGP_IE_IMSI), true); + if (rc < 0 || req->mi_imsi.type != GSM_MI_TYPE_IMSI) { + LOGP(DBSSGP, LOGL_ERROR, "Invalid IMSI Mobile Identity\n"); + return BSSGP_CAUSE_INV_MAND_INF; + } + req->mi_imsi_present = true; + + /* TIMSI is optional */ + req->mi_tmsi_present = false; + if (TLVP_PRESENT(tp, BSSGP_IE_TMSI)) { + /* Be safe against an evil SGSN - check the length */ + if (TLVP_LEN(tp, BSSGP_IE_TMSI) != GSM23003_TMSI_NUM_BYTES) { + LOGP(DBSSGP, LOGL_NOTICE, "TMSI IE has odd length (!= 4)\n"); + return BSSGP_CAUSE_COND_IE_ERR; + } + + /* NOTE: TMSI (unlike IMSI) IE comes without MI type header */ + req->mi_tmsi = (struct osmo_mobile_identity){ + .type = GSM_MI_TYPE_TMSI, + }; + req->mi_tmsi.tmsi = osmo_load32be(TLVP_VAL(tp, BSSGP_IE_TMSI)); + req->mi_tmsi_present = true; + } + + if (TLVP_PRESENT(tp, BSSGP_IE_TLLI)) + req->tlli = osmo_load32be(TLVP_VAL(tp, BSSGP_IE_TLLI)); + else + req->tlli = GSM_RESERVED_TMSI; + + return 0; +} + +static int gprs_bssgp_pcu_rx_paging_cs(struct msgb *msg, const struct tlv_parsed *tp) +{ + struct paging_req_cs req; + struct gprs_rlcmac_bts *bts; + struct GprsMs *ms; + int rc; + + if ((rc = get_paging_cs_mi(&req, tp)) > 0) + return bssgp_tx_status((enum gprs_bssgp_cause) rc, NULL, msg); + + /* We need to page all BTSs since even if a BTS has a matching MS, it + * may have already moved to a newer BTS. On Each BTS, if the MS is + * known, then bts_add_paging() can optimize and page only on PDCHs the + * target MS is using. */ + llist_for_each_entry(bts, &the_pcu->bts_list, list) { + /* TODO: Match by TMSI before IMSI if present?! */ + ms = bts_ms_by_tlli(bts, req.tlli, req.tlli); + if (!ms && req.mi_imsi_present) + ms = bts_ms_by_imsi(bts, req.mi_imsi.imsi); + bts_add_paging(bts, &req, ms); + } + + return 0; +} + /* Returns 0 on success, suggested BSSGP cause otherwise */ -static unsigned int get_paging_mi(struct osmo_mobile_identity *mi, const struct tlv_parsed *tp) +static unsigned int get_paging_ps_mi(struct osmo_mobile_identity *mi, const struct tlv_parsed *tp) { /* Use TMSI (if present) or IMSI */ if (TLVP_PRESENT(tp, BSSGP_IE_TMSI)) { @@ -202,22 +272,6 @@ static unsigned int get_paging_mi(struct osmo_mobile_identity *mi, const struct return 0; } -static int gprs_bssgp_pcu_rx_paging_cs(struct msgb *msg, const struct tlv_parsed *tp) -{ - struct osmo_mobile_identity mi; - struct gprs_rlcmac_bts *bts; - int rc; - - if ((rc = get_paging_mi(&mi, tp)) > 0) - return bssgp_tx_status((enum gprs_bssgp_cause) rc, NULL, msg); - - /* FIXME: look if MS is attached a specific BTS and then only page on that one? */ - llist_for_each_entry(bts, &the_pcu->bts_list, list) { - bts_add_paging(bts, tlvp_val8(tp, BSSGP_IE_CHAN_NEEDED, 0), &mi); - } - return 0; -} - static int gprs_bssgp_pcu_rx_paging_ps(struct msgb *msg, const struct tlv_parsed *tp) { struct osmo_mobile_identity mi_imsi; @@ -242,7 +296,7 @@ static int gprs_bssgp_pcu_rx_paging_ps(struct msgb *msg, const struct tlv_parsed return bssgp_tx_status(BSSGP_CAUSE_INV_MAND_INF, NULL, msg); } - if ((rc = get_paging_mi(&paging_mi, tp)) > 0) + if ((rc = get_paging_ps_mi(&paging_mi, tp)) > 0) return bssgp_tx_status((enum gprs_bssgp_cause) rc, NULL, msg); /* FIXME: look if MS is attached a specific BTS and then only page on that one? */ diff --git a/src/pcu_l1_if.cpp b/src/pcu_l1_if.cpp index 0ccf642e..cfd36d77 100644 --- a/src/pcu_l1_if.cpp +++ b/src/pcu_l1_if.cpp @@ -817,6 +817,9 @@ static int pcu_rx_time_ind(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_time_i static int pcu_rx_pag_req(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_pag_req *pag_req) { struct osmo_mobile_identity mi; + struct GprsMs *ms = NULL; + struct paging_req_cs req = { .chan_needed = pag_req->chan_needed, + .tlli = GSM_RESERVED_TMSI }; int rc; LOGP(DL1IF, LOGL_DEBUG, "Paging request received: chan_needed=%d " @@ -835,7 +838,23 @@ static int pcu_rx_pag_req(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_pag_req return -EINVAL; } - return bts_add_paging(bts, pag_req->chan_needed, &mi); + switch (mi.type) { + case GSM_MI_TYPE_TMSI: + req.mi_tmsi = mi; + req.mi_tmsi_present = true; + /* TODO: look up MS by TMSI? Derive TLLI? */ + break; + case GSM_MI_TYPE_IMSI: + req.mi_imsi = mi; + req.mi_imsi_present = true; + ms = bts_ms_by_imsi(bts, req.mi_imsi.imsi); + break; + default: + LOGP(DL1IF, LOGL_ERROR, "Unexpected MI type %u\n", mi.type); + return -EINVAL; + } + + return bts_add_paging(bts, &req, ms); } static int pcu_rx_susp_req(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_susp_req *susp_req)