aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeels Hofmeyr <neels@hofmeyr.de>2019-11-08 04:49:13 +0100
committerNeels Hofmeyr <neels@hofmeyr.de>2019-11-11 05:46:27 +0100
commitadeb6e7b5fdc7c7ba1c4d4da6f37f390863ab8c9 (patch)
treeafbb71ad2a0f6192fcbf7a2847aaaf6e0dda50a9
parent0f2cfe6c7968dbaca6f9aaef0b499953dc21a3ca (diff)
add osmo_gsup_req for GSUP request->response association
-rw-r--r--configure.ac1
-rw-r--r--src/Makefile.am4
-rw-r--r--src/db.h6
-rw-r--r--src/db_hlr.c61
-rw-r--r--src/dgsm.c131
-rw-r--r--src/dgsm.h2
-rw-r--r--src/global_title.c12
-rw-r--r--src/global_title.h2
-rw-r--r--src/gsup_router.c16
-rw-r--r--src/gsup_router.h3
-rw-r--r--src/gsup_send.c17
-rw-r--r--src/gsup_server.c267
-rw-r--r--src/gsup_server.h78
-rw-r--r--src/hlr.c491
-rw-r--r--src/hlr.h4
-rw-r--r--src/hlr_ussd.c205
-rw-r--r--src/hlr_ussd.h7
-rw-r--r--src/logging.c6
-rw-r--r--src/logging.h1
-rw-r--r--src/lu_fsm.c287
-rw-r--r--src/lu_fsm.h2
-rw-r--r--src/luop.c265
-rw-r--r--src/luop.h83
-rw-r--r--src/remote_hlr.c19
-rw-r--r--src/remote_hlr.h2
-rw-r--r--tests/Makefile.am1
-rw-r--r--tests/db/db_test.c2
-rw-r--r--tests/gsup/Makefile.am43
-rw-r--r--tests/gsup/gsup_test.c89
-rw-r--r--tests/gsup/gsup_test.err2
-rw-r--r--tests/gsup/gsup_test.ok1
-rw-r--r--tests/gsup_server/Makefile.am1
-rw-r--r--tests/mslookup_manual_test/Makefile.am1
-rw-r--r--tests/testsuite.at7
34 files changed, 980 insertions, 1139 deletions
diff --git a/configure.ac b/configure.ac
index c0cf28b..370c12c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -185,7 +185,6 @@ AC_OUTPUT(
tests/auc/Makefile
tests/auc/gen_ts_55_205_test_sets/Makefile
tests/gsup_server/Makefile
- tests/gsup/Makefile
tests/db/Makefile
tests/db_upgrade/Makefile
tests/mslookup_manual_test/Makefile
diff --git a/src/Makefile.am b/src/Makefile.am
index 3485ce6..826b9b5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -28,7 +28,6 @@ noinst_HEADERS = \
auc.h \
db.h \
hlr.h \
- luop.h \
gsup_router.h \
gsup_server.h \
logging.h \
@@ -44,6 +43,7 @@ noinst_HEADERS = \
global_title.h \
mslookup_server.h \
mslookup_server_mdns.h \
+ lu_fsm.h \
$(NULL)
bin_PROGRAMS = \
@@ -56,7 +56,6 @@ osmo_hlr_SOURCES = \
auc.c \
ctrl.c \
db.c \
- luop.c \
db_auc.c \
db_hlr.c \
gsup_router.c \
@@ -75,6 +74,7 @@ osmo_hlr_SOURCES = \
mslookup_server.c \
mslookup_server_mdns.c \
global_title.c \
+ lu_fsm.c \
$(NULL)
osmo_hlr_LDADD = \
diff --git a/src/db.h b/src/db.h
index 3f7e5c4..5773474 100644
--- a/src/db.h
+++ b/src/db.h
@@ -158,14 +158,12 @@ int db_subscr_get_by_id(struct db_context *dbc, int64_t id,
int db_subscr_get_by_imei(struct db_context *dbc, const char *imei, struct hlr_subscriber *subscr);
int db_subscr_nam(struct db_context *dbc, const char *imsi, bool nam_val, bool is_ps);
int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
- const struct global_title *gsup_peer,
- const struct global_title *vlr_or_sgsn_number, bool is_ps);
+ const struct global_title *vlr_name, bool is_ps,
+ const struct global_title *via_proxy);
int db_subscr_purge(struct db_context *dbc, const char *by_imsi,
bool purge_val, bool is_ps);
-int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps);
-
/*! Call sqlite3_column_text() and copy result to a char[].
* \param[out] buf A char[] used as sizeof() arg(!) and osmo_strlcpy() target.
* \param[in] stmt An sqlite3_stmt*.
diff --git a/src/db_hlr.c b/src/db_hlr.c
index 161c0bd..1a0a4d5 100644
--- a/src/db_hlr.c
+++ b/src/db_hlr.c
@@ -37,7 +37,6 @@
#include "hlr.h"
#include "db.h"
#include "gsup_server.h"
-#include "luop.h"
#define LOGHLR(imsi, level, fmt, args ...) LOGP(DAUC, level, "IMSI='%s': " fmt, imsi, ## args)
@@ -723,8 +722,8 @@ out:
* -EIO on database errors.
*/
int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
- const struct global_title *gsup_peer,
- const struct global_title *vlr_or_sgsn_number, bool is_ps)
+ const struct global_title *vlr_name, bool is_ps,
+ const struct global_title *via_proxy)
{
sqlite3_stmt *stmt;
int rc, ret = 0;
@@ -736,13 +735,11 @@ int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
return -EIO;
- if (!db_bind_text(stmt, "$number", (char*)vlr_or_sgsn_number->val))
+ if (!db_bind_text(stmt, "$number", (char*)vlr_name->val))
return -EIO;
- /* If the VLR/SGSN is not the direct GSUP peer, the gsup_peer is a proxy towards the actual VLR/SGSN.
- * -> store the gsup_peer as proxy only when it differs from the vlr_or_sgsn_number. */
- if (global_title_cmp(gsup_peer, vlr_or_sgsn_number)) {
- if (!db_bind_text(stmt, "$proxy", (char*)gsup_peer->val))
+ if (via_proxy && via_proxy->len) {
+ if (!db_bind_text(stmt, "$proxy", (char*)via_proxy->val))
return -EIO;
} else {
if (!db_bind_null(stmt, "$proxy"))
@@ -873,51 +870,3 @@ out:
return ret;
}
-
-/*! Update nam_cs/nam_ps in the db and trigger notifications to GSUP clients.
- * \param[in,out] hlr Global hlr context.
- * \param[in] subscr Subscriber from a fresh db_subscr_get_by_*() call.
- * \param[in] nam_val True to enable CS/PS, false to disable.
- * \param[in] is_ps True to enable/disable PS, false for CS.
- * \returns 0 on success, ENOEXEC if there is no need to change, a negative
- * value on error.
- */
-int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps)
-{
- int rc;
- struct lu_operation *luop;
- struct osmo_gsup_conn *co;
- bool is_val = is_ps? subscr->nam_ps : subscr->nam_cs;
-
- if (is_val == nam_val) {
- LOGHLR(subscr->imsi, LOGL_DEBUG, "Already has the requested value when asked to %s %s\n",
- nam_val ? "enable" : "disable", is_ps ? "PS" : "CS");
- return ENOEXEC;
- }
-
- rc = db_subscr_nam(hlr->dbc, subscr->imsi, nam_val, is_ps);
- if (rc)
- return rc > 0? -rc : rc;
-
- /* If we're disabling, send a notice out to the GSUP client that is
- * responsible. Otherwise no need. */
- if (nam_val)
- return 0;
-
- /* FIXME: only send to single SGSN where latest update for IMSI came from */
- llist_for_each_entry(co, &hlr->gs->clients, list) {
- luop = lu_op_alloc_conn(co, NULL);
- if (!luop) {
- LOGHLR(subscr->imsi, LOGL_ERROR,
- "Cannot notify GSUP client, cannot allocate lu_operation,"
- " for %s:%u\n",
- co && co->conn && co->conn->server? co->conn->server->addr : "unset",
- co && co->conn && co->conn->server? co->conn->server->port : 0);
- continue;
- }
- luop->subscr = *subscr;
- lu_op_tx_del_subscr_data(luop);
- lu_op_free(luop);
- }
- return 0;
-}
diff --git a/src/dgsm.c b/src/dgsm.c
index 229485d..1bc72c4 100644
--- a/src/dgsm.c
+++ b/src/dgsm.c
@@ -7,6 +7,7 @@
#include "hlr.h"
#include "db.h"
#include "gsup_router.h"
+#include "gsup_server.h"
#include "dgsm.h"
#include "proxy.h"
#include "remote_hlr.h"
@@ -150,8 +151,7 @@ static void *dgsm_pending_messages_ctx = NULL;
struct pending_gsup_message {
struct llist_head entry;
- char imsi[GSM23003_IMSI_MAX_DIGITS+1];
- struct msgb *gsup;
+ struct osmo_gsup_req *req;
struct timeval received_at;
};
static LLIST_HEAD(pending_gsup_messages);
@@ -159,72 +159,29 @@ static LLIST_HEAD(pending_gsup_messages);
/* Defer a GSUP message until we know a remote HLR to proxy to.
* Where to send this GSUP message is indicated by its IMSI: as soon as an MS lookup has yielded the IMSI's home HLR,
* that's where the message should go. */
-static void defer_gsup_message(const struct osmo_gsup_message *gsup)
+static void defer_gsup_req(struct osmo_gsup_req *req)
{
struct pending_gsup_message *m;
m = talloc_zero(dgsm_pending_messages_ctx, struct pending_gsup_message);
OSMO_ASSERT(m);
+ m->req = req;
timestamp_update(&m->received_at);
- OSMO_STRLCPY_ARRAY(m->imsi, gsup->imsi);
-
- /* Since osmo_gsup_message has a lot of dangling external pointers, the only way to defer the message is to
- * store it encoded. */
- m->gsup = osmo_gsup_msgb_alloc("GSUP proxy defer");
- if (osmo_gsup_encode(m->gsup, gsup)) {
- LOGP(DDGSM, LOGL_ERROR, "GSUP proxy defer: unable to encode GSUP message: %s %s\n",
- osmo_gsup_message_type_name(gsup->message_type), osmo_quote_str(gsup->imsi, -1));
- msgb_free(m->gsup);
- talloc_free(m);
- return;
- }
-
llist_add_tail(&m->entry, &pending_gsup_messages);
}
/* Unable to resolve remote HLR for this IMSI, Answer with error back to the sender. */
static void defer_gsup_message_err(struct pending_gsup_message *m)
{
- struct osmo_gsup_message gsup;
- struct osmo_gsup_conn *conn;
-
- printf("whaha %p %p\n", m, m->gsup);
- if (osmo_gsup_decode(m->gsup->data, m->gsup->len, &gsup)) {
- LOGP(DDGSM, LOGL_ERROR, "Unable to decode deferred GSUP message for %s\n", m->imsi);
- msgb_free(m->gsup);
- m->gsup = NULL;
- return;
- }
-
- conn = gsup_route_find(g_hlr->gs, gsup.source_name, gsup.source_name_len);
- if (!conn) {
- LOGP(DDGSM, LOGL_ERROR, "Unable to send error reply for %s %s, source_name is not known: %s\n",
- osmo_gsup_message_type_name(gsup.message_type), gsup.imsi,
- osmo_quote_str_c(OTC_SELECT, (char*)gsup.source_name, gsup.source_name_len));
- return;
- }
- osmo_gsup_conn_send_err_reply(conn, &gsup, GMM_CAUSE_IMSI_UNKNOWN);
- msgb_free(m->gsup);
- m->gsup = NULL;
+ osmo_gsup_req_respond_err(m->req, GMM_CAUSE_IMSI_UNKNOWN, "could not reach home HLR");
+ m->req = NULL;
}
/* Forward spooled message for this IMSI to remote HLR. */
static void defer_gsup_message_send(struct pending_gsup_message *m, struct remote_hlr *remote_hlr)
{
- /* To be able to log what we are forwarding, the spooled message needs to be decoded. Do that only if debug
- * logging is enabled and decoding succeeds. If decoding fails, don't change anything about the code path, so
- * that logging doesn't affect how osmo-hlr works. */
- if (log_check_level(DDGSM, LOGL_DEBUG)) {
- struct osmo_gsup_message gsup;
- if (osmo_gsup_decode(m->gsup->data, m->gsup->len, &gsup))
- LOGP(DDGSM, LOGL_ERROR, "Unable to decode deferred GSUP message for %s\n", m->imsi);
- else
- LOGP(DDGSM, LOGL_DEBUG,
- "Forwarding deferred GSUP: %s %s from %s to " OSMO_SOCKADDR_STR_FMT "\n",
- osmo_gsup_message_type_name(gsup.message_type), gsup.imsi,
- osmo_quote_str_c(OTC_SELECT, (char*)gsup.source_name, gsup.source_name_len),
- OSMO_SOCKADDR_STR_FMT_ARGS(&remote_hlr->addr));
- }
+ LOG_GSUP_REQ(m->req, LOGL_DEBUG, "Forwarding deferred message to " OSMO_SOCKADDR_STR_FMT "\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&remote_hlr->addr));
/* If sending fails, still discard. */
if (!remote_hlr->gsupc || !remote_hlr->gsupc->is_connected) {
@@ -234,7 +191,10 @@ static void defer_gsup_message_send(struct pending_gsup_message *m, struct remot
return;
}
- remote_hlr_msgb_send(remote_hlr, m->gsup);
+ remote_hlr_msgb_send(remote_hlr, m->req->msg);
+ m->req->msg = NULL;
+ osmo_gsup_req_free(m->req);
+ m->req = NULL;
}
/* Result of looking for remote HLR. If it failed, pass remote_hlr as NULL. */
@@ -249,7 +209,7 @@ static void defer_gsup_message_pop(const char *imsi, struct remote_hlr *remote_h
LOG_DGSM(imsi, LOGL_ERROR, "No remote HLR found, dropping spooled GSUP messages\n");
llist_for_each_entry_safe(m, n, &pending_gsup_messages, entry) {
- if (strcmp(m->imsi, imsi))
+ if (strcmp(m->req->gsup.imsi, imsi))
continue;
if (!remote_hlr)
@@ -262,40 +222,36 @@ static void defer_gsup_message_pop(const char *imsi, struct remote_hlr *remote_h
}
}
-void dgsm_send_to_remote_hlr(const struct proxy_subscr *proxy_subscr, const struct osmo_gsup_message *gsup)
+void dgsm_send_to_remote_hlr(const struct proxy_subscr *proxy_subscr, struct osmo_gsup_req *req)
{
struct remote_hlr *remote_hlr;
if (!osmo_sockaddr_str_is_nonzero(&proxy_subscr->remote_hlr_addr)) {
/* We don't know the remote target yet. Still waiting for an MS lookup response. */
- LOG_DGSM(gsup->imsi, LOGL_DEBUG, "GSUP Proxy: deferring until remote proxy is known: %s\n",
- osmo_gsup_message_type_name(gsup->message_type));
- defer_gsup_message(gsup);
+ LOG_GSUP_REQ(req, LOGL_DEBUG, "Proxy: deferring until remote HLR is known\n");
+ defer_gsup_req(req);
return;
}
- LOG_DGSM(gsup->imsi, LOGL_DEBUG, "GSUP Proxy: forwarding to " OSMO_SOCKADDR_STR_FMT ": %s\n",
- OSMO_SOCKADDR_STR_FMT_ARGS(&proxy_subscr->remote_hlr_addr),
- osmo_gsup_message_type_name(gsup->message_type));
+ LOG_GSUP_REQ(req, LOGL_DEBUG, "Proxy: forwarding to " OSMO_SOCKADDR_STR_FMT "\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&proxy_subscr->remote_hlr_addr));
remote_hlr = remote_hlr_get(&proxy_subscr->remote_hlr_addr, true);
if (!remote_hlr) {
- LOG_DGSM(gsup->imsi, LOGL_ERROR, "Failed to establish connection to remote HLR " OSMO_SOCKADDR_STR_FMT
- ", discarding GSUP: %s\n",
- OSMO_SOCKADDR_STR_FMT_ARGS(&proxy_subscr->remote_hlr_addr),
- osmo_gsup_message_type_name(gsup->message_type));
+ osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL,
+ "Proxy: Failed to establish connection to remote HLR " OSMO_SOCKADDR_STR_FMT,
+ OSMO_SOCKADDR_STR_FMT_ARGS(&proxy_subscr->remote_hlr_addr));
return;
}
if (!remote_hlr->gsupc || !remote_hlr->gsupc->is_connected) {
/* GSUP link is still busy establishing... */
- LOG_DGSM(gsup->imsi, LOGL_DEBUG, "GSUP Proxy: deferring until link to remote proxy is up: %s\n",
- osmo_gsup_message_type_name(gsup->message_type));
- defer_gsup_message(gsup);
+ LOG_GSUP_REQ(req, LOGL_DEBUG, "Proxy: deferring until link to remote HLR is up\n");
+ defer_gsup_req(req);
return;
}
- remote_hlr_gsup_send(remote_hlr, gsup);
+ remote_hlr_gsup_send(remote_hlr, req);
}
static void resolve_hlr_result_cb(struct osmo_mslookup_client *client,
@@ -392,34 +348,21 @@ void dgsm_remote_hlr_up(struct remote_hlr *remote_hlr)
}
/* Return true when the message has been handled by D-GSM. */
-bool dgsm_check_forward_gsup_msg(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup)
+bool dgsm_check_forward_gsup_msg(struct osmo_gsup_req *req)
{
const struct proxy_subscr *proxy_subscr;
struct proxy_subscr proxy_subscr_new;
- struct gsup_route *route;
- struct osmo_gsup_message gsup_copy;
struct proxy *proxy = g_hlr->proxy;
struct osmo_mslookup_query query;
struct osmo_mslookup_query_handling handling;
uint32_t request_handle;
- /* To forward to a remote HLR, we need to indicate the source MSC's name to make sure the reply can be routed
- * back. Store the sender MSC in gsup->source_name -- the remote HLR is required to return this as
- * gsup->destination_name so that the reply gets routed to the original MSC. */
- route = gsup_route_find_by_conn(conn);
- if (!route) {
- /* The conn has not sent its IPA unit name yet, and hence we won't be able to proxy responses back from
- * a remote HLR. Send GSUP error and indicate that this message has been handled. */
- osmo_gsup_conn_send_err_reply(conn, gsup, GMM_CAUSE_NET_FAIL);
- return true;
- }
-
/* If the IMSI is known in the local HLR, then we won't proxy. */
- if (db_subscr_exists_by_imsi(g_hlr->dbc, gsup->imsi) == 0)
+ if (db_subscr_exists_by_imsi(g_hlr->dbc, req->gsup.imsi) == 0)
return false;
/* Are we already forwarding this IMSI to a remote HLR? */
- proxy_subscr = proxy_subscr_get_by_imsi(proxy, gsup->imsi);
+ proxy_subscr = proxy_subscr_get_by_imsi(proxy, req->gsup.imsi);
if (proxy_subscr)
goto yes_we_are_proxying;
@@ -429,8 +372,7 @@ bool dgsm_check_forward_gsup_msg(struct osmo_gsup_conn *conn, const struct osmo_
/* Kick off an mslookup for the remote HLR. */
if (!g_hlr->mslookup.client.client) {
- LOGP(DDGSM, LOGL_DEBUG, "mslookup client not running, cannot query remote home HLR for %s\n",
- gsup->imsi);
+ LOG_GSUP_REQ(req, LOGL_DEBUG, "mslookup client not running, cannot query remote home HLR\n");
return false;
}
@@ -439,7 +381,7 @@ bool dgsm_check_forward_gsup_msg(struct osmo_gsup_conn *conn, const struct osmo_
.type = OSMO_MSLOOKUP_ID_IMSI,
}
};
- OSMO_STRLCPY_ARRAY(query.id.imsi, gsup->imsi);
+ OSMO_STRLCPY_ARRAY(query.id.imsi, req->gsup.imsi);
OSMO_STRLCPY_ARRAY(query.service, OSMO_MSLOOKUP_SERVICE_HLR_GSUP);
handling = (struct osmo_mslookup_query_handling){
.result_timeout_milliseconds = g_hlr->mslookup.client.result_timeout_milliseconds,
@@ -447,31 +389,24 @@ bool dgsm_check_forward_gsup_msg(struct osmo_gsup_conn *conn, const struct osmo_
};
request_handle = osmo_mslookup_client_request(g_hlr->mslookup.client.client, &query, &handling);
if (!request_handle) {
- LOG_DGSM(gsup->imsi, LOGL_ERROR, "Error dispatching mslookup query for home HLR: %s\n",
+ LOG_DGSM(req->gsup.imsi, LOGL_ERROR, "Error dispatching mslookup query for home HLR: %s\n",
osmo_mslookup_result_name_c(OTC_SELECT, &query, NULL));
- proxy_subscr_del(proxy, gsup->imsi);
+ proxy_subscr_del(proxy, req->gsup.imsi);
return false;
}
/* Add a proxy entry without a remote address to indicate that we are busy querying for a remote HLR. */
proxy_subscr_new = (struct proxy_subscr){};
- OSMO_STRLCPY_ARRAY(proxy_subscr_new.imsi, gsup->imsi);
+ OSMO_STRLCPY_ARRAY(proxy_subscr_new.imsi, req->gsup.imsi);
proxy_subscr = &proxy_subscr_new;
proxy_subscr_update(proxy, proxy_subscr);
yes_we_are_proxying:
OSMO_ASSERT(proxy_subscr);
- /* Be aware that osmo_gsup_message has a lot of external pointer references, so this is not a deep copy.
- * dgsm_send_to_remote_hlr() may need to defer the GSUP message until the remote HLR has answered, in which case
- * it will take care to deep copy (encode into a msgb), i.e. it is fine to pass a shallow copy. */
- gsup_copy = *gsup;
- gsup_copy.source_name = route->addr;
- gsup_copy.source_name_len = talloc_total_size(route->addr);
-
/* If the remote HLR is already known, directly forward the GSUP message; otherwise, spool the GSUP message
* until the remote HLR will respond / until timeout aborts. */
- dgsm_send_to_remote_hlr(proxy_subscr, &gsup_copy);
+ dgsm_send_to_remote_hlr(proxy_subscr, req);
return true;
}
diff --git a/src/dgsm.h b/src/dgsm.h
index 972d598..e3130e8 100644
--- a/src/dgsm.h
+++ b/src/dgsm.h
@@ -68,7 +68,7 @@ struct dgsm_msc_config *dgsm_config_msc_get(const struct global_title *msc_name,
void dgsm_init(void *ctx);
void dgsm_start(void *ctx);
-bool dgsm_check_forward_gsup_msg(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup);
+bool dgsm_check_forward_gsup_msg(struct osmo_gsup_req *req);
void dgsm_vty_init();
diff --git a/src/global_title.c b/src/global_title.c
index 10fbb11..0a6c439 100644
--- a/src/global_title.c
+++ b/src/global_title.c
@@ -16,9 +16,17 @@ int global_title_set(struct global_title *gt, const uint8_t *val, size_t len)
return 0;
}
-int global_title_set_str(struct global_title *gt, const char *str)
+int global_title_set_str(struct global_title *gt, const char *str_fmt, ...)
{
- return global_title_set(gt, (const uint8_t*)str, str ? strlen(str)+1 : 0);
+ va_list ap;
+ if (!str_fmt)
+ return global_title_set(gt, NULL, 0);
+
+ va_start(ap, str_fmt);
+ vsnprintf((char*)(gt->val), sizeof(gt->val), str_fmt, ap);
+ va_end(ap);
+ gt->len = strlen((char*)(gt->val))+1;
+ return 0;
}
int global_title_cmp(const struct global_title *a, const struct global_title *b)
diff --git a/src/global_title.h b/src/global_title.h
index 54e23a3..a2ab6de 100644
--- a/src/global_title.h
+++ b/src/global_title.h
@@ -12,6 +12,6 @@ struct global_title {
};
int global_title_set(struct global_title *gt, const uint8_t *val, size_t len);
-int global_title_set_str(struct global_title *gt, const char *str);
+int global_title_set_str(struct global_title *gt, const char *str_fmt, ...);
int global_title_cmp(const struct global_title *a, const struct global_title *b);
const char *global_title_name(const struct global_title *gt);
diff --git a/src/gsup_router.c b/src/gsup_router.c
index 7794320..727b97e 100644
--- a/src/gsup_router.c
+++ b/src/gsup_router.c
@@ -72,10 +72,15 @@ struct gsup_route *gsup_route_find_by_conn(const struct osmo_gsup_conn *conn)
int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addrlen)
{
struct gsup_route *gr;
+ struct osmo_gsup_conn *exists_on_conn;
/* Check if we already have a route for this address */
- if (gsup_route_find(conn->server, addr, addrlen))
- return -EEXIST;
+ exists_on_conn = gsup_route_find(conn->server, addr, addrlen);
+ if (exists_on_conn) {
+ if (exists_on_conn != conn)
+ return -EEXIST;
+ return 0;
+ }
/* allocate new route and populate it */
gr = talloc_zero(conn->server, struct gsup_route);
@@ -91,6 +96,11 @@ int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addr
return 0;
}
+int gsup_route_add_gt(struct osmo_gsup_conn *conn, const struct global_title *gt)
+{
+ return gsup_route_add(conn, gt->val, gt->len);
+}
+
/* delete all routes for the given connection */
int gsup_route_del_conn(struct osmo_gsup_conn *conn)
{
@@ -100,7 +110,7 @@ int gsup_route_del_conn(struct osmo_gsup_conn *conn)
llist_for_each_entry_safe(gr, gr2, &conn->server->routes, list) {
if (gr->conn == conn) {
LOGP(DMAIN, LOGL_INFO, "Removing GSUP route for %s (GSUP disconnect)\n",
- gr->addr);
+ osmo_quote_str_c(OTC_SELECT, (char*)gr->addr, talloc_total_size(gr->addr)));
llist_del(&gr->list);
talloc_free(gr);
num_deleted++;
diff --git a/src/gsup_router.h b/src/gsup_router.h
index 0eb3ba9..380a1fb 100644
--- a/src/gsup_router.h
+++ b/src/gsup_router.h
@@ -18,6 +18,7 @@ struct osmo_gsup_conn *gsup_route_find_gt(struct osmo_gsup_server *gs, const str
struct gsup_route *gsup_route_find_by_conn(const struct osmo_gsup_conn *conn);
/* add a new route for the given address to the given conn */
+int gsup_route_add_gt(struct osmo_gsup_conn *conn, const struct global_title *gt);
int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addrlen);
/* delete all routes for the given connection */
@@ -27,3 +28,5 @@ int osmo_gsup_addr_send(struct osmo_gsup_server *gs,
const uint8_t *addr, size_t addrlen,
struct msgb *msg);
int osmo_gsup_gt_send(struct osmo_gsup_server *gs, const struct global_title *gt, struct msgb *msg);
+int osmo_gsup_gt_enc_send(struct osmo_gsup_server *gs, const struct global_title *gt,
+ const struct osmo_gsup_message *gsup);
diff --git a/src/gsup_send.c b/src/gsup_send.c
index f211639..1c66704 100644
--- a/src/gsup_send.c
+++ b/src/gsup_send.c
@@ -72,3 +72,20 @@ int osmo_gsup_gt_send(struct osmo_gsup_server *gs, const struct global_title *gt
}
return osmo_gsup_addr_send(gs, gt->val, gt->len, msg);
}
+
+int osmo_gsup_gt_enc_send(struct osmo_gsup_server *gs, const struct global_title *gt,
+ const struct osmo_gsup_message *gsup)
+{
+ struct msgb *msg = osmo_gsup_msgb_alloc("GSUP Tx");
+ int rc;
+ rc = osmo_gsup_encode(msg, gsup);
+ if (rc) {
+ LOGP(DLGSUP, LOGL_ERROR, "IMSI-%s: Cannot encode GSUP: %s\n",
+ gsup->imsi, osmo_gsup_message_type_name(gsup->message_type));
+ msgb_free(msg);
+ return -EINVAL;
+ }
+
+ LOGP(DLGSUP, LOGL_DEBUG, "IMSI-%s: Tx: %s\n", gsup->imsi, osmo_gsup_message_type_name(gsup->message_type));
+ return osmo_gsup_gt_send(gs, gt, msg);
+}
diff --git a/src/gsup_server.c b/src/gsup_server.c
index f10034b..caeacb8 100644
--- a/src/gsup_server.c
+++ b/src/gsup_server.c
@@ -26,7 +26,9 @@
#include <osmocom/abis/ipaccess.h>
#include <osmocom/gsm/gsm48_ie.h>
#include <osmocom/gsm/apn.h>
+#include <osmocom/gsm/gsm23003.h>
+#include "hlr.h"
#include "gsup_server.h"
#include "gsup_router.h"
@@ -57,6 +59,180 @@ int osmo_gsup_conn_send(struct osmo_gsup_conn *conn, struct msgb *msg)
return 0;
}
+/* Only for requests originating here. When answering to a remote request, rather use osmo_gsup_req_respond() or
+ * osmo_gsup_req_respond_err(). */
+int osmo_gsup_conn_enc_send(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup)
+{
+ struct msgb *msg = osmo_gsup_msgb_alloc("GSUP Tx");
+ int rc;
+ rc = osmo_gsup_encode(msg, gsup);
+ if (rc) {
+ LOG_GSUP_CONN(conn, LOGL_ERROR, "Cannot encode GSUP: %s\n",
+ osmo_gsup_message_type_name(gsup->message_type));
+ msgb_free(msg);
+ return -EINVAL;
+ }
+
+ LOG_GSUP_CONN(conn, LOGL_DEBUG, "Tx: %s\n", osmo_gsup_message_type_name(gsup->message_type));
+ rc = osmo_gsup_conn_send(conn, msg);
+ if (rc)
+ LOG_GSUP_CONN(conn, LOGL_ERROR, "Unable to send: %s\n",
+ osmo_gsup_message_type_name(gsup->message_type));
+ return rc;
+}
+
+struct osmo_gsup_req *osmo_gsup_req_new(struct osmo_gsup_conn *conn, struct msgb *msg)
+{
+ static unsigned int next_req_nr = 1;
+ struct osmo_gsup_req *req;
+ struct osmo_gsup_message gsup_err;
+ int rc;
+
+ if (!msgb_l2(msg) || !msgb_l2len(msg)) {
+ LOGP(DLGSUP, LOGL_ERROR, "Rx GSUP message: missing or empty L2 data\n");
+ msgb_free(msg);
+ return NULL;
+ }
+
+ req = talloc_zero(conn->server, struct osmo_gsup_req);
+ OSMO_ASSERT(req);
+ req->nr = next_req_nr++;
+ req->server = conn->server;
+ req->msg = msg;
+ req->source_name = conn->peer_name;
+
+ rc = osmo_gsup_decode(msgb_l2(req->msg), msgb_l2len(req->msg), (struct osmo_gsup_message*)&req->gsup);
+ if (rc < 0) {
+ LOGP(DLGSUP, LOGL_ERROR, "Rx GSUP message: cannot decode (rc=%d)\n", rc);
+ osmo_gsup_req_free(req);
+ return NULL;
+ }
+
+ LOG_GSUP_REQ(req, LOGL_DEBUG, "new\n");
+
+ if (req->gsup.source_name_len) {
+ if (global_title_set(&req->source_name, req->gsup.source_name, req->gsup.source_name_len)) {
+ LOGP(DLGSUP, LOGL_ERROR, "cannot decode GSUP message's source_name, message is not routable\n");
+ goto unroutable_error;
+ }
+
+ if (global_title_cmp(&req->source_name, &conn->peer_name)) {
+ /* The source of the GSUP message is not the immediate GSUP peer, but that peer is our proxy for that
+ * source. Add it to the routes for this conn (so we can route responses back). */
+ if (gsup_route_add_gt(conn, &req->source_name)) {
+ LOGP(DLGSUP, LOGL_ERROR,
+ "GSUP message received from %s via peer %s, but there already exists a different"
+ " route to this source, message is not routable\n",
+ global_title_name(&req->source_name),
+ global_title_name(&conn->peer_name));
+ goto unroutable_error;
+ }
+
+ req->via_proxy = conn->peer_name;
+ }
+ }
+
+ if (!osmo_imsi_str_valid(req->gsup.imsi)) {
+ osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "invalid IMSI: %s",
+ osmo_quote_str(req->gsup.imsi, -1));
+ return NULL;
+ }
+
+ return req;
+
+unroutable_error:
+ gsup_err = (struct osmo_gsup_message){
+ .message_type = OSMO_GSUP_MSGT_E_ROUTING_ERROR,
+ .destination_name = req->gsup.destination_name,
+ .destination_name_len = req->gsup.destination_name_len,
+ .source_name = req->gsup.source_name,
+ .source_name_len = req->gsup.source_name_len,
+ };
+ osmo_gsup_set_reply(&req->gsup, &gsup_err);
+ osmo_gsup_conn_enc_send(conn, &gsup_err);
+ osmo_gsup_req_free(req);
+ return NULL;
+}
+
+void _osmo_gsup_req_free(struct osmo_gsup_req *req)
+{
+ if (req->msg)
+ msgb_free(req->msg);
+ talloc_free(req);
+}
+
+int osmo_gsup_req_respond_nonfinal(struct osmo_gsup_req *req, struct osmo_gsup_message *response)
+{
+ struct osmo_gsup_conn *conn;
+ struct msgb *msg;
+ int rc;
+
+ conn = gsup_route_find_gt(req->server, &req->source_name);
+ if (!conn) {
+ LOG_GSUP_REQ(req, LOGL_ERROR, "GSUP client that sent this request was disconnected, cannot respond\n");
+ return -EINVAL;
+ }
+
+ osmo_gsup_set_reply(&req->gsup, response);
+
+ msg = osmo_gsup_msgb_alloc("GSUP response");
+ rc = osmo_gsup_encode(msg, response);
+ if (rc) {
+ LOG_GSUP_REQ(req, LOGL_ERROR, "Unable to encode response: %s\n",
+ osmo_gsup_message_type_name(response->message_type));
+ return -EINVAL;
+ }
+
+ LOG_GSUP_REQ(req, LOGL_DEBUG, "Tx response: %s\n", osmo_gsup_message_type_name(response->message_type));
+
+ rc = osmo_gsup_conn_send(conn, msg);
+ if (rc)
+ LOG_GSUP_REQ(req, LOGL_ERROR, "Unable to send response: %s\n",
+ osmo_gsup_message_type_name(response->message_type));
+ return rc;
+}
+
+/* Make sure the response message contains all IEs that are required to be a valid response for the received GSUP
+ * request, and send back to the requesting peer. */
+int osmo_gsup_req_respond(struct osmo_gsup_req *req, struct osmo_gsup_message *response)
+{
+ int rc = osmo_gsup_req_respond_nonfinal(req, response);
+ osmo_gsup_req_free(req);
+ return rc;
+}
+
+int osmo_gsup_req_respond_msgt(struct osmo_gsup_req *req, enum osmo_gsup_message_type message_type)
+{
+ struct osmo_gsup_message response = {
+ .message_type = message_type,
+ };
+ return osmo_gsup_req_respond(req, &response);
+}
+
+#define osmo_gsup_req_respond_err(REQ, CAUSE, FMT, args...) do { \
+ LOG_GSUP_REQ(REQ, LOGL_ERROR, "%s: " FMT "\n", \
+ get_value_string(gsm48_gmm_cause_names, CAUSE), ##args); \
+ _osmo_gsup_req_respond_err(REQ, CAUSE); \
+ } while(0)
+void _osmo_gsup_req_respond_err(struct osmo_gsup_req *req, enum gsm48_gmm_cause cause)
+{
+ struct osmo_gsup_message response = {
+ .cause = cause,
+ .message_type = OSMO_GSUP_TO_MSGT_ERROR(req->gsup.message_type),
+ };
+
+ /* No need to answer if we couldn't parse an ERROR message type, only REQUESTs need an error reply. */
+ if (!OSMO_GSUP_IS_MSGT_REQUEST(req->gsup.message_type)) {
+ osmo_gsup_req_free(req);
+ return;
+ }
+
+ if (req->gsup.session_state != OSMO_GSUP_SESSION_STATE_NONE)
+ response.session_state = OSMO_GSUP_SESSION_STATE_END;
+
+ osmo_gsup_req_respond(req, &response);
+}
+
/* Encode an error reponse to the given GSUP message with the given cause.
* Determine the error message type via OSMO_GSUP_TO_MSGT_ERROR(gsup_orig->message_type).
* Only send an error response if the original message is a Request message.
@@ -251,10 +427,18 @@ static int osmo_gsup_server_ccm_cb(struct ipa_server_conn *conn,
return -EINVAL;
}
- gsup_route_add(clnt, addr, addr_len);
+ global_title_set(&clnt->peer_name, addr, addr_len);
+ gsup_route_add_gt(clnt, &clnt->peer_name);
return 0;
}
+static void osmo_gsup_conn_free(struct osmo_gsup_conn *conn)
+{
+ gsup_route_del_conn(conn);
+ llist_del(&conn->list);
+ talloc_free(conn);
+}
+
static int osmo_gsup_server_closed_cb(struct ipa_server_conn *conn)
{
struct osmo_gsup_conn *clnt = (struct osmo_gsup_conn *)conn->data;
@@ -262,10 +446,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);
-
+ osmo_gsup_conn_free(clnt);
return 0;
}
@@ -347,8 +528,7 @@ failed:
struct osmo_gsup_server *
osmo_gsup_server_create(void *ctx, const char *ip_addr, uint16_t tcp_port,
- osmo_gsup_read_cb_t read_cb,
- struct llist_head *lu_op_lst, void *priv)
+ osmo_gsup_read_cb_t read_cb, void *priv)
{
struct osmo_gsup_server *gsups;
int rc;
@@ -374,8 +554,6 @@ osmo_gsup_server_create(void *ctx, const char *ip_addr, uint16_t tcp_port,
if (rc < 0)
goto failed;
- gsups->luop = lu_op_lst;
-
return gsups;
failed:
@@ -416,6 +594,7 @@ int osmo_gsup_configure_wildcard_apn(struct osmo_gsup_message *gsup,
return 0;
}
+
/**
* Populate a gsup message structure with an Insert Subscriber Data Message.
* All required memory buffers for data pointed to by pointers in struct omso_gsup_message
@@ -432,39 +611,41 @@ int osmo_gsup_configure_wildcard_apn(struct osmo_gsup_message *gsup,
* \returns 0 on success, and negative on error.
*/
int osmo_gsup_create_insert_subscriber_data_msg(struct osmo_gsup_message *gsup, const char *imsi, const char *msisdn,
- uint8_t *msisdn_enc, size_t msisdn_enc_size,
- uint8_t *apn_buf, size_t apn_buf_size,
- enum osmo_gsup_cn_domain cn_domain)
+ uint8_t *msisdn_enc, size_t msisdn_enc_size,
+ uint8_t *apn_buf, size_t apn_buf_size,
+ enum osmo_gsup_cn_domain cn_domain)
{
- int len;
-
- OSMO_ASSERT(gsup);
-
- gsup->message_type = OSMO_GSUP_MSGT_INSERT_DATA_REQUEST;
- osmo_strlcpy(gsup->imsi, imsi, sizeof(gsup->imsi));
-
- if (msisdn_enc_size < OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN)
- return -ENOSPC;
-
- OSMO_ASSERT(msisdn_enc);
- len = gsm48_encode_bcd_number(msisdn_enc, msisdn_enc_size, 0, msisdn);
- if (len < 1) {
- LOGP(DLGSUP, LOGL_ERROR, "%s: Error: cannot encode MSISDN '%s'\n", imsi, msisdn);
- return -ENOSPC;
- }
- gsup->msisdn_enc = msisdn_enc;
- gsup->msisdn_enc_len = len;
-
- #pragma message "FIXME: deal with encoding the following data: gsup.hlr_enc"
-
- gsup->cn_domain = cn_domain;
- if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS) {
- OSMO_ASSERT(apn_buf_size >= APN_MAXLEN);
- OSMO_ASSERT(apn_buf);
- /* FIXME: PDP infos - use more fine-grained access control
- instead of wildcard APN */
- osmo_gsup_configure_wildcard_apn(gsup, apn_buf, apn_buf_size);
- }
-
- return 0;
+ int len;
+
+ OSMO_ASSERT(gsup);
+ *gsup = (struct osmo_gsup_message){
+ .message_type = OSMO_GSUP_MSGT_INSERT_DATA_REQUEST,
+ };
+
+ osmo_strlcpy(gsup->imsi, imsi, sizeof(gsup->imsi));
+
+ if (msisdn_enc_size < OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN)
+ return -ENOSPC;
+
+ OSMO_ASSERT(msisdn_enc);
+ len = gsm48_encode_bcd_number(msisdn_enc, msisdn_enc_size, 0, msisdn);
+ if (len < 1) {
+ LOGP(DLGSUP, LOGL_ERROR, "%s: Error: cannot encode MSISDN '%s'\n", imsi, msisdn);
+ return -ENOSPC;
+ }
+ gsup->msisdn_enc = msisdn_enc;
+ gsup->msisdn_enc_len = len;
+
+ #pragma message "FIXME: deal with encoding the following data: gsup.hlr_enc"
+
+ gsup->cn_domain = cn_domain;
+ if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS) {
+ OSMO_ASSERT(apn_buf_size >= APN_MAXLEN);
+ OSMO_ASSERT(apn_buf);
+ /* FIXME: PDP infos - use more fine-grained access control
+ instead of wildcard APN */
+ osmo_gsup_configure_wildcard_apn(gsup, apn_buf, apn_buf_size);
+ }
+
+ return 0;
}
diff --git a/src/gsup_server.h b/src/gsup_server.h
index b2f94a7..49f90f4 100644
--- a/src/gsup_server.h
+++ b/src/gsup_server.h
@@ -2,25 +2,41 @@
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/msgb.h>
+#include <osmocom/core/socket.h>
#include <osmocom/abis/ipa.h>
#include <osmocom/abis/ipaccess.h>
#include <osmocom/gsm/gsup.h>
+#include "global_title.h"
#ifndef OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN
#define OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN 43 /* TS 24.008 10.5.4.7 */
#endif
-#define LOG_GSUPCONN(conn, level, fmt, args...) \
+#define LOG_GSUP_CONN(conn, level, fmt, args...) \
LOGP(DLGSUP, level, "GSUP from %s: " fmt, \
conn && conn->server && conn->server->link ? \
osmo_sock_get_name2_c(OTC_SELECT, conn->server->link->ofd.fd) \
: "?", \
##args)
-#define LOG_GSUPCONN_MSG(conn, gsup_msg, level, fmt, args...) \
- LOG_GSUPCONN(conn, level, "IMSI-%s %s: " fmt, (gsup_msg)->imsi, \
- osmo_gsup_message_type_name((gsup_msg)->message_type), \
- ##args)
+#if 0
+#define LOG_GSUP_CONN_MSG(conn, gsup_msg, level, fmt, args...) \
+ LOG_GSUP_CONN(conn, level, "IMSI-%s %s: " fmt, (gsup_msg)->imsi, \
+ osmo_gsup_message_type_name((gsup_msg)->message_type), \
+ ##args)
+
+#endif
+
+#define LOG_GSUP_REQ_CAT(req, subsys, level, fmt, args...) \
+ LOGP(subsys, level, "GSUP %u: %s: IMSI-%s %s: " fmt, \
+ (req) ? (req)->nr : 0, \
+ (req) ? global_title_name(&(req)->source_name) : "NULL", \
+ (req) ? (req)->gsup.imsi : "NULL", \
+ (req) ? osmo_gsup_message_type_name((req)->gsup.message_type) : "NULL", \
+ ##args)
+
+#define LOG_GSUP_REQ(req, level, fmt, args...) \
+ LOG_GSUP_REQ_CAT(req, DLGSUP, level, fmt, ##args)
struct osmo_gsup_conn;
@@ -34,9 +50,6 @@ struct osmo_gsup_server {
/* list of osmo_gsup_conn */
struct llist_head clients;
- /* lu_operations list */
- struct llist_head *luop;
-
struct ipa_server_link *link;
osmo_gsup_read_cb_t read_cb;
struct llist_head routes;
@@ -57,13 +70,57 @@ struct osmo_gsup_conn {
/* Set when Location Update is received: */
bool supports_cs; /* client supports OSMO_GSUP_CN_DOMAIN_CS */
bool supports_ps; /* client supports OSMO_GSUP_CN_DOMAIN_PS */
+
+ /* The IPA unit name received on this link. Routes with more unit names serviced by this link may exist in
+ * osmo_gsup_server->routes, but this is the name the immediate peer identified as in the IPA handshake. */
+ struct global_title peer_name;
};
+/* Keep track of an incoming request to which osmo-hlr composes (or routes back) a response.
+ * Particularly, if a request contained a source_name, we need to add this as destination_name in the response for any
+ * intermediate GSUP proxies to be able to route back to the initial requestor. */
+struct osmo_gsup_req {
+ struct osmo_gsup_server *server;
+ /* Identify this request by number, for logging. */
+ unsigned int nr;
+
+ /* A decoded GSUP message still points into the received msgb. For a decoded osmo_gsup_message to remain valid,
+ * we also need to keep the msgb. */
+ struct msgb *msg;
+ /* Decoded msg. */
+ int decode_rc;
+ const struct osmo_gsup_message gsup;
+ /* The ultimate source of this message: the source_name form the GSUP message, or, if not present, then the
+ * immediate GSUP peer. GSUP messages going via a proxy reflect the initial source in the source_name.
+ * This source_name is implicitly added to the routes for the conn the message was received on. */
+ struct global_title source_name;
+ /* If the source_name is not an immediate GSUP peer, this is set to the closest intermediate peer between here
+ * and source_name. */
+ struct global_title via_proxy;
+};
+
+struct osmo_gsup_req *osmo_gsup_req_new(struct osmo_gsup_conn *conn, struct msgb *msg);
+#define osmo_gsup_req_free(REQ) do { \
+ LOG_GSUP_REQ(REQ, LOGL_DEBUG, "free\n"); \
+ _osmo_gsup_req_free(REQ); \
+ } while(0)
+void _osmo_gsup_req_free(struct osmo_gsup_req *req);
+
+int osmo_gsup_req_respond(struct osmo_gsup_req *req, struct osmo_gsup_message *response);
+int osmo_gsup_req_respond_nonfinal(struct osmo_gsup_req *req, struct osmo_gsup_message *response);
+int osmo_gsup_req_respond_msgt(struct osmo_gsup_req *req, enum osmo_gsup_message_type message_type);
+
+#define osmo_gsup_req_respond_err(REQ, CAUSE, FMT, args...) do { \
+ LOG_GSUP_REQ(REQ, LOGL_ERROR, "%s: " FMT "\n", \
+ get_value_string(gsm48_gmm_cause_names, CAUSE), ##args); \
+ _osmo_gsup_req_respond_err(REQ, CAUSE); \
+ } while(0)
+void _osmo_gsup_req_respond_err(struct osmo_gsup_req *req, enum gsm48_gmm_cause cause);
+
struct msgb *osmo_gsup_msgb_alloc(const char *label);
int osmo_gsup_conn_send(struct osmo_gsup_conn *conn, struct msgb *msg);
-void osmo_gsup_conn_send_err_reply(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup_orig,
- enum gsm48_gmm_cause cause);
+int osmo_gsup_conn_enc_send(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup);
int osmo_gsup_conn_ccm_get(const struct osmo_gsup_conn *clnt, uint8_t **addr,
uint8_t tag);
@@ -71,7 +128,6 @@ struct osmo_gsup_server *osmo_gsup_server_create(void *ctx,
const char *ip_addr,
uint16_t tcp_port,
osmo_gsup_read_cb_t read_cb,
- struct llist_head *lu_op_lst,
void *priv);
void osmo_gsup_server_destroy(struct osmo_gsup_server *gsups);
diff --git a/src/hlr.c b/src/hlr.c
index f51561f..b045771 100644
--- a/src/hlr.c
+++ b/src/hlr.c
@@ -36,6 +36,7 @@
#include <osmocom/gsm/gsm48_ie.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
+#include <osmocom/gsm/gsm23003.h>
#include <osmocom/mslookup/mslookup_client.h>
#include "db.h"
@@ -45,16 +46,25 @@
#include "gsup_server.h"
#include "gsup_router.h"
#include "rand.h"
-#include "luop.h"
#include "hlr_vty.h"
#include "hlr_ussd.h"
#include "dgsm.h"
#include "proxy.h"
+#include "global_title.h"
+#include "lu_fsm.h"
struct hlr *g_hlr;
static void *hlr_ctx = NULL;
static int quit = 0;
+#define RAN_TDEFS \
+
+struct osmo_tdef g_hlr_tdefs[] = {
+ /* 4222 is also the OSMO_GSUP_PORT */
+ { .T = -4222, .default_val = 30, .desc = "GSUP Update Location timeout" },
+ {}
+};
+
/* Trigger 'Insert Subscriber Data' messages to all connected GSUP clients.
*
* \param[in] subscr A subscriber we have new data to send for.
@@ -72,6 +82,8 @@ osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr)
return;
}
+ /* FIXME: send only to current vlr_number and sgsn_number */
+
llist_for_each_entry(co, &g_hlr->gs->clients, list) {
struct osmo_gsup_message gsup = { };
uint8_t msisdn_enc[OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN];
@@ -225,135 +237,92 @@ static int subscr_create_on_demand(const char *imsi)
return 0;
}
+/*! Update nam_cs/nam_ps in the db and trigger notifications to GSUP clients.
+ * \param[in,out] hlr Global hlr context.
+ * \param[in] subscr Subscriber from a fresh db_subscr_get_by_*() call.
+ * \param[in] nam_val True to enable CS/PS, false to disable.
+ * \param[in] is_ps True to enable/disable PS, false for CS.
+ * \returns 0 on success, ENOEXEC if there is no need to change, a negative
+ * value on error.
+ */
+int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps)
+{
+ int rc;
+ bool is_val = is_ps? subscr->nam_ps : subscr->nam_cs;
+ struct global_title vlr_name;
+ struct osmo_gsup_message gsup_del_data = {
+ .message_type = OSMO_GSUP_MSGT_DELETE_DATA_REQUEST,
+ };
+ OSMO_STRLCPY_ARRAY(gsup_del_data.imsi, subscr->imsi);
+
+ if (is_val == nam_val) {
+ LOGP(DAUC, LOGL_DEBUG, "IMSI-%s: Already has the requested value when asked to %s %s\n",
+ subscr->imsi, nam_val ? "enable" : "disable", is_ps ? "PS" : "CS");
+ return ENOEXEC;
+ }
+
+ rc = db_subscr_nam(hlr->dbc, subscr->imsi, nam_val, is_ps);
+ if (rc)
+ return rc > 0? -rc : rc;
+
+ /* If we're disabling, send a notice out to the GSUP client that is
+ * responsible. Otherwise no need. */
+ if (nam_val)
+ return 0;
+
+ if (subscr->vlr_number && global_title_set_str(&vlr_name, subscr->vlr_number))
+ osmo_gsup_gt_enc_send(g_hlr->gs, &vlr_name, &gsup_del_data);
+ if (subscr->sgsn_number && global_title_set_str(&vlr_name, subscr->sgsn_number))
+ osmo_gsup_gt_enc_send(g_hlr->gs, &vlr_name, &gsup_del_data);
+ return 0;
+}
+
/***********************************************************************
* 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,
- struct db_context *dbc)
+static int rx_send_auth_info(unsigned int auc_3g_ind, struct osmo_gsup_req *req)
{
- struct osmo_gsup_message gsup_out = {};
- struct msgb *msg_out;
+ struct osmo_gsup_message gsup_out = {
+ .message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT,
+ };
int rc;
- subscr_create_on_demand(gsup->imsi);
-
- /* initialize return message structure */
- osmo_gsup_set_reply(gsup, &gsup_out);
+ subscr_create_on_demand(req->gsup.imsi);
- rc = db_get_auc(dbc, gsup->imsi, conn->auc_3g_ind,
+ rc = db_get_auc(g_hlr->dbc, req->gsup.imsi, auc_3g_ind,
gsup_out.auth_vectors,
ARRAY_SIZE(gsup_out.auth_vectors),
- gsup->rand, gsup->auts);
+ req->gsup.rand, req->gsup.auts);
if (rc <= 0) {
- gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR;
switch (rc) {
case 0:
/* 0 means "0 tuples generated", which shouldn't happen.
* Treat the same as "no auth data". */
case -ENOKEY:
- LOGP(DAUC, LOGL_NOTICE, "%s: IMSI known, but has no auth data;"
- " Returning slightly inaccurate cause 'IMSI Unknown' via GSUP\n",
- gsup->imsi);
- gsup_out.cause = GMM_CAUSE_IMSI_UNKNOWN;
- break;
+ osmo_gsup_req_respond_err(req, GMM_CAUSE_IMSI_UNKNOWN,
+ "IMSI known, but has no auth data;"
+ " Returning slightly inaccurate cause 'IMSI Unknown' via GSUP");
+ return rc;
case -ENOENT:
- LOGP(DAUC, LOGL_NOTICE, "%s: IMSI not known\n", gsup->imsi);
- gsup_out.cause = GMM_CAUSE_IMSI_UNKNOWN;
- break;
+ osmo_gsup_req_respond_err(req, GMM_CAUSE_IMSI_UNKNOWN, "IMSI unknown");
+ return rc;
default:
- LOGP(DAUC, LOGL_ERROR, "%s: failure to look up IMSI in db\n", gsup->imsi);
- gsup_out.cause = GMM_CAUSE_NET_FAIL;
- break;
+ osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "failure to look up IMSI in db");
+ return rc;
}
- } else {
- gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT;
- gsup_out.num_auth_vectors = rc;
}
- msg_out = osmo_gsup_msgb_alloc("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);
-
-/*! 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);
-}
-
-/*! 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;
- }
+ gsup_out.num_auth_vectors = rc;
+ osmo_gsup_req_respond(req, &gsup_out);
+ return 0;
}
-/*! 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)
+/*! Receive Update Location Request, creates new lu_operation */
+static int rx_upd_loc_req(struct osmo_gsup_conn *conn, struct osmo_gsup_req *req)
{
- struct hlr_subscriber *subscr;
- struct lu_operation *luop = lu_op_alloc_conn(conn, gsup);
- if (!luop) {
- LOGP(DMAIN, LOGL_ERROR, "LU REQ from conn without addr?\n");
- return -EINVAL;
- }
-
- subscr = &luop->subscr;
-
- lu_op_statechg(luop, LU_S_LU_RECEIVED);
-
- switch (gsup->cn_domain) {
+ switch (req->gsup.cn_domain) {
case OSMO_GSUP_CN_DOMAIN_CS:
conn->supports_cs = true;
break;
@@ -364,133 +333,64 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
* a request, the PS Domain is assumed." */
case OSMO_GSUP_CN_DOMAIN_PS:
conn->supports_ps = true;
- luop->is_ps = true;
break;
}
- llist_add(&luop->list, &g_lu_ops);
-
- subscr_create_on_demand(gsup->imsi);
-
- /* Roughly follwing "Process Update_Location_HLR" of TS 09.02 */
-
- /* check if subscriber is known at all */
- if (!lu_op_fill_subscr(luop, g_hlr->dbc, gsup->imsi)) {
- /* Send Error back: Subscriber Unknown in HLR */
- osmo_strlcpy(luop->subscr.imsi, gsup->imsi, sizeof(luop->subscr.imsi));
- lu_op_tx_error(luop, GMM_CAUSE_IMSI_UNKNOWN);
- return 0;
- }
-
- /* Check if subscriber is generally permitted on CS or PS
- * service (as requested) */
- if (!luop->is_ps && !luop->subscr.nam_cs) {
- lu_op_tx_error(luop, GMM_CAUSE_PLMN_NOTALLOWED);
- return 0;
- } else if (luop->is_ps && !luop->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)) {
- lu_op_tx_cancel_old(luop);
- } else if (luop->is_ps == true &&
- strcmp(subscr->sgsn_number, sgsn_number)) {
- lu_op_tx_cancel_old(luop);
- } else
-#endif
-
- /* Store the VLR / SGSN number with the subscriber, so we know where it was last seen. */
- if (global_title_cmp(&luop->vlr_number, &luop->peer))
- LOGP(DAUC, LOGL_DEBUG, "IMSI='%s': storing %s = %s, via proxy %s\n",
- subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number",
- global_title_name(&luop->vlr_number), global_title_name(&luop->peer));
- else
- LOGP(DAUC, LOGL_DEBUG, "IMSI='%s': storing %s = %s\n",
- subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number",
- global_title_name(&luop->vlr_number));
-
- if (db_subscr_lu(g_hlr->dbc, subscr->id, &luop->peer, &luop->vlr_number, luop->is_ps))
- LOGP(DAUC, LOGL_ERROR, "IMSI='%s': Cannot update %s in the database\n",
- subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number");
- /* 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);
+ subscr_create_on_demand(req->gsup.imsi);
+ lu_rx_gsup(req);
return 0;
}
-static int rx_purge_ms_req(struct osmo_gsup_conn *conn,
- const struct osmo_gsup_message *gsup)
+static int rx_purge_ms_req(struct osmo_gsup_req *req)
{
- struct osmo_gsup_message gsup_reply = {};
- struct msgb *msg_out;
- bool is_ps = false;
+ bool is_ps = (req->gsup.cn_domain != OSMO_GSUP_CN_DOMAIN_CS);
int rc;
- osmo_gsup_set_reply(gsup, &gsup_reply);
-
- if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS)
- is_ps = true;
-
- LOGP(DAUC, LOGL_INFO, "%s: Purge MS (%s)\n", gsup->imsi,
- is_ps ? "PS" : "CS");
+ LOG_GSUP_REQ_CAT(req, DAUC, LOGL_INFO, "Purge MS (%s)\n", is_ps ? "PS" : "CS");
/* FIXME: check if the VLR that sends the purge is the same that
* we have on record. Only update if yes */
/* Perform the actual update of the DB */
- rc = db_subscr_purge(g_hlr->dbc, gsup->imsi, true, is_ps);
+ rc = db_subscr_purge(g_hlr->dbc, req->gsup.imsi, true, is_ps);
if (rc == 0)
- gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_RESULT;
- else if (rc == -ENOENT) {
- gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_ERROR;
- gsup_reply.cause = GMM_CAUSE_IMSI_UNKNOWN;
- } else {
- gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_ERROR;
- gsup_reply.cause = GMM_CAUSE_NET_FAIL;
- }
-
- msg_out = osmo_gsup_msgb_alloc("GSUP AUC response");
- osmo_gsup_encode(msg_out, &gsup_reply);
- return osmo_gsup_conn_send(conn, msg_out);
+ osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_PURGE_MS_RESULT);
+ else if (rc == -ENOENT)
+ osmo_gsup_req_respond_err(req, GMM_CAUSE_IMSI_UNKNOWN, "IMSI unknown");
+ else
+ osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "db error");
+ return rc;
}
-static int rx_check_imei_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup)
+static int rx_check_imei_req(struct osmo_gsup_req *req)
{
- struct osmo_gsup_message gsup_reply = {0};
- struct msgb *msg_out;
+ struct osmo_gsup_message gsup_reply;
char imei[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1] = {0};
+ const struct osmo_gsup_message *gsup = &req->gsup;
int rc;
/* Require IMEI */
if (!gsup->imei_enc) {
- LOGP(DMAIN, LOGL_ERROR, "%s: missing IMEI\n", gsup->imsi);
- osmo_gsup_conn_send_err_reply(conn, gsup, GMM_CAUSE_INV_MAND_INFO);
+ osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "missing IMEI");
return -1;
}
/* Decode IMEI (fails if IMEI is too long) */
rc = gsm48_decode_bcd_number2(imei, sizeof(imei), gsup->imei_enc, gsup->imei_enc_len, 0);
if (rc < 0) {
- LOGP(DMAIN, LOGL_ERROR, "%s: failed to decode IMEI (rc: %i)\n", gsup->imsi, rc);
- osmo_gsup_conn_send_err_reply(conn, gsup, GMM_CAUSE_INV_MAND_INFO);
+ osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO,
+ "failed to decode IMEI %s (rc: %d)",
+ osmo_hexdump_c(OTC_SELECT, gsup->imei_enc, gsup->imei_enc_len),
+ rc);
return -1;
}
/* Check if IMEI is too short */
- if (strlen(imei) != GSM23003_IMEI_NUM_DIGITS_NO_CHK) {
- LOGP(DMAIN, LOGL_ERROR, "%s: wrong encoded IMEI length (IMEI: '%s', %lu, %i)\n", gsup->imsi, imei,
- strlen(imei), GSM23003_IMEI_NUM_DIGITS_NO_CHK);
- osmo_gsup_conn_send_err_reply(conn, gsup, GMM_CAUSE_INV_MAND_INFO);
+ if (!osmo_imei_str_valid(imei, false)) {
+ osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO,
+ "invalid IMEI: %s", osmo_quote_str_c(OTC_SELECT, imei, -1));
return -1;
}
@@ -500,7 +400,7 @@ static int rx_check_imei_req(struct osmo_gsup_conn *conn, const struct osmo_gsup
if (g_hlr->store_imei) {
LOGP(DAUC, LOGL_DEBUG, "IMSI='%s': storing IMEI = %s\n", gsup->imsi, imei);
if (db_subscr_update_imei_by_imsi(g_hlr->dbc, gsup->imsi, imei) < 0) {
- osmo_gsup_conn_send_err_reply(conn, gsup, GMM_CAUSE_INV_MAND_INFO);
+ osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "Failed to store IMEI in HLR db");
return -1;
}
} else {
@@ -508,185 +408,146 @@ static int rx_check_imei_req(struct osmo_gsup_conn *conn, const struct osmo_gsup
LOGP(DMAIN, LOGL_INFO, "IMSI='%s': has IMEI = %s (consider setting 'store-imei')\n", gsup->imsi, imei);
struct hlr_subscriber subscr;
if (db_subscr_get_by_imsi(g_hlr->dbc, gsup->imsi, &subscr) < 0) {
- osmo_gsup_conn_send_err_reply(conn, gsup, GMM_CAUSE_INV_MAND_INFO);
+ osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "IMSI unknown");
return -1;
}
}
/* Accept all IMEIs */
- gsup_reply.imei_result = OSMO_GSUP_IMEI_RESULT_ACK;
- gsup_reply.message_type = OSMO_GSUP_MSGT_CHECK_IMEI_RESULT;
- msg_out = osmo_gsup_msgb_alloc("GSUP Check_IMEI response");
- memcpy(gsup_reply.imsi, gsup->imsi, sizeof(gsup_reply.imsi));
- osmo_gsup_encode(msg_out, &gsup_reply);
- return osmo_gsup_conn_send(conn, msg_out);
+ gsup_reply = (struct osmo_gsup_message){
+ .message_type = OSMO_GSUP_MSGT_CHECK_IMEI_RESULT,
+ .imei_result = OSMO_GSUP_IMEI_RESULT_ACK,
+ };
+ return osmo_gsup_req_respond(req, &gsup_reply);
}
static char namebuf[255];
#define LOGP_GSUP_FWD(gsup, level, fmt, args ...) \
LOGP(DMAIN, level, "Forward %s (class=%s, IMSI=%s, %s->%s): " fmt, \
- osmo_gsup_message_type_name(gsup->message_type), \
- osmo_gsup_message_class_name(gsup->message_class), \
- gsup->imsi, \
- osmo_quote_str((const char *)gsup->source_name, gsup->source_name_len), \
- osmo_quote_str_buf2(namebuf, sizeof(namebuf), (const char *)gsup->destination_name, gsup->destination_name_len), \
+ osmo_gsup_message_type_name((gsup)->message_type), \
+ osmo_gsup_message_class_name((gsup)->message_class), \
+ (gsup)->imsi, \
+ osmo_quote_str((const char *)(gsup)->source_name, (gsup)->source_name_len), \
+ osmo_quote_str_buf2(namebuf, sizeof(namebuf), (const char *)(gsup)->destination_name, (gsup)->destination_name_len), \
## args)
-static int read_cb_forward(struct osmo_gsup_conn *conn, struct msgb *msg, const struct osmo_gsup_message *gsup)
+static int read_cb_forward(struct osmo_gsup_req *req)
{
int ret = -EINVAL;
- struct osmo_gsup_message *gsup_err;
-
- /* FIXME: it would be better if the msgb never were deallocated immediately by osmo_gsup_addr_send(), which a
- * select-loop volatile talloc context could facilitate. Then we would still be able to access gsup-> members
- * (pointing into the msgb) even after sending failed, and we wouldn't need to copy this data before sending: */
- /* Prepare error message (before IEs get deallocated) */
- gsup_err = talloc_zero(hlr_ctx, struct osmo_gsup_message);
- OSMO_STRLCPY_ARRAY(gsup_err->imsi, gsup->imsi);
- gsup_err->message_class = gsup->message_class;
- gsup_err->destination_name = talloc_memdup(gsup_err, gsup->destination_name, gsup->destination_name_len);
- gsup_err->destination_name_len = gsup->destination_name_len;
- gsup_err->message_type = gsup->message_type;
- gsup_err->session_state = gsup->session_state;
- gsup_err->session_id = gsup->session_id;
- gsup_err->source_name = talloc_memdup(gsup_err, gsup->source_name, gsup->source_name_len);
- gsup_err->source_name_len = gsup->source_name_len;
+ const struct osmo_gsup_message *gsup = &req->gsup;
+ struct osmo_gsup_message gsup_err;
+ struct msgb *forward_msg;
+ struct global_title destination_name;
/* Check for routing IEs */
- if (!gsup->source_name || !gsup->source_name_len || !gsup->destination_name || !gsup->destination_name_len) {
- LOGP_GSUP_FWD(gsup, LOGL_ERROR, "missing routing IEs\n");
- goto end;
+ if (!req->gsup.source_name[0] || !req->gsup.source_name_len
+ || !req->gsup.destination_name[0] || !req->gsup.destination_name_len) {
+ LOGP_GSUP_FWD(&req->gsup, LOGL_ERROR, "missing routing IEs\n");
+ goto routing_error;
}
- /* Verify source name (e.g. "MSC-00-00-00-00-00-00") */
- if (gsup_route_find(conn->server, gsup->source_name, gsup->source_name_len) != conn) {
- LOGP_GSUP_FWD(gsup, LOGL_ERROR, "mismatching source name\n");
- goto end;
+ if (global_title_set(&destination_name, req->gsup.destination_name, req->gsup.destination_name_len)) {
+ LOGP_GSUP_FWD(&req->gsup, LOGL_ERROR, "invalid destination name\n");
+ goto routing_error;
}
- /* Forward message without re-encoding (so we don't remove unknown IEs) */
- LOGP_GSUP_FWD(gsup, LOGL_INFO, "checks passed, forwarding\n");
-
- /* Remove incoming IPA header to be able to prepend an outgoing IPA header */
- msgb_pull_to_l2(msg);
- ret = osmo_gsup_addr_send(g_hlr->gs, gsup->destination_name, gsup->destination_name_len, msg);
- /* AT THIS POINT, THE msg MAY BE DEALLOCATED and the data like gsup->imsi, gsup->source_name etc may all be
- * invalid and cause segfaults. */
- msg = NULL;
- gsup = NULL;
- if (ret == -ENODEV)
- LOGP_GSUP_FWD(gsup_err, LOGL_ERROR, "destination not connected\n");
- else if (ret)
- LOGP_GSUP_FWD(gsup_err, LOGL_ERROR, "unknown error %i\n", ret);
-
-end:
- /* Send error back to source */
+ LOG_GSUP_REQ(req, LOGL_INFO, "Forwarding to %s\n", global_title_name(&destination_name));
+
+ /* Forward message without re-encoding (so we don't remove unknown IEs).
+ * Copy GSUP part to forward, removing incoming IPA header to be able to prepend an outgoing IPA header */
+ forward_msg = osmo_gsup_msgb_alloc("GSUP forward");
+ forward_msg->l2h = msgb_put(forward_msg, msgb_l2len(req->msg));
+ memcpy(forward_msg->l2h, msgb_l2(req->msg), msgb_l2len(req->msg));
+ ret = osmo_gsup_gt_send(g_hlr->gs, &destination_name, forward_msg);
if (ret) {
- struct msgb *msg_err = osmo_gsup_msgb_alloc("GSUP forward ERR response");
- OSMO_ASSERT(msg_err);
- gsup_err->message_type = OSMO_GSUP_MSGT_E_ROUTING_ERROR;
- osmo_gsup_encode(msg_err, gsup_err);
- LOGP_GSUP_FWD(gsup_err, LOGL_NOTICE, "Tx %s\n", osmo_gsup_message_type_name(gsup_err->message_type));
- osmo_gsup_conn_send(conn, msg_err);
+ LOGP_GSUP_FWD(gsup, LOGL_ERROR, "%s (rc=%d)\n",
+ ret == -ENODEV ? "destination not connected" : "unknown error",
+ ret);
+ goto routing_error;
}
- talloc_free(gsup_err);
- if (msg)
- msgb_free(msg);
- return ret;
+ osmo_gsup_req_free(req);
+ return 0;
+
+routing_error:
+ gsup_err = (struct osmo_gsup_message){
+ .message_type = OSMO_GSUP_MSGT_E_ROUTING_ERROR,
+ .destination_name = gsup->destination_name,
+ .destination_name_len = gsup->destination_name_len,
+ .source_name = gsup->source_name,
+ .source_name_len = gsup->source_name_len,
+ };
+ osmo_gsup_req_respond(req, &gsup_err);
+ return -1;
}
static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
{
- struct osmo_gsup_message gsup;
- int rc;
+ struct osmo_gsup_req *req = osmo_gsup_req_new(conn, msg);
- if (!msgb_l2(msg) || !msgb_l2len(msg)) {
- LOGP(DMAIN, LOGL_ERROR, "missing or empty L2 data\n");
- msgb_free(msg);
+ if (!req)
return -EINVAL;
- }
- 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);
- msgb_free(msg);
- return rc;
- }
-
- /* 3GPP TS 23.003 Section 2.2 clearly states that an IMSI with less than 5
- * digits is impossible. Even 5 digits is a highly theoretical case */
- if (strlen(gsup.imsi) < 5) { /* TODO: move this check to libosmogsm/gsup.c? */
- LOGP(DMAIN, LOGL_ERROR, "IMSI too short: %s\n", osmo_quote_str(gsup.imsi, -1));
- osmo_gsup_conn_send_err_reply(conn, &gsup, GMM_CAUSE_INV_MAND_INFO);
- msgb_free(msg);
- return -EINVAL;
+ /* If the GSUP recipient is other than this HLR, forward. */
+ if (req->gsup.destination_name_len) {
+ struct global_title destination_name;
+ struct global_title my_name;
+ global_title_set_str(&my_name, g_hlr->gsup_unit_name.serno);
+ if (!global_title_set(&destination_name, req->gsup.destination_name, req->gsup.destination_name_len)
+ && global_title_cmp(&destination_name, &my_name)) {
+ return read_cb_forward(req);
+ }
}
- if (gsup.destination_name_len)
- return read_cb_forward(conn, msg, &gsup);
-
/* Distributed GSM: check whether to proxy for / lookup a remote HLR.
* It would require less database hits to do this only if a local-only operation fails with "unknown IMSI", but
* it becomes semantically easier if we do this once-off ahead of time. */
- if (osmo_mslookup_client_active(g_hlr->mslookup.client.client)
- && dgsm_check_forward_gsup_msg(conn, &gsup))
- goto cleanup_and_exit;
+ if (osmo_mslookup_client_active(g_hlr->mslookup.client.client)) {
+ if (dgsm_check_forward_gsup_msg(req))
+ return 0;
+ }
- switch (gsup.message_type) {
+ switch (req->gsup.message_type) {
/* requests sent to us */
case OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST:
- rx_send_auth_info(conn, &gsup, g_hlr->dbc);
+ rx_send_auth_info(conn->auc_3g_ind, req);
break;
case OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST:
- rx_upd_loc_req(conn, &gsup);
+ rx_upd_loc_req(conn, req);
break;
case OSMO_GSUP_MSGT_PURGE_MS_REQUEST:
- rx_purge_ms_req(conn, &gsup);
+ rx_purge_ms_req(req);
break;
/* responses to requests sent by us */
case OSMO_GSUP_MSGT_DELETE_DATA_ERROR:
- LOGP(DMAIN, LOGL_ERROR, "Error while deleting subscriber data "
- "for IMSI %s\n", gsup.imsi);
+ LOG_GSUP_REQ(req, LOGL_ERROR, "Peer responds with: Error while deleting subscriber data\n");
+ osmo_gsup_req_free(req);
break;
case OSMO_GSUP_MSGT_DELETE_DATA_RESULT:
- LOGP(DMAIN, LOGL_ERROR, "Deleting subscriber data for IMSI %s\n",
- gsup.imsi);
+ LOG_GSUP_REQ(req, LOGL_DEBUG, "Peer responds with: Subscriber data deleted\n");
+ osmo_gsup_req_free(req);
break;
case OSMO_GSUP_MSGT_PROC_SS_REQUEST:
case OSMO_GSUP_MSGT_PROC_SS_RESULT:
- rx_proc_ss_req(conn, &gsup);
+ rx_proc_ss_req(req);
break;
case OSMO_GSUP_MSGT_PROC_SS_ERROR:
- rx_proc_ss_error(conn, &gsup);
+ rx_proc_ss_error(req);
break;
case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
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,
- &g_lu_ops);
- if (!luop) {
- LOGP(DMAIN, LOGL_ERROR, "GSUP message %s for "
- "unknown IMSI %s\n",
- osmo_gsup_message_type_name(gsup.message_type),
- gsup.imsi);
- break;
- }
- lu_op_rx_gsup(luop, &gsup);
- }
+ lu_rx_gsup(req);
break;
case OSMO_GSUP_MSGT_CHECK_IMEI_REQUEST:
- rx_check_imei_req(conn, &gsup);
+ rx_check_imei_req(req);
break;
default:
LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %s\n",
- osmo_gsup_message_type_name(gsup.message_type));
+ osmo_gsup_message_type_name(req->gsup.message_type));
+ osmo_gsup_req_free(req);
break;
}
-
-cleanup_and_exit:
- msgb_free(msg);
return 0;
}
@@ -902,7 +763,7 @@ int main(int argc, char **argv)
g_hlr->gs = osmo_gsup_server_create(hlr_ctx, g_hlr->gsup_bind_addr, OSMO_GSUP_PORT,
- read_cb, &g_lu_ops, g_hlr);
+ read_cb, g_hlr);
if (!g_hlr->gs) {
LOGP(DMAIN, LOGL_FATAL, "Error starting GSUP server\n");
exit(1);
diff --git a/src/hlr.h b/src/hlr.h
index 5c92899..0871d06 100644
--- a/src/hlr.h
+++ b/src/hlr.h
@@ -25,6 +25,7 @@
#include <stdbool.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/gsm/ipa.h>
+#include <osmocom/core/tdef.h>
#include "dgsm.h"
#define HLR_DEFAULT_DB_FILE_PATH "hlr.db"
@@ -33,6 +34,8 @@ struct hlr_euse;
struct osmo_gsup_conn;
enum osmo_gsup_message_type;
+extern struct osmo_tdef g_hlr_tdefs[];
+
struct hlr {
/* GSUP server pointer */
struct osmo_gsup_server *gs;
@@ -91,3 +94,4 @@ extern struct hlr *g_hlr;
struct hlr_subscriber;
void osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr);
+int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps);
diff --git a/src/hlr_ussd.c b/src/hlr_ussd.c
index e2dbde1..0271c33 100644
--- a/src/hlr_ussd.c
+++ b/src/hlr_ussd.c
@@ -170,12 +170,14 @@ struct ss_session {
/* subscriber's vlr_number
* MO USSD: originating MSC's vlr_number
* MT USSD: looked up once per session and cached here */
- uint8_t *vlr_number;
- size_t vlr_number_len;
+ struct global_title vlr_name;
/* we don't keep a pointer to the osmo_gsup_{route,conn} towards the MSC/VLR here,
* as this might change during inter-VLR hand-over, and we simply look-up the serving MSC/VLR
* every time we receive an USSD component from the EUSE */
+
+ struct osmo_gsup_req *initial_req_from_ms;
+ struct osmo_gsup_req *initial_req_from_euse;
};
struct ss_session *ss_session_find(struct hlr *hlr, const char *imsi, uint32_t session_id)
@@ -191,6 +193,10 @@ struct ss_session *ss_session_find(struct hlr *hlr, const char *imsi, uint32_t s
void ss_session_free(struct ss_session *ss)
{
osmo_timer_del(&ss->timeout);
+ if (ss->initial_req_from_ms)
+ osmo_gsup_req_free(ss->initial_req_from_ms);
+ if (ss->initial_req_from_euse)
+ osmo_gsup_req_free(ss->initial_req_from_euse);
llist_del(&ss->list);
talloc_free(ss);
}
@@ -230,44 +236,58 @@ struct ss_session *ss_session_alloc(struct hlr *hlr, const char *imsi, uint32_t
***********************************************************************/
/* Resolve the target MSC by ss->imsi and send GSUP message. */
-static int ss_gsup_send(struct ss_session *ss, struct osmo_gsup_server *gs, struct msgb *msg)
+static int ss_gsup_send_to_ms(struct ss_session *ss, struct osmo_gsup_server *gs, struct osmo_gsup_message *gsup)
{
struct hlr_subscriber subscr = {};
+ struct msgb *msg;
int rc;
+ if (ss->initial_req_from_ms) {
+ /* If this is a response to an incoming GSUP request from the MS, respond via osmo_gsup_req_respond() to
+ * make sure that all required routing information is kept intact.
+ * Use osmo_gsup_req_respond_nonfinal() to not deallocate the ss->initial_req_from_ms */
+ osmo_gsup_req_respond_nonfinal(ss->initial_req_from_ms, gsup);
+ return 0;
+ }
+
+ msg = osmo_gsup_msgb_alloc("GSUP USSD FW");
+ rc = osmo_gsup_encode(msg, gsup);
+ if (rc) {
+ LOGPSS(ss, LOGL_ERROR, "Failed to encode GSUP message\n");
+ return rc;
+ }
+
/* Use vlr_number as looked up by the caller, or look up now. */
- if (!ss->vlr_number) {
+ if (!ss->vlr_name.len) {
rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
if (rc < 0) {
LOGPSS(ss, LOGL_ERROR, "Cannot find subscriber, cannot route GSUP message\n");
msgb_free(msg);
return -EINVAL;
}
- ss->vlr_number = (uint8_t *)talloc_strdup(ss, subscr.vlr_number);
- ss->vlr_number_len = strlen(subscr.vlr_number) + 1;
+ global_title_set_str(&ss->vlr_name, subscr.vlr_number);
}
/* Check for empty string (all vlr_number strings end in "\0", because otherwise gsup_route_find() fails) */
- if (ss->vlr_number_len == 1) {
+ if (ss->vlr_name.len <= 1) {
LOGPSS(ss, LOGL_ERROR, "Cannot send GSUP message, no VLR number stored for subscriber\n");
msgb_free(msg);
return -EINVAL;
}
- LOGPSS(ss, LOGL_DEBUG, "Tx SS/USSD to VLR %s\n", osmo_quote_str((char *)ss->vlr_number, ss->vlr_number_len));
- return osmo_gsup_addr_send(gs, ss->vlr_number, ss->vlr_number_len, msg);
+ LOGPSS(ss, LOGL_DEBUG, "Tx SS/USSD to VLR %s\n", global_title_name(&ss->vlr_name));
+ return osmo_gsup_gt_send(gs, &ss->vlr_name, msg);
}
static int ss_tx_to_ms(struct ss_session *ss, enum osmo_gsup_message_type gsup_msg_type,
- bool final, struct msgb *ss_msg)
+ bool final, struct msgb *ss_msg)
{
struct osmo_gsup_message resp = {
.message_type = gsup_msg_type,
- .cn_domain = OSMO_GSUP_CN_DOMAIN_CS,
.session_id = ss->session_id,
};
- struct msgb *resp_msg;
+ int rc;
OSMO_STRLCPY_ARRAY(resp.imsi, ss->imsi);
if (final)
@@ -279,12 +299,10 @@ static int ss_tx_to_ms(struct ss_session *ss, enum osmo_gsup_message_type gsup_m
resp.ss_info_len = msgb_length(ss_msg);
}
- resp_msg = msgb_alloc_headroom(4000, 64, __func__);
- OSMO_ASSERT(resp_msg);
- osmo_gsup_encode(resp_msg, &resp);
- msgb_free(ss_msg);
+ rc = ss_gsup_send_to_ms(ss, g_hlr->gs, &resp);
- return ss_gsup_send(ss, g_hlr->gs, resp_msg);
+ msgb_free(ss_msg);
+ return rc;
}
#if 0
@@ -299,7 +317,7 @@ static int ss_tx_reject(struct ss_session *ss, int invoke_id, uint8_t problem_ta
}
#endif
-static int ss_tx_error(struct ss_session *ss, uint8_t invoke_id, uint8_t error_code)
+static int ss_tx_to_ms_error(struct ss_session *ss, uint8_t invoke_id, uint8_t error_code)
{
struct msgb *msg = gsm0480_gen_return_error(invoke_id, error_code);
LOGPSS(ss, LOGL_NOTICE, "Tx ReturnError(%u, 0x%02x)\n", invoke_id, error_code);
@@ -307,7 +325,7 @@ static int ss_tx_error(struct ss_session *ss, uint8_t invoke_id, uint8_t error_c
return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, true, msg);
}
-static int ss_tx_ussd_7bit(struct ss_session *ss, bool final, uint8_t invoke_id, const char *text)
+static int ss_tx_to_ms_ussd_7bit(struct ss_session *ss, bool final, uint8_t invoke_id, const char *text)
{
struct msgb *msg = gsm0480_gen_ussd_resp_7bit(invoke_id, text);
LOGPSS(ss, LOGL_INFO, "Tx USSD '%s'\n", text);
@@ -321,7 +339,7 @@ static int ss_tx_ussd_7bit(struct ss_session *ss, bool final, uint8_t invoke_id,
#include "db.h"
-static int handle_ussd_own_msisdn(struct osmo_gsup_conn *conn, struct ss_session *ss,
+static int handle_ussd_own_msisdn(struct ss_session *ss,
const struct osmo_gsup_message *gsup, const struct ss_request *req)
{
struct hlr_subscriber subscr;
@@ -335,25 +353,25 @@ static int handle_ussd_own_msisdn(struct osmo_gsup_conn *conn, struct ss_session
snprintf(buf, sizeof(buf), "You have no MSISDN!");
else
snprintf(buf, sizeof(buf), "Your extension is %s", subscr.msisdn);
- ss_tx_ussd_7bit(ss, true, req->invoke_id, buf);
+ ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id, buf);
break;
case -ENOENT:
- ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
+ ss_tx_to_ms_error(ss, req->invoke_id, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
break;
case -EIO:
default:
- ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_SYSTEM_FAILURE);
+ ss_tx_to_ms_error(ss, req->invoke_id, GSM0480_ERR_CODE_SYSTEM_FAILURE);
break;
}
return 0;
}
-static int handle_ussd_own_imsi(struct osmo_gsup_conn *conn, struct ss_session *ss,
+static int handle_ussd_own_imsi(struct ss_session *ss,
const struct osmo_gsup_message *gsup, const struct ss_request *req)
{
char buf[GSM0480_USSD_7BIT_STRING_LEN+1];
snprintf(buf, sizeof(buf), "Your IMSI is %s", ss->imsi);
- ss_tx_ussd_7bit(ss, true, req->invoke_id, buf);
+ ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id, buf);
return 0;
}
@@ -400,37 +418,26 @@ static bool ss_op_is_ussd(uint8_t opcode)
}
/* is this GSUP connection an EUSE (true) or not (false)? */
-static bool conn_is_euse(struct osmo_gsup_conn *conn)
+static bool peer_name_is_euse(const struct global_title *peer_name)
{
- int rc;
- uint8_t *addr;
-
- rc = osmo_gsup_conn_ccm_get(conn, &addr, IPAC_IDTAG_SERNR);
- if (rc <= 5)
+ if (peer_name->len <= 5)
return false;
- if (!strncmp((char *)addr, "EUSE-", 5))
+ if (!strncmp((char *)(peer_name->val), "EUSE-", 5))
return true;
else
return false;
}
-static struct hlr_euse *euse_by_conn(struct osmo_gsup_conn *conn)
+static struct hlr_euse *euse_by_name(const struct global_title *peer_name)
{
- int rc;
- char *addr;
- struct hlr *hlr = conn->server->priv;
-
- rc = osmo_gsup_conn_ccm_get(conn, (uint8_t **) &addr, IPAC_IDTAG_SERNR);
- if (rc <= 5)
- return NULL;
- if (strncmp(addr, "EUSE-", 5))
+ if (!peer_name_is_euse(peer_name))
return NULL;
- return euse_find(hlr, addr+5);
+ return euse_find(g_hlr, (const char*)(peer_name->val)+5);
}
-static int handle_ss(struct ss_session *ss, const struct osmo_gsup_message *gsup,
- const struct ss_request *req)
+static int handle_ss(struct ss_session *ss, bool is_euse_originated, const struct osmo_gsup_message *gsup,
+ const struct ss_request *req)
{
uint8_t comp_type = gsup->ss_info[0];
@@ -443,17 +450,16 @@ static int handle_ss(struct ss_session *ss, const struct osmo_gsup_message *gsup
* we don't handle "structured" SS requests at all.
*/
LOGPSS(ss, LOGL_NOTICE, "Structured SS requests are not supported, rejecting...\n");
- ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_FACILITY_NOT_SUPPORTED);
+ ss_tx_to_ms_error(ss, req->invoke_id, GSM0480_ERR_CODE_FACILITY_NOT_SUPPORTED);
return -ENOTSUP;
}
/* Handle a USSD GSUP message for a given SS Session received from VLR or EUSE */
-static int handle_ussd(struct osmo_gsup_conn *conn, struct ss_session *ss,
- const struct osmo_gsup_message *gsup, const struct ss_request *req)
+static int handle_ussd(struct ss_session *ss, bool is_euse_originated, const struct osmo_gsup_message *gsup,
+ const struct ss_request *req)
{
uint8_t comp_type = gsup->ss_info[0];
struct msgb *msg_out;
- bool is_euse_originated = conn_is_euse(conn);
LOGPSS(ss, LOGL_INFO, "USSD CompType=%s, OpCode=%s '%s'\n",
gsm0480_comp_type_name(comp_type), gsm0480_op_code_name(req->opcode),
@@ -461,27 +467,27 @@ static int handle_ussd(struct osmo_gsup_conn *conn, struct ss_session *ss,
if ((ss->is_external && !ss->u.euse) || !ss->u.iuse) {
LOGPSS(ss, LOGL_NOTICE, "USSD for unknown code '%s'\n", req->ussd_text);
- ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_SS_NOT_AVAILABLE);
+ ss_tx_to_ms_error(ss, req->invoke_id, GSM0480_ERR_CODE_SS_NOT_AVAILABLE);
return 0;
}
if (is_euse_originated) {
- msg_out = osmo_gsup_msgb_alloc("GSUP USSD FW");
- OSMO_ASSERT(msg_out);
/* Received from EUSE, Forward to VLR */
- osmo_gsup_encode(msg_out, gsup);
- ss_gsup_send(ss, conn->server, msg_out);
+ /* Need a non-const osmo_gsup_message, because sending might modify some (routing related?) parts. */
+ struct osmo_gsup_message forward = *gsup;
+ ss_gsup_send_to_ms(ss, g_hlr->gs, &forward);
} else {
/* Received from VLR (MS) */
if (ss->is_external) {
/* Forward to EUSE */
- char addr[128];
- strcpy(addr, "EUSE-");
- osmo_strlcpy(addr+5, ss->u.euse->name, sizeof(addr)-5);
- conn = gsup_route_find(conn->server, (uint8_t *)addr, strlen(addr)+1);
+ struct global_title euse_name;
+ struct osmo_gsup_conn *conn;
+ global_title_set_str(&euse_name, "EUSE-%s", ss->u.euse->name);
+ conn = gsup_route_find_gt(g_hlr->gs, &euse_name);
if (!conn) {
- LOGPSS(ss, LOGL_ERROR, "Cannot find conn for EUSE %s\n", addr);
- ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_SYSTEM_FAILURE);
+ LOGPSS(ss, LOGL_ERROR, "Cannot find conn for EUSE %s\n",
+ global_title_name(&euse_name));
+ ss_tx_to_ms_error(ss, req->invoke_id, GSM0480_ERR_CODE_SYSTEM_FAILURE);
} else {
msg_out = osmo_gsup_msgb_alloc("GSUP USSD FW");
OSMO_ASSERT(msg_out);
@@ -490,9 +496,10 @@ static int handle_ussd(struct osmo_gsup_conn *conn, struct ss_session *ss,
}
} else {
/* Handle internally */
- ss->u.iuse->handle_ussd(conn, ss, gsup, req);
+ ss->u.iuse->handle_ussd(ss, gsup, req);
/* Release session immediately */
ss_session_free(ss);
+ return 0;
}
}
@@ -502,12 +509,16 @@ static int handle_ussd(struct osmo_gsup_conn *conn, struct ss_session *ss,
/* this function is called for any SS_REQ/SS_RESP messages from both the MSC/VLR side as well
* as from the EUSE side */
-int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup)
+void rx_proc_ss_req(struct osmo_gsup_req *gsup_req)
{
- struct hlr *hlr = conn->server->priv;
+ struct hlr *hlr = g_hlr;
struct ss_session *ss;
struct ss_request req = {0};
- struct gsup_route *gsup_rt;
+ const struct osmo_gsup_message *gsup = &gsup_req->gsup;
+ /* Remember whether this function should free the incoming gsup_req: if it is placed as ss->initial_req_from_*,
+ * do not free it here. If not, free it here. */
+ struct osmo_gsup_req *free_gsup_req = gsup_req;
+ bool is_euse_originated = peer_name_is_euse(&gsup_req->source_name);
LOGP(DSS, LOGL_DEBUG, "%s/0x%08x: Process SS (%s)\n", gsup->imsi, gsup->session_id,
osmo_gsup_session_state_name(gsup->session_state));
@@ -518,14 +529,15 @@ int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *
LOGP(DSS, LOGL_ERROR, "%s/0x%082x: Unable to parse SS request: %s\n",
gsup->imsi, gsup->session_id,
osmo_hexdump(gsup->ss_info, gsup->ss_info_len));
- /* FIXME: Send a Reject component? */
- goto out_err;
+ osmo_gsup_req_respond_err(gsup_req, GMM_CAUSE_INV_MAND_INFO, "error parsing SS request");
+ return;
}
} else if (gsup->session_state != OSMO_GSUP_SESSION_STATE_END) {
LOGP(DSS, LOGL_ERROR, "%s/0x%082x: Missing SS payload for '%s'\n",
gsup->imsi, gsup->session_id,
osmo_gsup_session_state_name(gsup->session_state));
- goto out_err;
+ osmo_gsup_req_respond_err(gsup_req, GMM_CAUSE_INV_MAND_INFO, "missing SS payload");
+ return;
}
switch (gsup->session_state) {
@@ -534,32 +546,29 @@ int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *
if (ss_session_find(hlr, gsup->imsi, gsup->session_id)) {
LOGP(DSS, LOGL_ERROR, "%s/0x%08x: BEGIN with non-unique session ID!\n",
gsup->imsi, gsup->session_id);
- goto out_err;
+ osmo_gsup_req_respond_err(gsup_req, GMM_CAUSE_INV_MAND_INFO, "BEGIN with non-unique session ID");
+ return;
}
ss = ss_session_alloc(hlr, gsup->imsi, gsup->session_id);
if (!ss) {
LOGP(DSS, LOGL_ERROR, "%s/0x%08x: Unable to allocate SS session\n",
gsup->imsi, gsup->session_id);
- goto out_err;
+ osmo_gsup_req_respond_err(gsup_req, GMM_CAUSE_NET_FAIL, "Unable to allocate SS session");
+ return;
}
/* Get IPA name from VLR conn and save as ss->vlr_number */
- if (!conn_is_euse(conn)) {
- gsup_rt = gsup_route_find_by_conn(conn);
- if (gsup_rt) {
- ss->vlr_number = (uint8_t *)talloc_strdup(ss, (const char *)gsup_rt->addr);
- ss->vlr_number_len = strlen((const char *)gsup_rt->addr) + 1;
- LOGPSS(ss, LOGL_DEBUG, "Destination IPA name retrieved from GSUP route: %s\n",
- osmo_quote_str((const char *)ss->vlr_number, ss->vlr_number_len));
- } else {
- LOGPSS(ss, LOGL_NOTICE, "Could not find GSUP route, therefore can't set the destination"
- " IPA name. We'll try to look it up later, but this should not"
- " have happened.\n");
- }
+ if (!is_euse_originated) {
+ ss->initial_req_from_ms = gsup_req;
+ free_gsup_req = NULL;
+ ss->vlr_name = gsup_req->source_name;
+ } else {
+ ss->initial_req_from_euse = gsup_req;
+ free_gsup_req = NULL;
}
if (ss_op_is_ussd(req.opcode)) {
- if (conn_is_euse(conn)) {
+ if (is_euse_originated) {
/* EUSE->VLR: MT USSD. EUSE is known ('conn'), VLR is to be resolved */
- ss->u.euse = euse_by_conn(conn);
+ ss->u.euse = euse_by_name(&gsup_req->source_name);
} else {
/* VLR->EUSE: MO USSD. VLR is known ('conn'), EUSE is to be resolved */
struct hlr_ussd_route *rt;
@@ -580,10 +589,10 @@ int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *
}
}
/* dispatch unstructured SS to routing */
- handle_ussd(conn, ss, gsup, &req);
+ handle_ussd(ss, is_euse_originated, &gsup_req->gsup, &req);
} else {
/* dispatch non-call SS to internal code */
- handle_ss(ss, gsup, &req);
+ handle_ss(ss, is_euse_originated, &gsup_req->gsup, &req);
}
break;
case OSMO_GSUP_SESSION_STATE_CONTINUE:
@@ -591,7 +600,8 @@ int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *
if (!ss) {
LOGP(DSS, LOGL_ERROR, "%s/0x%08x: CONTINUE for unknown SS session\n",
gsup->imsi, gsup->session_id);
- goto out_err;
+ osmo_gsup_req_respond_err(gsup_req, GMM_CAUSE_INV_MAND_INFO, "CONTINUE for unknown SS session");
+ return;
}
/* Reschedule self-destruction timer */
@@ -600,10 +610,10 @@ int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *
if (ss_op_is_ussd(req.opcode)) {
/* dispatch unstructured SS to routing */
- handle_ussd(conn, ss, gsup, &req);
+ handle_ussd(ss, is_euse_originated, &gsup_req->gsup, &req);
} else {
/* dispatch non-call SS to internal code */
- handle_ss(ss, gsup, &req);
+ handle_ss(ss, is_euse_originated, &gsup_req->gsup, &req);
}
break;
case OSMO_GSUP_SESSION_STATE_END:
@@ -611,17 +621,17 @@ int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *
if (!ss) {
LOGP(DSS, LOGL_ERROR, "%s/0x%08x: END for unknown SS session\n",
gsup->imsi, gsup->session_id);
- goto out_err;
+ return;
}
/* SS payload is optional for END */
if (gsup->ss_info && gsup->ss_info_len) {
if (ss_op_is_ussd(req.opcode)) {
/* dispatch unstructured SS to routing */
- handle_ussd(conn, ss, gsup, &req);
+ handle_ussd(ss, is_euse_originated, &gsup_req->gsup, &req);
} else {
/* dispatch non-call SS to internal code */
- handle_ss(ss, gsup, &req);
+ handle_ss(ss, is_euse_originated, &gsup_req->gsup, &req);
}
}
@@ -630,18 +640,15 @@ int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *
default:
LOGP(DSS, LOGL_ERROR, "%s/0x%08x: Unknown SS State %d\n", gsup->imsi,
gsup->session_id, gsup->session_state);
- goto out_err;
+ break;
}
- return 0;
-
-out_err:
- return 0;
+ if (free_gsup_req)
+ osmo_gsup_req_free(free_gsup_req);
}
-int rx_proc_ss_error(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup)
+void rx_proc_ss_error(struct osmo_gsup_req *req)
{
- LOGP(DSS, LOGL_NOTICE, "%s/0x%08x: Process SS ERROR (%s)\n", gsup->imsi, gsup->session_id,
- osmo_gsup_session_state_name(gsup->session_state));
- return 0;
+ LOGP(DSS, LOGL_NOTICE, "%s/0x%08x: Process SS ERROR (%s)\n", req->gsup.imsi, req->gsup.session_id,
+ osmo_gsup_session_state_name(req->gsup.session_state));
}
diff --git a/src/hlr_ussd.h b/src/hlr_ussd.h
index 393ca6c..8d5cf9b 100644
--- a/src/hlr_ussd.h
+++ b/src/hlr_ussd.h
@@ -46,8 +46,8 @@ struct hlr_ussd_route *ussd_route_prefix_alloc_ext(struct hlr *hlr, const char *
struct hlr_euse *euse);
void ussd_route_del(struct hlr_ussd_route *rt);
-int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup);
-int rx_proc_ss_error(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup);
+void rx_proc_ss_req(struct osmo_gsup_req *req);
+void rx_proc_ss_error(struct osmo_gsup_req *req);
struct ss_session;
struct ss_request;
@@ -56,6 +56,5 @@ struct ss_request;
struct hlr_iuse {
const char *name;
/* call-back to be called for any incoming USSD messages for this IUSE */
- int (*handle_ussd)(struct osmo_gsup_conn *conn, struct ss_session *ss,
- const struct osmo_gsup_message *gsup, const struct ss_request *req);
+ int (*handle_ussd)(struct ss_session *ss, const struct osmo_gsup_message *gsup, const struct ss_request *req);
};
diff --git a/src/logging.c b/src/logging.c
index 31746e9..c5f5102 100644
--- a/src/logging.c
+++ b/src/logging.c
@@ -31,6 +31,12 @@ const struct log_info_cat hlr_log_info_cat[] = {
.color = "\033[1;35m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
+ [DLU] = {
+ .name = "DLU",
+ .description = "Location Updating",
+ .color = "\033[1;33m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
};
diff --git a/src/logging.h b/src/logging.h
index 0ad2ac3..afef0c6 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -9,6 +9,7 @@ enum {
DAUC,
DSS,
DDGSM,
+ DLU,
};
extern const struct log_info hlr_log_info;
diff --git a/src/lu_fsm.c b/src/lu_fsm.c
new file mode 100644
index 0000000..00214f7
--- /dev/null
+++ b/src/lu_fsm.c
@@ -0,0 +1,287 @@
+/* Roughly follwing "Process Update_Location_HLR" of TS 09.02 */
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/fsm.h>
+#include <osmocom/gsm/apn.h>
+#include <osmocom/gsm/gsm48_ie.h>
+
+#include "logging.h"
+#include "hlr.h"
+#include "gsup_server.h"
+
+#include "db.h"
+
+#define LOG_LU(lu, level, fmt, args...) \
+ LOGPFSML((lu)? (lu)->fi : NULL, level, fmt, ##args)
+
+#define LOG_LU_REQ(lu, req, level, fmt, args...) \
+ LOGPFSML((lu)? (lu)->fi : NULL, level, "%s:" fmt, \
+ osmo_gsup_message_type_name((req)->gsup.message_type), ##args)
+
+struct lu {
+ struct llist_head entry;
+ struct osmo_fsm_inst *fi;
+
+ struct osmo_gsup_req *update_location_req;
+
+ /* Subscriber state at time of initial Update Location Request */
+ struct hlr_subscriber subscr;
+ bool is_ps;
+
+ /* VLR requesting the LU. */
+ struct global_title vlr_name;
+
+ /* If the LU request was received via a proxy and not immediately from a local VLR, this indicates the closest
+ * peer that forwarded the GSUP message. */
+ struct global_title via_proxy;
+};
+LLIST_HEAD(g_all_lu);
+
+enum lu_fsm_event {
+ LU_EV_RX_GSUP,
+};
+
+enum lu_fsm_state {
+ LU_ST_UNVALIDATED,
+ LU_ST_WAIT_INSERT_DATA_RESULT,
+ LU_ST_WAIT_LOCATION_CANCEL_RESULT,
+};
+
+static const struct value_string lu_fsm_event_names[] = {
+ OSMO_VALUE_STRING(LU_EV_RX_GSUP),
+ {}
+};
+
+static struct osmo_tdef_state_timeout lu_fsm_timeouts[32] = {
+ [LU_ST_WAIT_INSERT_DATA_RESULT] = { .T = -4222 },
+ [LU_ST_WAIT_LOCATION_CANCEL_RESULT] = { .T = -4222 },
+};
+
+#define lu_state_chg(lu, state) \
+ osmo_tdef_fsm_inst_state_chg((lu)->fi, state, lu_fsm_timeouts, g_hlr_tdefs, 5)
+
+static void lu_success(struct lu *lu)
+{
+ if (!lu->update_location_req)
+ LOG_LU(lu, LOGL_ERROR, "No request for this LU\n");
+ else
+ osmo_gsup_req_respond_msgt(lu->update_location_req, OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT);
+ lu->update_location_req = NULL;
+ osmo_fsm_inst_term(lu->fi, OSMO_FSM_TERM_REGULAR, NULL);
+}
+
+#define lu_failure(LU, CAUSE, log_msg, args...) do { \
+ if (!(LU)->update_location_req) \
+ LOG_LU(LU, LOGL_ERROR, "No request for this LU\n"); \
+ else \
+ osmo_gsup_req_respond_err((LU)->update_location_req, CAUSE, log_msg, ##args); \
+ (LU)->update_location_req = NULL; \
+ osmo_fsm_inst_term((LU)->fi, OSMO_FSM_TERM_REGULAR, NULL); \
+ } while(0)
+
+static struct osmo_fsm lu_fsm;
+
+static void lu_start(struct osmo_gsup_req *update_location_req)
+{
+ struct osmo_fsm_inst *fi;
+ struct lu *lu;
+
+ OSMO_ASSERT(update_location_req);
+ OSMO_ASSERT(update_location_req->gsup.message_type == OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST);
+
+ fi = osmo_fsm_inst_alloc(&lu_fsm, g_hlr, NULL, LOGL_DEBUG, update_location_req->gsup.imsi);
+ OSMO_ASSERT(fi);
+
+ lu = talloc(fi, struct lu);
+ OSMO_ASSERT(lu);
+ fi->priv = lu;
+ *lu = (struct lu){
+ .fi = fi,
+ .update_location_req = update_location_req,
+ .vlr_name = update_location_req->source_name,
+ .via_proxy = update_location_req->via_proxy,
+ /* According to GSUP specs, OSMO_GSUP_CN_DOMAIN_PS is the default. */
+ .is_ps = (update_location_req->gsup.cn_domain != OSMO_GSUP_CN_DOMAIN_CS),
+ };
+ llist_add(&lu->entry, &g_all_lu);
+
+ osmo_fsm_inst_update_id_f_sanitize(fi, '_', "%s:IMSI-%s", lu->is_ps ? "PS" : "CS", update_location_req->gsup.imsi);
+
+ if (!lu->vlr_name.len) {
+ lu_failure(lu, GMM_CAUSE_NET_FAIL, "LU without a VLR");
+ return;
+ }
+
+ if (db_subscr_get_by_imsi(g_hlr->dbc, update_location_req->gsup.imsi, &lu->subscr) < 0) {
+ lu_failure(lu, GMM_CAUSE_IMSI_UNKNOWN, "Subscriber does not exist");
+ return;
+ }
+
+ /* Check if subscriber is generally permitted on CS or PS
+ * service (as requested) */
+ if (!lu->is_ps && !lu->subscr.nam_cs) {
+ lu_failure(lu, GMM_CAUSE_PLMN_NOTALLOWED, "nam_cs == false");
+ return;
+ }
+ if (lu->is_ps && !lu->subscr.nam_ps) {
+ lu_failure(lu, GMM_CAUSE_GPRS_NOTALLOWED, "nam_ps == false");
+ return;
+ }
+
+ /* TODO: Set subscriber tracing = deactive in VLR/SGSN */
+
+#if 0
+ /* Cancel in old VLR/SGSN, if new VLR/SGSN differs from old */
+ if (!lu->is_ps && strcmp(subscr->vlr_number, vlr_number)) {
+ lu_op_tx_cancel_old(lu);
+ } else if (lu->is_ps && strcmp(subscr->sgsn_number, sgsn_number)) {
+ lu_op_tx_cancel_old(lu);
+ }
+#endif
+
+ /* Store the VLR / SGSN number with the subscriber, so we know where it was last seen. */
+ if (lu->via_proxy.len) {
+ LOG_GSUP_REQ(update_location_req, LOGL_DEBUG, "storing %s = %s, via proxy %s\n",
+ lu->is_ps ? "SGSN number" : "VLR number",
+ global_title_name(&lu->vlr_name),
+ global_title_name(&lu->via_proxy));
+ } else {
+ LOG_GSUP_REQ(update_location_req, LOGL_DEBUG, "storing %s = %s\n",
+ lu->is_ps ? "SGSN number" : "VLR number",
+ global_title_name(&lu->vlr_name));
+ }
+
+ if (db_subscr_lu(g_hlr->dbc, lu->subscr.id, &lu->vlr_name, lu->is_ps, &lu->via_proxy)) {
+ lu_failure(lu, GMM_CAUSE_NET_FAIL, "Cannot update %s in the database",
+ lu->is_ps ? "SGSN number" : "VLR number");
+ return;
+ }
+
+ /* 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_state_chg(lu, LU_ST_WAIT_INSERT_DATA_RESULT);
+}
+
+void lu_rx_gsup(struct osmo_gsup_req *req)
+{
+ struct lu *lu;
+ if (req->gsup.message_type == OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST)
+ return lu_start(req);
+
+ llist_for_each_entry(lu, &g_all_lu, entry) {
+ if (strcmp(lu->subscr.imsi, req->gsup.imsi))
+ continue;
+ if (osmo_fsm_inst_dispatch(lu->fi, LU_EV_RX_GSUP, req)) {
+ LOG_LU_REQ(lu, req, LOGL_ERROR, "Cannot receive GSUP messages in this state\n");
+ osmo_gsup_req_respond_err(req, GMM_CAUSE_MSGT_INCOMP_P_STATE,
+ "LU does not accept GSUP rx");
+ }
+ return;
+ }
+ osmo_gsup_req_respond_err(req, GMM_CAUSE_MSGT_INCOMP_P_STATE, "No Location Updating in progress for this IMSI");
+}
+
+static int lu_fsm_timer_cb(struct osmo_fsm_inst *fi)
+{
+ struct lu *lu = fi->priv;
+ lu_failure(lu, GSM_CAUSE_NET_FAIL, "Timeout");
+ return 0;
+}
+
+static void lu_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
+{
+ struct lu *lu = fi->priv;
+ if (lu->update_location_req)
+ osmo_gsup_req_respond_err(lu->update_location_req, GSM_CAUSE_NET_FAIL, "LU aborted");
+ lu->update_location_req = NULL;
+ llist_del(&lu->entry);
+}
+
+static void lu_fsm_wait_insert_data_result_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ /* Transmit Insert Data Request to the VLR */
+ struct lu *lu = fi->priv;
+ struct hlr_subscriber *subscr = &lu->subscr;
+ struct osmo_gsup_message gsup;
+ uint8_t msisdn_enc[OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN];
+ uint8_t apn[APN_MAXLEN];
+
+ if (osmo_gsup_create_insert_subscriber_data_msg(&gsup, subscr->imsi,
+ subscr->msisdn, msisdn_enc, sizeof(msisdn_enc),
+ apn, sizeof(apn),
+ lu->is_ps? OSMO_GSUP_CN_DOMAIN_PS : OSMO_GSUP_CN_DOMAIN_CS)) {
+ lu_failure(lu, GMM_CAUSE_NET_FAIL, "cannot encode Insert Subscriber Data message");
+ return;
+ }
+
+ if (osmo_gsup_req_respond_nonfinal(lu->update_location_req, &gsup))
+ lu_failure(lu, GMM_CAUSE_NET_FAIL, "cannot send %s", osmo_gsup_message_type_name(gsup.message_type));
+}
+
+void lu_fsm_wait_insert_data_result(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct lu *lu = fi->priv;
+ struct osmo_gsup_req *req;
+
+ switch (event) {
+ case LU_EV_RX_GSUP:
+ req = data;
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+
+ switch (req->gsup.message_type) {
+ case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
+ osmo_gsup_req_free(req);
+ lu_success(lu);
+ break;
+
+ case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
+ lu_failure(lu, GMM_CAUSE_NET_FAIL, "Rx %s", osmo_gsup_message_type_name(req->gsup.message_type));
+ break;
+
+ default:
+ osmo_gsup_req_respond_err(req, GMM_CAUSE_MSGT_INCOMP_P_STATE, "unexpected message type in this state");
+ break;
+ }
+}
+
+#define S(x) (1 << (x))
+
+static const struct osmo_fsm_state lu_fsm_states[] = {
+ [LU_ST_UNVALIDATED] = {
+ .name = "UNVALIDATED",
+ .out_state_mask = 0
+ | S(LU_ST_WAIT_INSERT_DATA_RESULT)
+ ,
+ },
+ [LU_ST_WAIT_INSERT_DATA_RESULT] = {
+ .name = "WAIT_INSERT_DATA_RESULT",
+ .in_event_mask = 0
+ | S(LU_EV_RX_GSUP)
+ ,
+ .onenter = lu_fsm_wait_insert_data_result_onenter,
+ .action = lu_fsm_wait_insert_data_result,
+ },
+};
+
+static struct osmo_fsm lu_fsm = {
+ .name = "lu",
+ .states = lu_fsm_states,
+ .num_states = ARRAY_SIZE(lu_fsm_states),
+ .log_subsys = DLU,
+ .event_names = lu_fsm_event_names,
+ .timer_cb = lu_fsm_timer_cb,
+ .cleanup = lu_fsm_cleanup,
+};
+
+static __attribute__((constructor)) void lu_fsm_init()
+{
+ OSMO_ASSERT(osmo_fsm_register(&lu_fsm) == 0);
+}
diff --git a/src/lu_fsm.h b/src/lu_fsm.h
new file mode 100644
index 0000000..f9a33bd
--- /dev/null
+++ b/src/lu_fsm.h
@@ -0,0 +1,2 @@
+
+void lu_rx_gsup(struct osmo_gsup_req *req);
diff --git a/src/luop.c b/src/luop.c
deleted file mode 100644
index d539c3e..0000000
--- a/src/luop.c
+++ /dev/null
@@ -1,265 +0,0 @@
-/* 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/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 = osmo_gsup_msgb_alloc("GSUP LUOP");
- OSMO_ASSERT(msg_out);
- osmo_gsup_encode(msg_out, gsup);
-
- osmo_gsup_gt_send(luop->gsup_server, &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;
- out->cn_domain = lu->is_ps ? OSMO_GSUP_CN_DOMAIN_PS : OSMO_GSUP_CN_DOMAIN_CS;
-}
-
-/* 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_by_imsi(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;
- osmo_timer_setup(&luop->timer, lu_op_timer_cb, luop);
-
- return luop;
-}
-
-void lu_op_free(struct lu_operation *luop)
-{
- /* Only attempt to remove when it was ever added to a list. */
- if (luop->list.next)
- llist_del(&luop->list);
-
- /* Delete timer just in case it is still pending. */
- osmo_timer_del(&luop->timer);
-
- talloc_free(luop);
-}
-
-struct lu_operation *lu_op_alloc_conn(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup)
-{
- 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) {
- lu_op_free(luop);
- return NULL;
- }
-
- if (global_title_set(&luop->peer, peer_addr, rc)) {
- LOGP(DMAIN, LOGL_ERROR, "Invalid GSUP peer name: %s\n",
- osmo_quote_str((char*)peer_addr, rc));
- lu_op_free(luop);
- return NULL;
- }
-
- if (gsup && gsup->source_name_len)
- global_title_set(&luop->vlr_number, gsup->source_name, gsup->source_name_len);
- else
- luop->vlr_number = luop->peer;
-
- return luop;
-}
-
-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;
-}
-
-/*! 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);
-
- lu_op_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);
-
- lu_op_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 hlr_subscriber *subscr = &luop->subscr;
- struct osmo_gsup_message gsup = { };
- uint8_t msisdn_enc[OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN];
- uint8_t apn[APN_MAXLEN];
- enum osmo_gsup_cn_domain cn_domain;
-
- OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED ||
- luop->state == LU_S_CANCEL_ACK_RECEIVED);
-
- if (luop->is_ps)
- cn_domain = OSMO_GSUP_CN_DOMAIN_PS;
- else
- cn_domain = OSMO_GSUP_CN_DOMAIN_CS;
-
- if (osmo_gsup_create_insert_subscriber_data_msg(&gsup, subscr->imsi, subscr->msisdn, msisdn_enc,
- sizeof(msisdn_enc), apn, sizeof(apn), cn_domain) != 0) {
- LOGP(DMAIN, LOGL_ERROR,
- "IMSI='%s': Cannot notify GSUP client; could not create gsup message for %s\n",
- subscr->imsi, global_title_name(&luop->peer));
- return;
- }
-
- /* 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);
-}
-
-/*! Transmit Delete Subscriber Data to new VLR/SGSN.
- * The luop is not freed. */
-void lu_op_tx_del_subscr_data(struct lu_operation *luop)
-{
- struct osmo_gsup_message gsup;
-
- fill_gsup_msg(&gsup, luop, OSMO_GSUP_MSGT_DELETE_DATA_REQUEST);
-
- /* Send ISD to new VLR/SGSN */
- _luop_tx_gsup(luop, &gsup);
-}
diff --git a/src/luop.h b/src/luop.h
deleted file mode 100644
index 0848510..0000000
--- a/src/luop.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/* 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/>.
- *
- */
-
-#pragma once
-
-#include <stdbool.h>
-
-#include <osmocom/core/timer.h>
-#include <osmocom/gsm/gsup.h>
-
-#include "db.h"
-#include "gsup_server.h"
-
-#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,
-};
-
-extern const struct value_string lu_state_names[];
-
-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;
- /*! Immediate peer VLR/SGSN/proxy starting the request */
- struct global_title peer;
- /*! Actual VLR, possibly behind the immediate peer. */
- struct global_title vlr_number;
-};
-
-
-struct lu_operation *lu_op_alloc(struct osmo_gsup_server *srv);
-struct lu_operation *lu_op_alloc_conn(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup);
-void lu_op_statechg(struct lu_operation *luop, enum lu_state new_state);
-bool lu_op_fill_subscr(struct lu_operation *luop, struct db_context *dbc,
- const char *imsi);
-struct lu_operation *lu_op_by_imsi(const char *imsi,
- const struct llist_head *lst);
-
-void lu_op_tx_error(struct lu_operation *luop, enum gsm48_gmm_cause cause);
-void lu_op_tx_ack(struct lu_operation *luop);
-void lu_op_tx_cancel_old(struct lu_operation *luop);
-void lu_op_tx_insert_subscr_data(struct lu_operation *luop);
-void lu_op_tx_del_subscr_data(struct lu_operation *luop);
-
-void lu_op_free(struct lu_operation *luop);
diff --git a/src/remote_hlr.c b/src/remote_hlr.c
index 3c9d66b..d5c2880 100644
--- a/src/remote_hlr.c
+++ b/src/remote_hlr.c
@@ -172,15 +172,22 @@ int remote_hlr_msgb_send(struct remote_hlr *remote_hlr, struct msgb *msg)
return rc;
}
-int remote_hlr_gsup_send(struct remote_hlr *remote_hlr, const struct osmo_gsup_message *gsup)
+void remote_hlr_gsup_send(struct remote_hlr *remote_hlr, struct osmo_gsup_req *req)
{
int rc;
struct msgb *msg = osmo_gsup_msgb_alloc("GSUP proxy to remote HLR");
- rc = osmo_gsup_encode(msg, gsup);
+ /* To forward to a remote HLR, we need to indicate the source MSC's name in the Source Name IE to make sure the
+ * reply can be routed back. Store the sender MSC in gsup->source_name -- the remote HLR is required to return
+ * this as gsup->destination_name so that the reply gets routed to the original MSC. */
+ struct osmo_gsup_message forward = req->gsup;
+ forward.source_name = req->source_name.val;
+ forward.source_name_len = req->source_name.len;
+
+ rc = osmo_gsup_encode(msg, &forward);
if (rc) {
- LOG_DGSM(gsup->imsi, LOGL_ERROR, "Failed to encode GSUP message: %s\n",
- osmo_gsup_message_type_name(gsup->message_type));
- return rc;
+ osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Failed to encode GSUP message for forwarding\n");
+ return;
}
- return remote_hlr_msgb_send(remote_hlr, msg);
+ remote_hlr_msgb_send(remote_hlr, msg);
+ osmo_gsup_req_free(req);
}
diff --git a/src/remote_hlr.h b/src/remote_hlr.h
index d6e5e19..c579d37 100644
--- a/src/remote_hlr.h
+++ b/src/remote_hlr.h
@@ -24,4 +24,4 @@ struct remote_hlr {
struct remote_hlr *remote_hlr_get(const struct osmo_sockaddr_str *addr, bool create);
void remote_hlr_destroy(struct remote_hlr *remote_hlr);
int remote_hlr_msgb_send(struct remote_hlr *remote_hlr, struct msgb *msg);
-int remote_hlr_gsup_send(struct remote_hlr *remote_hlr, const struct osmo_gsup_message *gsup);
+void remote_hlr_gsup_send(struct remote_hlr *remote_hlr, struct osmo_gsup_req *req);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index e580678..b0a2966 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -2,7 +2,6 @@ SUBDIRS = \
auc \
gsup_server \
db \
- gsup \
db_upgrade \
mslookup_manual_test \
$(NULL)
diff --git a/tests/db/db_test.c b/tests/db/db_test.c
index 94295e1..1e262f8 100644
--- a/tests/db/db_test.c
+++ b/tests/db/db_test.c
@@ -242,7 +242,7 @@ static int db_subscr_lu_str(struct db_context *dbc, int64_t subscr_id,
{
struct global_title vlr_nr;
global_title_set_str(&vlr_nr, vlr_or_sgsn_number);
- return db_subscr_lu(dbc, subscr_id, &vlr_nr, &vlr_nr, is_ps);
+ return db_subscr_lu(dbc, subscr_id, &vlr_nr, is_ps, NULL);
}
static void test_subscr_create_update_sel_delete()
diff --git a/tests/gsup/Makefile.am b/tests/gsup/Makefile.am
deleted file mode 100644
index e5a81c4..0000000
--- a/tests/gsup/Makefile.am
+++ /dev/null
@@ -1,43 +0,0 @@
-AM_CPPFLAGS = \
- $(all_includes) \
- -I$(top_srcdir)/src \
- $(NULL)
-
-AM_CFLAGS = \
- -Wall \
- -ggdb3 \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOGSM_CFLAGS) \
- $(LIBOSMOABIS_CFLAGS) \
- $(NULL)
-
-AM_LDFLAGS = \
- -no-install \
- $(NULL)
-
-EXTRA_DIST = \
- gsup_test.ok \
- gsup_test.err \
- $(NULL)
-
-noinst_PROGRAMS = \
- gsup_test \
- $(NULL)
-
-gsup_test_SOURCES = \
- gsup_test.c \
- $(NULL)
-
-gsup_test_LDADD = \
- $(top_srcdir)/src/luop.c \
- $(top_srcdir)/src/gsup_server.c \
- $(top_srcdir)/src/gsup_router.c \
- $(top_srcdir)/src/global_title.c \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- $(LIBOSMOABIS_LIBS) \
- $(NULL)
-
-.PHONY: update_exp
-update_exp:
- $(builddir)/gsup_test >"$(srcdir)/gsup_test.ok" 2>"$(srcdir)/gsup_test.err"
diff --git a/tests/gsup/gsup_test.c b/tests/gsup/gsup_test.c
deleted file mode 100644
index e03b6ae..0000000
--- a/tests/gsup/gsup_test.c
+++ /dev/null
@@ -1,89 +0,0 @@
-/* (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
- *
- * 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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <string.h>
-
-#include <osmocom/core/logging.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/core/application.h>
-#include <osmocom/gsm/gsup.h>
-
-#include "logging.h"
-#include "luop.h"
-
-struct osmo_gsup_server;
-
-/* override osmo_gsup_gt_send() to not actually send anything. */
-int osmo_gsup_gt_send(struct osmo_gsup_server *gs, const struct global_title *gt, struct msgb *msg)
-{
- LOGP(DMAIN, LOGL_DEBUG, "%s\n", msgb_hexdump(msg));
- msgb_free(msg);
- return 0;
-}
-
-int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi,
- struct hlr_subscriber *subscr)
-{
- return 0;
-}
-
-/* Verify that the internally allocated msgb is large enough */
-void test_gsup_tx_insert_subscr_data()
-{
- struct lu_operation luop = {
- .state = LU_S_LU_RECEIVED,
- .subscr = {
- .imsi = "123456789012345",
- .msisdn = "987654321098765",
- .nam_cs = true,
- .nam_ps = true,
- },
- .is_ps = true,
- };
-
- lu_op_tx_insert_subscr_data(&luop);
-}
-
-const struct log_info_cat default_categories[] = {
- [DMAIN] = {
- .name = "DMAIN",
- .description = "Main Program",
- .enabled = 1, .loglevel = LOGL_DEBUG,
- },
-};
-
-static struct log_info info = {
- .cat = default_categories,
- .num_cat = ARRAY_SIZE(default_categories),
-};
-
-int main(int argc, char **argv)
-{
- void *ctx = talloc_named_const(NULL, 0, "gsup_test");
- osmo_init_logging2(ctx, &info);
- log_set_print_filename(osmo_stderr_target, 0);
- log_set_print_timestamp(osmo_stderr_target, 0);
- log_set_use_color(osmo_stderr_target, 0);
- log_set_print_category(osmo_stderr_target, 1);
-
- test_gsup_tx_insert_subscr_data();
-
- printf("Done.\n");
- return EXIT_SUCCESS;
-}
diff --git a/tests/gsup/gsup_test.err b/tests/gsup/gsup_test.err
deleted file mode 100644
index 0aeae30..0000000
--- a/tests/gsup/gsup_test.err
+++ /dev/null
@@ -1,2 +0,0 @@
-DMAIN 10 01 08 21 43 65 87 09 21 43 f5 08 09 08 89 67 45 23 01 89 67 f5 05 07 10 01 01 12 02 01 2a 28 01 01
-DMAIN LU OP state change: LU RECEIVED -> ISD SENT
diff --git a/tests/gsup/gsup_test.ok b/tests/gsup/gsup_test.ok
deleted file mode 100644
index 619c561..0000000
--- a/tests/gsup/gsup_test.ok
+++ /dev/null
@@ -1 +0,0 @@
-Done.
diff --git a/tests/gsup_server/Makefile.am b/tests/gsup_server/Makefile.am
index 686ce37..9c5d566 100644
--- a/tests/gsup_server/Makefile.am
+++ b/tests/gsup_server/Makefile.am
@@ -31,6 +31,7 @@ gsup_server_test_SOURCES = \
gsup_server_test_LDADD = \
$(top_srcdir)/src/gsup_server.c \
$(top_srcdir)/src/gsup_router.c \
+ $(top_srcdir)/src/global_title.c \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOABIS_LIBS) \
diff --git a/tests/mslookup_manual_test/Makefile.am b/tests/mslookup_manual_test/Makefile.am
index 20c3059..ef92a47 100644
--- a/tests/mslookup_manual_test/Makefile.am
+++ b/tests/mslookup_manual_test/Makefile.am
@@ -1,6 +1,7 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/src \
+ -I$(top_srcdir)/include \
$(NULL)
AM_CFLAGS = \
diff --git a/tests/testsuite.at b/tests/testsuite.at
index a8efe42..140c24b 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -15,13 +15,6 @@ cat $abs_srcdir/auc/auc_ts_55_205_test_sets.err > experr
AT_CHECK([$abs_top_builddir/tests/auc/auc_ts_55_205_test_sets], [], [expout], [experr])
AT_CLEANUP
-AT_SETUP([gsup])
-AT_KEYWORDS([gsup])
-cat $abs_srcdir/gsup/gsup_test.ok > expout
-cat $abs_srcdir/gsup/gsup_test.err > experr
-AT_CHECK([$abs_top_builddir/tests/gsup/gsup_test], [], [expout], [experr])
-AT_CLEANUP
-
AT_SETUP([gsup_server])
AT_KEYWORDS([gsup_server])
cat $abs_srcdir/gsup_server/gsup_server_test.ok > expout