From 3596f92fadfe55e6262098727fffdb1edfb91a6d Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Tue, 3 May 2016 18:49:27 +0200 Subject: Major update; Code now supports SAI, LU and ISD transactions We also introduce a 'gsup_router' which enables us to route a transaction to a given VLR. It works based on the SERIAL attribute communicated at time of the IPA multiplex setup as part of the CCM sub-protocol. --- osmo-gsup-hlr/src/Makefile | 6 +- osmo-gsup-hlr/src/db.c | 5 +- osmo-gsup-hlr/src/db.h | 44 ++++- osmo-gsup-hlr/src/db_hlr.c | 136 +++++++++++++++ osmo-gsup-hlr/src/gsup_router.c | 85 ++++++++++ osmo-gsup-hlr/src/gsup_router.h | 8 + osmo-gsup-hlr/src/gsup_server.c | 63 ++++++- osmo-gsup-hlr/src/gsup_server.h | 2 + osmo-gsup-hlr/src/hlr.c | 357 +++++++++++++++++++++++++++++++++++++++- 9 files changed, 695 insertions(+), 11 deletions(-) create mode 100644 osmo-gsup-hlr/src/db_hlr.c create mode 100644 osmo-gsup-hlr/src/gsup_router.c create mode 100644 osmo-gsup-hlr/src/gsup_router.h diff --git a/osmo-gsup-hlr/src/Makefile b/osmo-gsup-hlr/src/Makefile index d7c597e..adc3e89 100644 --- a/osmo-gsup-hlr/src/Makefile +++ b/osmo-gsup-hlr/src/Makefile @@ -1,12 +1,14 @@ LDFLAGS += -losmocore -losmogsm -losmoabis -lsqlite3 -ltalloc CFLAGS += -g -Wall -OBJS = auc.o db.o db_auc.o logging.o +OBJS = auc.o db.o db_auc.o db_hlr.o logging.o + +all: db_test hlr db_test: db_test.o rand_fake.o $(OBJS) $(CC) $(LDFLAGS) -o $@ $^ -hlr: hlr.o gsup_server.o rand_urandom.o $(OBJS) +hlr: hlr.o gsup_server.o gsup_router.o rand_urandom.o $(OBJS) $(CC) $(LDFLAGS) -o $@ $^ %.o: %.c diff --git a/osmo-gsup-hlr/src/db.c b/osmo-gsup-hlr/src/db.c index b4fadca..4424307 100644 --- a/osmo-gsup-hlr/src/db.c +++ b/osmo-gsup-hlr/src/db.c @@ -25,8 +25,9 @@ #include "db.h" static const char *stmt_sql[] = { - [SEL_BY_IMSI] = "SELECT * FROM subscriber WHERE imsi = ?", - [UPD_BY_IMSI] = "UPDATE subscriber SET vlr_number = ? WHERE imsi = ?", + [SEL_BY_IMSI] = "SELECT id,imsi,msisdn,vlr_number,sgsn_number,sgsn_address,periodic_lu_tmr,periodic_rau_tau_tmr,nam_cs,nam_ps,lmsi,ms_purged_cs,ms_purged_ps FROM subscriber WHERE imsi = ?", + [UPD_VLR_BY_ID] = "UPDATE subscriber SET vlr_number = ? WHERE id = ?", + [UPD_SGSN_BY_ID] = "UPDATE subscriber SET sgsn_number = ? WHERE id = ?", [AUC_BY_IMSI] = "SELECT id, algo_id_2g, ki, algo_id_3g, k, op, opc, sqn FROM subscriber LEFT JOIN auc_2g ON auc_2g.subscriber_id = subscriber.id LEFT JOIN auc_3g ON auc_3g.subscriber_id = subscriber.id WHERE imsi = ?", [AUC_UPD_SQN] = "UPDATE auc_3g SET sqn = ? WHERE subscriber_id = ?", }; diff --git a/osmo-gsup-hlr/src/db.h b/osmo-gsup-hlr/src/db.h index f0545a4..a776099 100644 --- a/osmo-gsup-hlr/src/db.h +++ b/osmo-gsup-hlr/src/db.h @@ -1,12 +1,14 @@ #pragma once +#include #include enum stmt_idx { SEL_BY_IMSI = 0, - UPD_BY_IMSI = 1, - AUC_BY_IMSI = 2, - AUC_UPD_SQN = 3, + UPD_VLR_BY_ID = 1, + UPD_SGSN_BY_ID = 2, + AUC_BY_IMSI = 3, + AUC_UPD_SQN = 4, _NUM_STMT }; @@ -33,3 +35,39 @@ int db_update_sqn(struct db_context *dbc, uint64_t id, int db_get_auc(struct db_context *dbc, const char *imsi, struct osmo_auth_vector *vec, unsigned int num_vec, const uint8_t *rand_auts, const uint8_t *auts); + +#include +#include + +/* TODO: Get this from somewhere? */ +#define GT_MAX_DIGITS 15 + +struct hlr_subscriber { + struct llist_head list; + + uint64_t id; + char imsi[GSM23003_IMSI_MAX_DIGITS+1]; + char msisdn[GT_MAX_DIGITS+1]; + /* imeisv? */ + char vlr_number[GT_MAX_DIGITS+1]; + char sgsn_number[GT_MAX_DIGITS+1]; + char sgsn_address[GT_MAX_DIGITS+1]; + /* ggsn number + address */ + /* gmlc number */ + /* smsc number */ + uint32_t periodic_lu_timer; + uint32_t periodic_rau_tau_timer; + bool nam_cs; + bool nam_ps; + uint32_t lmsi; + bool ms_purged_cs; + bool ms_purged_ps; +}; + +int db_subscr_get(struct db_context *dbc, const char *imsi, + struct hlr_subscriber *subscr); + +int db_subscr_lu(struct db_context *dbc, + const struct hlr_subscriber *subscr, + const char *vlr_or_sgsn_number, + bool lu_is_ps); diff --git a/osmo-gsup-hlr/src/db_hlr.c b/osmo-gsup-hlr/src/db_hlr.c new file mode 100644 index 0000000..5d22717 --- /dev/null +++ b/osmo-gsup-hlr/src/db_hlr.c @@ -0,0 +1,136 @@ +/* (C) 2015 by Harald Welte + * + * 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 "logging.h" +#include "db.h" + +#define LOGHLR(imsi, level, fmt, args ...) LOGP(DAUC, level, "%s: " fmt, imsi, ## args) + +#define SL3_TXT(x, stmt, idx) do { \ + const char *_txt = (const char *) sqlite3_column_text(stmt, idx); \ + if (_txt) \ + strncpy(x, _txt, sizeof(x)); \ + x[sizeof(x)-1] = '\0'; \ + } while (0) + +int db_subscr_get(struct db_context *dbc, const char *imsi, + struct hlr_subscriber *subscr) +{ + sqlite3_stmt *stmt = dbc->stmt[SEL_BY_IMSI]; + int rc, ret = 0; + + rc = sqlite3_bind_text(stmt, 1, imsi, -1, SQLITE_STATIC); + if (rc != SQLITE_OK) { + LOGHLR(imsi, LOGL_ERROR, "Error binding IMSI: %d\n", rc); + return -1; + } + + /* execute the statement */ + rc = sqlite3_step(stmt); + if (rc != SQLITE_ROW) { + LOGHLR(imsi, LOGL_ERROR, "Error executing SQL: %d\n", rc); + ret = -2; + goto out; + } + + /* obtain the various columns */ + subscr->id = sqlite3_column_int64(stmt, 0); + SL3_TXT(subscr->imsi, stmt, 1); + SL3_TXT(subscr->msisdn, stmt, 2); + SL3_TXT(subscr->vlr_number, stmt, 3); + SL3_TXT(subscr->sgsn_number, stmt, 4); + SL3_TXT(subscr->sgsn_address, stmt, 5); + subscr->periodic_lu_timer = sqlite3_column_int(stmt, 6); + subscr->periodic_rau_tau_timer = sqlite3_column_int(stmt, 7); + subscr->nam_cs = sqlite3_column_int(stmt, 8); + subscr->nam_ps = sqlite3_column_int(stmt, 9); + subscr->lmsi = sqlite3_column_int(stmt, 10); + subscr->ms_purged_cs = sqlite3_column_int(stmt, 11); + subscr->ms_purged_ps = sqlite3_column_int(stmt, 12); + +out: + /* remove bindings and reset statement to be re-executed */ + rc = sqlite3_clear_bindings(stmt); + if (rc != SQLITE_OK) { + LOGP(DAUC, LOGL_ERROR, "Error clerearing bindings: %d\n", rc); + } + rc = sqlite3_reset(stmt); + if (rc != SQLITE_OK) { + LOGP(DAUC, LOGL_ERROR, "Error in sqlite3_reset: %d\n", rc); + } + + return ret; +} + +int db_subscr_lu(struct db_context *dbc, + const struct hlr_subscriber *subscr, + const char *vlr_or_sgsn_number, bool lu_is_ps) +{ + sqlite3_stmt *stmt = dbc->stmt[UPD_VLR_BY_ID]; + const char *txt; + int rc, ret = 0; + + if (lu_is_ps) { + stmt = dbc->stmt[UPD_SGSN_BY_ID]; + txt = subscr->sgsn_number; + } else { + stmt = dbc->stmt[UPD_VLR_BY_ID]; + txt = subscr->vlr_number; + } + + rc = sqlite3_bind_int64(stmt, 1, subscr->id); + if (rc != SQLITE_OK) { + LOGP(DAUC, LOGL_ERROR, "Error binding ID: %d\n", rc); + return -1; + } + + rc = sqlite3_bind_text(stmt, 2, txt, -1, SQLITE_STATIC); + if (rc != SQLITE_OK) { + LOGP(DAUC, LOGL_ERROR, "Error binding VLR/SGSN Number: %d\n", rc); + ret = -2; + goto out; + } + + /* execute the statement */ + rc = sqlite3_step(stmt); + if (rc != SQLITE_DONE) { + LOGP(DAUC, LOGL_ERROR, "Error updating SQN: %d\n", rc); + ret = -3; + goto out; + } +out: + /* remove bindings and reset statement to be re-executed */ + rc = sqlite3_clear_bindings(stmt); + if (rc != SQLITE_OK) { + LOGP(DAUC, LOGL_ERROR, "Error clerearing bindings: %d\n", rc); + } + rc = sqlite3_reset(stmt); + if (rc != SQLITE_OK) { + LOGP(DAUC, LOGL_ERROR, "Error in sqlite3_reset: %d\n", rc); + } + + return ret; +} diff --git a/osmo-gsup-hlr/src/gsup_router.c b/osmo-gsup-hlr/src/gsup_router.c new file mode 100644 index 0000000..e9aed78 --- /dev/null +++ b/osmo-gsup-hlr/src/gsup_router.c @@ -0,0 +1,85 @@ +/* (C) 2016 by Harald Welte + * + * 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 "gsup_server.h" + +struct gsup_route { + struct llist_head list; + + uint8_t *addr; + struct osmo_gsup_conn *conn; +}; + +/* find a route for the given address */ +struct osmo_gsup_conn *gsup_route_find(struct osmo_gsup_server *gs, + const uint8_t *addr, size_t addrlen) +{ + struct gsup_route *gr; + + llist_for_each_entry(gr, &gs->routes, list) { + if (talloc_total_size(gr->addr) == addrlen && + !memcmp(gr->addr, addr, addrlen)) + return gr->conn; + } + return NULL; +} + +/* add a new route for the given address to the given conn */ +int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addrlen) +{ + struct gsup_route *gr; + + /* Check if we already have a route for this address */ + if (gsup_route_find(conn->server, addr, addrlen)) + return -EEXIST; + + /* allocate new route and populate it */ + gr = talloc_zero(conn->server, struct gsup_route); + if (!gr) + return -ENOMEM; + + gr->addr = talloc_memdup(gr, addr, addrlen); + gr->conn = conn; + llist_add_tail(&gr->list, &conn->server->routes); + + return 0; +} + +/* delete all routes for the given connection */ +int gsup_route_del_conn(struct osmo_gsup_conn *conn) +{ + struct gsup_route *gr, *gr2; + unsigned int num_deleted = 0; + + llist_for_each_entry_safe(gr, gr2, &conn->server->routes, list) { + if (gr->conn == conn) { + llist_del(&gr->list); + talloc_free(gr); + num_deleted++; + } + } + + return num_deleted; +} diff --git a/osmo-gsup-hlr/src/gsup_router.h b/osmo-gsup-hlr/src/gsup_router.h new file mode 100644 index 0000000..7a5bd25 --- /dev/null +++ b/osmo-gsup-hlr/src/gsup_router.h @@ -0,0 +1,8 @@ +struct osmo_gsup_conn *gsup_route_find(struct osmo_gsup_server *gs, + const uint8_t *addr, size_t addrlen); + +/* add a new route for the given address to the given conn */ +int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addrlen); + +/* delete all routes for the given connection */ +int gsup_route_del_conn(struct osmo_gsup_conn *conn); diff --git a/osmo-gsup-hlr/src/gsup_server.c b/osmo-gsup-hlr/src/gsup_server.c index b9a21ab..c86a2a1 100644 --- a/osmo-gsup-hlr/src/gsup_server.c +++ b/osmo-gsup-hlr/src/gsup_server.c @@ -103,12 +103,71 @@ invalid: } +static void osmo_tlvp_dump(const struct tlv_parsed *tlvp, + int subsys, int level) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(tlvp->lv); i++) { + if (!TLVP_PRESENT(tlvp, i)) + continue; + + LOGP(subsys, level, "%u: %s\n", i, + TLVP_VAL(tlvp, i)); + LOGP(subsys, level, "%u: %s\n", i, + osmo_hexdump(TLVP_VAL(tlvp, i), + TLVP_LEN(tlvp, i))); + } +} + +/* FIXME: should this be parrt of ipas_server handling, not GSUP? */ +static void tlvp_copy(void *ctx, struct tlv_parsed *out, const struct tlv_parsed *in) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(out->lv); i++) { + if (!TLVP_PRESENT(in, i)) { + if (TLVP_PRESENT(out, i)) { + talloc_free((void *) out->lv[i].val); + out->lv[i].val = NULL; + out->lv[i].len = 0; + } + continue; + } + out->lv[i].val = talloc_memdup(ctx, in->lv[i].val, in->lv[i].len); + out->lv[i].len = in->lv[i].len; + } +} + +int osmo_gsup_conn_ccm_get(const struct osmo_gsup_conn *clnt, uint8_t **addr, + uint8_t tag) +{ + if (!TLVP_PRESENT(&clnt->ccm, tag)) + return -ENODEV; + *addr = (uint8_t *) TLVP_VAL(&clnt->ccm, tag); + + return TLVP_LEN(&clnt->ccm, tag); +} + static int osmo_gsup_server_ccm_cb(struct ipa_server_conn *conn, struct msgb *msg, struct tlv_parsed *tlvp, struct ipaccess_unit *unit) { + struct osmo_gsup_conn *clnt = (struct osmo_gsup_conn *)conn->data; + uint8_t *addr; + size_t addr_len; + LOGP(DLGSUP, LOGL_INFO, "CCM Callback\n"); - /* TODO: ? */ + + /* FIXME: should this be parrt of ipas_server handling, not + * GSUP? */ + tlvp_copy(clnt, &clnt->ccm, tlvp); + osmo_tlvp_dump(tlvp, DLGSUP, LOGL_INFO); + + addr_len = osmo_gsup_conn_ccm_get(clnt, &addr, IPAC_IDTAG_SERNR); + if (addr_len) + gsup_route_add(clnt, addr, addr_len); + return 0; } @@ -119,6 +178,7 @@ static int osmo_gsup_server_closed_cb(struct ipa_server_conn *conn) LOGP(DLGSUP, LOGL_INFO, "Lost GSUP client %s:%d\n", conn->addr, conn->port); + gsup_route_del_conn(clnt); llist_del(&clnt->list); talloc_free(clnt); @@ -176,6 +236,7 @@ osmo_gsup_server_create(void *ctx, const char *ip_addr, OSMO_ASSERT(gsups); INIT_LLIST_HEAD(&gsups->clients); + INIT_LLIST_HEAD(&gsups->routes); gsups->link = ipa_server_link_create(gsups, /* no e1inp */ NULL, diff --git a/osmo-gsup-hlr/src/gsup_server.h b/osmo-gsup-hlr/src/gsup_server.h index 390bf2e..bf1a570 100644 --- a/osmo-gsup-hlr/src/gsup_server.h +++ b/osmo-gsup-hlr/src/gsup_server.h @@ -16,6 +16,7 @@ struct osmo_gsup_server { struct ipa_server_link *link; osmo_gsup_read_cb_t read_cb; + struct llist_head routes; }; @@ -26,6 +27,7 @@ struct osmo_gsup_conn { struct osmo_gsup_server *server; struct ipa_server_conn *conn; //struct oap_state oap_state; + struct tlv_parsed ccm; }; diff --git a/osmo-gsup-hlr/src/hlr.c b/osmo-gsup-hlr/src/hlr.c index 054558e..237950d 100644 --- a/osmo-gsup-hlr/src/hlr.c +++ b/osmo-gsup-hlr/src/hlr.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -8,10 +9,15 @@ #include "db.h" #include "logging.h" #include "gsup_server.h" +#include "gsup_router.h" #include "rand.h" static struct db_context *g_dbc; +/*********************************************************************** + * Send Auth Info handling + ***********************************************************************/ + /* process an incoming SAI request */ static int rx_send_auth_info(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup) @@ -32,17 +38,350 @@ static int rx_send_auth_info(struct osmo_gsup_conn *conn, gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR; } - msg_out = msgb_alloc(1024, "GSUP response"); + msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP AUC response"); osmo_gsup_encode(msg_out, &gsup_out); return osmo_gsup_conn_send(conn, msg_out); } +/*********************************************************************** + * LU Operation State / Structure + ***********************************************************************/ + +static LLIST_HEAD(g_lu_ops); + +#define CANCEL_TIMEOUT_SECS 30 +#define ISD_TIMEOUT_SECS 30 + +enum lu_state { + LU_S_NULL, + LU_S_LU_RECEIVED, + LU_S_CANCEL_SENT, + LU_S_CANCEL_ACK_RECEIVED, + LU_S_ISD_SENT, + LU_S_ISD_ACK_RECEIVED, + LU_S_COMPLETE, +}; + +static 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 } +}; + +struct lu_operation { + /*! entry in global list of location update operations */ + struct llist_head list; + /*! to which gsup_server do we belong */ + struct osmo_gsup_server *gsup_server; + /*! state of the location update */ + enum lu_state state; + /*! CS (false) or PS (true) Location Update? */ + bool is_ps; + /*! currently running timer */ + struct osmo_timer_list timer; + + /*! subscriber related to this operation */ + struct hlr_subscriber subscr; + /*! peer VLR/SGSN starting the request */ + uint8_t *peer; +}; + +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; +} + +struct lu_operation *lu_op_by_imsi(const char *imsi) +{ + struct lu_operation *luop; + + llist_for_each_entry(luop, &g_lu_ops, list) { + if (!strcmp(imsi, luop->subscr.imsi)) + return luop; + } + return NULL; +} + +/* 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); +} + +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); +} + +/*! 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=%u)\n", + luop->subscr.imsi, cause); + + memset(&gsup, 0, sizeof(gsup)); + gsup.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR; + strncpy(&gsup.imsi, luop->subscr.imsi, sizeof(gsup.imsi)); + gsup.imsi[sizeof(gsup.imsi)-1] = '\0'; + gsup.cause = cause; + + _luop_tx_gsup(luop, &gsup); + + llist_del(&luop->list); + talloc_free(luop); +} + +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); +} + +/*! Transmit UPD_LOC_RESULT and destroy lu_operation */ +void lu_op_tx_ack(struct lu_operation *luop) +{ + struct osmo_gsup_message gsup; + + memset(&gsup, 0, sizeof(gsup)); + gsup.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT; + strncpy(gsup.imsi, luop->subscr.imsi, sizeof(gsup.imsi)-1); + //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); + + memset(&gsup, 0, sizeof(gsup)); + gsup.message_type = 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); +} + +/*! Receive Cancel Location Result from old VLR/SGSN */ +void lu_op_rx_cancel_old_ack(struct lu_operation *luop, + const struct osmo_gsup_message *gsup) +{ + OSMO_ASSERT(luop->state == LU_S_CANCEL_SENT); + /* FIXME: Check for spoofing */ + + osmo_timer_del(&luop->timer); + + /* FIXME */ + + lu_op_tx_insert_subscr_data(luop); +} + +/*! Transmit Insert Subscriber Data to new VLR/SGSN */ +void lu_op_tx_insert_subscr_data(struct lu_operation *luop) +{ + struct osmo_gsup_message gsup; + + OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED || + luop->state == LU_S_CANCEL_ACK_RECEIVED); + + memset(&gsup, 0, sizeof(gsup)); + gsup.message_type = OSMO_GSUP_MSGT_INSERT_DATA_REQUEST; + strncpy(gsup.imsi, luop->subscr.imsi, sizeof(gsup.imsi)-1); + gsup.msisdn_enc; + gsup.hlr_enc; + + if (luop->is_ps) { + /* FIXME: PDP infos */ + } + + /* 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); +} + +/*! Receive Insert Subscriber Data Result from new VLR/SGSN */ +static void lu_op_rx_insert_subscr_data_ack(struct lu_operation *luop, + const struct osmo_gsup_message *gsup) +{ + OSMO_ASSERT(luop->state == LU_S_ISD_SENT); + /* FIXME: Check for spoofing */ + + osmo_timer_del(&luop->timer); + + /* Subscriber_Present_HLR */ + /* CS only: Check_SS_required? -> MAP-FW-CHECK_SS_IND.req */ + + /* Send final ACK towards inquiring VLR/SGSN */ + lu_op_tx_ack(luop); +} + +/*! Receive GSUP message for given \ref lu_operation */ +void lu_op_rx_gsup(struct lu_operation *luop, + const struct osmo_gsup_message *gsup) +{ + switch (gsup->message_type) { + case OSMO_GSUP_MSGT_INSERT_DATA_ERROR: + /* FIXME */ + break; + case OSMO_GSUP_MSGT_INSERT_DATA_RESULT: + lu_op_rx_insert_subscr_data_ack(luop, gsup); + break; + case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR: + /* FIXME */ + break; + case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT: + lu_op_rx_cancel_old_ack(luop, gsup); + break; + default: + LOGP(DMAIN, LOGL_ERROR, "Unhandled GSUP msg_type 0x%02x\n", + gsup->message_type); + break; + } +} + +static 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; +} + +/*! Receive Update Location Request, creates new \ref lu_operation */ +static int rx_upd_loc_req(struct osmo_gsup_conn *conn, + const struct osmo_gsup_message *gsup) +{ + int rc; + bool is_ps; + struct lu_operation *luop; + struct hlr_subscriber *subscr; + uint8_t *peer_addr; + + rc = osmo_gsup_conn_ccm_get(conn, &peer_addr, IPAC_IDTAG_SERNR); + if (rc < 0) { + LOGP(DMAIN, LOGL_ERROR, "LU REQ from conn without addr?\n"); + return rc; + } + + luop = lu_op_alloc(conn->server); + luop->peer = talloc_memdup(luop, peer_addr, rc); + lu_op_statechg(luop, LU_S_LU_RECEIVED); + subscr = &luop->subscr; + if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS) + luop->is_ps = true; + llist_add(&luop->list, &g_lu_ops); + + /* Roughly follwing "Process Update_Location_HLR" of TS 09.02 */ + + /* check if subscriber is known at all */ + rc = db_subscr_get(g_dbc, gsup->imsi, subscr); + if (rc < 0) { + /* Send Error back: Subscriber Unknown in HLR */ + strcpy(luop->subscr.imsi, gsup->imsi); + lu_op_tx_error(luop, GMM_CAUSE_IMSI_UNKNOWN); + return 0; + } + + if (!is_ps && !subscr->nam_cs) { + lu_op_tx_error(luop, GMM_CAUSE_PLMN_NOTALLOWED); + return 0; + } else if (is_ps && !subscr->nam_ps) { + lu_op_tx_error(luop, GMM_CAUSE_GPRS_NOTALLOWED); + return 0; + } + + /* TODO: Set subscriber tracing = deactive in VLR/SGSN */ + +#if 0 + /* Cancel in old VLR/SGSN, if new VLR/SGSN differs from old */ + if (luop->is_ps == false && + strcmp(subscr->vlr_number, vlr_number)) { + /* FIXME: start location cancel towards old VLR */ + lu_op_tx_cancel_old(luop); + } else if (luop->is_ps == true && + strcmp(subscr->sgsn_number, sgsn_number)) { + /* FIXME: start location cancel towards old VLR */ + lu_op_tx_cancel_old(luop); + } else +#endif + { + /* TODO: Subscriber allowed to roam in PLMN? */ + /* TODO: Update RoutingInfo */ + /* TODO: Reset Flag MS Purged (cs/ps) */ + /* TODO: Control_Tracing_HLR / Control_Tracing_HLR_with_SGSN */ + lu_op_tx_insert_subscr_data(luop); + } + return 0; +} + static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg) { static struct osmo_gsup_message gsup; int rc; - rc = osmo_gsup_decode(msgb_l3(msg), msgb_l3len(msg), &gsup); + rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup); if (rc < 0) { LOGP(DMAIN, LOGL_ERROR, "error in GSUP decode: %d\n", rc); return rc; @@ -54,11 +393,23 @@ static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg) rx_send_auth_info(conn, &gsup); break; case OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST: + rx_upd_loc_req(conn, &gsup); break; /* responses to requests sent by us */ case OSMO_GSUP_MSGT_INSERT_DATA_ERROR: - break; case OSMO_GSUP_MSGT_INSERT_DATA_RESULT: + case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR: + case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT: + { + struct lu_operation *luop = lu_op_by_imsi(gsup.imsi); + if (!luop) { + LOGP(DMAIN, LOGL_ERROR, "GSUP message %u for " + "unknown IMSI %s\n", gsup.message_type, + gsup.imsi); + break; + } + lu_op_rx_gsup(luop, &gsup); + } break; default: LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %u\n", -- cgit v1.2.3