summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeels Hofmeyr <nhofmeyr@sysmocom.de>2016-05-30 15:04:56 +0200
committerNeels Hofmeyr <nhofmeyr@sysmocom.de>2016-11-12 16:05:05 +0100
commitfbd934be94acef01c5bd0d6f54e18735d3d23e07 (patch)
treea3d4c98a087b9bded7df4d5f7f5a3583db609428
parent5d80cbe481a0207675120194d0fedbd8744d9a0d (diff)
complete IuCS paging implementation
Add paging timeout to struct gsm_subscriber. Previously, paging timeout was implemented only on BSC level, where each request has its own timeout value. The MSC will still send individual requests to BSC or RNC level, where they timeout individually. However, the MSC must also have an own timeout to be sure to discard stale pagings that the BSC or RNC never replied for. Add handle_paging_resp(), copying the few libmsc relevant parts of gsm48_handle_paging_resp().
-rw-r--r--openbsc/include/openbsc/gsm_subscriber.h1
-rw-r--r--openbsc/src/libmsc/gsm_04_08.c39
-rw-r--r--openbsc/src/libmsc/gsm_subscriber.c110
3 files changed, 115 insertions, 35 deletions
diff --git a/openbsc/include/openbsc/gsm_subscriber.h b/openbsc/include/openbsc/gsm_subscriber.h
index 0628d1f..601aeb2 100644
--- a/openbsc/include/openbsc/gsm_subscriber.h
+++ b/openbsc/include/openbsc/gsm_subscriber.h
@@ -71,6 +71,7 @@ struct gsm_subscriber {
/* pending requests */
int is_paging;
+ struct osmo_timer_list paging_timeout;
struct llist_head requests;
/* GPRS/SGSN related fields */
diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c
index 8c93287..5e86dcf 100644
--- a/openbsc/src/libmsc/gsm_04_08.c
+++ b/openbsc/src/libmsc/gsm_04_08.c
@@ -1249,6 +1249,30 @@ static int gsm0408_rcv_mm(struct gsm_subscriber_connection *conn, struct msgb *m
return rc;
}
+static int handle_paging_resp(struct msgb *msg,
+ struct gsm_subscriber_connection *conn,
+ struct gsm_subscriber *subscr)
+{
+ if (!conn->subscr) {
+ conn->subscr = subscr;
+ } else if (conn->subscr != subscr) {
+ LOGP(DPAG, LOGL_ERROR,
+ "Connection already owned by another subscriber?\n");
+ subscr_put(subscr);
+ return -EINVAL;
+ } else {
+ DEBUGP(DPAG, "Connection already owned by the subscriber\n");
+ subscr_put(subscr);
+ subscr = conn->subscr;
+ }
+
+ /* TODO: count successful paging *in MSC*, similarly to BSC:
+ * rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_PAGING_COMPLETED]); */
+#warning "missing rate counter for paging on MSC level"
+
+ return subscr_rx_paging_response(msg, conn);
+}
+
/* Receive a PAGING RESPONSE message from the MS */
static int gsm48_rx_rr_pag_resp(struct gsm_subscriber_connection *conn, struct msgb *msg)
{
@@ -1292,14 +1316,7 @@ static int gsm48_rx_rr_pag_resp(struct gsm_subscriber_connection *conn, struct m
/* We received a paging */
conn->expire_timer_stopped = 1;
-#if 0
- TODO implement paging response in libmsc!
- Excluding this to be able to link without libbsc:
- rc = gsm48_handle_paging_resp(conn, msg, subscr);
- return rc;
-#else
- return -ENOTSUP;
-#endif
+ return handle_paging_resp(msg, conn, subscr);
}
static int gsm48_rx_rr_app_info(struct gsm_subscriber_connection *conn, struct msgb *msg)
@@ -3581,12 +3598,12 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg)
trans_free(trans);
return 0;
}
- /* store setup informations until paging was successfull */
+ /* store setup information until paging succeeds */
memcpy(&trans->cc.msg, data, sizeof(struct gsm_mncc));
/* Request a channel */
- trans->paging_request = subscr_request_channel(subscr,
- RSL_CHANNEED_TCH_F, setup_trig_pag_evt,
+ trans->paging_request = subscr_request_conn(subscr,
+ setup_trig_pag_evt,
trans);
if (!trans->paging_request) {
LOGP(DCC, LOGL_ERROR, "Failed to allocate paging token.\n");
diff --git a/openbsc/src/libmsc/gsm_subscriber.c b/openbsc/src/libmsc/gsm_subscriber.c
index 41a8236..534b093 100644
--- a/openbsc/src/libmsc/gsm_subscriber.c
+++ b/openbsc/src/libmsc/gsm_subscriber.c
@@ -59,23 +59,33 @@ static struct gsm_subscriber *get_subscriber(struct gsm_subscriber_group *sgrp,
return subscr;
}
-/*
- * We got the channel assigned and can now hand this channel
- * over to one of our callbacks.
- */
+/* A connection is established and the paging callbacks may run now. */
static int subscr_paging_dispatch(unsigned int hooknum, unsigned int event,
- struct msgb *msg, void *data, void *param)
+ struct msgb *msg, void *data, void *param)
{
struct subscr_request *request, *tmp;
struct gsm_subscriber_connection *conn = data;
struct gsm_subscriber *subscr = param;
struct paging_signal_data sig_data;
- OSMO_ASSERT(subscr->is_paging);
+ OSMO_ASSERT(hooknum == GSM_HOOK_RR_PAGING);
+ OSMO_ASSERT(subscr);
+ OSMO_ASSERT(!(conn && (conn->subscr != subscr)));
+ OSMO_ASSERT(!((event == GSM_PAGING_SUCCEEDED) && !conn));
+
+ LOGP(DPAG, LOGL_DEBUG, "Paging %s for %s (event=%d)\n",
+ event == GSM_PAGING_SUCCEEDED ? "success" : "failure",
+ subscr_name(subscr), event);
+
+ if (!subscr->is_paging) {
+ LOGP(DPAG, LOGL_ERROR,
+ "Paging Response received for subscriber"
+ " that is not paging.\n");
+ return -EINVAL;
+ }
/* Inform parts of the system we don't know */
sig_data.subscr = subscr;
- sig_data.bts = conn ? conn->bts : NULL;
sig_data.conn = conn;
sig_data.paging_result = event;
osmo_signal_dispatch(
@@ -87,38 +97,75 @@ static int subscr_paging_dispatch(unsigned int hooknum, unsigned int event,
llist_for_each_entry_safe(request, tmp, &subscr->requests, entry) {
llist_del(&request->entry);
- request->cbfn(hooknum, event, msg, data, request->param);
+ if (request->cbfn) {
+ LOGP(DPAG, LOGL_DEBUG, "Calling paging cbfn.\n");
+ request->cbfn(hooknum, event, msg, data, request->param);
+ } else
+ LOGP(DPAG, LOGL_DEBUG, "Paging without action.\n");
talloc_free(request);
}
/* balanced with the moment we start paging */
subscr->is_paging = 0;
+
+ /* balanced with the moment we receive a paging response */
subscr_put(subscr);
return 0;
}
+static void paging_timeout_release(struct gsm_subscriber *subscr)
+{
+ DEBUGP(DPAG, "Paging timeout released for %s\n", subscr_name(subscr));
+ osmo_timer_del(&subscr->paging_timeout);
+}
+
+static void paging_timeout(void *data)
+{
+ struct gsm_subscriber *subscr = data;
+ DEBUGP(DPAG, "Paging timeout reached for %s\n", subscr_name(subscr));
+ paging_timeout_release(subscr);
+ subscr_paging_dispatch(GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED,
+ NULL, NULL, subscr);
+}
+
+static void paging_timeout_start(struct gsm_subscriber *subscr)
+{
+ DEBUGP(DPAG, "Starting paging timeout for %s\n", subscr_name(subscr));
+ subscr->paging_timeout.data = subscr;
+ subscr->paging_timeout.cb = paging_timeout;
+ osmo_timer_schedule(&subscr->paging_timeout, 10, 0);
+ /* TODO: configurable timeout duration? */
+}
+
+
static int subscr_paging_sec_cb(unsigned int hooknum, unsigned int event,
struct msgb *msg, void *data, void *param)
{
int rc;
+ struct gsm_subscriber_connection *conn = data;
+ OSMO_ASSERT(conn);
switch (event) {
case GSM_SECURITY_AUTH_FAILED:
- /* Dispatch as paging failure */
+ LOGP(DPAG, LOGL_ERROR,
+ "Dropping Paging Response:"
+ " authorization failed for subscriber %s\n",
+ subscr_name(conn->subscr));
rc = subscr_paging_dispatch(
GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED,
- msg, data, param);
+ msg, conn, conn->subscr);
break;
case GSM_SECURITY_NOAVAIL:
case GSM_SECURITY_SUCCEEDED:
- /* Dispatch as paging failure */
rc = subscr_paging_dispatch(
GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED,
- msg, data, param);
+ msg, conn, conn->subscr);
break;
default:
+ LOGP(DPAG, LOGL_FATAL,
+ "Invalid authorization event: %d\n", event);
rc = -EINVAL;
}
@@ -135,6 +182,8 @@ int subscr_rx_paging_response(struct msgb *msg,
gh = msgb_l3(msg);
pr = (struct gsm48_pag_resp *)gh->data;
+ paging_timeout_release(conn->subscr);
+
/* Secure the connection */
if (subscr_authorized(conn->subscr))
return gsm48_secure_channel(conn, pr->key_seq,
@@ -146,32 +195,45 @@ int subscr_rx_paging_response(struct msgb *msg,
return -1;
}
-struct subscr_request *subscr_request_channel(struct gsm_subscriber *subscr,
- int channel_type, gsm_cbfn *cbfn, void *param)
+static int msc_paging_request(struct gsm_subscriber *subscr)
+{
+ /* The subscriber was last seen in subscr->lac. Find out which
+ * BSCs/RNCs are responsible and send them a paging request via open
+ * SCCP connections (if any). */
+ /* TODO Implementing only RNC paging, since this is code on the iu branch.
+ * Need to add BSC paging at some point. */
+ return iu_page_cs(subscr->imsi,
+ subscr->tmsi == GSM_RESERVED_TMSI?
+ NULL : &subscr->tmsi,
+ subscr->lac);
+}
+
+struct subscr_request *subscr_request_conn(struct gsm_subscriber *subscr,
+ gsm_cbfn *cbfn, void *param)
{
int rc;
struct subscr_request *request;
/* Start paging.. we know it is async so we can do it before */
if (!subscr->is_paging) {
- LOGP(DMM, LOGL_DEBUG, "Subscriber %s not paged yet.\n",
+ LOGP(DMM, LOGL_DEBUG, "Subscriber %s not paged yet, start paging.\n",
subscr_name(subscr));
-#if 0
- TODO implement paging response in libmsc!
- Excluding this to be able to link without libbsc:
- rc = paging_request(subscr->group->net, subscr, channel_type,
- subscr_paging_cb, subscr);
-#else
- rc = -ENOTSUP;
-#endif
+ rc = msc_paging_request(subscr);
if (rc <= 0) {
LOGP(DMM, LOGL_ERROR, "Subscriber %s paging failed: %d\n",
subscr_name(subscr), rc);
return NULL;
}
- /* reduced on the first paging callback */
+ /* reduced in subscr_rx_paging_response() */
subscr_get(subscr);
subscr->is_paging = 1;
+ LOGP(DMM, LOGL_DEBUG, "Paged subscriber %s.\n",
+ subscr_name(subscr));
+ paging_timeout_start(subscr);
+ }
+ else {
+ LOGP(DMM, LOGL_DEBUG, "Subscriber %s already paged.\n",
+ subscr_name(subscr));
}
/* TODO: Stop paging in case of memory allocation failure */