/* The concept of a subscriber for the MSC, roughly HLR/VLR functionality */ /* (C) 2008 by Harald Welte * (C) 2009,2013 by Holger Hans Peter Freyther * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void *tall_sub_req_ctx; extern struct llist_head *subscr_bsc_active_subscribers(void); int gsm48_secure_channel(struct gsm_subscriber_connection *conn, int key_seq, gsm_cbfn *cb, void *cb_data); static struct gsm_subscriber *get_subscriber(struct gsm_subscriber_group *sgrp, int type, const char *ident) { struct gsm_subscriber *subscr = db_get_subscriber(type, ident); if (subscr) subscr->group = sgrp; return subscr; } /* * We got the channel assigned and can now hand this channel * over to one of our callbacks. */ static int subscr_paging_dispatch(unsigned int hooknum, unsigned int event, 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); #if BEFORE_MSCSPLIT /* * 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); #endif /* Inform parts of the system we don't know */ sig_data.subscr = subscr; sig_data.lac = conn ? conn->lac : 0; sig_data.conn = conn; sig_data.paging_result = event; osmo_signal_dispatch( SS_PAGING, event == GSM_PAGING_SUCCEEDED ? S_PAGING_SUCCEEDED : S_PAGING_EXPIRED, &sig_data ); llist_for_each_entry_safe(request, tmp, &subscr->requests, entry) { llist_del(&request->entry); request->cbfn(hooknum, event, msg, data, request->param); talloc_free(request); } /* balanced with the moment we start paging */ subscr->is_paging = 0; subscr_put(subscr); return 0; } static int subscr_paging_sec_cb(unsigned int hooknum, unsigned int event, struct msgb *msg, void *data, void *param) { int rc; switch (event) { case GSM_SECURITY_AUTH_FAILED: /* Dispatch as paging failure */ rc = subscr_paging_dispatch( GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED, msg, data, param); 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); break; default: rc = -EINVAL; } return rc; } static int subscr_paging_cb(unsigned int hooknum, unsigned int event, struct msgb *msg, void *data, void *param) { struct gsm_subscriber_connection *conn = data; struct gsm48_hdr *gh; struct gsm48_pag_resp *pr; /* Non-success implies a problem, dispatch directly */ if (event != GSM_PAGING_SUCCEEDED) return subscr_paging_dispatch(hooknum, event, msg, data, param); /* Get paging response */ 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); } int msc_paging_request(struct gsm_network *network, struct gsm_subscriber *subscr, int type, gsm_cbfn *cbfn, void *data) { /* 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) { 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, start paging.\n", subscr_name(subscr)); rc = msc_paging_request(subscr->group->net, subscr, channel_type, subscr_paging_cb, 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 */ subscr_get(subscr); subscr->is_paging = 1; LOGP(DMM, LOGL_DEBUG, "Paged subscriber %s.\n", subscr_name(subscr)); } else { LOGP(DMM, LOGL_DEBUG, "Subscriber %s already paged.\n", subscr_name(subscr)); } /* TODO: Stop paging in case of memory allocation failure */ request = talloc_zero(subscr, struct subscr_request); if (!request) return NULL; request->cbfn = cbfn; request->param = param; llist_add_tail(&request->entry, &subscr->requests); return request; } void subscr_remove_request(struct subscr_request *request) { llist_del(&request->entry); talloc_free(request); } struct gsm_subscriber *subscr_create_subscriber(struct gsm_subscriber_group *sgrp, const char *imsi) { struct gsm_subscriber *subscr = db_create_subscriber(imsi); if (subscr) subscr->group = sgrp; return subscr; } struct gsm_subscriber *subscr_get_by_tmsi(struct gsm_subscriber_group *sgrp, uint32_t tmsi) { char tmsi_string[14]; struct gsm_subscriber *subscr; /* we might have a record in memory already */ llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { if (tmsi == subscr->tmsi) return subscr_get(subscr); } sprintf(tmsi_string, "%u", tmsi); return get_subscriber(sgrp, GSM_SUBSCRIBER_TMSI, tmsi_string); } struct gsm_subscriber *subscr_get_by_imsi(struct gsm_subscriber_group *sgrp, const char *imsi) { struct gsm_subscriber *subscr; llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { if (strcmp(subscr->imsi, imsi) == 0) return subscr_get(subscr); } return get_subscriber(sgrp, GSM_SUBSCRIBER_IMSI, imsi); } struct gsm_subscriber *subscr_get_by_extension(struct gsm_subscriber_group *sgrp, const char *ext) { struct gsm_subscriber *subscr; llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { if (strcmp(subscr->extension, ext) == 0) return subscr_get(subscr); } return get_subscriber(sgrp, GSM_SUBSCRIBER_EXTENSION, ext); } struct gsm_subscriber *subscr_get_by_id(struct gsm_subscriber_group *sgrp, unsigned long long id) { struct gsm_subscriber *subscr; char buf[32]; sprintf(buf, "%llu", id); llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { if (subscr->id == id) return subscr_get(subscr); } return get_subscriber(sgrp, GSM_SUBSCRIBER_ID, buf); } int subscr_update_expire_lu(struct gsm_network *network, struct gsm_subscriber *s) { int rc; /* Table 10.5.33: The T3212 timeout value field is coded as the * binary representation of the timeout value for * periodic updating in decihours. Mark the subscriber as * inactive if it missed two consecutive location updates. * Timeout is twice the t3212 value plus one minute */ /* Is expiration handling enabled? */ if (network->t3212 == 0) s->expire_lu = GSM_SUBSCRIBER_NO_EXPIRATION; else 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_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 = network->subscr_group; /* Indicate "attached to LAC" */ s->lac = lac; LOGP(DMM, LOGL_INFO, "Subscriber %s ATTACHED LAC=%u\n", subscr_name(s), s->lac); /* * 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(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 (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); db_subscriber_update(s); osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_DETACHED, s); break; default: fprintf(stderr, "subscr_update with unknown reason: %d\n", reason); rc = db_sync_subscriber(s); db_subscriber_update(s); break; }; return rc; } void subscr_update_from_db(struct gsm_subscriber *sub) { db_subscriber_update(sub); } static void subscr_expire_callback(void *data, long long unsigned int id) { struct gsm_network *net = data; struct gsm_subscriber *s = subscr_get_by_id(net->subscr_group, id); struct gsm_subscriber_connection *conn = connection_for_subscr(s); /* * The subscriber is active and the phone stopped the timer. As * we don't want to periodically update the database for active * subscribers we will just do it when the subscriber was selected * for expiration. This way on the next around another subscriber * will be selected. */ 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(conn->network, s); subscr_put(s); return; } LOGP(DMM, LOGL_NOTICE, "Expiring inactive subscriber %s (ID %llu)\n", subscr_name(s), id); s->lac = GSM_LAC_RESERVED_DETACHED; db_sync_subscriber(s); subscr_put(s); } void subscr_expire(struct gsm_subscriber_group *sgrp) { db_subscriber_expire(sgrp->net, subscr_expire_callback); } struct gsm_subscriber_connection *msc_subscr_con_allocate(struct gsm_network *network) { struct gsm_subscriber_connection *conn; conn = talloc_zero(network, struct gsm_subscriber_connection); if (!conn) return NULL; conn->network = network; llist_add_tail(&conn->entry, &network->subscr_conns); return conn; } void msc_subscr_con_free(struct gsm_subscriber_connection *conn) { if (!conn) return; if (conn->subscr) { subscr_put(conn->subscr); conn->subscr = NULL; } llist_del(&conn->entry); talloc_free(conn); }