diff options
author | Max <msuraev@sysmocom.de> | 2017-02-14 16:53:04 +0100 |
---|---|---|
committer | Max <msuraev@sysmocom.de> | 2017-02-16 12:29:12 +0100 |
commit | ea8b0d46ebab1ed5bf7df024a69f349249296738 (patch) | |
tree | 632ba50f88cb39e8efb32fb08d34abd347a9e19c /src/luop.c | |
parent | aa0fefd5d6a356ecde57a8e81c5d7e291765ff55 (diff) |
Move lu_operation into separate file
Create luop.(c|h) and move lu_operation and corresponding TX
functions there to facilitate re-use in upcoming control interface.
Change-Id: Ic55a45d56b37be2ba43d96f7da2af43b46af9813
Related: OS#1645
Diffstat (limited to 'src/luop.c')
-rw-r--r-- | src/luop.c | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/src/luop.c b/src/luop.c new file mode 100644 index 0000000..ecf31b4 --- /dev/null +++ b/src/luop.c @@ -0,0 +1,268 @@ +/* OsmoHLR TX/RX lu operations */ + +/* (C) 2017 sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * + * Author: Harald Welte <laforge@gnumonks.org> + * + * 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <stdbool.h> +#include <string.h> +#include <errno.h> + +#include <osmocom/core/logging.h> +#include <osmocom/gsm/gsm48_ie.h> +#include <osmocom/gsm/gsup.h> +#include <osmocom/gsm/apn.h> + +#include "gsup_server.h" +#include "gsup_router.h" +#include "logging.h" +#include "luop.h" + +const struct value_string lu_state_names[] = { + { LU_S_NULL, "NULL" }, + { LU_S_LU_RECEIVED, "LU RECEIVED" }, + { LU_S_CANCEL_SENT, "CANCEL SENT" }, + { LU_S_CANCEL_ACK_RECEIVED, "CANCEL-ACK RECEIVED" }, + { LU_S_ISD_SENT, "ISD SENT" }, + { LU_S_ISD_ACK_RECEIVED, "ISD-ACK RECEIVED" }, + { LU_S_COMPLETE, "COMPLETE" }, + { 0, NULL } +}; + +/* Transmit a given GSUP message for the given LU operation */ +static void _luop_tx_gsup(struct lu_operation *luop, + const struct osmo_gsup_message *gsup) +{ + struct msgb *msg_out; + + msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP LUOP"); + osmo_gsup_encode(msg_out, gsup); + + osmo_gsup_addr_send(luop->gsup_server, luop->peer, + talloc_total_size(luop->peer), + msg_out); +} + +static inline void fill_gsup_msg(struct osmo_gsup_message *out, + const struct lu_operation *lu, + enum osmo_gsup_message_type mt) +{ + memset(out, 0, sizeof(struct osmo_gsup_message)); + if (lu) + osmo_strlcpy(out->imsi, lu->subscr.imsi, + GSM23003_IMSI_MAX_DIGITS + 1); + out->message_type = mt; +} + +/* timer call-back in case LU operation doesn't receive an response */ +static void lu_op_timer_cb(void *data) +{ + struct lu_operation *luop = data; + + DEBUGP(DMAIN, "LU OP timer expired in state %s\n", + get_value_string(lu_state_names, luop->state)); + + switch (luop->state) { + case LU_S_CANCEL_SENT: + break; + case LU_S_ISD_SENT: + break; + default: + break; + } + + lu_op_tx_error(luop, GMM_CAUSE_NET_FAIL); +} + +bool lu_op_fill_subscr(struct lu_operation *luop, struct db_context *dbc, + const char *imsi) +{ + struct hlr_subscriber *subscr = &luop->subscr; + + if (db_subscr_get(dbc, imsi, subscr) < 0) + return false; + + return true; +} + +struct lu_operation *lu_op_alloc(struct osmo_gsup_server *srv) +{ + struct lu_operation *luop; + + luop = talloc_zero(srv, struct lu_operation); + OSMO_ASSERT(luop); + luop->gsup_server = srv; + luop->timer.cb = lu_op_timer_cb; + luop->timer.data = luop; + + return luop; +} + +struct lu_operation *lu_op_alloc_conn(struct osmo_gsup_conn *conn) +{ + uint8_t *peer_addr; + struct lu_operation *luop = lu_op_alloc(conn->server); + int rc = osmo_gsup_conn_ccm_get(conn, &peer_addr, IPAC_IDTAG_SERNR); + if (rc < 0) + return NULL; + + luop->peer = talloc_memdup(luop, peer_addr, rc); + + return luop; +} + +/* FIXME: this doesn't seem to work at all */ +struct lu_operation *lu_op_by_imsi(const char *imsi, + const struct llist_head *lst) +{ + struct lu_operation *luop; + + llist_for_each_entry(luop, lst, list) { + if (!strcmp(imsi, luop->subscr.imsi)) + return luop; + } + return NULL; +} + +void lu_op_statechg(struct lu_operation *luop, enum lu_state new_state) +{ + enum lu_state old_state = luop->state; + + DEBUGP(DMAIN, "LU OP state change: %s -> ", + get_value_string(lu_state_names, old_state)); + DEBUGPC(DMAIN, "%s\n", + get_value_string(lu_state_names, new_state)); + + luop->state = new_state; +} + +/* Send a msgb to a given address using routing */ +int osmo_gsup_addr_send(struct osmo_gsup_server *gs, + const uint8_t *addr, size_t addrlen, + struct msgb *msg) +{ + struct osmo_gsup_conn *conn; + + conn = gsup_route_find(gs, addr, addrlen); + if (!conn) { + DEBUGP(DMAIN, "Cannot find route for addr %s\n", addr); + msgb_free(msg); + return -ENODEV; + } + + return osmo_gsup_conn_send(conn, msg); +} + +/*! Transmit UPD_LOC_ERROR and destroy lu_operation */ +void lu_op_tx_error(struct lu_operation *luop, enum gsm48_gmm_cause cause) +{ + struct osmo_gsup_message gsup; + + DEBUGP(DMAIN, "%s: LU OP Tx Error (cause %s)\n", + luop->subscr.imsi, get_value_string(gsm48_gmm_cause_names, + cause)); + + fill_gsup_msg(&gsup, luop, OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR); + gsup.cause = cause; + + _luop_tx_gsup(luop, &gsup); + + llist_del(&luop->list); + talloc_free(luop); +} + +/*! Transmit UPD_LOC_RESULT and destroy lu_operation */ +void lu_op_tx_ack(struct lu_operation *luop) +{ + struct osmo_gsup_message gsup; + + fill_gsup_msg(&gsup, luop, OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT); + //FIXME gsup.hlr_enc; + + _luop_tx_gsup(luop, &gsup); + + llist_del(&luop->list); + talloc_free(luop); +} + +/*! Send Cancel Location to old VLR/SGSN */ +void lu_op_tx_cancel_old(struct lu_operation *luop) +{ + struct osmo_gsup_message gsup; + + OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED); + + fill_gsup_msg(&gsup, NULL, OSMO_GSUP_MSGT_LOCATION_CANCEL_REQUEST); + //gsup.cause = FIXME; + //gsup.cancel_type = FIXME; + + _luop_tx_gsup(luop, &gsup); + + lu_op_statechg(luop, LU_S_CANCEL_SENT); + osmo_timer_schedule(&luop->timer, CANCEL_TIMEOUT_SECS, 0); +} + +/*! Transmit Insert Subscriber Data to new VLR/SGSN */ +void lu_op_tx_insert_subscr_data(struct lu_operation *luop) +{ + struct osmo_gsup_message gsup; + uint8_t apn[APN_MAXLEN]; + uint8_t msisdn_enc[43]; /* TODO use constant; TS 24.008 10.5.4.7 */ + int l; + + OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED || + luop->state == LU_S_CANCEL_ACK_RECEIVED); + + fill_gsup_msg(&gsup, luop, OSMO_GSUP_MSGT_INSERT_DATA_REQUEST); + + l = gsm48_encode_bcd_number(msisdn_enc, sizeof(msisdn_enc), 0, + luop->subscr.msisdn); + if (l < 1) { + LOGP(DMAIN, LOGL_ERROR, + "%s: Error: cannot encode MSISDN '%s'\n", + luop->subscr.imsi, luop->subscr.msisdn); + lu_op_tx_error(luop, GMM_CAUSE_PROTO_ERR_UNSPEC); + return; + } + gsup.msisdn_enc = msisdn_enc; + gsup.msisdn_enc_len = l; + + /* FIXME: deal with encoding the following data */ + gsup.hlr_enc; + + if (luop->is_ps) { + /* FIXME: PDP infos - use more fine-grained access control + instead of wildcard APN */ + l = osmo_apn_from_str(apn, sizeof(apn), "*"); + if (l > 0) { + gsup.pdp_infos[0].apn_enc = apn; + gsup.pdp_infos[0].apn_enc_len = l; + gsup.pdp_infos[0].have_info = 1; + gsup.num_pdp_infos = 1; + /* FIXME: use real value: */ + gsup.pdp_infos[0].context_id = 1; + } + } + + /* Send ISD to new VLR/SGSN */ + _luop_tx_gsup(luop, &gsup); + + lu_op_statechg(luop, LU_S_ISD_SENT); + osmo_timer_schedule(&luop->timer, ISD_TIMEOUT_SECS, 0); +} |