diff options
-rw-r--r-- | doc/examples/osmo-hlr.cfg | 1 | ||||
-rw-r--r-- | src/hlr.c | 2 | ||||
-rw-r--r-- | src/hlr.h | 3 | ||||
-rw-r--r-- | src/hlr_ussd.c | 192 | ||||
-rw-r--r-- | src/hlr_ussd.h | 35 | ||||
-rw-r--r-- | src/hlr_vty.c | 105 | ||||
-rw-r--r-- | tests/test_nodes.vty | 6 |
7 files changed, 256 insertions, 88 deletions
diff --git a/doc/examples/osmo-hlr.cfg b/doc/examples/osmo-hlr.cfg index ebc22ec..35d942d 100644 --- a/doc/examples/osmo-hlr.cfg +++ b/doc/examples/osmo-hlr.cfg @@ -21,3 +21,4 @@ ctrl hlr gsup bind ip 127.0.0.1 + ussd route prefix *#100# internal own-msisdn @@ -568,7 +568,9 @@ int main(int argc, char **argv) g_hlr = talloc_zero(hlr_ctx, struct hlr); INIT_LLIST_HEAD(&g_hlr->euse_list); + INIT_LLIST_HEAD(&g_hlr->iuse_list); INIT_LLIST_HEAD(&g_hlr->ss_sessions); + INIT_LLIST_HEAD(&g_hlr->ussd_routes); rc = osmo_init_logging2(hlr_ctx, &hlr_log_info); if (rc < 0) { @@ -43,6 +43,9 @@ struct hlr { struct llist_head euse_list; struct hlr_euse *euse_default; + struct llist_head iuse_list; + + struct llist_head ussd_routes; struct llist_head ss_sessions; }; diff --git a/src/hlr_ussd.c b/src/hlr_ussd.c index d23debf..f9399d2 100644 --- a/src/hlr_ussd.c +++ b/src/hlr_ussd.c @@ -27,6 +27,7 @@ #include <osmocom/gsm/protocol/gsm_04_80.h> #include <stdint.h> #include <string.h> +#include <errno.h> #include "hlr.h" #include "hlr_ussd.h" @@ -58,7 +59,6 @@ struct hlr_euse *euse_alloc(struct hlr *hlr, const char *name) euse = talloc_zero(hlr, struct hlr_euse); euse->name = talloc_strdup(euse, name); euse->hlr = hlr; - INIT_LLIST_HEAD(&euse->routes); llist_add_tail(&euse->list, &hlr->euse_list); return euse; @@ -71,54 +71,68 @@ void euse_del(struct hlr_euse *euse) } -struct hlr_euse_route *euse_route_find(struct hlr_euse *euse, const char *prefix) +struct hlr_ussd_route *ussd_route_find_prefix(struct hlr *hlr, const char *prefix) { - struct hlr_euse_route *rt; + struct hlr_ussd_route *rt; - llist_for_each_entry(rt, &euse->routes, list) { + llist_for_each_entry(rt, &hlr->ussd_routes, list) { if (!strcmp(rt->prefix, prefix)) return rt; } return NULL; } -struct hlr_euse_route *euse_route_prefix_alloc(struct hlr_euse *euse, const char *prefix) +struct hlr_ussd_route *ussd_route_prefix_alloc_int(struct hlr *hlr, const char *prefix, + const struct hlr_iuse *iuse) { - struct hlr_euse_route *rt; + struct hlr_ussd_route *rt; - if (euse_route_find(euse, prefix)) + if (ussd_route_find_prefix(hlr, prefix)) return NULL; - rt = talloc_zero(euse, struct hlr_euse_route); + rt = talloc_zero(hlr, struct hlr_ussd_route); rt->prefix = talloc_strdup(rt, prefix); - rt->euse = euse; - llist_add_tail(&rt->list, &euse->routes); + rt->u.iuse = iuse; + llist_add_tail(&rt->list, &hlr->ussd_routes); return rt; } -void euse_route_del(struct hlr_euse_route *rt) +struct hlr_ussd_route *ussd_route_prefix_alloc_ext(struct hlr *hlr, const char *prefix, + struct hlr_euse *euse) +{ + struct hlr_ussd_route *rt; + + if (ussd_route_find_prefix(hlr, prefix)) + return NULL; + + rt = talloc_zero(hlr, struct hlr_ussd_route); + rt->prefix = talloc_strdup(rt, prefix); + rt->is_external = true; + rt->u.euse = euse; + llist_add_tail(&rt->list, &hlr->ussd_routes); + + return rt; +} + +void ussd_route_del(struct hlr_ussd_route *rt) { llist_del(&rt->list); talloc_free(rt); } -struct hlr_euse *ussd_euse_find_7bit_gsm(struct hlr *hlr, const char *ussd_code) +static struct hlr_ussd_route *ussd_route_lookup_7bit(struct hlr *hlr, const char *ussd_code) { - struct hlr_euse *euse; - - llist_for_each_entry(euse, &hlr->euse_list, list) { - struct hlr_euse_route *rt; - llist_for_each_entry(rt, &euse->routes, list) { - if (!strncmp(ussd_code, rt->prefix, strlen(rt->prefix))) { - LOGP(DSS, LOGL_DEBUG, "Found EUSE %s (prefix %s) for USSD Code '%s'\n", - rt->euse->name, rt->prefix, ussd_code); - return rt->euse; - } + struct hlr_ussd_route *rt; + llist_for_each_entry(rt, &hlr->ussd_routes, list) { + if (!strncmp(ussd_code, rt->prefix, strlen(rt->prefix))) { + LOGP(DSS, LOGL_DEBUG, "Found EUSE %s (prefix %s) for USSD Code '%s'\n", + rt->u.euse->name, rt->prefix, ussd_code); + return rt; } } - LOGP(DSS, LOGL_DEBUG, "Could not find Route/EUSE for USSD Code '%s'\n", ussd_code); + LOGP(DSS, LOGL_DEBUG, "Could not find Route for USSD Code '%s'\n", ussd_code); return NULL; } @@ -141,8 +155,15 @@ struct ss_session { /* time-out when we will delete the session */ struct osmo_timer_list timeout; - /* external USSD Entity responsible for this session */ - struct hlr_euse *euse; + /* is this USSD for an external handler (EUSE): true */ + bool is_external; + union { + /* external USSD Entity responsible for this session */ + struct hlr_euse *euse; + /* internal USSD Entity responsible for this session */ + const struct hlr_iuse *iuse; + } u; + /* 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 */ @@ -247,6 +268,79 @@ 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) +{ + struct msgb *msg = gsm0480_gen_ussd_resp_7bit(invoke_id, text); + LOGPSS(ss, LOGL_INFO, "Tx USSD '%s'\n", text); + OSMO_ASSERT(msg); + return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, final, msg); +} + +/*********************************************************************** + * Internal USSD Handlers + ***********************************************************************/ + +#include "db.h" + +static int handle_ussd_own_msisdn(struct osmo_gsup_conn *conn, struct ss_session *ss, + const struct osmo_gsup_message *gsup, const struct ss_request *req) +{ + struct hlr_subscriber subscr; + char buf[GSM0480_USSD_7BIT_STRING_LEN+1]; + int rc; + + rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr); + switch (rc) { + case 0: + if (strlen(subscr.msisdn) == 0) + snprintf(buf, sizeof(buf), "You have no MSISDN!"); + else + snprintf(buf, sizeof(buf), "Your extension is %s\r", subscr.msisdn); + ss_tx_ussd_7bit(ss, true, req->invoke_id, buf); + break; + case -ENOENT: + ss_tx_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER); + break; + case -EIO: + default: + ss_tx_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE); + break; + } + return 0; +} + +static int handle_ussd_own_imsi(struct osmo_gsup_conn *conn, 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!\n", ss->imsi); + ss_tx_ussd_7bit(ss, true, req->invoke_id, buf); + return 0; +} + + +static const struct hlr_iuse hlr_iuses[] = { + { + .name = "own-msisdn", + .handle_ussd = handle_ussd_own_msisdn, + }, + { + .name = "own-imsi", + .handle_ussd = handle_ussd_own_imsi, + }, +}; + +const struct hlr_iuse *iuse_find(const char *name) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(hlr_iuses); i++) { + const struct hlr_iuse *iuse = &hlr_iuses[i]; + if (!strcmp(name, iuse->name)) + return iuse; + } + return NULL; +} /*********************************************************************** @@ -307,6 +401,7 @@ static int handle_ss(struct ss_session *ss, const struct osmo_gsup_message *gsup return 0; } +/* 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) { @@ -318,8 +413,7 @@ static int handle_ussd(struct osmo_gsup_conn *conn, struct ss_session *ss, gsm0480_comp_type_name(comp_type), gsm0480_op_code_name(req->opcode), req->ussd_text); - - if (!ss->euse) { + 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); return 0; @@ -333,19 +427,25 @@ static int handle_ussd(struct osmo_gsup_conn *conn, struct ss_session *ss, /* FIXME: resolve this based on the database vlr_addr */ osmo_gsup_addr_send(conn->server, (uint8_t *)"MSC-00-00-00-00-00-00", 22, msg_out); } else { - /* Received from VLR, Forward to EUSE */ - char addr[128]; - strcpy(addr, "EUSE-"); - osmo_strlcpy(addr+5, ss->euse->name, sizeof(addr)-5); - conn = gsup_route_find(conn->server, (uint8_t *)addr, strlen(addr)+1); - 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); + /* 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); + 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); + } else { + msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP USSD FW"); + OSMO_ASSERT(msg_out); + osmo_gsup_encode(msg_out, gsup); + osmo_gsup_conn_send(conn, msg_out); + } } else { - msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP USSD FW"); - OSMO_ASSERT(msg_out); - osmo_gsup_encode(msg_out, gsup); - osmo_gsup_conn_send(conn, msg_out); + /* Handle internally */ + ss->u.iuse->handle_ussd(conn, ss, gsup, req); } } @@ -392,10 +492,20 @@ int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message * if (ss_op_is_ussd(req.opcode)) { if (conn_is_euse(conn)) { /* EUSE->VLR: MT USSD. EUSE is known ('conn'), VLR is to be resolved */ - ss->euse = euse_by_conn(conn); + ss->u.euse = euse_by_conn(conn); } else { /* VLR->EUSE: MO USSD. VLR is known ('conn'), EUSE is to be resolved */ - ss->euse = ussd_euse_find_7bit_gsm(hlr, (const char *) req.ussd_text); + struct hlr_ussd_route *rt; + rt = ussd_route_lookup_7bit(hlr, (const char *) req.ussd_text); + if (rt) { + if (rt->is_external) { + ss->is_external = true; + ss->u.euse = rt->u.euse; + } else if (rt) { + ss->is_external = false; + ss->u.iuse = rt->u.iuse; + } + } } /* dispatch unstructured SS to routing */ handle_ussd(conn, ss, gsup, &req); diff --git a/src/hlr_ussd.h b/src/hlr_ussd.h index 433a7f2..d1b9fe0 100644 --- a/src/hlr_ussd.h +++ b/src/hlr_ussd.h @@ -5,11 +5,15 @@ struct osmo_gsup_conn; -struct hlr_euse_route { - /* hlr_euse.routes */ +struct hlr_ussd_route { + /* g_hlr.routes */ struct llist_head list; - struct hlr_euse *euse; const char *prefix; + bool is_external; + union { + struct hlr_euse *euse; + const struct hlr_iuse *iuse; + } u; }; struct hlr_euse { @@ -20,21 +24,34 @@ struct hlr_euse { const char *name; /* human-readable description */ const char *description; - /* list of hlr_euse_route */ - struct llist_head routes; /* GSUP connection to the EUSE, if any */ struct osmo_gsup_conn *conn; }; - struct hlr_euse *euse_find(struct hlr *hlr, const char *name); struct hlr_euse *euse_alloc(struct hlr *hlr, const char *name); void euse_del(struct hlr_euse *euse); -struct hlr_euse_route *euse_route_find(struct hlr_euse *euse, const char *prefix); -struct hlr_euse_route *euse_route_prefix_alloc(struct hlr_euse *euse, const char *prefix); -void euse_route_del(struct hlr_euse_route *rt); +const struct hlr_iuse *iuse_find(const char *name); + +struct hlr_ussd_route *ussd_route_find_prefix(struct hlr *hlr, const char *prefix); +struct hlr_ussd_route *ussd_route_prefix_alloc_int(struct hlr *hlr, const char *prefix, + const struct hlr_iuse *iuse); +struct hlr_ussd_route *ussd_route_prefix_alloc_ext(struct hlr *hlr, const char *prefix, + 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); + +struct ss_session; +struct ss_request; + +/* Internal USSD Handler */ +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); +}; diff --git a/src/hlr_vty.c b/src/hlr_vty.c index 5c359b7..9532a03 100644 --- a/src/hlr_vty.c +++ b/src/hlr_vty.c @@ -32,6 +32,7 @@ #include <osmocom/vty/misc.h> #include <osmocom/abis/ipa.h> +#include "hlr.h" #include "hlr_vty.h" #include "hlr_vty_subscr.h" #include "gsup_server.h" @@ -122,47 +123,77 @@ DEFUN(cfg_hlr_gsup_bind_ip, } /*********************************************************************** - * External USSD Entity + * USSD Entity ***********************************************************************/ #include "hlr_ussd.h" -DEFUN(cfg_euse_route_pfx, cfg_euse_route_pfx_cmd, - "route prefix PREFIX", - "") +#define USSD_STR "USSD Configuration\n" +#define UROUTE_STR "Routing Configuration\n" +#define PREFIX_STR "Prefix-Matching Route\n" "USSD Prefix\n" + +#define INT_CHOICE "(own-msisdn|own-imsi)" +#define INT_STR "Internal USSD Handler\n" \ + "Respond with subscribers' own MSISDN\n" \ + "Respond with subscribers' own IMSI\n" + +#define EXT_STR "External USSD Handler\n" \ + "Name of External USSD Handler (IPA CCM ID)\n" + +DEFUN(cfg_ussd_route_pfx_int, cfg_ussd_route_pfx_int_cmd, + "ussd route prefix PREFIX internal " INT_CHOICE, + USSD_STR UROUTE_STR PREFIX_STR INT_STR) { - struct hlr_euse *euse = vty->index; - struct hlr_euse_route *rt = euse_route_find(euse, argv[0]); + const struct hlr_iuse *iuse = iuse_find(argv[1]); + struct hlr_ussd_route *rt = ussd_route_find_prefix(g_hlr, argv[0]); + if (rt) { + vty_out(vty, "%% Cannot add [another?] route for prefix %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + ussd_route_prefix_alloc_int(g_hlr, argv[0], iuse); + return CMD_SUCCESS; +} + +DEFUN(cfg_ussd_route_pfx_ext, cfg_ussd_route_pfx_ext_cmd, + "ussd route prefix PREFIX external EUSE", + USSD_STR UROUTE_STR PREFIX_STR EXT_STR) +{ + struct hlr_euse *euse = euse_find(g_hlr, argv[1]); + struct hlr_ussd_route *rt = ussd_route_find_prefix(g_hlr, argv[0]); if (rt) { vty_out(vty, "%% Cannot add [another?] route for prefix %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } - euse_route_prefix_alloc(euse, argv[0]); + if (!euse) { + vty_out(vty, "%% Cannot find euse '%s'%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + ussd_route_prefix_alloc_ext(g_hlr, argv[0], euse); return CMD_SUCCESS; } -DEFUN(cfg_euse_no_route_pfx, cfg_euse_no_route_pfx_cmd, - "no route prefix PREFIX", - NO_STR "") +DEFUN(cfg_ussd_no_route_pfx, cfg_ussd_no_route_pfx_cmd, + "no ussd route prefix PREFIX", + NO_STR USSD_STR UROUTE_STR PREFIX_STR) { - struct hlr_euse *euse = vty->index; - struct hlr_euse_route *rt = euse_route_find(euse, argv[0]); + struct hlr_ussd_route *rt = ussd_route_find_prefix(g_hlr, argv[0]); if (!rt) { vty_out(vty, "%% Cannot find route for prefix %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } - euse_route_del(rt); + ussd_route_del(rt); return CMD_SUCCESS; } -DEFUN(cfg_euse_defaultroute, cfg_euse_defaultroute_cmd, - "default-route", - "Set this EUSE as default-route for all USSD to unknown destinations\n") +DEFUN(cfg_ussd_defaultroute, cfg_ussd_defaultroute_cmd, + "ussd default-route external EUSE", + USSD_STR "Configure default-route for all USSD to unknown destinations\n" + EXT_STR) { - struct hlr_euse *euse = vty->index; + struct hlr_euse *euse = euse_find(g_hlr, argv[0]); if (g_hlr->euse_default != euse) { vty_out(vty, "Switching default route from %s to %s%s", @@ -174,16 +205,10 @@ DEFUN(cfg_euse_defaultroute, cfg_euse_defaultroute_cmd, return CMD_SUCCESS; } -DEFUN(cfg_euse_no_defaultroute, cfg_euse_no_defaultroute_cmd, - "no default-route", - NO_STR "Remove this EUSE as default-route for all USSD to unknown destinations\n") +DEFUN(cfg_ussd_no_defaultroute, cfg_ussd_no_defaultroute_cmd, + "no ussd default-route", + NO_STR USSD_STR "Remove the default-route for all USSD to unknown destinations\n") { - struct hlr_euse *euse = vty->index; - - if (g_hlr->euse_default != euse) { - vty_out(vty, "%% Current EUSE is no default route, cannot delete it%s", VTY_NEWLINE); - return CMD_WARNING; - } g_hlr->euse_default = NULL; return CMD_SUCCESS; @@ -236,24 +261,27 @@ DEFUN(cfg_no_euse, cfg_no_euse_cmd, static void dump_one_euse(struct vty *vty, struct hlr_euse *euse) { - struct hlr_euse_route *er; - vty_out(vty, " euse %s%s", euse->name, VTY_NEWLINE); - - llist_for_each_entry(er, &euse->routes, list) - vty_out(vty, " route prefix %s%s", er->prefix, VTY_NEWLINE); - - if (g_hlr->euse_default == euse) - vty_out(vty, " default-route%s", VTY_NEWLINE); } static int config_write_euse(struct vty *vty) { struct hlr_euse *euse; + struct hlr_ussd_route *rt; llist_for_each_entry(euse, &g_hlr->euse_list, list) dump_one_euse(vty, euse); + llist_for_each_entry(rt, &g_hlr->ussd_routes, list) { + vty_out(vty, " ussd route prefix %s %s %s%s", rt->prefix, + rt->is_external ? "external" : "internal", + rt->is_external ? rt->u.euse->name : rt->u.iuse->name, + VTY_NEWLINE); + } + + if (g_hlr->euse_default) + vty_out(vty, " ussd default-route external %s%s", g_hlr->euse_default->name, VTY_NEWLINE); + return 0; } @@ -314,10 +342,11 @@ void hlr_vty_init(const struct log_info *cat) install_element(HLR_NODE, &cfg_euse_cmd); install_element(HLR_NODE, &cfg_no_euse_cmd); install_node(&euse_node, config_write_euse); - install_element(EUSE_NODE, &cfg_euse_route_pfx_cmd); - install_element(EUSE_NODE, &cfg_euse_no_route_pfx_cmd); - install_element(EUSE_NODE, &cfg_euse_defaultroute_cmd); - install_element(EUSE_NODE, &cfg_euse_no_defaultroute_cmd); + install_element(HLR_NODE, &cfg_ussd_route_pfx_int_cmd); + install_element(HLR_NODE, &cfg_ussd_route_pfx_ext_cmd); + install_element(HLR_NODE, &cfg_ussd_no_route_pfx_cmd); + install_element(HLR_NODE, &cfg_ussd_defaultroute_cmd); + install_element(HLR_NODE, &cfg_ussd_no_defaultroute_cmd); hlr_vty_subscriber_init(); } diff --git a/tests/test_nodes.vty b/tests/test_nodes.vty index 4badad5..58f5c61 100644 --- a/tests/test_nodes.vty +++ b/tests/test_nodes.vty @@ -72,6 +72,11 @@ OsmoHLR(config-hlr)# list gsup euse NAME no euse NAME + ussd route prefix PREFIX internal (own-msisdn|own-imsi) + ussd route prefix PREFIX external EUSE + no ussd route prefix PREFIX + ussd default-route external EUSE + no ussd default-route OsmoHLR(config-hlr)# gsup OsmoHLR(config-hlr-gsup)# list @@ -122,4 +127,5 @@ ctrl hlr gsup bind ip 127.0.0.1 + ussd route prefix *#100# internal own-msisdn end |