aboutsummaryrefslogtreecommitdiffstats
path: root/src/gprs_bssgp_pcu.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gprs_bssgp_pcu.c')
-rw-r--r--src/gprs_bssgp_pcu.c176
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... */