aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeels Hofmeyr <nhofmeyr@sysmocom.de>2016-05-30 15:04:56 +0200
committerNeels Hofmeyr <nhofmeyr@sysmocom.de>2017-02-23 22:27:37 +0100
commit302093a474245166458342e96fb45b8972ec1ce0 (patch)
treea6e3e4991b4976d29ecc399e35e92019b8a841f8
parentdf2bcce119df6efc4d2eb478c14f57b34d93a52b (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 0628d1f47..601aeb259 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 d92d570de..1cd169ce7 100644
--- a/openbsc/src/libmsc/gsm_04_08.c
+++ b/openbsc/src/libmsc/gsm_04_08.c
@@ -1483,6 +1483,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)
{
@@ -1526,14 +1550,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)
@@ -3813,12 +3830,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 493304b0e..bd6b09988 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 */