diff options
Diffstat (limited to 'src/gprs_bssgp_pcu.c')
-rw-r--r-- | src/gprs_bssgp_pcu.c | 176 |
1 files changed, 128 insertions, 48 deletions
diff --git a/src/gprs_bssgp_pcu.c b/src/gprs_bssgp_pcu.c index 4170a662..2c5a97a7 100644 --- a/src/gprs_bssgp_pcu.c +++ b/src/gprs_bssgp_pcu.c @@ -12,10 +12,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <gprs_rlcmac.h> @@ -33,11 +29,14 @@ #include <osmocom/gsm/protocol/gsm_23_003.h> #include <osmocom/gprs/protocol/gsm_08_16.h> #include <osmocom/core/utils.h> +#include <osmocom/core/stats.h> #include <osmocom/gsm/gsm48.h> #include "coding_scheme.h" #include "tbf_dl.h" #include "llc.h" #include "gprs_rlcmac.h" +#include "bts_pch_timer.h" +#include "alloc_algo.h" /* Tuning parameters for BSSGP flow control */ #define FC_DEFAULT_LIFE_TIME_SECS 10 /* experimental value, 10s */ @@ -53,6 +52,19 @@ extern void *tall_pcu_ctx; extern uint16_t spoof_mcc, spoof_mnc; extern bool spoof_mnc_3_digits; +static const struct rate_ctr_desc sgsn_ctr_description[] = { + [SGSN_CTR_RX_PAGING_CS] = { "rx_paging_cs", "Amount of paging CS requests received" }, + [SGSN_CTR_RX_PAGING_PS] = { "rx_paging_ps", "Amount of paging PS requests received" }, +}; + +static const struct rate_ctr_group_desc sgsn_ctrg_desc = { + .group_name_prefix = "pcu:sgsn", + .group_description = "SGSN Statistics", + .class_id = OSMO_STATS_CLASS_SUBSCRIBER, + .num_ctr = ARRAY_SIZE(sgsn_ctr_description), + .ctr_desc = sgsn_ctr_description, +}; + static void bvc_timeout(void *_priv); static int parse_ra_cap(struct tlv_parsed *tp, MS_Radio_Access_capability_t *rac) @@ -93,12 +105,8 @@ static int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp) uint8_t egprs_ms_class = 0; int rc; MS_Radio_Access_capability_t rac; - /* TODO: is it really necessary to initialize this as a "000" IMSI? It seems, the function should just return an - * error if no IMSI IE was found. */ - struct osmo_mobile_identity mi_imsi = { - .type = GSM_MI_TYPE_TMSI, - }; - OSMO_STRLCPY_ARRAY(mi_imsi.imsi, "000"); + const char *imsi = NULL; + struct osmo_mobile_identity mi_imsi; budh = (struct bssgp_ud_hdr *)msgb_bssgph(msg); tlli = ntohl(budh->tlli); @@ -129,6 +137,7 @@ static int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp) LOGP(DBSSGP, LOGL_NOTICE, "Failed to parse IMSI IE (rc=%d)\n", rc); return bssgp_tx_status(BSSGP_CAUSE_COND_IE_ERR, NULL, msg); } + imsi = &mi_imsi.imsi[0]; } /* parse ms radio access capability */ @@ -165,14 +174,87 @@ static int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp) "TLLI (old) IE\n"); } - LOGP(DBSSGP, LOGL_INFO, "LLC [SGSN -> PCU] = TLLI: 0x%08x IMSI: %s len: %d\n", tlli, mi_imsi.imsi, len); + LOGP(DBSSGP, LOGL_INFO, "LLC [SGSN -> PCU] = TLLI: 0x%08x IMSI: %s len: %d\n", + tlli, imsi ? : "none", len); + + return dl_tbf_handle(the_pcu->bssgp.bts, tlli, tlli_old, imsi, 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; + + /* TMSI 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 dl_tbf_handle(the_pcu->bssgp.bts, tlli, tlli_old, mi_imsi.imsi, - ms_class, egprs_ms_class, delay_csec, data, len); + 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; + + rate_ctr_inc(rate_ctr_group_get_ctr(the_pcu->bssgp.ctrs, SGSN_CTR_RX_PAGING_CS)); + + 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_get_ms_by_tlli(bts, req.tlli, req.tlli); + if (!ms && req.mi_imsi_present) + ms = bts_get_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,30 +284,15 @@ 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; struct osmo_mobile_identity paging_mi; struct gprs_rlcmac_bts *bts; - uint16_t pgroup; int rc; + rate_ctr_inc(rate_ctr_group_get_ctr(the_pcu->bssgp.ctrs, SGSN_CTR_RX_PAGING_PS)); + if (!TLVP_PRESENT(tp, BSSGP_IE_IMSI)) { LOGP(DBSSGP, LOGL_ERROR, "No IMSI\n"); return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); @@ -236,18 +303,20 @@ static int gprs_bssgp_pcu_rx_paging_ps(struct msgb *msg, const struct tlv_parsed LOGP(DBSSGP, LOGL_NOTICE, "Failed to parse IMSI IE (rc=%d)\n", rc); return bssgp_tx_status(BSSGP_CAUSE_INV_MAND_INF, NULL, msg); } - pgroup = imsi2paging_group(mi_imsi.imsi); - if (pgroup > 999) { - LOGP(DBSSGP, LOGL_NOTICE, "Failed to compute IMSI %s paging group\n", mi_imsi.imsi); - 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? */ llist_for_each_entry(bts, &the_pcu->bts_list, list) { - gprs_rlcmac_paging_request(bts, &paging_mi, pgroup); + if (bts_pch_timer_get_by_imsi(bts, mi_imsi.imsi)) { + LOGP(DBSSGP, LOGL_INFO, "PS-Paging request already pending for IMSI=%s\n", mi_imsi.imsi); + bts_do_rate_ctr_inc(bts, CTR_PCH_REQUESTS_ALREADY); + continue; + } + if (gprs_rlcmac_paging_request(bts, &paging_mi, mi_imsi.imsi) < 0) + continue; + bts_pch_timer_start(bts, &paging_mi, mi_imsi.imsi); } return 0; } @@ -484,8 +553,8 @@ static int gprs_bssgp_pcu_rcvmsg(struct msgb *msg) if (bctx) { log_set_context(LOG_CTX_GB_BVC, bctx); - rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_IN]); - rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_IN], msgb_bssgp_len(msg)); + rate_ctr_inc(rate_ctr_group_get_ctr(bctx->ctrg, BSSGP_CTR_PKTS_IN)); + rate_ctr_add(rate_ctr_group_get_ctr(bctx->ctrg, BSSGP_CTR_BYTES_IN), msgb_bssgp_len(msg)); } if (ns_bvci == BVCI_SIGNALLING) @@ -708,7 +777,7 @@ static unsigned count_pdch(const struct gprs_rlcmac_bts *bts) for (ts_no = 0; ts_no < ARRAY_SIZE(trx->pdch); ++ts_no) { const struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts_no]; - if (pdch->m_is_enabled) + if (pdch_is_enabled(pdch)) num_pdch += 1; } } @@ -975,7 +1044,7 @@ static void bvc_timeout(void *_priv) if (!the_pcu->bssgp.bvc_sig_reset) { LOGP(DBSSGP, LOGL_INFO, "Sending reset on BVCI 0\n"); bssgp_tx_bvc_reset(the_pcu->bssgp.bctx, 0, BSSGP_CAUSE_OML_INTERV); - secs = osmo_tdef_get(the_pcu->T_defs, 2, OSMO_TDEF_S, -1); + secs = osmo_tdef_get(the_pcu->T_defs, -102, OSMO_TDEF_S, -1); osmo_timer_schedule(&the_pcu->bssgp.bvc_timer, secs, 0); return; } @@ -984,7 +1053,7 @@ static void bvc_timeout(void *_priv) LOGP(DBSSGP, LOGL_INFO, "Sending reset on BVCI %d\n", the_pcu->bssgp.bctx->bvci); bssgp_tx_bvc_reset(the_pcu->bssgp.bctx, the_pcu->bssgp.bctx->bvci, BSSGP_CAUSE_OML_INTERV); - secs = osmo_tdef_get(the_pcu->T_defs, 2, OSMO_TDEF_S, -1); + secs = osmo_tdef_get(the_pcu->T_defs, -102, OSMO_TDEF_S, -1); osmo_timer_schedule(&the_pcu->bssgp.bvc_timer, secs, 0); return; } @@ -993,7 +1062,7 @@ static void bvc_timeout(void *_priv) LOGP(DBSSGP, LOGL_INFO, "Sending unblock on BVCI %d\n", the_pcu->bssgp.bctx->bvci); bssgp_tx_bvc_unblock(the_pcu->bssgp.bctx); - secs = osmo_tdef_get(the_pcu->T_defs, 1, OSMO_TDEF_S, -1); + secs = osmo_tdef_get(the_pcu->T_defs, -101, OSMO_TDEF_S, -1); osmo_timer_schedule(&the_pcu->bssgp.bvc_timer, secs, 0); return; } @@ -1021,12 +1090,13 @@ static int ns_configure_nse(struct gprs_rlcmac_bts *bts, const uint16_t *nsvci, uint16_t valid) { - int i, rc; + unsigned int i; + int rc; uint16_t binds = 0; bool nsvcs = false; struct gprs_ns2_vc *nsvc; struct gprs_ns2_vc_bind *bind[PCU_IF_NUM_NSVC] = { }; - char name[5]; + char name[16]; bool sns_configured = false; if (!valid) @@ -1045,8 +1115,9 @@ static int ns_configure_nse(struct gprs_rlcmac_bts *bts, if (!(valid & (1 << i))) continue; - if (!gprs_ns2_ip_bind_by_sockaddr(the_pcu->nsi, &local[i])) { - snprintf(name, sizeof(name), "pcu%d", i); + bind[i] = gprs_ns2_ip_bind_by_sockaddr(the_pcu->nsi, &local[i]); + if (!bind[i]) { + snprintf(name, sizeof(name), "pcu%u", i); rc = gprs_ns2_ip_bind(the_pcu->nsi, name, &local[i], 0, &bind[i]); if (rc < 0) { LOGP(DBSSGP, LOGL_ERROR, "Failed to bind to %s\n", osmo_sockaddr_to_str(&local[i])); @@ -1060,6 +1131,11 @@ static int ns_configure_nse(struct gprs_rlcmac_bts *bts, continue; } } + + if (the_pcu->vty.ns_ip_dscp != -1) + gprs_ns2_ip_bind_set_dscp(bind[i], the_pcu->vty.ns_ip_dscp); + if (the_pcu->vty.ns_priority != -1) + gprs_ns2_ip_bind_set_priority(bind[i], the_pcu->vty.ns_priority); } binds |= 1 << i; @@ -1228,11 +1304,15 @@ struct gprs_bssgp_pcu *gprs_bssgp_init( osmo_timer_setup(&the_pcu->bssgp.bvc_timer, bvc_timeout, bts); + the_pcu->bssgp.ctrs = rate_ctr_group_alloc(the_pcu, &sgsn_ctrg_desc, 0); + OSMO_ASSERT(the_pcu->bssgp.ctrs) + return &the_pcu->bssgp; } void gprs_bssgp_destroy(struct gprs_rlcmac_bts *bts) { + rate_ctr_group_free(the_pcu->bssgp.ctrs); osmo_timer_del(&the_pcu->bssgp.bvc_timer); /* FIXME: blocking... */ |