osmo-hlr/tests/db/db_test.c

1063 lines
32 KiB
C

/* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
*
* 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 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 <stdio.h>
#include <errno.h>
#include <getopt.h>
#include <inttypes.h>
#include <osmocom/core/application.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/gsupclient/cni_peer_id.h>
#include <osmocom/hlr/db.h>
#include <osmocom/hlr/logging.h>
#define comment_start() fprintf(stderr, "\n===== %s\n", __func__);
#define comment(fmt, args...) fprintf(stderr, "\n--- " fmt "\n\n", ## args);
#define comment_end() fprintf(stderr, "===== %s: SUCCESS\n\n", __func__);
#define fill_invalid(x) _fill_invalid(&x, sizeof(x))
static void _fill_invalid(void *dest, size_t size)
{
uint8_t *pos = dest;
size_t remain = size;
int wrote = 0;
do {
remain -= wrote;
pos += wrote;
wrote = snprintf((void*)pos, remain, "-invalid-data");
} while (wrote < remain);
}
/* Perform a function call and verbosely assert that its return value is as expected.
* The return code is then available in g_rc. */
#define ASSERT_RC(call, expect_rc) \
do { \
if ((expect_rc) == -ENOKEY) \
fprintf(stderr, #call " --> -ENOKEY\n"); \
else if ((expect_rc) == -ENOTSUP) \
fprintf(stderr, #call " --> -ENOTSUP\n"); \
else \
fprintf(stderr, #call " --> " #expect_rc "\n"); \
g_rc = call; \
if (g_rc != (expect_rc)) \
fprintf(stderr, " MISMATCH: got rc = %d, expected: " \
#expect_rc " = %d\n", g_rc, expect_rc); \
OSMO_ASSERT(g_rc == (expect_rc)); \
fprintf(stderr, "\n"); \
} while (0)
/* Do db_subscr_get_by_xxxx and verbosely assert that its return value is as expected.
* Print the subscriber struct to stderr to be validated by db_test.err.
* The result is then available in g_subscr. */
#define ASSERT_SEL(by, val, expect_rc) \
do { \
int rc; \
fill_invalid(g_subscr); \
if ((expect_rc) == -ENOKEY) \
fprintf(stderr, "db_subscr_get_by_" #by "(dbc, " #val ", &g_subscr) --> -ENOKEY \n"); \
else if ((expect_rc) == -ENOTSUP) \
fprintf(stderr, "db_subscr_get_by_" #by "(dbc, " #val ", &g_subscr) --> -ENOTSUP \n"); \
else \
fprintf(stderr, "db_subscr_get_by_" #by "(dbc, " #val ", &g_subscr) --> " \
#expect_rc "\n"); \
rc = db_subscr_get_by_##by(dbc, val, &g_subscr); \
if (rc != (expect_rc)) \
fprintf(stderr, " MISMATCH: got rc = %d, expected: " \
#expect_rc " = %d\n", rc, expect_rc); \
OSMO_ASSERT(rc == (expect_rc)); \
if (!rc) \
dump_subscr(&g_subscr); \
fprintf(stderr, "\n"); \
} while (0)
/* Do db_get_auth_data() and verbosely assert that its return value is as expected.
* Print the subscriber struct to stderr to be validated by db_test.err.
* The results are then available in g_aud2g and g_aud3g. */
#define ASSERT_SEL_AUD(imsi, expect_rc, expect_id) \
do { \
fill_invalid(g_aud2g); \
fill_invalid(g_aud3g); \
g_id = 0; \
ASSERT_RC(db_get_auth_data(dbc, imsi, &g_aud2g, &g_aud3g, &g_id), expect_rc); \
if (!g_rc) { \
dump_aud("2G", &g_aud2g); \
dump_aud("3G", &g_aud3g); \
}\
if (g_id != expect_id) {\
fprintf(stderr, "MISMATCH: got subscriber id %"PRId64 \
", expected %"PRId64"\n", g_id, (int64_t)(expect_id)); \
OSMO_ASSERT(g_id == expect_id); \
} \
fprintf(stderr, "\n"); \
} while (0)
#define N_VECTORS 3
#define ASSERT_DB_GET_AUC(imsi, expect_rc) \
do { \
struct osmo_auth_vector vec[N_VECTORS]; \
ASSERT_RC(db_get_auc(dbc, imsi, 3, vec, N_VECTORS, NULL, NULL, false), expect_rc); \
} while (0)
/* Not linking the real auc_compute_vectors(), just returning num_vec.
* This gets called by db_get_auc(), but we're only interested in its rc. */
int auc_compute_vectors(struct osmo_auth_vector *vec, unsigned int num_vec,
struct osmo_sub_auth_data *aud2g,
struct osmo_sub_auth_data *aud3g,
const uint8_t *rand_auts, const uint8_t *auts)
{ return num_vec; }
static struct db_context *dbc = NULL;
static void *ctx = NULL;
static struct hlr_subscriber g_subscr;
static struct osmo_sub_auth_data g_aud2g;
static struct osmo_sub_auth_data g_aud3g;
static int g_rc;
static int64_t g_id;
#define Pfv(name, fmt, val) \
fprintf(stderr, " ." #name " = " fmt ",\n", val)
#define Pfo(name, fmt, obj) \
Pfv(name, fmt, obj->name)
/* Print a subscriber struct to stderr to be validated by db_test.err. */
void dump_subscr(struct hlr_subscriber *subscr)
{
#define Ps(name) \
if (*subscr->name) \
Pfo(name, "'%s'", subscr)
#define Pgt(name) \
Pfv(name, "%s", osmo_ipa_name_to_str(&subscr->name))
#define Pd(name) \
Pfv(name, "%"PRId64, (int64_t)subscr->name)
#define Pd_nonzero(name) \
if (subscr->name) \
Pd(name)
#define Pb(if_val, name) \
if (subscr->name == (if_val)) \
Pfv(name, "%s", subscr->name ? "true" : "false")
fprintf(stderr, "struct hlr_subscriber {\n");
Pd(id);
Ps(imsi);
Ps(msisdn);
Ps(imei);
Ps(vlr_number);
Ps(sgsn_number);
Ps(sgsn_address);
Pd_nonzero(periodic_lu_timer);
Pd_nonzero(periodic_rau_tau_timer);
Pb(false, nam_cs);
Pb(false, nam_ps);
if (subscr->lmsi)
Pfo(lmsi, "0x%x", subscr);
Pb(true, ms_purged_cs);
Pb(true, ms_purged_ps);
fprintf(stderr, "}\n");
#undef Ps
#undef Pd
#undef Pd_nonzero
#undef Pb
}
void dump_aud(const char *label, struct osmo_sub_auth_data *aud)
{
if (aud->type == OSMO_AUTH_TYPE_NONE) {
fprintf(stderr, "%s: none\n", label);
return;
}
fprintf(stderr, "%s: struct osmo_sub_auth_data {\n", label);
#define Pf(name, fmt) \
Pfo(name, fmt, aud)
#define Phex(name) \
Pfv(name, "'%s'", osmo_hexdump_nospc(aud->name, sizeof(aud->name)))
Pfv(type, "%s", osmo_sub_auth_type_name(aud->type));
Pfv(algo, "%s", osmo_auth_alg_name(aud->algo));
switch (aud->type) {
case OSMO_AUTH_TYPE_GSM:
Phex(u.gsm.ki);
break;
case OSMO_AUTH_TYPE_UMTS:
Phex(u.umts.opc);
Pf(u.umts.opc_is_op, "%u");
Phex(u.umts.k);
Phex(u.umts.amf);
if (aud->u.umts.sqn) {
Pf(u.umts.sqn, "%"PRIu64);
Pf(u.umts.sqn, "0x%"PRIx64);
}
if (aud->u.umts.ind_bitlen)
Pf(u.umts.ind_bitlen, "%u");
break;
default:
OSMO_ASSERT(false);
}
fprintf(stderr, "}\n");
#undef Pf
#undef Phex
}
void db_raw_sql(struct db_context *dbc, const char *sql)
{
sqlite3_stmt *stmt;
fprintf(stderr, "raw SQL: %s\n", sql);
ASSERT_RC(sqlite3_prepare_v2(dbc->db, sql, -1, &stmt, NULL), SQLITE_OK);
ASSERT_RC(sqlite3_step(stmt), SQLITE_DONE);
db_remove_reset(stmt);
sqlite3_finalize(stmt);
}
static const char *imsi0 = "123456789000000";
static const char *imsi1 = "123456789000001";
static const char *imsi2 = "123456789000002";
static const char *short_imsi = "123456";
static const char *unknown_imsi = "999999999";
static int db_subscr_lu_str(struct db_context *dbc, int64_t subscr_id,
const char *vlr_or_sgsn_number, bool is_ps)
{
struct osmo_ipa_name vlr_nr;
osmo_ipa_name_set_str(&vlr_nr, vlr_or_sgsn_number);
return db_subscr_lu(dbc, subscr_id, &vlr_nr, is_ps, NULL);
}
static void test_subscr_create_update_sel_delete()
{
int64_t id0, id1, id2, id_short;
comment_start();
comment("Create with valid / invalid IMSI");
ASSERT_RC(db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0);
ASSERT_SEL(imsi, imsi0, 0);
id0 = g_subscr.id;
ASSERT_RC(db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0);
ASSERT_SEL(imsi, imsi1, 0);
id1 = g_subscr.id;
ASSERT_RC(db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0);
ASSERT_SEL(imsi, imsi2, 0);
id2 = g_subscr.id;
ASSERT_RC(db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EIO);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EIO);
ASSERT_RC(db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EIO);
ASSERT_SEL(imsi, imsi1, 0);
ASSERT_RC(db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EIO);
ASSERT_RC(db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EIO);
ASSERT_SEL(imsi, imsi2, 0);
ASSERT_RC(db_subscr_create(dbc, "123456789 000003", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EINVAL);
ASSERT_SEL(imsi, "123456789000003", -ENOENT);
ASSERT_RC(db_subscr_create(dbc, "123456789000002123456", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS),
-EINVAL);
ASSERT_SEL(imsi, "123456789000002123456", -ENOENT);
ASSERT_RC(db_subscr_create(dbc, "foobar123", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EINVAL);
ASSERT_SEL(imsi, "foobar123", -ENOENT);
ASSERT_RC(db_subscr_create(dbc, "123", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EINVAL);
ASSERT_SEL(imsi, "123", -ENOENT);
ASSERT_RC(db_subscr_create(dbc, short_imsi, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0);
ASSERT_SEL(imsi, short_imsi, 0);
id_short = g_subscr.id;
comment("Check if subscriber exists (by IMSI)");
ASSERT_RC(db_subscr_exists_by_imsi(dbc, imsi0), 0);
ASSERT_RC(db_subscr_exists_by_imsi(dbc, unknown_imsi), -ENOENT);
comment("Set valid / invalid MSISDN");
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, imsi0, "54321"), 0);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_SEL(msisdn, "54321", 0);
ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, imsi0,
"54321012345678912345678"), -EINVAL);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_SEL(msisdn, "54321", 0);
ASSERT_SEL(msisdn, "54321012345678912345678", -ENOENT);
ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, imsi0,
"543 21"), -EINVAL);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_SEL(msisdn, "543 21", -ENOENT);
ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, imsi0,
"foobar123"), -EINVAL);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_SEL(msisdn, "foobar123", -ENOENT);
ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, imsi0,
"5"), 0);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_SEL(msisdn, "5", 0);
ASSERT_SEL(msisdn, "54321", -ENOENT);
ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, imsi0,
"543210123456789"), 0);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_SEL(msisdn, "543210123456789", 0);
ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, imsi0,
"5432101234567891"), -EINVAL);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_SEL(msisdn, "5432101234567891", -ENOENT);
comment("Check if subscriber exists (by MSISDN)");
ASSERT_RC(db_subscr_exists_by_msisdn(dbc, "543210123456789"), 0);
ASSERT_RC(db_subscr_exists_by_msisdn(dbc, "5432101234567891"), -ENOENT);
comment("Set MSISDN on non-existent / invalid IMSI");
ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, unknown_imsi, "99"), -ENOENT);
ASSERT_SEL(msisdn, "99", -ENOENT);
ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, "foobar", "99"), -ENOENT);
ASSERT_SEL(msisdn, "99", -ENOENT);
comment("Set valid / invalid IMEI");
ASSERT_RC(db_subscr_update_imei_by_imsi(dbc, imsi0, "12345678901234"), 0);
ASSERT_SEL(imei, "12345678901234", 0);
ASSERT_RC(db_subscr_update_imei_by_imsi(dbc, imsi0, "123456789012345"), -EINVAL); /* too long */
ASSERT_SEL(imei, "12345678901234", 0);
ASSERT_SEL(imei, "123456789012345", -ENOENT);
comment("Set the same IMEI again");
ASSERT_RC(db_subscr_update_imei_by_imsi(dbc, imsi0, "12345678901234"), 0);
ASSERT_SEL(imei, "12345678901234", 0);
comment("Remove IMEI");
ASSERT_RC(db_subscr_update_imei_by_imsi(dbc, imsi0, NULL), 0);
ASSERT_SEL(imei, "12345678901234", -ENOENT);
comment("Set / unset nam_cs and nam_ps");
/* nam_val, is_ps */
ASSERT_RC(db_subscr_nam(dbc, imsi0, false, true), 0);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_nam(dbc, imsi0, false, false), 0);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_nam(dbc, imsi0, true, false), 0);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_nam(dbc, imsi0, true, true), 0);
ASSERT_SEL(imsi, imsi0, 0);
comment("Set / unset nam_cs and nam_ps *again*");
ASSERT_RC(db_subscr_nam(dbc, imsi0, false, true), 0);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_nam(dbc, imsi0, false, true), 0);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_nam(dbc, imsi0, false, false), 0);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_nam(dbc, imsi0, false, false), 0);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_nam(dbc, imsi0, true, true), 0);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_nam(dbc, imsi0, true, true), 0);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_nam(dbc, imsi0, true, false), 0);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_nam(dbc, imsi0, true, false), 0);
ASSERT_SEL(imsi, imsi0, 0);
comment("Set nam_cs and nam_ps on non-existent / invalid IMSI");
ASSERT_RC(db_subscr_nam(dbc, unknown_imsi, false, true), -ENOENT);
ASSERT_RC(db_subscr_nam(dbc, unknown_imsi, false, false), -ENOENT);
ASSERT_SEL(imsi, unknown_imsi, -ENOENT);
ASSERT_RC(db_subscr_nam(dbc, "foobar", false, true), -ENOENT);
ASSERT_RC(db_subscr_nam(dbc, "foobar", false, false), -ENOENT);
comment("Record LU for PS and CS (SGSN and VLR names)");
ASSERT_RC(db_subscr_lu_str(dbc, id0, "5952", true), 0);
ASSERT_SEL(id, id0, 0);
ASSERT_RC(db_subscr_lu_str(dbc, id0, "712", false), 0);
ASSERT_SEL(id, id0, 0);
comment("Record LU for PS and CS (SGSN and VLR names) *again*");
ASSERT_RC(db_subscr_lu_str(dbc, id0, "111", true), 0);
ASSERT_SEL(id, id0, 0);
ASSERT_RC(db_subscr_lu_str(dbc, id0, "111", true), 0);
ASSERT_SEL(id, id0, 0);
ASSERT_RC(db_subscr_lu_str(dbc, id0, "222", false), 0);
ASSERT_SEL(id, id0, 0);
ASSERT_RC(db_subscr_lu_str(dbc, id0, "222", false), 0);
ASSERT_SEL(id, id0, 0);
comment("Unset LU info for PS and CS (SGSN and VLR names)");
ASSERT_RC(db_subscr_lu_str(dbc, id0, "", true), 0);
ASSERT_SEL(id, id0, 0);
ASSERT_RC(db_subscr_lu_str(dbc, id0, "", false), 0);
ASSERT_SEL(id, id0, 0);
ASSERT_RC(db_subscr_lu_str(dbc, id0, "111", true), 0);
ASSERT_RC(db_subscr_lu_str(dbc, id0, "222", false), 0);
ASSERT_SEL(id, id0, 0);
ASSERT_RC(db_subscr_lu_str(dbc, id0, NULL, true), 0);
ASSERT_SEL(id, id0, 0);
ASSERT_RC(db_subscr_lu_str(dbc, id0, NULL, false), 0);
ASSERT_SEL(id, id0, 0);
comment("Record LU for non-existent ID");
ASSERT_RC(db_subscr_lu_str(dbc, 99999, "5952", true), -ENOENT);
ASSERT_RC(db_subscr_lu_str(dbc, 99999, "712", false), -ENOENT);
ASSERT_SEL(id, 99999, -ENOENT);
comment("Purge and un-purge PS and CS");
/* purge_val, is_ps */
ASSERT_RC(db_subscr_purge(dbc, imsi0, true, true), 0);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_purge(dbc, imsi0, true, false), 0);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_purge(dbc, imsi0, false, false), 0);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_purge(dbc, imsi0, false, true), 0);
ASSERT_SEL(imsi, imsi0, 0);
comment("Purge PS and CS *again*");
ASSERT_RC(db_subscr_purge(dbc, imsi0, true, true), 0);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_purge(dbc, imsi0, true, true), 0);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_purge(dbc, imsi0, false, true), 0);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_purge(dbc, imsi0, false, true), 0);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_purge(dbc, imsi0, true, false), 0);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_purge(dbc, imsi0, true, false), 0);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_purge(dbc, imsi0, false, false), 0);
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_purge(dbc, imsi0, false, false), 0);
ASSERT_SEL(imsi, imsi0, 0);
comment("Purge on non-existent / invalid IMSI");
ASSERT_RC(db_subscr_purge(dbc, unknown_imsi, true, true), -ENOENT);
ASSERT_SEL(imsi, unknown_imsi, -ENOENT);
ASSERT_RC(db_subscr_purge(dbc, unknown_imsi, true, false), -ENOENT);
ASSERT_SEL(imsi, unknown_imsi, -ENOENT);
comment("Delete non-existent / invalid IDs");
ASSERT_RC(db_subscr_delete_by_id(dbc, 999), -ENOENT);
ASSERT_RC(db_subscr_delete_by_id(dbc, -10), -ENOENT);
comment("Delete subscribers");
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_delete_by_id(dbc, id0), 0);
ASSERT_SEL(imsi, imsi0, -ENOENT);
ASSERT_RC(db_subscr_delete_by_id(dbc, id0), -ENOENT);
ASSERT_SEL(imsi, imsi1, 0);
ASSERT_RC(db_subscr_delete_by_id(dbc, id1), 0);
ASSERT_SEL(imsi, imsi1, -ENOENT);
ASSERT_SEL(imsi, imsi2, 0);
ASSERT_RC(db_subscr_delete_by_id(dbc, id2), 0);
ASSERT_SEL(imsi, imsi2, -ENOENT);
ASSERT_SEL(imsi, short_imsi, 0);
ASSERT_RC(db_subscr_delete_by_id(dbc, id_short), 0);
ASSERT_SEL(imsi, short_imsi, -ENOENT);
comment("Create and delete subscribers with non-default nam_cs and nam_ps");
ASSERT_RC(db_subscr_create(dbc, imsi0, 0x00), 0);
ASSERT_SEL(imsi, imsi0, 0);
id0 = g_subscr.id;
ASSERT_RC(db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS), 0);
ASSERT_SEL(imsi, imsi1, 0);
id1 = g_subscr.id;
ASSERT_RC(db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_PS), 0);
ASSERT_SEL(imsi, imsi2, 0);
id2 = g_subscr.id;
ASSERT_RC(db_subscr_delete_by_id(dbc, id0), 0);
ASSERT_RC(db_subscr_delete_by_id(dbc, id1), 0);
ASSERT_RC(db_subscr_delete_by_id(dbc, id2), 0);
comment_end();
}
static const struct sub_auth_data_str *mk_aud_2g(enum osmo_auth_algo algo,
const char *ki)
{
static struct sub_auth_data_str aud;
aud = (struct sub_auth_data_str){
.type = OSMO_AUTH_TYPE_GSM,
.algo = algo,
.u.gsm.ki = ki,
};
return &aud;
}
static const struct sub_auth_data_str *mk_aud_3g(enum osmo_auth_algo algo,
const char *opc, bool opc_is_op,
const char *k, unsigned int ind_bitlen)
{
static struct sub_auth_data_str aud;
aud = (struct sub_auth_data_str){
.type = OSMO_AUTH_TYPE_UMTS,
.algo = algo,
.u.umts.k = k,
.u.umts.opc = opc,
.u.umts.opc_is_op = opc_is_op ? 1 : 0,
.u.umts.ind_bitlen = ind_bitlen,
};
return &aud;
}
static void test_subscr_aud()
{
int64_t id;
comment_start();
comment("Get auth data for non-existent subscriber");
ASSERT_SEL_AUD(unknown_imsi, -ENOENT, 0);
ASSERT_DB_GET_AUC(imsi0, -ENOENT);
comment("Create subscriber");
ASSERT_RC(db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0);
ASSERT_SEL(imsi, imsi0, 0);
id = g_subscr.id;
ASSERT_SEL_AUD(imsi0, -ENOKEY, id);
ASSERT_DB_GET_AUC(imsi0, -ENOKEY);
comment("Set auth data, 2G only");
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_2g(OSMO_AUTH_ALG_COMP128v1, "0123456789abcdef0123456789abcdef")),
0);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_DB_GET_AUC(imsi0, N_VECTORS);
/* same again */
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_2g(OSMO_AUTH_ALG_COMP128v1, "0123456789abcdef0123456789abcdef")),
0);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_2g(OSMO_AUTH_ALG_COMP128v2, "BeadedBeeAced1EbbedDefacedFacade")),
0);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_2g(OSMO_AUTH_ALG_COMP128v3, "DeafBeddedBabeAcceededFadedDecaf")),
0);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_2g(OSMO_AUTH_ALG_XOR, "CededEffacedAceFacedBadFadedBeef")),
0);
ASSERT_SEL_AUD(imsi0, 0, id);
comment("Remove 2G auth data");
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_2g(OSMO_AUTH_ALG_NONE, NULL)),
0);
ASSERT_SEL_AUD(imsi0, -ENOKEY, id);
ASSERT_DB_GET_AUC(imsi0, -ENOKEY);
/* Removing nothing results in -ENOENT */
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_2g(OSMO_AUTH_ALG_NONE, NULL)),
-ENOENT);
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_2g(OSMO_AUTH_ALG_XOR, "CededEffacedAceFacedBadFadedBeef")),
0);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_2g(OSMO_AUTH_ALG_NONE, "f000000000000f00000000000f000000")),
0);
ASSERT_SEL_AUD(imsi0, -ENOKEY, id);
ASSERT_DB_GET_AUC(imsi0, -ENOKEY);
comment("Set auth data, 3G only");
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_3g(OSMO_AUTH_ALG_MILENAGE,
"BeefedCafeFaceAcedAddedDecadeFee", true,
"C01ffedC1cadaeAc1d1f1edAcac1aB0a", 5)),
0);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_DB_GET_AUC(imsi0, N_VECTORS);
/* same again */
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_3g(OSMO_AUTH_ALG_MILENAGE,
"BeefedCafeFaceAcedAddedDecadeFee", true,
"C01ffedC1cadaeAc1d1f1edAcac1aB0a", 5)),
0);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_3g(OSMO_AUTH_ALG_MILENAGE,
"Deaf0ff1ceD0d0DabbedD1ced1ceF00d", true,
"F1bbed0afD0eF0bD0ffed0ddF1fe0b0e", 0)),
0);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_3g(OSMO_AUTH_ALG_MILENAGE,
"BeefedCafeFaceAcedAddedDecadeFee", false,
"DeafBeddedBabeAcceededFadedDecaf",
OSMO_MILENAGE_IND_BITLEN_MAX)),
0);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_3g(OSMO_AUTH_ALG_MILENAGE,
"CededEffacedAceFacedBadFadedBeef", false,
"BeefedCafeFaceAcedAddedDecadeFee", 5)),
0);
ASSERT_SEL_AUD(imsi0, 0, id);
comment("Remove 3G auth data");
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_3g(OSMO_AUTH_ALG_NONE, NULL, false, NULL, 0)),
0);
ASSERT_SEL_AUD(imsi0, -ENOKEY, id);
ASSERT_DB_GET_AUC(imsi0, -ENOKEY);
/* Removing nothing results in -ENOENT */
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_3g(OSMO_AUTH_ALG_NONE, NULL, false, NULL, 0)),
-ENOENT);
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_3g(OSMO_AUTH_ALG_MILENAGE,
"CededEffacedAceFacedBadFadedBeef", false,
"BeefedCafeFaceAcedAddedDecadeFee", 5)),
0);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_DB_GET_AUC(imsi0, N_VECTORS);
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_3g(OSMO_AUTH_ALG_NONE,
"asdfasdfasd", false,
"asdfasdfasdf", 99999)),
0);
ASSERT_SEL_AUD(imsi0, -ENOKEY, id);
ASSERT_DB_GET_AUC(imsi0, -ENOKEY);
comment("Set auth data, 2G and 3G");
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_2g(OSMO_AUTH_ALG_COMP128v3, "CededEffacedAceFacedBadFadedBeef")),
0);
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_3g(OSMO_AUTH_ALG_MILENAGE,
"BeefedCafeFaceAcedAddedDecadeFee", false,
"DeafBeddedBabeAcceededFadedDecaf", 5)),
0);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_DB_GET_AUC(imsi0, N_VECTORS);
comment("Set invalid auth data");
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_2g(99999, "f000000000000f00000000000f000000")),
-EINVAL);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_2g(OSMO_AUTH_ALG_XOR, "f000000000000f00000000000f000000f00000000")),
-EINVAL);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_2g(OSMO_AUTH_ALG_XOR, "f00")),
-EINVAL);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_2g(OSMO_AUTH_ALG_MILENAGE, "0123456789abcdef0123456789abcdef")),
-EINVAL);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_3g(OSMO_AUTH_ALG_MILENAGE,
"0f000000000000f00000000000f000000", false,
"f000000000000f00000000000f000000", 5)),
-EINVAL);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_3g(OSMO_AUTH_ALG_MILENAGE,
"f000000000000f00000000000f000000", false,
"000000000000f00000000000f000000", 5)),
-EINVAL);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_3g(OSMO_AUTH_ALG_MILENAGE,
"f000000000000f00000000000f000000", false,
"f000000000000f00000000000f000000",
OSMO_MILENAGE_IND_BITLEN_MAX + 1)),
-EINVAL);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_3g(OSMO_AUTH_ALG_MILENAGE,
"X000000000000f00000000000f000000", false,
"f000000000000f00000000000f000000", 5)),
-EINVAL);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_3g(OSMO_AUTH_ALG_MILENAGE,
"f000000000000f00000000000f000000", false,
"f000000000000 f00000000000 f000000", 5)),
-EINVAL);
ASSERT_SEL_AUD(imsi0, 0, id);
comment("Delete subscriber");
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_delete_by_id(dbc, id), 0);
ASSERT_SEL(imsi, imsi0, -ENOENT);
comment("Re-add subscriber and verify auth data didn't come back");
ASSERT_RC(db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0);
ASSERT_SEL(imsi, imsi0, 0);
/* For this test to work, we want to get the same subscriber ID back,
* and make sure there are no auth data leftovers for this ID. */
OSMO_ASSERT(id == g_subscr.id);
ASSERT_SEL_AUD(imsi0, -ENOKEY, id);
ASSERT_DB_GET_AUC(imsi0, -ENOKEY);
ASSERT_RC(db_subscr_delete_by_id(dbc, id), 0);
ASSERT_SEL(imsi, imsi0, -ENOENT);
ASSERT_DB_GET_AUC(imsi0, -ENOENT);
comment_end();
}
/* Make each key too short in this test. Note that we can't set them longer than the allowed size without changing the
* table structure. */
static void test_subscr_aud_invalid_len()
{
int64_t id;
comment_start();
comment("Create subscriber");
ASSERT_RC(db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0);
ASSERT_SEL(imsi, imsi0, 0);
id = g_subscr.id;
/* Invalid Ki length */
comment("Set auth data, 2G only, with invalid Ki length");
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_2g(OSMO_AUTH_ALG_COMP128v1, "0123456789abcdef0123456789abcdef")),
0);
/* Use raw SQL to avoid length check in db_subscr_update_aud_by_id(). This changes all rows in the table, which
* is fine for this test (implicit WHERE 1). */
db_raw_sql(dbc, "UPDATE auc_2g SET ki = '0123456789abcdef0123456789abcde'");
ASSERT_SEL_AUD(imsi0, -ENOKEY, id);
comment("Remove 2G auth data");
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_2g(OSMO_AUTH_ALG_NONE, NULL)),
0);
/* Invalid K length */
comment("Set auth data, 3G only, with invalid K length");
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_3g(OSMO_AUTH_ALG_MILENAGE,
"BeefedCafeFaceAcedAddedDecadeFee", true,
"C01ffedC1cadaeAc1d1f1edAcac1aB0a", 5)),
0);
db_raw_sql(dbc, "UPDATE auc_3g SET k = 'C01ffedC1cadaeAc1d1f1edAcac1aB0'");
ASSERT_SEL_AUD(imsi0, -EIO, id);
/* Invalid OP length */
comment("Set auth data, 3G only, with invalid OP length");
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_3g(OSMO_AUTH_ALG_MILENAGE,
"BeefedCafeFaceAcedAddedDecadeFee", true,
"C01ffedC1cadaeAc1d1f1edAcac1aB0a", 5)),
0);
db_raw_sql(dbc, "UPDATE auc_3g SET op = 'BeefedCafeFaceAcedAddedDecadeFe'");
ASSERT_SEL_AUD(imsi0, -EIO, id);
/* Invalid OPC length */
comment("Set auth data, 3G only, with invalid OPC length");
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_3g(OSMO_AUTH_ALG_MILENAGE,
"BeefedCafeFaceAcedAddedDecadeFee", false,
"C01ffedC1cadaeAc1d1f1edAcac1aB0a", 5)),
0);
db_raw_sql(dbc, "UPDATE auc_3g SET opc = 'BeefedCafeFaceAcedAddedDecadeFe'");
ASSERT_SEL_AUD(imsi0, -EIO, id);
comment("Delete subscriber");
ASSERT_RC(db_subscr_delete_by_id(dbc, id), 0);
comment_end();
}
static void test_subscr_sqn()
{
int64_t id;
comment_start();
comment("Set SQN for unknown subscriber");
ASSERT_RC(db_update_sqn(dbc, 99, 999), -ENOENT);
ASSERT_SEL(id, 99, -ENOENT);
ASSERT_RC(db_update_sqn(dbc, 9999, 99), -ENOENT);
ASSERT_SEL(id, 9999, -ENOENT);
comment("Create subscriber");
ASSERT_RC(db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0);
ASSERT_SEL(imsi, imsi0, 0);
id = g_subscr.id;
ASSERT_SEL_AUD(imsi0, -ENOKEY, id);
comment("Set SQN, but no 3G auth data present");
ASSERT_RC(db_update_sqn(dbc, id, 123), -ENOENT);
ASSERT_SEL_AUD(imsi0, -ENOKEY, id);
ASSERT_RC(db_update_sqn(dbc, id, 543), -ENOENT);
ASSERT_SEL_AUD(imsi0, -ENOKEY, id);
comment("Set auth 3G data");
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_3g(OSMO_AUTH_ALG_MILENAGE,
"BeefedCafeFaceAcedAddedDecadeFee", true,
"C01ffedC1cadaeAc1d1f1edAcac1aB0a", 5)),
0);
ASSERT_SEL_AUD(imsi0, 0, id);
comment("Set SQN");
ASSERT_RC(db_update_sqn(dbc, id, 23315), 0);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_RC(db_update_sqn(dbc, id, 23315), 0);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_RC(db_update_sqn(dbc, id, 423), 0);
ASSERT_SEL_AUD(imsi0, 0, id);
comment("Set SQN: thru uint64_t range, using the int64_t SQLite bind");
ASSERT_RC(db_update_sqn(dbc, id, 0), 0);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_RC(db_update_sqn(dbc, id, INT64_MAX), 0);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_RC(db_update_sqn(dbc, id, INT64_MIN), 0);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_RC(db_update_sqn(dbc, id, UINT64_MAX), 0);
ASSERT_SEL_AUD(imsi0, 0, id);
comment("Delete subscriber");
ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_delete_by_id(dbc, id), 0);
ASSERT_SEL(imsi, imsi0, -ENOENT);
comment_end();
}
static void test_ind()
{
comment_start();
#define ASSERT_IND(VLR, IND) do { \
unsigned int ind; \
struct osmo_cni_peer_id vlr; \
OSMO_ASSERT(!osmo_cni_peer_id_set_str(&vlr, OSMO_CNI_PEER_ID_IPA_NAME, VLR)); \
ASSERT_RC(db_ind(dbc, &vlr, &ind), 0); \
fprintf(stderr, "%s ind = %u\n\n", osmo_quote_str((char*)vlr.ipa_name.val, vlr.ipa_name.len), ind); \
if (ind != (IND)) \
fprintf(stderr, " ERROR: expected " #IND "\n"); \
} while (0)
#define IND_DEL(VLR) do { \
struct osmo_cni_peer_id vlr; \
OSMO_ASSERT(!osmo_cni_peer_id_set_str(&vlr, OSMO_CNI_PEER_ID_IPA_NAME, VLR)); \
ASSERT_RC(db_ind_del(dbc, &vlr), 0); \
fprintf(stderr, "%s ind deleted\n\n", osmo_quote_str((char*)vlr.ipa_name.val, vlr.ipa_name.len)); \
} while (0)
ASSERT_IND("msc-23", 1);
ASSERT_IND("sgsn-11", 2);
ASSERT_IND("msc-42", 3);
ASSERT_IND("sgsn-22", 4);
ASSERT_IND("msc-0x17", 5);
ASSERT_IND("sgsn-0xaa", 6);
ASSERT_IND("msc-42", 3);
ASSERT_IND("sgsn-22", 4);
ASSERT_IND("msc-0x17", 5);
ASSERT_IND("sgsn-0xaa", 6);
ASSERT_IND("sgsn-0xbb", 7);
ASSERT_IND("msc-0x2a", 8);
ASSERT_IND("msc-42", 3);
ASSERT_IND("sgsn-22", 4);
ASSERT_IND("msc-23", 1);
ASSERT_IND("sgsn-11", 2);
IND_DEL("msc-0x17"); /* dropped IND == 5 */
ASSERT_IND("msc-0x2a", 8); /* known CS remains where it is */
ASSERT_IND("any-unknown", 9); /* new VLR takes a new IND from the end */
comment_end();
}
static struct {
bool verbose;
} cmdline_opts = {
.verbose = false,
};
static void print_help(const char *program)
{
printf("Usage:\n"
" %s [-v] [N [N...]]\n"
"Options:\n"
" -h --help show this text.\n"
" -v --verbose print source file and line numbers\n",
program
);
}
static void handle_options(int argc, char **argv)
{
while (1) {
int option_index = 0, c;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"verbose", 1, 0, 'v'},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "hv",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'h':
print_help(argv[0]);
exit(0);
case 'v':
cmdline_opts.verbose = true;
break;
default:
/* catch unknown options *as well as* missing arguments. */
fprintf(stderr, "Error in command line options. Exiting.\n");
exit(-1);
break;
}
}
if (optind < argc) {
fprintf(stderr, "too many args\n");
exit(-1);
}
}
int main(int argc, char **argv)
{
printf("db_test.c\n");
ctx = talloc_named_const(NULL, 1, "db_test");
handle_options(argc, argv);
osmo_init_logging2(ctx, &hlr_log_info);
log_set_print_filename2(osmo_stderr_target,
cmdline_opts.verbose ?
LOG_FILENAME_BASENAME :
LOG_FILENAME_NONE);
log_set_print_timestamp(osmo_stderr_target, 0);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_category_hex(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 1);
log_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1");
/* omit the SQLite version and compilation flags from test output */
log_set_log_level(osmo_stderr_target, LOGL_ERROR);
/* Disable SQLite logging so that we're not vulnerable on SQLite error messages changing across
* library versions. */
dbc = db_open(ctx, "db_test.db", false, false);
log_set_log_level(osmo_stderr_target, 0);
OSMO_ASSERT(dbc);
test_subscr_create_update_sel_delete();
test_subscr_aud();
test_subscr_aud_invalid_len();
test_subscr_sqn();
test_ind();
printf("Done\n");
db_close(dbc);
return 0;
}
/* stubs */
void *lu_op_alloc_conn(void *conn)
{ OSMO_ASSERT(false); return NULL; }
void lu_op_tx_del_subscr_data(void *luop)
{ OSMO_ASSERT(false); }
void lu_op_free(void *luop)
{ OSMO_ASSERT(false); }