diff options
Diffstat (limited to 'openbsc/src/libmsc/gsm_subscriber.c')
-rw-r--r-- | openbsc/src/libmsc/gsm_subscriber.c | 193 |
1 files changed, 134 insertions, 59 deletions
diff --git a/openbsc/src/libmsc/gsm_subscriber.c b/openbsc/src/libmsc/gsm_subscriber.c index 57c10cf7e..7b66299ec 100644 --- a/openbsc/src/libmsc/gsm_subscriber.c +++ b/openbsc/src/libmsc/gsm_subscriber.c @@ -38,6 +38,7 @@ #include <openbsc/signal.h> #include <openbsc/db.h> #include <openbsc/chan_alloc.h> +#include <openbsc/iu.h> void *tall_sub_req_ctx; @@ -47,20 +48,6 @@ int gsm48_secure_channel(struct gsm_subscriber_connection *conn, int key_seq, gsm_cbfn *cb, void *cb_data); -/* - * Struct for pending channel requests. This is managed in the - * llist_head requests of each subscriber. The reference counting - * should work in such a way that a subscriber with a pending request - * remains in memory. - */ -struct subscr_request { - struct llist_head entry; - - /* the callback data */ - gsm_cbfn *cbfn; - void *param; -}; - static struct gsm_subscriber *get_subscriber(struct gsm_subscriber_group *sgrp, int type, const char *ident) { @@ -70,31 +57,32 @@ 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)); - /* - * Stop paging on all other BTS. E.g. if this is - * the first timeout on a BTS then the others will - * timeout soon as well. Let's just stop everything - * and forget we wanted to page. - */ - paging_request_stop(NULL, subscr, NULL, NULL); + 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_NOTICE, + "Paging Response received for subscriber" + " that is not paging.\n"); + } /* 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( @@ -106,83 +94,143 @@ 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; } return rc; } -static int subscr_paging_cb(unsigned int hooknum, unsigned int event, - struct msgb *msg, void *data, void *param) +int subscr_rx_paging_response(struct msgb *msg, + struct gsm_subscriber_connection *conn) { - struct gsm_subscriber_connection *conn = data; struct gsm48_hdr *gh; struct gsm48_pag_resp *pr; - /* Other cases mean problem, dispatch direclty */ - if (event != GSM_PAGING_SUCCEEDED) - return subscr_paging_dispatch(hooknum, event, msg, data, param); - - /* Get paging response */ + /* Get key_seq from Paging Response headers */ gh = msgb_l3(msg); pr = (struct gsm48_pag_resp *)gh->data; - /* We _really_ have a channel, secure it now ! */ - return gsm48_secure_channel(conn, pr->key_seq, subscr_paging_sec_cb, param); + paging_timeout_release(conn->subscr); + + /* Secure the connection */ + if (subscr_authorized(conn->subscr)) + return gsm48_secure_channel(conn, pr->key_seq, + subscr_paging_sec_cb, NULL); + + /* Not authorized. Failure. */ + subscr_paging_sec_cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_AUTH_FAILED, + msg, conn, NULL); + return -1; +} + +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_channel(struct gsm_subscriber *subscr, - int channel_type, gsm_cbfn *cbfn, void *param) +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)); - rc = paging_request(subscr->group->net, subscr, channel_type, - subscr_paging_cb, subscr); + 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 */ @@ -268,7 +316,7 @@ struct gsm_subscriber *subscr_get_by_id(struct gsm_subscriber_group *sgrp, return get_subscriber(sgrp, GSM_SUBSCRIBER_ID, buf); } -int subscr_update_expire_lu(struct gsm_subscriber *s, struct gsm_bts *bts) +int subscr_update_expire_lu(struct gsm_network *network, struct gsm_subscriber *s) { int rc; @@ -279,27 +327,27 @@ int subscr_update_expire_lu(struct gsm_subscriber *s, struct gsm_bts *bts) * Timeout is twice the t3212 value plus one minute */ /* Is expiration handling enabled? */ - if (bts->si_common.chan_desc.t3212 == 0) + if (network->t3212 == 0) s->expire_lu = GSM_SUBSCRIBER_NO_EXPIRATION; else - s->expire_lu = time(NULL) + - (bts->si_common.chan_desc.t3212 * 60 * 6 * 2) + 60; + s->expire_lu = time(NULL) + (network->t3212 * 60 * 6 * 2) + 60; rc = db_sync_subscriber(s); db_subscriber_update(s); return rc; } -int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason) +int subscr_update(struct gsm_network *network, struct gsm_subscriber *s, + uint16_t lac, int reason) { int rc; /* FIXME: Migrate pending requests from one BSC to another */ switch (reason) { case GSM_SUBSCRIBER_UPDATE_ATTACHED: - s->group = bts->network->subscr_group; + s->group = network->subscr_group; /* Indicate "attached to LAC" */ - s->lac = bts->location_area_code; + s->lac = lac; LOGP(DMM, LOGL_INFO, "Subscriber %s ATTACHED LAC=%u\n", subscr_name(s), s->lac); @@ -308,12 +356,12 @@ int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason) * The below will set a new expire_lu but as a side-effect * the new lac will be saved in the database. */ - rc = subscr_update_expire_lu(s, bts); + rc = subscr_update_expire_lu(network, s); osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_ATTACHED, s); break; case GSM_SUBSCRIBER_UPDATE_DETACHED: /* Only detach if we are currently in this area */ - if (bts->location_area_code == s->lac) + if (lac == s->lac) s->lac = GSM_LAC_RESERVED_DETACHED; LOGP(DMM, LOGL_INFO, "Subscriber %s DETACHED\n", subscr_name(s)); rc = db_sync_subscriber(s); @@ -352,7 +400,7 @@ static void subscr_expire_callback(void *data, long long unsigned int id) if (conn && conn->expire_timer_stopped) { LOGP(DMM, LOGL_DEBUG, "Not expiring subscriber %s (ID %llu)\n", subscr_name(s), id); - subscr_update_expire_lu(s, conn->bts); + subscr_update_expire_lu(conn->network, s); subscr_put(s); return; } @@ -370,3 +418,30 @@ void subscr_expire(struct gsm_subscriber_group *sgrp) { db_subscriber_expire(sgrp->net, subscr_expire_callback); } + +bool subscr_authorized(struct gsm_subscriber *subscriber) +{ + switch (subscriber->group->net->auth_policy) { + case GSM_AUTH_POLICY_CLOSED: + LOGP(DMM, LOGL_DEBUG, "subscriber %s authorized = %d\n", + subscr_name(subscriber), subscriber->authorized); + return subscriber->authorized ? true : false; + case GSM_AUTH_POLICY_TOKEN: + if (subscriber->authorized) { + LOGP(DMM, LOGL_DEBUG, + "subscriber %s authorized = %d\n", + subscr_name(subscriber), subscriber->authorized); + return subscriber->authorized; + } + LOGP(DMM, LOGL_DEBUG, "subscriber %s first contact = %d\n", + subscr_name(subscriber), + (int)(subscriber->flags & GSM_SUBSCRIBER_FIRST_CONTACT)); + return (subscriber->flags & GSM_SUBSCRIBER_FIRST_CONTACT); + case GSM_AUTH_POLICY_ACCEPT_ALL: + return true; + default: + LOGP(DMM, LOGL_DEBUG, "unknown auth_policy, rejecting" + " subscriber %s\n", subscr_name(subscriber)); + return false; + } +} |