/* The concept of a subscriber for the MSC, roughly HLR/VLR functionality */ /* (C) 2008 by Harald Welte * (C) 2009 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 void *tall_sub_req_ctx; extern struct llist_head *subscr_bsc_active_subscriber(void); 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; /* back reference */ struct gsm_subscriber *subscr; /* the requested channel type */ int channel_type; /* what did we do */ int state; /* the callback data */ gsm_cbfn *cbfn; void *param; }; enum { REQ_STATE_INITIAL, REQ_STATE_QUEUED, REQ_STATE_PAGED, REQ_STATE_FAILED_START, REQ_STATE_DISPATCHED, }; /* * 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; struct gsm_subscriber_connection *conn = data; struct gsm_subscriber *subscr = param; struct paging_signal_data sig_data; /* There is no request anymore... */ if (llist_empty(&subscr->requests)) return -1; /* Dispatch signal */ sig_data.subscr = subscr; sig_data.bts = conn ? conn->bts : NULL; 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 ); /* * FIXME: What to do with paging requests coming during * this callback? We must be sure to not start paging when * we have an active connection to a subscriber and to make * the subscr_put_channel work as required... */ request = (struct subscr_request *)subscr->requests.next; request->state = REQ_STATE_DISPATCHED; llist_del(&request->entry); subscr->in_callback = 1; request->cbfn(hooknum, event, msg, data, request->param); subscr->in_callback = 0; if (event != GSM_PAGING_SUCCEEDED) { /* * This is a workaround for a bigger issue. We have * issued paging that might involve multiple BTSes * and one of them have failed now. We will stop the * other paging requests as well as the next timeout * would work on the next paging request and the queue * will do bad things. This should be fixed by counting * the outstanding results. */ paging_request_stop(NULL, subscr, NULL, NULL); subscr_put_channel(subscr); } subscr_put(subscr); talloc_free(request); 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; /* Other cases mean problem, dispatch direclty */ 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); } static void subscr_send_paging_request(struct gsm_subscriber *subscr) { struct subscr_request *request; int rc; assert(!llist_empty(&subscr->requests)); request = (struct subscr_request *)subscr->requests.next; request->state = REQ_STATE_PAGED; rc = paging_request(subscr->net, subscr, request->channel_type, subscr_paging_cb, subscr); /* paging failed, quit now */ if (rc <= 0) { request->state = REQ_STATE_FAILED_START; subscr_paging_cb(GSM_HOOK_RR_PAGING, GSM_PAGING_BUSY, NULL, NULL, subscr); } } void subscr_get_channel(struct gsm_subscriber *subscr, int type, gsm_cbfn *cbfn, void *param) { struct subscr_request *request; request = talloc(tall_sub_req_ctx, struct subscr_request); if (!request) { if (cbfn) cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_OOM, NULL, NULL, param); return; } memset(request, 0, sizeof(*request)); request->subscr = subscr_get(subscr); request->channel_type = type; request->cbfn = cbfn; request->param = param; request->state = REQ_STATE_INITIAL; /* * FIXME: We might be able to assign more than one * channel, e.g. voice and SMS submit at the same * time. */ if (!subscr->in_callback && llist_empty(&subscr->requests)) { /* add to the list, send a request */ llist_add_tail(&request->entry, &subscr->requests); subscr_send_paging_request(subscr); } else { /* this will be picked up later, from subscr_put_channel */ llist_add_tail(&request->entry, &subscr->requests); request->state = REQ_STATE_QUEUED; } } void subscr_put_channel(struct gsm_subscriber *subscr) { /* * FIXME: Continue with other requests now... by checking * the gsm_subscriber inside the gsm_lchan. Drop the ref count * of the lchan after having asked the next requestee to handle * the channel. */ /* * FIXME: is the lchan is of a different type we could still * issue an immediate assignment for another channel and then * close this one. */ /* * Currently we will drop the last ref of the lchan which * will result in a channel release on RSL and we will start * the paging. This should work most of the time as the MS * will listen to the paging requests before we timeout */ if (subscr && !llist_empty(&subscr->requests)) subscr_send_paging_request(subscr); } struct gsm_subscriber *subscr_get_by_tmsi(struct gsm_network *net, 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_subscriber(), entry) { if (tmsi == subscr->tmsi) return subscr_get(subscr); } sprintf(tmsi_string, "%u", tmsi); return db_get_subscriber(net, GSM_SUBSCRIBER_TMSI, tmsi_string); } struct gsm_subscriber *subscr_get_by_imsi(struct gsm_network *net, const char *imsi) { struct gsm_subscriber *subscr; llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) { if (strcmp(subscr->imsi, imsi) == 0) return subscr_get(subscr); } return db_get_subscriber(net, GSM_SUBSCRIBER_IMSI, imsi); } struct gsm_subscriber *subscr_get_by_extension(struct gsm_network *net, const char *ext) { struct gsm_subscriber *subscr; llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) { if (strcmp(subscr->extension, ext) == 0) return subscr_get(subscr); } return db_get_subscriber(net, GSM_SUBSCRIBER_EXTENSION, ext); } struct gsm_subscriber *subscr_get_by_id(struct gsm_network *net, unsigned long long id) { struct gsm_subscriber *subscr; char buf[32]; sprintf(buf, "%llu", id); llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) { if (subscr->id == id) return subscr_get(subscr); } return db_get_subscriber(net, GSM_SUBSCRIBER_ID, buf); } int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason) { int rc; /* FIXME: Migrate pending requests from one BSC to another */ switch (reason) { case GSM_SUBSCRIBER_UPDATE_ATTACHED: s->net = bts->network; /* Indicate "attached to LAC" */ s->lac = bts->location_area_code; LOGP(DMM, LOGL_INFO, "Subscriber %s ATTACHED LAC=%u\n", subscr_name(s), s->lac); rc = db_sync_subscriber(s); db_subscriber_update(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) 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); } int subscr_pending_requests(struct gsm_subscriber *sub) { struct subscr_request *req; int pending = 0; llist_for_each_entry(req, &sub->requests, entry) pending += 1; return pending; } int subscr_pending_clear(struct gsm_subscriber *sub) { int deleted = 0; struct subscr_request *req, *tmp; llist_for_each_entry_safe(req, tmp, &sub->requests, entry) { subscr_put(req->subscr); llist_del(&req->entry); talloc_free(req); deleted += 1; } return deleted; } int subscr_pending_dump(struct gsm_subscriber *sub, struct vty *vty) { struct subscr_request *req; vty_out(vty, "Pending Requests for Subscriber %llu.%s", sub->id, VTY_NEWLINE); llist_for_each_entry(req, &sub->requests, entry) { vty_out(vty, "Channel type: %d State: %d Sub: %llu.%s", req->channel_type, req->state, req->subscr->id, VTY_NEWLINE); } return 0; } int subscr_pending_kick(struct gsm_subscriber *sub) { subscr_put_channel(sub); return 0; }