/* 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 "../../bscconfig.h" #include #include #include #include #include #include #include #include #include #ifdef BUILD_IU #include #else #include #endif #include #include #include #include #include #include #include #include #include void subscr_paging_cancel(struct vlr_subscr *vsub, enum gsm_paging_event event) { subscr_paging_dispatch(GSM_HOOK_RR_PAGING, event, NULL, NULL, vsub); } int subscr_paging_dispatch(unsigned int hooknum, unsigned int event, struct msgb *msg, void *data, void *param) { struct subscr_request *request, *tmp; struct ran_conn *conn = data; struct vlr_subscr *vsub = param; struct paging_signal_data sig_data; OSMO_ASSERT(vsub); OSMO_ASSERT(hooknum == GSM_HOOK_RR_PAGING); OSMO_ASSERT(!(conn && (conn->vsub != vsub))); OSMO_ASSERT(!((event == GSM_PAGING_SUCCEEDED) && !conn)); LOGP(DPAG, LOGL_DEBUG, "Paging %s for %s (event=%d)\n", event == GSM_PAGING_SUCCEEDED ? "success" : "failure", vlr_subscr_name(vsub), event); if (!vsub->cs.is_paging) { LOGP(DPAG, LOGL_ERROR, "Paging Response received for subscriber" " that is not paging.\n"); return -EINVAL; } osmo_timer_del(&vsub->cs.paging_response_timer); if (event == GSM_PAGING_SUCCEEDED || event == GSM_PAGING_EXPIRED) msc_stop_paging(vsub); /* Inform parts of the system we don't know */ sig_data.vsub = vsub; 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, &vsub->cs.requests, entry) { llist_del(&request->entry); 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 */ vsub->cs.is_paging = false; vlr_subscr_put(vsub); return 0; } /* Execute a paging on the currently active RAN. Returns the number of * delivered paging requests or -EINVAL in case of failure. */ static int msc_paging_request(struct vlr_subscr *vsub, enum sgsap_service_ind serv_ind) { /* 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). */ switch (vsub->cs.attached_via_ran) { case OSMO_RAT_GERAN_A: return a_iface_tx_paging(vsub->imsi, vsub->tmsi, vsub->cgi.lai.lac); case OSMO_RAT_UTRAN_IU: return ranap_iu_page_cs(vsub->imsi, vsub->tmsi == GSM_RESERVED_TMSI? NULL : &vsub->tmsi, vsub->cgi.lai.lac); case OSMO_RAT_EUTRAN_SGS: return sgs_iface_tx_paging(vsub, serv_ind); default: break; } LOGP(DPAG, LOGL_ERROR, "%s: Cannot page, subscriber not attached\n", vlr_subscr_name(vsub)); return -EINVAL; } static void paging_response_timer_cb(void *data) { struct vlr_subscr *vsub = data; subscr_paging_cancel(vsub, GSM_PAGING_EXPIRED); } /*! \brief Start a paging request for vsub, call cbfn(param) when done. * \param vsub subscriber to page. * \param cbfn function to call when the conn is established. * \param param caller defined param to pass to cbfn(). * \param label human readable label of the request kind used for logging. * \param serv_ind sgsap service indicator (in case SGs interface is used to page). */ struct subscr_request *subscr_request_conn(struct vlr_subscr *vsub, gsm_cbfn *cbfn, void *param, const char *label, enum sgsap_service_ind serv_ind) { int rc; struct subscr_request *request; struct gsm_network *net = vsub->vlr->user_ctx; /* Start paging.. we know it is async so we can do it before */ if (!vsub->cs.is_paging) { LOGP(DMM, LOGL_DEBUG, "Subscriber %s not paged yet, start paging.\n", vlr_subscr_name(vsub)); rc = msc_paging_request(vsub, serv_ind); if (rc <= 0) { LOGP(DMM, LOGL_ERROR, "Subscriber %s paging failed: %d\n", vlr_subscr_name(vsub), rc); return NULL; } /* reduced on the first paging callback */ vlr_subscr_get(vsub); vsub->cs.is_paging = true; osmo_timer_setup(&vsub->cs.paging_response_timer, paging_response_timer_cb, vsub); osmo_timer_schedule(&vsub->cs.paging_response_timer, net->paging_response_timer, 0); } else { LOGP(DMM, LOGL_DEBUG, "Subscriber %s already paged.\n", vlr_subscr_name(vsub)); } /* TODO: Stop paging in case of memory allocation failure */ request = talloc_zero(vsub, struct subscr_request); if (!request) return NULL; request->cbfn = cbfn; request->param = param; llist_add_tail(&request->entry, &vsub->cs.requests); return request; } void subscr_remove_request(struct subscr_request *request) { llist_del(&request->entry); talloc_free(request); } struct ran_conn *connection_for_subscr(struct vlr_subscr *vsub) { struct gsm_network *net = vsub->vlr->user_ctx; struct ran_conn *conn; llist_for_each_entry(conn, &net->ran_conns, entry) { if (conn->vsub == vsub) return conn; } return NULL; }