diff options
author | Neels Hofmeyr <neels@hofmeyr.de> | 2019-12-06 17:09:56 +0100 |
---|---|---|
committer | Neels Hofmeyr <neels@hofmeyr.de> | 2019-12-16 17:17:58 +0100 |
commit | e78241ae0796efc8d283a25b773b37abd2ed2675 (patch) | |
tree | 46d7b3b7470868401c6ff888accb30583502fc6e | |
parent | 62d916f3cd6a71526a7ad196178ddde8806c5fcf (diff) |
proxy routing refactor
Change-Id: I43ad27f6d768df02abb3459ac4c43bb80cc1cbeb
-rw-r--r-- | include/osmocom/hlr/dgsm.h | 1 | ||||
-rw-r--r-- | include/osmocom/hlr/gsup_server.h | 2 | ||||
-rw-r--r-- | include/osmocom/hlr/mslookup_server.h | 6 | ||||
-rw-r--r-- | include/osmocom/hlr/proxy.h | 12 | ||||
-rw-r--r-- | include/osmocom/hlr/remote_hlr.h | 15 | ||||
-rw-r--r-- | src/dgsm.c | 75 | ||||
-rw-r--r-- | src/dgsm_vty.c | 2 | ||||
-rw-r--r-- | src/gsup_server.c | 36 | ||||
-rw-r--r-- | src/hlr.c | 1 | ||||
-rw-r--r-- | src/mslookup_server.c | 54 | ||||
-rw-r--r-- | src/proxy.c | 218 | ||||
-rw-r--r-- | src/remote_hlr.c | 72 | ||||
-rw-r--r-- | tests/gsup_server/Makefile.am | 1 |
13 files changed, 292 insertions, 203 deletions
diff --git a/include/osmocom/hlr/dgsm.h b/include/osmocom/hlr/dgsm.h index f06e381..b3d73e9 100644 --- a/include/osmocom/hlr/dgsm.h +++ b/include/osmocom/hlr/dgsm.h @@ -21,6 +21,7 @@ #include <osmocom/mslookup/mslookup.h> #include <osmocom/hlr/gsup_server.h> +#include <osmocom/hlr/logging.h> #include <osmocom/gsupclient/gsup_peer_id.h> #include <osmocom/gsupclient/gsup_req.h> diff --git a/include/osmocom/hlr/gsup_server.h b/include/osmocom/hlr/gsup_server.h index b7cfb89..22c9a10 100644 --- a/include/osmocom/hlr/gsup_server.h +++ b/include/osmocom/hlr/gsup_server.h @@ -74,3 +74,5 @@ int osmo_gsup_create_insert_subscriber_data_msg(struct osmo_gsup_message *gsup, 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 osmo_gsup_forward_to_local_peer(struct osmo_gsup_server *server, const struct osmo_gsup_peer_id *to_peer, + struct osmo_gsup_req *req, struct osmo_gsup_message *modified_gsup); diff --git a/include/osmocom/hlr/mslookup_server.h b/include/osmocom/hlr/mslookup_server.h index f76e92f..aed7ad0 100644 --- a/include/osmocom/hlr/mslookup_server.h +++ b/include/osmocom/hlr/mslookup_server.h @@ -63,6 +63,10 @@ int mslookup_server_msc_service_del(struct mslookup_server_msc_cfg *msc, const c extern const struct osmo_ipa_name mslookup_server_msc_wildcard; struct mslookup_server_msc_cfg *mslookup_server_msc_get(const struct osmo_ipa_name *msc_name, bool create); -struct mslookup_service_host *mslookup_server_get_local_gsup_addr(); +const struct mslookup_service_host *mslookup_server_get_local_gsup_addr(); void mslookup_server_rx(const struct osmo_mslookup_query *query, struct osmo_mslookup_result *result); + +bool subscriber_has_done_lu_here(const struct osmo_mslookup_query *query, + uint32_t *lu_age_p, struct osmo_ipa_name *local_msc_name, + char *ret_imsi, size_t ret_imsi_len); diff --git a/include/osmocom/hlr/proxy.h b/include/osmocom/hlr/proxy.h index 8412dd2..92ed30a 100644 --- a/include/osmocom/hlr/proxy.h +++ b/include/osmocom/hlr/proxy.h @@ -28,12 +28,6 @@ struct osmo_gsup_req; struct remote_hlr; -struct proxy_pending_gsup_req { - struct llist_head entry; - struct osmo_gsup_req *req; - timestamp_t received_at; -}; - struct proxy { struct llist_head subscr_list; struct llist_head pending_gsup_reqs; @@ -87,8 +81,8 @@ void proxy_subscrs_get_by_remote_hlr(struct proxy *proxy, const struct osmo_sock int proxy_subscr_create_or_update(struct proxy *proxy, const struct proxy_subscr *proxy_subscr); int proxy_subscr_del(struct proxy *proxy, const char *imsi); -void proxy_subscr_forward_to_remote_hlr(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, - struct osmo_gsup_req *req); +int proxy_subscr_forward_to_remote_hlr(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, + struct osmo_gsup_req *req); void proxy_subscr_forward_to_remote_hlr_resolved(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, struct remote_hlr *remote_hlr, struct osmo_gsup_req *req); @@ -96,6 +90,6 @@ int proxy_subscr_forward_to_vlr(struct proxy *proxy, const struct proxy_subscr * const struct osmo_gsup_message *gsup, struct remote_hlr *from_remote_hlr); void proxy_subscr_remote_hlr_resolved(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, - struct remote_hlr *remote_hlr); + const struct osmo_sockaddr_str *remote_hlr_addr); void proxy_subscr_remote_hlr_up(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, struct remote_hlr *remote_hlr); diff --git a/include/osmocom/hlr/remote_hlr.h b/include/osmocom/hlr/remote_hlr.h index 4f9f939..6a4e8a1 100644 --- a/include/osmocom/hlr/remote_hlr.h +++ b/include/osmocom/hlr/remote_hlr.h @@ -40,9 +40,20 @@ struct remote_hlr { struct llist_head entry; struct osmo_sockaddr_str addr; struct osmo_gsup_client *gsupc; + struct llist_head pending_up_callbacks; }; -struct remote_hlr *remote_hlr_get(const struct osmo_sockaddr_str *addr, bool create); +/*! Receive a remote_hlr address when connecting succeeded, or remote_hlr == NULL on error. + * \param addr GSUP IP address and port for which the connection was requested. + * \param remote_hlr The connected remote_hlr ready for sending, or NULL if connecting failed. + * \param data Same a passed to remote_hlr_get_or_connect(). */ +typedef void (*remote_hlr_connect_result_cb_t)(const struct osmo_sockaddr_str *addr, struct remote_hlr *remote_hlr, void *data); + +struct remote_hlr *remote_hlr_get_or_connect(const struct osmo_sockaddr_str *addr, bool connect, + remote_hlr_connect_result_cb_t connect_result_cb, void *data); void remote_hlr_destroy(struct remote_hlr *remote_hlr); int remote_hlr_msgb_send(struct remote_hlr *remote_hlr, struct msgb *msg); -void remote_hlr_gsup_forward_to_remote_hlr(struct remote_hlr *remote_hlr, struct osmo_gsup_req *req); +void remote_hlr_gsup_forward_to_remote_hlr(struct remote_hlr *remote_hlr, struct osmo_gsup_req *req, + struct osmo_gsup_message *modified_gsup); + +bool remote_hlr_is_up(struct remote_hlr *remote_hlr); @@ -44,8 +44,7 @@ static void resolve_hlr_result_cb(struct osmo_mslookup_client *client, { struct proxy *proxy = g_hlr->gs->proxy; struct proxy_subscr proxy_subscr; - const struct osmo_sockaddr_str *use_addr; - struct remote_hlr *remote_hlr; + const struct osmo_sockaddr_str *remote_hlr_addr; /* A remote HLR is answering back, indicating that it is the home HLR for a given IMSI. * There should be a mostly empty proxy entry for that IMSI. @@ -64,9 +63,9 @@ static void resolve_hlr_result_cb(struct osmo_mslookup_client *client, } if (osmo_sockaddr_str_is_nonzero(&result->host_v4)) - use_addr = &result->host_v4; + remote_hlr_addr = &result->host_v4; else if (osmo_sockaddr_str_is_nonzero(&result->host_v6)) - use_addr = &result->host_v6; + remote_hlr_addr = &result->host_v6; else { LOG_DGSM(query->id.imsi, LOGL_ERROR, "Invalid address for remote HLR: %s\n", osmo_mslookup_result_name_c(OTC_SELECT, query, result)); @@ -74,30 +73,13 @@ static void resolve_hlr_result_cb(struct osmo_mslookup_client *client, return; } - remote_hlr = remote_hlr_get(use_addr, true); - if (!remote_hlr) { - proxy_subscr_del(proxy, query->id.imsi); - return; - } - if (proxy_subscr_get_by_imsi(&proxy_subscr, proxy, query->id.imsi)) { LOG_DGSM(query->id.imsi, LOGL_ERROR, "No proxy entry for mslookup result: %s\n", osmo_mslookup_result_name_c(OTC_SELECT, query, result)); return; } - /* The remote HLR already exists and is connected. Messages for this IMSI were spooled because we did not know - * which remote HLR was responsible. Now we know, send this IMSI's messages now. */ - LOG_DGSM(query->id.imsi, LOGL_DEBUG, "Resolved remote HLR, sending spooled GSUP messages: %s\n", - osmo_mslookup_result_name_c(OTC_SELECT, query, result)); - - proxy_subscr_remote_hlr_resolved(proxy, &proxy_subscr, remote_hlr); - - if (!remote_hlr->gsupc || !remote_hlr->gsupc->is_connected) { - LOG_REMOTE_HLR(remote_hlr, LOGL_DEBUG, "Waiting for link-up\n"); - return; - } - proxy_subscr_remote_hlr_up(proxy, &proxy_subscr, remote_hlr); + proxy_subscr_remote_hlr_resolved(proxy, &proxy_subscr, remote_hlr_addr); } /* Return true when the message has been handled by D-GSM. */ @@ -114,8 +96,10 @@ bool dgsm_check_forward_gsup_msg(struct osmo_gsup_req *req) return false; /* Are we already forwarding this IMSI to a remote HLR? */ - if (proxy_subscr_get_by_imsi(&proxy_subscr, proxy, req->gsup.imsi) == 0) - goto yes_we_are_proxying; + if (proxy_subscr_get_by_imsi(&proxy_subscr, proxy, req->gsup.imsi) == 0) { + proxy_subscr_forward_to_remote_hlr(proxy, &proxy_subscr, req); + return true; + } /* The IMSI is not known locally, so we want to proxy to a remote HLR, but no proxy entry exists yet. We need to * look up the subscriber in remote HLRs via D-GSM mslookup, forward GSUP and reply once a result is back from @@ -125,40 +109,41 @@ bool dgsm_check_forward_gsup_msg(struct osmo_gsup_req *req) proxy_subscr = (struct proxy_subscr){}; OSMO_STRLCPY_ARRAY(proxy_subscr.imsi, req->gsup.imsi); if (proxy_subscr_create_or_update(proxy, &proxy_subscr)) { - LOG_DGSM(req->gsup.imsi, LOGL_ERROR, "Failed to create proxy entry\n"); - return false; + osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Failed to create proxy entry\n"); + return true; } /* Is a fixed gateway proxy configured? */ if (osmo_sockaddr_str_is_nonzero(&g_hlr->mslookup.client.gsup_gateway_proxy)) { - struct remote_hlr *gsup_gateway_proxy = remote_hlr_get(&g_hlr->mslookup.client.gsup_gateway_proxy, true); - if (!gsup_gateway_proxy) { - LOG_DGSM(req->gsup.imsi, LOGL_ERROR, - "Failed to set up fixed gateway proxy " OSMO_SOCKADDR_STR_FMT "\n", - OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.client.gsup_gateway_proxy)); - return false; - } + proxy_subscr_remote_hlr_resolved(proxy, &proxy_subscr, &g_hlr->mslookup.client.gsup_gateway_proxy); - proxy_subscr_remote_hlr_resolved(proxy, &proxy_subscr, gsup_gateway_proxy); - - /* Update info */ + /* Proxy database modified, update info */ if (proxy_subscr_get_by_imsi(&proxy_subscr, proxy, req->gsup.imsi)) { - LOG_DGSM(req->gsup.imsi, LOGL_ERROR, "Proxy entry disappeared\n"); - return false; + osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Internal proxy error\n"); + return true; } - goto yes_we_are_proxying; + + proxy_subscr_forward_to_remote_hlr(proxy, &proxy_subscr, req); + return true; } - /* Kick off an mslookup for the remote HLR. */ - if (!g_hlr->mslookup.client.client) { + /* Kick off an mslookup for the remote HLR? This check could be up first on the top, but do it only now so that + * if the mslookup client disconnected, we still continue to service open proxy entries. */ + if (!osmo_mslookup_client_active(g_hlr->mslookup.client.client)) { LOG_GSUP_REQ(req, LOGL_DEBUG, "mslookup client not running, cannot query remote home HLR\n"); return false; } + /* First spool message, then kick off mslookup. If the proxy denies this message type, then don't do anything. */ + if (proxy_subscr_forward_to_remote_hlr(proxy, &proxy_subscr, req)) { + /* If the proxy denied forwarding, an error response was already generated. */ + return true; + } + query = (struct osmo_mslookup_query){ .id = { .type = OSMO_MSLOOKUP_ID_IMSI, - } + }, }; OSMO_STRLCPY_ARRAY(query.id.imsi, req->gsup.imsi); OSMO_STRLCPY_ARRAY(query.service, OSMO_MSLOOKUP_SERVICE_HLR_GSUP); @@ -171,14 +156,10 @@ bool dgsm_check_forward_gsup_msg(struct osmo_gsup_req *req) 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, req->gsup.imsi); + /* mslookup seems to not be working. Try handling it locally. */ return false; } -yes_we_are_proxying: - - /* 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. */ - proxy_subscr_forward_to_remote_hlr(proxy, &proxy_subscr, req); return true; } diff --git a/src/dgsm_vty.c b/src/dgsm_vty.c index 62f07c7..88ea58a 100644 --- a/src/dgsm_vty.c +++ b/src/dgsm_vty.c @@ -516,7 +516,7 @@ DEFUN(do_mslookup_show_services, "List configured service addresses as sent to remote mslookup requests\n") { struct mslookup_server_msc_cfg *msc; - struct mslookup_service_host *local_hlr = mslookup_server_get_local_gsup_addr(); + const struct mslookup_service_host *local_hlr = mslookup_server_get_local_gsup_addr(); vty_out(vty, "Local GSUP HLR address returned in mslookup responses for local IMSIs:"); if (osmo_sockaddr_str_is_nonzero(&local_hlr->host_v4)) diff --git a/src/gsup_server.c b/src/gsup_server.c index 4819ea4..9a9a57b 100644 --- a/src/gsup_server.c +++ b/src/gsup_server.c @@ -503,3 +503,39 @@ int osmo_gsup_create_insert_subscriber_data_msg(struct osmo_gsup_message *gsup, return 0; } + +int osmo_gsup_forward_to_local_peer(struct osmo_gsup_server *server, const struct osmo_gsup_peer_id *to_peer, + struct osmo_gsup_req *req, struct osmo_gsup_message *modified_gsup) +{ + int rc; + /* To forward to a remote entity (HLR, SMSC,...), we need to indicate the original source name in the Source + * Name IE to make sure the reply can be routed back. Store the sender in gsup->source_name -- the remote entity + * is required to return this as gsup->destination_name so that the reply gets routed to the original sender. */ + struct osmo_gsup_message forward = *(modified_gsup? : &req->gsup); + + if (req->source_name.type != OSMO_GSUP_PEER_ID_IPA_NAME) { + osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Unsupported GSUP peer id type: %s", + osmo_gsup_peer_id_type_name(req->source_name.type)); + rc = -ENOTSUP; + goto routing_error; + } + forward.source_name = req->source_name.ipa_name.val; + forward.source_name_len = req->source_name.ipa_name.len; + + if (to_peer->type != OSMO_GSUP_PEER_ID_IPA_NAME) { + osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Unsupported GSUP peer id type: %s", + osmo_gsup_peer_id_type_name(to_peer->type)); + rc = -ENOTSUP; + goto routing_error; + } + LOG_GSUP_REQ(req, LOGL_INFO, "Forwarding to %s\n", osmo_gsup_peer_id_to_str(to_peer)); + rc = osmo_gsup_enc_send_to_ipa_name(server, &to_peer->ipa_name, &forward); + if (rc) + goto routing_error; + osmo_gsup_req_free(req); + return 0; + +routing_error: + osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_ROUTING_ERROR, true); + return rc; +} @@ -507,6 +507,7 @@ static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg) return 0; } + /* HLR related messages that are handled at this HLR instance */ switch (req->gsup.message_type) { /* requests sent to us */ case OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST: diff --git a/src/mslookup_server.c b/src/mslookup_server.c index 29768c8..72729b3 100644 --- a/src/mslookup_server.c +++ b/src/mslookup_server.c @@ -49,7 +49,7 @@ static void set_result(struct osmo_mslookup_result *result, result->age = age; } -struct mslookup_service_host *mslookup_server_get_local_gsup_addr() +const struct mslookup_service_host *mslookup_server_get_local_gsup_addr() { static struct mslookup_service_host gsup_bind = {}; struct mslookup_service_host *host; @@ -190,7 +190,7 @@ int mslookup_server_msc_service_del(struct mslookup_server_msc_cfg *msc, const c static void mslookup_server_rx_hlr_gsup(const struct osmo_mslookup_query *query, struct osmo_mslookup_result *result) { - struct mslookup_service_host *host; + const struct mslookup_service_host *host; int rc; switch (query->id.type) { case OSMO_MSLOOKUP_ID_IMSI: @@ -231,18 +231,21 @@ static void mslookup_server_rx_hlr_gsup(const struct osmo_mslookup_query *query, * VLR, we will find a valid location updating with vlr_number, and no vlr_via_proxy entry. */ static bool subscriber_has_done_lu_here_hlr(const struct osmo_mslookup_query *query, uint32_t *lu_age, - struct osmo_ipa_name *local_msc_name) + struct osmo_ipa_name *local_msc_name, + struct hlr_subscriber *ret_subscr) { - struct hlr_subscriber subscr; + struct hlr_subscriber _subscr; int rc; uint32_t age; + struct hlr_subscriber *subscr = ret_subscr ? : &_subscr; + switch (query->id.type) { case OSMO_MSLOOKUP_ID_IMSI: - rc = db_subscr_get_by_imsi(g_hlr->dbc, query->id.imsi, &subscr); + rc = db_subscr_get_by_imsi(g_hlr->dbc, query->id.imsi, subscr); break; case OSMO_MSLOOKUP_ID_MSISDN: - rc = db_subscr_get_by_msisdn(g_hlr->dbc, query->id.msisdn, &subscr); + rc = db_subscr_get_by_msisdn(g_hlr->dbc, query->id.msisdn, subscr); break; default: LOGP(DMSLOOKUP, LOGL_ERROR, "Unknown mslookup ID type: %d\n", query->id.type); @@ -255,22 +258,22 @@ static bool subscriber_has_done_lu_here_hlr(const struct osmo_mslookup_query *qu return false; } - if (!subscr.vlr_number[0]) { + if (!subscr->vlr_number[0]) { LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: not attached (vlr_number unset)\n", osmo_mslookup_result_name_c(OTC_SELECT, query, NULL)); } - if (subscr.vlr_via_proxy.len) { + if (subscr->vlr_via_proxy.len) { /* The VLR is behind a proxy, the subscriber is not attached to a local VLR but a remote one. That * remote proxy should instead respond to the service lookup request. */ LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: last attach is not at local VLR, but at VLR '%s' via proxy %s\n", osmo_mslookup_result_name_c(OTC_SELECT, query, NULL), - subscr.vlr_number, - osmo_ipa_name_to_str(&subscr.vlr_via_proxy)); + subscr->vlr_number, + osmo_ipa_name_to_str(&subscr->vlr_via_proxy)); return false; } - if (!timestamp_age(&subscr.last_lu_seen, &age)) { + if (!timestamp_age(&subscr->last_lu_seen, &age)) { LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: Invalid last_lu_seen timestamp for subscriber\n", osmo_mslookup_result_name_c(OTC_SELECT, query, NULL)); return false; @@ -283,7 +286,7 @@ static bool subscriber_has_done_lu_here_hlr(const struct osmo_mslookup_query *qu } *lu_age = age; - osmo_ipa_name_set_str(local_msc_name, subscr.vlr_number); + osmo_ipa_name_set_str(local_msc_name, subscr->vlr_number); LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: attached %u seconds ago at local VLR %s\n", osmo_mslookup_result_name_c(OTC_SELECT, query, NULL), age, osmo_ipa_name_to_str(local_msc_name)); @@ -297,7 +300,8 @@ static bool subscriber_has_done_lu_here_hlr(const struct osmo_mslookup_query *qu */ static bool subscriber_has_done_lu_here_proxy(const struct osmo_mslookup_query *query, uint32_t *lu_age, - struct osmo_ipa_name *local_msc_name) + struct osmo_ipa_name *local_msc_name, + struct proxy_subscr *ret_proxy_subscr) { struct proxy_subscr proxy_subscr; uint32_t age; @@ -350,12 +354,14 @@ static bool subscriber_has_done_lu_here_proxy(const struct osmo_mslookup_query * age, osmo_ipa_name_to_str(local_msc_name), OSMO_SOCKADDR_STR_FMT_ARGS(&proxy_subscr.remote_hlr_addr)); + if (ret_proxy_subscr) + *ret_proxy_subscr = proxy_subscr; return true; } -static bool subscriber_has_done_lu_here(const struct osmo_mslookup_query *query, - uint32_t *lu_age_p, - struct osmo_ipa_name *local_msc_name) +bool subscriber_has_done_lu_here(const struct osmo_mslookup_query *query, + uint32_t *lu_age_p, struct osmo_ipa_name *local_msc_name, + char *ret_imsi, size_t ret_imsi_len) { bool attached_here; uint32_t lu_age = 0; @@ -363,6 +369,9 @@ static bool subscriber_has_done_lu_here(const struct osmo_mslookup_query *query, bool attached_here_proxy; uint32_t proxy_lu_age = 0; struct osmo_ipa_name proxy_msc_name = {}; + struct proxy_subscr proxy_subscr; + struct hlr_subscriber db_subscr; + /* First ask the local HLR db, but if the local proxy record indicates a more recent LU, use that instead. * For all usual cases, only one of these will reflect a LU, even if a subscriber had more than one home HLR: @@ -372,14 +381,19 @@ static bool subscriber_has_done_lu_here(const struct osmo_mslookup_query *query, * the local HLR database, there might occur a situation where both reflect a LU. So, to be safe against all * situations, compare the two entries. */ - attached_here = subscriber_has_done_lu_here_hlr(query, &lu_age, &msc_name); - attached_here_proxy = subscriber_has_done_lu_here_proxy(query, &proxy_lu_age, &proxy_msc_name); + attached_here = subscriber_has_done_lu_here_hlr(query, &lu_age, &msc_name, &db_subscr); + attached_here_proxy = subscriber_has_done_lu_here_proxy(query, &proxy_lu_age, &proxy_msc_name, &proxy_subscr); /* If proxy has a younger lu, replace. */ if (attached_here_proxy && (!attached_here || (proxy_lu_age < lu_age))) { attached_here = true; lu_age = proxy_lu_age; msc_name = proxy_msc_name; + if (ret_imsi) + osmo_strlcpy(ret_imsi, proxy_subscr.imsi, ret_imsi_len); + } else if (attached_here) { + if (ret_imsi) + osmo_strlcpy(ret_imsi, db_subscr.imsi, ret_imsi_len); } if (attached_here && !msc_name.len) { @@ -403,7 +417,7 @@ static bool subscriber_has_done_lu_here(const struct osmo_mslookup_query *query, /* A remote entity is asking us whether we are providing the given service for the given subscriber. */ void mslookup_server_rx(const struct osmo_mslookup_query *query, - struct osmo_mslookup_result *result) + struct osmo_mslookup_result *result) { const struct mslookup_service_host *service_host; uint32_t age; @@ -417,7 +431,7 @@ void mslookup_server_rx(const struct osmo_mslookup_query *query, /* All other service types: answer when the subscriber has done a LU that is either listed in the local HLR or * in the GSUP proxy database: i.e. if the subscriber has done a Location Updating at an VLR belonging to this * HLR. Respond with whichever services are configured in the osmo-hlr.cfg. */ - if (!subscriber_has_done_lu_here(query, &age, &msc_name)) { + if (!subscriber_has_done_lu_here(query, &age, &msc_name, NULL, 0)) { *result = not_found; return; } diff --git a/src/proxy.c b/src/proxy.c index 0d3fd13..b9cd313 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -47,7 +47,8 @@ (gsup_msg) ? osmo_gsup_message_type_name((gsup_msg)->message_type) : "NULL", \ ##args) -/* Why have a separate struct to add an llist_head entry? +/* The proxy subscriber database. + * Why have a separate struct to add an llist_head entry? * This is to keep the option open to store the proxy data in the database instead, without any visible effect outside * of proxy.c. */ struct proxy_subscr_listentry { @@ -56,10 +57,16 @@ struct proxy_subscr_listentry { struct proxy_subscr data; }; +struct proxy_pending_gsup_req { + struct llist_head entry; + struct osmo_gsup_req *req; + timestamp_t received_at; +}; + /* 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 proxy_defer_gsup_req(struct proxy *proxy, struct osmo_gsup_req *req) +static void proxy_deferred_gsup_req_add(struct proxy *proxy, struct osmo_gsup_req *req) { struct proxy_pending_gsup_req *m; @@ -70,50 +77,47 @@ static void proxy_defer_gsup_req(struct proxy *proxy, struct osmo_gsup_req *req) llist_add_tail(&m->entry, &proxy->pending_gsup_reqs); } -/* Unable to resolve remote HLR for this IMSI, Answer with error back to the sender. */ -static void proxy_defer_gsup_message_err(struct proxy *proxy, struct proxy_pending_gsup_req *m) +static void proxy_pending_req_remote_hlr_connect_result(struct osmo_gsup_req *req, struct remote_hlr *remote_hlr) { - osmo_gsup_req_respond_err(m->req, GMM_CAUSE_IMSI_UNKNOWN, "could not reach home HLR"); - m->req = NULL; + if (!remote_hlr || !remote_hlr_is_up(remote_hlr)) { + osmo_gsup_req_respond_err(req, GMM_CAUSE_IMSI_UNKNOWN, "Proxy: Failed to connect to home HLR"); + return; + } + + remote_hlr_gsup_forward_to_remote_hlr(remote_hlr, req, NULL); } -/* Forward spooled message for this IMSI to remote HLR. */ -static void proxy_defer_gsup_message_send(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, - struct proxy_pending_gsup_req *m, struct remote_hlr *remote_hlr) +static bool proxy_deferred_gsup_req_waiting(struct proxy *proxy, const char *imsi) { - LOG_PROXY_SUBSCR_MSG(proxy_subscr, &m->req->gsup, LOGL_INFO, "Forwarding deferred message\n"); - proxy_subscr_forward_to_remote_hlr_resolved(proxy, proxy_subscr, remote_hlr, m->req); - m->req = NULL; + struct proxy_pending_gsup_req *p; + OSMO_ASSERT(imsi); + + llist_for_each_entry(p, &proxy->pending_gsup_reqs, entry) { + if (strcmp(p->req->gsup.imsi, imsi)) + continue; + return true; + } + return false; } -/* Result of looking for remote HLR. If it failed, pass remote_hlr as NULL. On failure, the proxy_subscr and the - * remote_hlr may be passed NULL. The IMSI then reflects who the error was for. */ -static void proxy_defer_gsup_message_pop(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, - const char *imsi, struct remote_hlr *remote_hlr) +/* Result of looking for remote HLR. If it failed, pass remote_hlr as NULL. On failure, the remote_hlr may be passed + * NULL. */ +static void proxy_deferred_gsup_req_pop(struct proxy *proxy, const char *imsi, struct remote_hlr *remote_hlr) { - struct proxy_pending_gsup_req *m, *n; - if (!imsi && proxy_subscr) - imsi = proxy_subscr->imsi; + struct proxy_pending_gsup_req *p, *n; OSMO_ASSERT(imsi); - if (!remote_hlr) - LOGP(DDGSM, LOGL_ERROR, "IMSI-%s: No remote HLR found, dropping spooled GSUP messages\n", imsi); - - llist_for_each_entry_safe(m, n, &proxy->pending_gsup_reqs, entry) { - if (strcmp(m->req->gsup.imsi, imsi)) + llist_for_each_entry_safe(p, n, &proxy->pending_gsup_reqs, entry) { + if (strcmp(p->req->gsup.imsi, imsi)) continue; - if (!remote_hlr) - proxy_defer_gsup_message_err(proxy, m); - else - proxy_defer_gsup_message_send(proxy, proxy_subscr, m, remote_hlr); - - llist_del(&m->entry); - talloc_free(m); + proxy_pending_req_remote_hlr_connect_result(p->req, remote_hlr); + p->req = NULL; + llist_del(&p->entry); + talloc_free(p); } } - static bool proxy_subscr_matches_imsi(const struct proxy_subscr *proxy_subscr, const char *imsi) { if (!proxy_subscr || !imsi) @@ -170,21 +174,6 @@ int proxy_subscr_get_by_msisdn(struct proxy_subscr *dst, struct proxy *proxy, co return 0; } -void proxy_subscrs_get_by_remote_hlr(struct proxy *proxy, const struct osmo_sockaddr_str *remote_hlr_addr, - bool (*yield)(struct proxy *proxy, const struct proxy_subscr *subscr, void *data), - void *data) -{ - struct proxy_subscr_listentry *e; - if (!proxy) - return; - llist_for_each_entry(e, &proxy->subscr_list, entry) { - if (!osmo_sockaddr_str_cmp(remote_hlr_addr, &e->data.remote_hlr_addr)) { - if (!yield(proxy, &e->data, data)) - return; - } - } -} - int proxy_subscr_create_or_update(struct proxy *proxy, const struct proxy_subscr *proxy_subscr) { struct proxy_subscr_listentry *e = _proxy_get_by_imsi(proxy, proxy_subscr->imsi); @@ -207,7 +196,7 @@ int _proxy_subscr_del(struct proxy_subscr_listentry *e) int proxy_subscr_del(struct proxy *proxy, const char *imsi) { struct proxy_subscr_listentry *e; - proxy_defer_gsup_message_pop(proxy, NULL, imsi, NULL); + proxy_deferred_gsup_req_pop(proxy, imsi, NULL); e = _proxy_get_by_imsi(proxy, imsi); if (!e) return -ENOENT; @@ -264,42 +253,6 @@ void proxy_del(struct proxy *proxy) talloc_free(proxy); } -void proxy_subscr_remote_hlr_up(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, - struct remote_hlr *remote_hlr) -{ - proxy_defer_gsup_message_pop(proxy, proxy_subscr, proxy_subscr->imsi, remote_hlr); -} - -void proxy_subscr_remote_hlr_resolved(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, - struct remote_hlr *remote_hlr) -{ - struct proxy_subscr proxy_subscr_new; - - if (osmo_sockaddr_str_is_nonzero(&proxy_subscr->remote_hlr_addr)) { - if (!osmo_sockaddr_str_cmp(&remote_hlr->addr, &proxy_subscr->remote_hlr_addr)) { - /* Already have this remote address */ - return; - } else { - LOG_PROXY_SUBSCR(proxy_subscr, LOGL_NOTICE, - "Remote HLR address changes to " OSMO_SOCKADDR_STR_FMT "\n", - OSMO_SOCKADDR_STR_FMT_ARGS(&remote_hlr->addr)); - } - } - - /* Store the address. Make a copy to modify. */ - proxy_subscr_new = *proxy_subscr; - proxy_subscr_new.remote_hlr_addr = remote_hlr->addr; - - if (proxy_subscr_create_or_update(proxy, &proxy_subscr_new)) { - LOG_PROXY_SUBSCR(proxy_subscr, LOGL_ERROR, "Failed to store proxy entry for remote HLR\n"); - /* If no remote HLR is known for the IMSI, the proxy entry is pointless. */ - proxy_subscr_del(proxy, proxy_subscr_new.imsi); - return; - } - proxy_subscr = &proxy_subscr_new; - LOG_PROXY_SUBSCR(proxy_subscr, LOGL_DEBUG, "Remote HLR resolved, stored address\n"); -} - /* All GSUP messages sent to the remote HLR pass through this function, to modify the subscriber state or disallow * sending the message. Return 0 to allow sending the message. */ static int proxy_acknowledge_gsup_to_remote_hlr(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, @@ -457,27 +410,77 @@ static int proxy_acknowledge_gsup_from_remote_hlr(struct proxy *proxy, const str return 0; } +static void proxy_remote_hlr_connect_result_cb(const struct osmo_sockaddr_str *addr, struct remote_hlr *remote_hlr, + void *data) +{ + struct proxy *proxy = data; + struct proxy_subscr_listentry *e; + if (!proxy) + return; + llist_for_each_entry(e, &proxy->subscr_list, entry) { + if (!osmo_sockaddr_str_cmp(addr, &e->data.remote_hlr_addr)) { + proxy_deferred_gsup_req_pop(proxy, e->data.imsi, remote_hlr); + } + } +} -void proxy_subscr_forward_to_remote_hlr_resolved(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, - struct remote_hlr *remote_hlr, struct osmo_gsup_req *req) +/* Store the remote HLR's GSUP address for this proxy subscriber. + * This can be set before the remote_hlr is connected, or after. + * And, this can be set before the gsup_req has been queued for this HLR, or after. + */ +void proxy_subscr_remote_hlr_resolved(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, + const struct osmo_sockaddr_str *remote_hlr_addr) { - if (proxy_acknowledge_gsup_to_remote_hlr(proxy, proxy_subscr, req)) { - osmo_gsup_req_respond_err(req, GMM_CAUSE_PROTO_ERR_UNSPEC, "Proxy does not allow this message"); + struct proxy_subscr proxy_subscr_new; + + if (osmo_sockaddr_str_is_nonzero(&proxy_subscr->remote_hlr_addr)) { + if (!osmo_sockaddr_str_cmp(remote_hlr_addr, &proxy_subscr->remote_hlr_addr)) { + /* Already have this remote address */ + return; + } else { + LOG_PROXY_SUBSCR(proxy_subscr, LOGL_NOTICE, + "Remote HLR address changes to " OSMO_SOCKADDR_STR_FMT "\n", + OSMO_SOCKADDR_STR_FMT_ARGS(remote_hlr_addr)); + } + } + + /* Store the address. Make a copy to modify. */ + proxy_subscr_new = *proxy_subscr; + proxy_subscr = &proxy_subscr_new; + proxy_subscr_new.remote_hlr_addr = *remote_hlr_addr; + + if (proxy_subscr_create_or_update(proxy, proxy_subscr)) { + LOG_PROXY_SUBSCR(proxy_subscr, LOGL_ERROR, "Failed to store proxy entry for remote HLR\n"); + /* If no remote HLR is known for the IMSI, the proxy entry is pointless. */ + proxy_subscr_del(proxy, proxy_subscr->imsi); return; } + LOG_PROXY_SUBSCR(proxy_subscr, LOGL_DEBUG, "Remote HLR resolved, stored address\n"); - remote_hlr_gsup_forward_to_remote_hlr(remote_hlr, req); + /* If any messages for this HLR are already spooled, connect now. Otherwise wait for + * proxy_subscr_forward_to_remote_hlr() to connect then. */ + if (proxy_deferred_gsup_req_waiting(proxy, proxy_subscr->imsi)) + remote_hlr_get_or_connect(&proxy_subscr->remote_hlr_addr, true, + proxy_remote_hlr_connect_result_cb, proxy); } -void proxy_subscr_forward_to_remote_hlr(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, struct osmo_gsup_req *req) +int proxy_subscr_forward_to_remote_hlr(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, struct osmo_gsup_req *req) { struct remote_hlr *remote_hlr; + int rc; + + rc = proxy_acknowledge_gsup_to_remote_hlr(proxy, proxy_subscr, req); + if (rc) { + osmo_gsup_req_respond_err(req, GMM_CAUSE_PROTO_ERR_UNSPEC, "Proxy does not allow this message"); + return rc; + } 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. */ + /* We don't know the remote target yet. Still waiting for an MS lookup response, which will end up + * calling proxy_subscr_remote_hlr_resolved(). See dgsm.c. */ LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_DEBUG, "deferring until remote HLR is known\n"); - proxy_defer_gsup_req(proxy, req); - return; + proxy_deferred_gsup_req_add(proxy, req); + return 0; } if (!osmo_gsup_peer_id_is_empty(&req->via_proxy)) { @@ -489,23 +492,22 @@ void proxy_subscr_forward_to_remote_hlr(struct proxy *proxy, const struct proxy_ osmo_gsup_peer_id_to_str(&req->source_name)); } - remote_hlr = remote_hlr_get(&proxy_subscr->remote_hlr_addr, true); - if (!remote_hlr) { - 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; + /* We could always store in the defer queue and empty the queue if the connection is already up. + * Slight optimisation: if the remote_hlr is already up and running, skip the defer queue. + * First ask for an existing remote_hlr. */ + remote_hlr = remote_hlr_get_or_connect(&proxy_subscr->remote_hlr_addr, false, NULL, NULL); + if (remote_hlr && remote_hlr_is_up(remote_hlr)) { + proxy_pending_req_remote_hlr_connect_result(req, remote_hlr); + return 0; } - if (!remote_hlr->gsupc || !remote_hlr->gsupc->is_connected) { - /* GSUP link is still busy establishing... */ - LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_DEBUG, - "deferring until link to remote HLR is up\n"); - proxy_defer_gsup_req(proxy, req); - return; - } - - proxy_subscr_forward_to_remote_hlr_resolved(proxy, proxy_subscr, remote_hlr, req); + /* Not existing or not up. Defer req and ask to be notified when it is up. + * If the remote_hlr exists but is not connected yet, there should actually already be a pending + * proxy_remote_hlr_connect_result_cb queued, but it doesn't hurt to do that more often. */ + proxy_deferred_gsup_req_add(proxy, req); + remote_hlr_get_or_connect(&proxy_subscr->remote_hlr_addr, true, + proxy_remote_hlr_connect_result_cb, proxy); + return 0; } int proxy_subscr_forward_to_vlr(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, diff --git a/src/remote_hlr.c b/src/remote_hlr.c index 42bf700..e2e7d47 100644 --- a/src/remote_hlr.c +++ b/src/remote_hlr.c @@ -100,16 +100,16 @@ static int remote_hlr_rx(struct osmo_gsup_client *gsupc, struct msgb *msg) return 0; } -static bool remote_hlr_up_yield(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, void *data) -{ - struct remote_hlr *remote_hlr = data; - proxy_subscr_remote_hlr_up(proxy, proxy_subscr, remote_hlr); - return true; -} +struct remote_hlr_pending_up { + struct llist_head entry; + remote_hlr_connect_result_cb_t connect_result_cb; + void *data; +}; static bool remote_hlr_up_down(struct osmo_gsup_client *gsupc, bool up) { struct remote_hlr *remote_hlr = gsupc->data; + struct remote_hlr_pending_up *p, *n; if (!up) { LOG_REMOTE_HLR(remote_hlr, LOGL_NOTICE, "link to remote HLR is down, removing GSUP client\n"); remote_hlr_destroy(remote_hlr); @@ -117,22 +117,41 @@ static bool remote_hlr_up_down(struct osmo_gsup_client *gsupc, bool up) } LOG_REMOTE_HLR(remote_hlr, LOGL_NOTICE, "link up\n"); - proxy_subscrs_get_by_remote_hlr(g_hlr->gs->proxy, &remote_hlr->addr, remote_hlr_up_yield, remote_hlr); + llist_for_each_entry_safe(p, n, &remote_hlr->pending_up_callbacks, entry) { + if (p->connect_result_cb) + p->connect_result_cb(&remote_hlr->addr, remote_hlr, p->data); + llist_del(&p->entry); + } return true; } -struct remote_hlr *remote_hlr_get(const struct osmo_sockaddr_str *addr, bool create) +bool remote_hlr_is_up(struct remote_hlr *remote_hlr) +{ + return remote_hlr && remote_hlr->gsupc && remote_hlr->gsupc->is_connected; +} + +struct remote_hlr *remote_hlr_get_or_connect(const struct osmo_sockaddr_str *addr, bool connect, + remote_hlr_connect_result_cb_t connect_result_cb, void *data) { - struct remote_hlr *rh; + struct remote_hlr *rh = NULL; + struct remote_hlr *rh_i; struct osmo_gsup_client_config cfg; - llist_for_each_entry(rh, &remote_hlrs, entry) { - if (!osmo_sockaddr_str_cmp(&rh->addr, addr)) - return rh; + llist_for_each_entry(rh_i, &remote_hlrs, entry) { + if (!osmo_sockaddr_str_cmp(&rh_i->addr, addr)) { + rh = rh_i; + break; + } } - if (!create) + if (rh) + goto add_result_cb; + + if (!connect) { + if (connect_result_cb) + connect_result_cb(addr, NULL, data); return NULL; + } /* Doesn't exist yet, create a GSUP client to remote HLR. */ cfg = (struct osmo_gsup_client_config){ @@ -150,15 +169,33 @@ struct remote_hlr *remote_hlr_get(const struct osmo_sockaddr_str *addr, bool cre .addr = *addr, .gsupc = osmo_gsup_client_create3(rh, &cfg), }; + INIT_LLIST_HEAD(&rh->pending_up_callbacks); if (!rh->gsupc) { LOGP(DDGSM, LOGL_ERROR, "Failed to establish connection to remote HLR " OSMO_SOCKADDR_STR_FMT "\n", OSMO_SOCKADDR_STR_FMT_ARGS(addr)); talloc_free(rh); + if (connect_result_cb) + connect_result_cb(addr, NULL, data); return NULL; } + rh->gsupc->data = rh; llist_add(&rh->entry, &remote_hlrs); + +add_result_cb: + if (connect_result_cb) { + if (remote_hlr_is_up(rh)) { + connect_result_cb(addr, rh, data); + } else { + struct remote_hlr_pending_up *p; + p = talloc_zero(rh, struct remote_hlr_pending_up); + OSMO_ASSERT(p); + p->connect_result_cb = connect_result_cb; + p->data = data; + llist_add_tail(&p->entry, &rh->pending_up_callbacks); + } + } return rh; } @@ -182,14 +219,19 @@ int remote_hlr_msgb_send(struct remote_hlr *remote_hlr, struct msgb *msg) } /* A GSUP message was received from the MS/MSC side, forward it to the remote HLR. */ -void remote_hlr_gsup_forward_to_remote_hlr(struct remote_hlr *remote_hlr, struct osmo_gsup_req *req) +void remote_hlr_gsup_forward_to_remote_hlr(struct remote_hlr *remote_hlr, struct osmo_gsup_req *req, + struct osmo_gsup_message *modified_gsup) { int rc; struct msgb *msg; /* 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; + struct osmo_gsup_message forward; + if (modified_gsup) + forward = *modified_gsup; + else + forward = req->gsup; if (req->source_name.type != OSMO_GSUP_PEER_ID_IPA_NAME) { osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Unsupported GSUP peer id type: %s", diff --git a/tests/gsup_server/Makefile.am b/tests/gsup_server/Makefile.am index 6df538f..0b18d61 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/gsup_send.c \ $(top_srcdir)/src/gsupclient/gsup_peer_id.c \ $(top_srcdir)/src/gsupclient/gsup_req.c \ $(LIBOSMOCORE_LIBS) \ |