diff --git a/include/osmocom/sgsn/gprs_bssgp.h b/include/osmocom/sgsn/gprs_bssgp.h index abfe9f197..e2faf38a9 100644 --- a/include/osmocom/sgsn/gprs_bssgp.h +++ b/include/osmocom/sgsn/gprs_bssgp.h @@ -11,5 +11,5 @@ int sgsn_bssgp_rx_prim(struct osmo_prim_hdr *oph); /* called by the bssgp layer to send NS PDUs */ int sgsn_bssgp_dispatch_ns_unitdata_req_cb(void *ctx, struct msgb *msg); -/* page a MS in its routing area */ -int sgsn_bssgp_page_ps_ra(struct sgsn_mm_ctx *mmctx); +/* page a MS in a single cell */ +int sgsn_bssgp_page_ps_bvci(struct sgsn_mm_ctx *mmctx, uint16_t nsei, uint16_t bvci); diff --git a/include/osmocom/sgsn/gprs_routing_area.h b/include/osmocom/sgsn/gprs_routing_area.h index 34e29cb9a..caaed47b8 100644 --- a/include/osmocom/sgsn/gprs_routing_area.h +++ b/include/osmocom/sgsn/gprs_routing_area.h @@ -9,6 +9,7 @@ #include struct sgsn_instance; +struct sgsn_mm_ctx; struct sgsn_ra_global { /* list of struct sgsn_ra */ @@ -86,3 +87,6 @@ struct sgsn_ra *sgsn_ra_get_ra(const struct osmo_routing_area_id *ra_id); typedef int (sgsn_ra_cb_t)(struct sgsn_ra_cell *ra_cell, void *cb_data); int sgsn_ra_foreach_cell(struct sgsn_ra *ra, sgsn_ra_cb_t *cb, void *cb_data); int sgsn_ra_foreach_cell2(struct osmo_routing_area_id *rai, sgsn_ra_cb_t *cb, void *cb_data); + +/* Page the whole routing area for this mmctx */ +int sgsn_ra_geran_page_ra(struct osmo_routing_area_id *ra_id, struct sgsn_mm_ctx *mmctx); diff --git a/src/sgsn/gprs_bssgp.c b/src/sgsn/gprs_bssgp.c index fd82b154b..e738f0bd3 100644 --- a/src/sgsn/gprs_bssgp.c +++ b/src/sgsn/gprs_bssgp.c @@ -22,7 +22,6 @@ */ #include -#include #include #include @@ -95,26 +94,20 @@ int sgsn_bssgp_rx_prim(struct osmo_prim_hdr *oph) return 0; } -int sgsn_bssgp_page_ps_ra(struct sgsn_mm_ctx *mmctx) +int sgsn_bssgp_page_ps_bvci(struct sgsn_mm_ctx *mmctx, uint16_t nsei, uint16_t bvci) { struct bssgp_paging_info pinfo; - int rc; - - /* FIXME: page whole routing area, not only the last known cell */ /* initiate PS PAGING procedure */ memset(&pinfo, 0, sizeof(pinfo)); pinfo.mode = BSSGP_PAGING_PS; pinfo.scope = BSSGP_PAGING_BVCI; - pinfo.bvci = mmctx->gb.bvci; + pinfo.bvci = bvci; pinfo.imsi = mmctx->imsi; pinfo.ptmsi = &mmctx->p_tmsi; pinfo.drx_params = mmctx->drx_parms; pinfo.qos[0] = 0; // FIXME - rc = bssgp_tx_paging(mmctx->gb.nsei, 0, &pinfo); - rate_ctr_inc(rate_ctr_group_get_ctr(mmctx->ctrg, GMM_CTR_PAGING_PS)); - - return rc; + return bssgp_tx_paging(nsei, 0, &pinfo); } /* called by the bssgp layer to send NS PDUs */ diff --git a/src/sgsn/gprs_routing_area.c b/src/sgsn/gprs_routing_area.c index 977caa279..67b15aed7 100644 --- a/src/sgsn/gprs_routing_area.c +++ b/src/sgsn/gprs_routing_area.c @@ -22,11 +22,11 @@ #include #include -#include - - +#include #include - +#include +#include +#include #include #include @@ -330,6 +330,28 @@ int sgsn_ra_nsei_failure_ind(uint16_t nsei) return found ? 0 : -ENOENT; } +int sgsn_ra_geran_page_ra(struct osmo_routing_area_id *ra_id, struct sgsn_mm_ctx *mmctx) +{ + struct sgsn_ra *ra; + struct sgsn_ra_cell *cell; + int ret = -ENOENT; + + rate_ctr_inc(rate_ctr_group_get_ctr(mmctx->ctrg, GMM_CTR_PAGING_PS)); + + ra = sgsn_ra_get_ra(ra_id); + if (!ra) + return -ENOENT; + + llist_for_each_entry(cell, &ra->cells, list) { + if (cell->ran_type == RA_TYPE_GERAN_Gb) { + sgsn_bssgp_page_ps_bvci(mmctx, cell->u.geran.nsei, cell->u.geran.bvci); + ret = 0; + } + } + + + return ret; +} void sgsn_ra_init(struct sgsn_instance *inst) { diff --git a/src/sgsn/sgsn_libgtp.c b/src/sgsn/sgsn_libgtp.c index faf0e7f57..8eb77b060 100644 --- a/src/sgsn/sgsn_libgtp.c +++ b/src/sgsn/sgsn_libgtp.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -864,7 +865,7 @@ static int cb_data_ind(struct pdp_t *lib, void *packet, unsigned int len) LOGMMCTXP(LOGL_INFO, mm, "Paging MS in GMM state %s, MM state %s\n", osmo_fsm_inst_state_name(mm->gmm_fsm), osmo_fsm_inst_state_name(mm->gb.mm_state_fsm)); - sgsn_bssgp_page_ps_ra(mm); + sgsn_ra_geran_page_ra(&mm->ra, mm); /* FIXME: queue the packet we received from GTP */ break; diff --git a/src/sgsn/sgsn_vty.c b/src/sgsn/sgsn_vty.c index bcb8dc208..a7e804529 100644 --- a/src/sgsn/sgsn_vty.c +++ b/src/sgsn/sgsn_vty.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -1333,7 +1334,7 @@ DEFUN(page_subscr, page_subscr_info_cmd, return CMD_WARNING; } - sgsn_bssgp_page_ps_ra(mm); + sgsn_ra_geran_page_ra(&mm->ra, mm); return CMD_SUCCESS; } diff --git a/tests/gprs_routing_area/gprs_routing_area_test.c b/tests/gprs_routing_area/gprs_routing_area_test.c index d879f39ed..e1aec0f6d 100644 --- a/tests/gprs_routing_area/gprs_routing_area_test.c +++ b/tests/gprs_routing_area/gprs_routing_area_test.c @@ -44,6 +44,17 @@ void *tall_sgsn_ctx; struct sgsn_instance *sgsn; +struct paging_exp { + uint16_t nsei; + uint16_t bvci; + /* paged when we send one paging request */ + bool paged; + /* valid when this entry contains valid data */ + bool valid; +}; + +struct paging_exp g_paging[4]; + static void cleanup_test(void) { TALLOC_FREE(sgsn); @@ -326,6 +337,93 @@ void test_routing_area_nsei_free(void) cleanup_test(); } +/* BSSGP Paging RA */ +int bssgp_tx_paging(uint16_t nsei, uint16_t _bvci, + struct bssgp_paging_info *pinfo) +{ + bool found = false; + + OSMO_ASSERT(pinfo); + fprintf(stderr, "Tx paging for nsei %05u / bvci %05u\n", nsei, pinfo->bvci); + /* match against list of expect pagings */ + for (int i = 0; i < ARRAY_SIZE(g_paging); i++) { + struct paging_exp *exp = &g_paging[i]; + if (exp->paged || !exp->valid) + continue; + + if (exp->nsei == nsei && exp->bvci == pinfo->bvci) { + exp->paged = true; + found = true; + break; + } + } + + OSMO_ASSERT(found); + return 0; +} + +static void check_paging(void) +{ + for (int i = 0; i < ARRAY_SIZE(g_paging); i++) { + struct paging_exp *exp = &g_paging[i]; + if (!exp->valid) + continue; + OSMO_ASSERT(exp->paged) + } +} + +void test_routing_area_paging(void) +{ + struct sgsn_mm_ctx *mmctx; + struct osmo_routing_area_id ra_id = { + .lac = { + .plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false }, + .lac = 24 + }, + .rac = 43 + }; + + uint16_t cell_id = 9999; + struct osmo_cell_global_id_ps cgi_ps = { + .rai = ra_id, + .cell_identity = cell_id, + }; + + uint16_t nsei = 2, bvci = 3; + int rc; + + printf("Testing Routing Area paging\n"); + + sgsn = sgsn_instance_alloc(tall_sgsn_ctx); + + memset(g_paging, 0, sizeof(g_paging)); + g_paging[0].bvci = bvci; + g_paging[0].nsei = nsei; + g_paging[0].valid = true; + g_paging[0].paged = false; + + rc = sgsn_ra_bvc_reset_ind(nsei, bvci, &cgi_ps); + OSMO_ASSERT(rc == 0); + + cgi_ps.cell_identity++; + rc = sgsn_ra_bvc_reset_ind(nsei, bvci+1, &cgi_ps); + OSMO_ASSERT(rc == 0); + + g_paging[1].bvci = bvci+1; + g_paging[1].nsei = nsei; + g_paging[1].valid = true; + g_paging[1].paged = false; + + mmctx = sgsn_mm_ctx_alloc_gb(0xc0001234, &ra_id); + + sgsn_ra_geran_page_ra(&ra_id, mmctx); + check_paging(); + + sgsn_mm_ctx_cleanup_free(mmctx); + + cleanup_test(); +} + static struct log_info_cat gprs_categories[] = { [DMM] = { .name = "DMM", @@ -387,6 +485,7 @@ int main(int argc, char **argv) test_routing_area_free_empty(); test_routing_area_reset_ind(); test_routing_area_nsei_free(); + test_routing_area_paging(); printf("Done\n"); talloc_report_full(osmo_sgsn_ctx, stderr); diff --git a/tests/gprs_routing_area/gprs_routing_area_test.ok b/tests/gprs_routing_area/gprs_routing_area_test.ok index dccbb5e86..78cee7e16 100644 --- a/tests/gprs_routing_area/gprs_routing_area_test.ok +++ b/tests/gprs_routing_area/gprs_routing_area_test.ok @@ -3,4 +3,5 @@ Testing Routing Area find Testing Routing Area create/free Testing Routing Area BSSGP BVC RESET IND Testing Routing Area nsei failure +Testing Routing Area paging Done