aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ctrl.c357
-rw-r--r--src/ctrl.h6
2 files changed, 326 insertions, 37 deletions
diff --git a/src/ctrl.c b/src/ctrl.c
index b49765d..3e81661 100644
--- a/src/ctrl.c
+++ b/src/ctrl.c
@@ -21,87 +21,372 @@
*/
#include <stdbool.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <string.h>
-#include <osmocom/ctrl/control_cmd.h>
-#include <osmocom/ctrl/control_if.h>
+#include <osmocom/gsm/gsm23003.h>
#include <osmocom/ctrl/ports.h>
-#include "gsup_server.h"
-#include "logging.h"
-#include "db.h"
#include "hlr.h"
-#include "luop.h"
#include "ctrl.h"
+#include "db.h"
+
+#define SEL_BY "by-"
+#define SEL_BY_IMSI SEL_BY "imsi-"
+#define SEL_BY_MSISDN SEL_BY "msisdn-"
+#define SEL_BY_ID SEL_BY "id-"
-static int handle_cmd_ps(struct hlr *ctx, struct ctrl_cmd *cmd, bool enable)
+#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
+
+static bool startswith(const char *str, const char *start)
{
- struct hlr_subscriber subscr;
+ return strncmp(str, start, strlen(start)) == 0;
+}
- if (db_subscr_get_by_imsi(ctx->dbc, cmd->value, &subscr) < 0) {
- cmd->reply = "Subscriber Unknown in HLR";
- return CTRL_CMD_ERROR;
+static int _get_subscriber(struct db_context *dbc,
+ const char *by_selector,
+ struct hlr_subscriber *subscr)
+{
+ const char *val;
+ if (startswith(by_selector, SEL_BY_IMSI)) {
+ val = by_selector + strlen(SEL_BY_IMSI);
+ if (!osmo_imsi_str_valid(val))
+ return -EINVAL;
+ return db_subscr_get_by_imsi(dbc, val, subscr);
+ }
+ if (startswith(by_selector, SEL_BY_MSISDN)) {
+ val = by_selector + strlen(SEL_BY_MSISDN);
+ if (!osmo_msisdn_str_valid(val))
+ return -EINVAL;
+ return db_subscr_get_by_msisdn(dbc, val, subscr);
}
+ if (startswith(by_selector, SEL_BY_ID)) {
+ int64_t id;
+ char *endptr;
+ val = by_selector + strlen(SEL_BY_ID);
+ if (*val == '+')
+ return -EINVAL;
+ errno = 0;
+ id = strtoll(val, &endptr, 10);
+ if (errno || *endptr)
+ return -EINVAL;
+ return db_subscr_get_by_id(dbc, id, subscr);
+ }
+ return -ENOTSUP;
+}
- if (hlr_subscr_nam(ctx, &subscr, enable, true) < 0) {
- cmd->reply = "Error updating DB";
- return CTRL_CMD_ERROR;
+static bool get_subscriber(struct db_context *dbc,
+ const char *by_selector,
+ struct hlr_subscriber *subscr,
+ struct ctrl_cmd *cmd)
+{
+ int rc = _get_subscriber(dbc, by_selector, subscr);
+ switch (rc) {
+ case 0:
+ return true;
+ case -ENOTSUP:
+ cmd->reply = "Not a known subscriber 'by-xxx-' selector.";
+ return false;
+ case -EINVAL:
+ cmd->reply = "Invalid value part of 'by-xxx-value' selector.";
+ return false;
+ case -ENOENT:
+ cmd->reply = "No such subscriber.";
+ return false;
+ default:
+ cmd->reply = "An unknown error has occured during get_subscriber().";
+ return false;
}
+}
- cmd->reply = "OK";
- return CTRL_CMD_REPLY;
+/* Optimization: if a subscriber operation is requested by-imsi, just return
+ * the IMSI right back. */
+static const char *get_subscriber_imsi(struct db_context *dbc,
+ const char *by_selector,
+ struct ctrl_cmd *cmd)
+{
+ static struct hlr_subscriber subscr;
+
+ if (startswith(by_selector, SEL_BY_IMSI))
+ return by_selector + strlen(SEL_BY_IMSI);
+ if (!get_subscriber(dbc, by_selector, &subscr, cmd))
+ return NULL;
+ return subscr.imsi;
+}
+
+/* printf fmt and arg to completely omit a string if it is empty. */
+#define FMT_S "%s%s%s%s"
+#define ARG_S(name, val) \
+ (val) && *(val) ? "\n" : "", \
+ (val) && *(val) ? name : "", \
+ (val) && *(val) ? "\t" : "", \
+ (val) && *(val) ? (val) : "" \
+
+/* printf fmt and arg to completely omit bool of given value. */
+#define FMT_BOOL "%s"
+#define ARG_BOOL(name, val) \
+ val ? "\n" name "\t1" : "\n" name "\t0"
+
+static void print_subscr_info(struct ctrl_cmd *cmd,
+ struct hlr_subscriber *subscr)
+{
+ ctrl_cmd_reply_printf(cmd,
+ "\nid\t%"PRIu64
+ FMT_S
+ FMT_S
+ FMT_BOOL
+ FMT_BOOL
+ FMT_S
+ FMT_S
+ FMT_S
+ FMT_BOOL
+ FMT_BOOL
+ "\nperiodic_lu_timer\t%u"
+ "\nperiodic_rau_tau_timer\t%u"
+ "\nlmsi\t%08x"
+ ,
+ subscr->id,
+ ARG_S("imsi", subscr->imsi),
+ ARG_S("msisdn", subscr->msisdn),
+ ARG_BOOL("nam_cs", subscr->nam_cs),
+ ARG_BOOL("nam_ps", subscr->nam_ps),
+ ARG_S("vlr_number", subscr->vlr_number),
+ ARG_S("sgsn_number", subscr->sgsn_number),
+ ARG_S("sgsn_address", subscr->sgsn_address),
+ ARG_BOOL("ms_purged_cs", subscr->ms_purged_cs),
+ ARG_BOOL("ms_purged_ps", subscr->ms_purged_ps),
+ subscr->periodic_lu_timer,
+ subscr->periodic_rau_tau_timer,
+ subscr->lmsi
+ );
+}
+
+static void print_subscr_info_aud2g(struct ctrl_cmd *cmd, struct osmo_sub_auth_data *aud)
+{
+ if (aud->algo == OSMO_AUTH_ALG_NONE)
+ return;
+ ctrl_cmd_reply_printf(cmd,
+ "\naud2g.algo\t%s"
+ "\naud2g.ki\t%s"
+ ,
+ osmo_auth_alg_name(aud->algo),
+ hexdump_buf(aud->u.gsm.ki));
}
-CTRL_CMD_DEFINE_WO_NOVRF(enable_ps, "enable-ps");
-static int set_enable_ps(struct ctrl_cmd *cmd, void *data)
+static void print_subscr_info_aud3g(struct ctrl_cmd *cmd, struct osmo_sub_auth_data *aud)
{
- return handle_cmd_ps(data, cmd, true);
+ if (aud->algo == OSMO_AUTH_ALG_NONE)
+ return;
+ ctrl_cmd_reply_printf(cmd,
+ "\naud3g.algo\t%s"
+ "\naud3g.k\t%s"
+ ,
+ osmo_auth_alg_name(aud->algo),
+ hexdump_buf(aud->u.umts.k));
+ /* hexdump uses a static string buffer, hence only one hexdump per
+ * printf(). */
+ ctrl_cmd_reply_printf(cmd,
+ "\naud3g.%s\t%s"
+ "\naud3g.ind_bitlen\t%u"
+ "\naud3g.sqn\t%"PRIu64
+ ,
+ aud->u.umts.opc_is_op? "op" : "opc",
+ hexdump_buf(aud->u.umts.opc),
+ aud->u.umts.ind_bitlen,
+ aud->u.umts.sqn);
}
-CTRL_CMD_DEFINE_WO_NOVRF(disable_ps, "disable-ps");
-static int set_disable_ps(struct ctrl_cmd *cmd, void *data)
+CTRL_CMD_DEFINE_RO(subscr_info, "info");
+static int get_subscr_info(struct ctrl_cmd *cmd, void *data)
{
- return handle_cmd_ps(data, cmd, false);
+ struct hlr_subscriber subscr;
+ struct hlr *hlr = data;
+ const char *by_selector = cmd->node;
+
+ if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
+ return CTRL_CMD_ERROR;
+
+ print_subscr_info(cmd, &subscr);
+
+ return CTRL_CMD_REPLY;
}
-CTRL_CMD_DEFINE_WO_NOVRF(status_ps, "status-ps");
-static int set_status_ps(struct ctrl_cmd *cmd, void *data)
+CTRL_CMD_DEFINE_RO(subscr_info_aud, "info-aud");
+static int get_subscr_info_aud(struct ctrl_cmd *cmd, void *data)
{
- struct hlr *ctx = data;
- struct lu_operation *luop = lu_op_alloc(ctx->gs);
- if (!luop) {
- cmd->reply = "Internal HLR error";
+ const char *imsi;
+ struct osmo_sub_auth_data aud2g;
+ struct osmo_sub_auth_data aud3g;
+ struct hlr *hlr = data;
+ const char *by_selector = cmd->node;
+ int rc;
+
+ imsi = get_subscriber_imsi(hlr->dbc, by_selector, cmd);
+ if (!imsi)
+ return CTRL_CMD_ERROR;
+
+ rc = db_get_auth_data(hlr->dbc, imsi, &aud2g, &aud3g, NULL);
+
+ if (rc == -ENOENT) {
+ /* No auth data found, tell the print*() functions about it. */
+ aud2g.algo = OSMO_AUTH_ALG_NONE;
+ aud3g.algo = OSMO_AUTH_ALG_NONE;
+ } else if (rc) {
+ cmd->reply = "Error retrieving authentication data.";
return CTRL_CMD_ERROR;
}
- if (!lu_op_fill_subscr(luop, ctx->dbc, cmd->value)) {
- cmd->reply = "Subscriber Unknown in HLR";
+ print_subscr_info_aud2g(cmd, &aud2g);
+ print_subscr_info_aud3g(cmd, &aud3g);
+
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE_RO(subscr_info_all, "info-all");
+static int get_subscr_info_all(struct ctrl_cmd *cmd, void *data)
+{
+ struct hlr_subscriber subscr;
+ struct osmo_sub_auth_data aud2g;
+ struct osmo_sub_auth_data aud3g;
+ struct hlr *hlr = data;
+ const char *by_selector = cmd->node;
+ int rc;
+
+ if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
+ return CTRL_CMD_ERROR;
+
+ rc = db_get_auth_data(hlr->dbc, subscr.imsi, &aud2g, &aud3g, NULL);
+
+ if (rc == -ENOENT) {
+ /* No auth data found, tell the print*() functions about it. */
+ aud2g.algo = OSMO_AUTH_ALG_NONE;
+ aud3g.algo = OSMO_AUTH_ALG_NONE;
+ } else if (rc) {
+ cmd->reply = "Error retrieving authentication data.";
return CTRL_CMD_ERROR;
}
- cmd->reply = luop->subscr.nam_ps ? "1" : "0";
+ print_subscr_info(cmd, &subscr);
+ print_subscr_info_aud2g(cmd, &aud2g);
+ print_subscr_info_aud3g(cmd, &aud3g);
+
+ return CTRL_CMD_REPLY;
+}
+
+static int verify_subscr_cs_ps_enabled(struct ctrl_cmd *cmd, const char *value, void *data)
+{
+ if (!value || !*value
+ || (strcmp(value, "0") && strcmp(value, "1")))
+ return 1;
+ return 0;
+}
+
+static int get_subscr_cs_ps_enabled(struct ctrl_cmd *cmd, void *data,
+ bool is_ps)
+{
+ struct hlr_subscriber subscr;
+ struct hlr *hlr = data;
+ const char *by_selector = cmd->node;
+
+ if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
+ return CTRL_CMD_ERROR;
+ cmd->reply = (is_ps ? subscr.nam_ps : subscr.nam_cs)
+ ? "1" : "0";
+ return CTRL_CMD_REPLY;
+}
+
+static int set_subscr_cs_ps_enabled(struct ctrl_cmd *cmd, void *data,
+ bool is_ps)
+{
+ const char *imsi;
+ struct hlr *hlr = data;
+ const char *by_selector = cmd->node;
+
+ imsi = get_subscriber_imsi(hlr->dbc, by_selector, cmd);
+ if (!imsi)
+ return CTRL_CMD_ERROR;
+ if (db_subscr_nam(hlr->dbc, imsi, strcmp(cmd->value, "1") == 0, is_ps))
+ return CTRL_CMD_ERROR;
+ cmd->reply = "OK";
return CTRL_CMD_REPLY;
}
+CTRL_CMD_DEFINE(subscr_ps_enabled, "ps-enabled");
+static int verify_subscr_ps_enabled(struct ctrl_cmd *cmd, const char *value, void *data)
+{
+ return verify_subscr_cs_ps_enabled(cmd, value, data);
+}
+static int get_subscr_ps_enabled(struct ctrl_cmd *cmd, void *data)
+{
+ return get_subscr_cs_ps_enabled(cmd, data, true);
+}
+static int set_subscr_ps_enabled(struct ctrl_cmd *cmd, void *data)
+{
+ return set_subscr_cs_ps_enabled(cmd, data, true);
+}
+
+CTRL_CMD_DEFINE(subscr_cs_enabled, "cs-enabled");
+static int verify_subscr_cs_enabled(struct ctrl_cmd *cmd, const char *value, void *data)
+{
+ return verify_subscr_cs_ps_enabled(cmd, value, data);
+}
+static int get_subscr_cs_enabled(struct ctrl_cmd *cmd, void *data)
+{
+ return get_subscr_cs_ps_enabled(cmd, data, false);
+}
+static int set_subscr_cs_enabled(struct ctrl_cmd *cmd, void *data)
+{
+ return set_subscr_cs_ps_enabled(cmd, data, false);
+}
+
int hlr_ctrl_cmds_install()
{
int rc = 0;
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_enable_ps);
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_disable_ps);
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_status_ps);
+ rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info);
+ rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info_aud);
+ rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info_all);
+ rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_ps_enabled);
+ rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_cs_enabled);
return rc;
}
+static int hlr_ctrl_node_lookup(void *data, vector vline, int *node_type,
+ void **node_data, int *i)
+{
+ const char *token = vector_slot(vline, *i);
+
+ switch (*node_type) {
+ case CTRL_NODE_ROOT:
+ if (strcmp(token, "subscriber") != 0)
+ return 0;
+ *node_data = NULL;
+ *node_type = CTRL_NODE_SUBSCR;
+ break;
+ case CTRL_NODE_SUBSCR:
+ if (!startswith(token, "by-"))
+ return 0;
+ *node_data = (void*)token;
+ *node_type = CTRL_NODE_SUBSCR_BY;
+ break;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
struct ctrl_handle *hlr_controlif_setup(struct hlr *hlr)
{
int rc;
struct ctrl_handle *hdl = ctrl_interface_setup_dynip2(hlr,
hlr->ctrl_bind_addr,
OSMO_CTRL_PORT_HLR,
- NULL,
- 0);
+ hlr_ctrl_node_lookup,
+ _LAST_CTRL_NODE_HLR);
if (!hdl)
return NULL;
diff --git a/src/ctrl.h b/src/ctrl.h
index 239deea..3f9ba3f 100644
--- a/src/ctrl.h
+++ b/src/ctrl.h
@@ -24,7 +24,11 @@
#include <osmocom/ctrl/control_if.h>
-#include "gsup_server.h"
+enum hlr_ctrl_node {
+ CTRL_NODE_SUBSCR = _LAST_CTRL_NODE,
+ CTRL_NODE_SUBSCR_BY,
+ _LAST_CTRL_NODE_HLR
+};
int hlr_ctrl_cmds_install();
struct ctrl_handle *hlr_controlif_setup(struct hlr *hlr);