Make random MSISDN assignment optional

Previously if subscriber was automatically created it got assigned
random MSISDN number. Make it optional (defaulting to previous behavior)
by adding following:

* new optional no-extension argument for subscriber-create-on-demand vty
  command
* db unit tests
* vty test

Note: using the db made with new code might result in subscribers with
empty extension. Such subscribers cannot be deleted using old
code. Make sure not to mix db versions or manually fix it by editing
sqlite with external program.

Fixes: OS#1658
Change-Id: Ibbc2e88e4722b08854ebc631485f19ed56443cbb
This commit is contained in:
Max 2016-06-30 10:25:49 +02:00 committed by Harald Welte
parent 9f8f9b8021
commit e6052c4cc7
12 changed files with 141 additions and 60 deletions

View File

@ -20,6 +20,8 @@
#ifndef _DB_H
#define _DB_H
#include <stdbool.h>
#include "gsm_subscriber.h"
struct gsm_equipment;
@ -36,7 +38,7 @@ int db_fini(void);
/* subscriber management */
struct gsm_subscriber *db_create_subscriber(const char *imsi, uint64_t smin,
uint64_t smax);
uint64_t smax, bool alloc_exten);
struct gsm_subscriber *db_get_subscriber(enum gsm_subscriber_field field,
const char *subscr);
int db_sync_subscriber(struct gsm_subscriber *subscriber);

View File

@ -4,6 +4,7 @@
#include <stdint.h>
#include <regex.h>
#include <sys/types.h>
#include <stdbool.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/select.h>
@ -21,12 +22,6 @@ struct gsm_subscriber_group;
#define OBSC_LINKID_CB(__msgb) (__msgb)->cb[3]
enum gsm_subscr_creation_mode {
GSM_SUBSCR_DONT_CREATE = 0,
GSM_SUBSCR_CREAT_W_RAND_EXT = 1,
GSM_SUBSCR_CREAT_W_REGEXP = 2,
};
enum gsm_security_event {
GSM_SECURITY_NOAVAIL,
GSM_SECURITY_AUTH_FAILED,
@ -289,7 +284,8 @@ struct gsm_network {
struct osmo_bsc_data *bsc_data;
/* subscriber related features */
int subscr_creation_mode;
bool auto_create_subscr;
bool auto_assign_exten;
uint64_t ext_min;
uint64_t ext_max;
struct gsm_subscriber_group *subscr_group;

View File

@ -5,6 +5,8 @@
#include <osmocom/core/linuxlist.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <stdbool.h>
#define GSM_NAME_LENGTH 160
#define GSM_EXTENSION_LENGTH 15 /* MSISDN can only be 15 digits length */
@ -90,8 +92,7 @@ enum gsm_subscriber_update_reason {
struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr);
struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr);
struct gsm_subscriber *subscr_create_subscriber(struct gsm_subscriber_group *sgrp,
const char *imsi, uint64_t smin,
uint64_t smax);
const char *imsi);
struct gsm_subscriber *subscr_get_by_tmsi(struct gsm_subscriber_group *sgrp,
uint32_t tmsi);
struct gsm_subscriber *subscr_get_by_imsi(struct gsm_subscriber_group *sgrp,

View File

@ -195,6 +195,10 @@ static void net_dump_vty(struct vty *vty, struct gsm_network *net)
if (net->authorized_reg_str)
vty_out(vty, ", authorized regexp: %s", net->authorized_reg_str);
vty_out(vty, "%s", VTY_NEWLINE);
vty_out(vty, " Auto create subscriber: %s%s",
net->auto_create_subscr ? "yes" : "no", VTY_NEWLINE);
vty_out(vty, " Auto assign extension: %s%s",
net->auto_assign_exten ? "yes" : "no", VTY_NEWLINE);
vty_out(vty, " Location updating reject cause: %u%s",
net->reject_cause, VTY_NEWLINE);
vty_out(vty, " Encryption: A5/%u%s", net->a5_encryption,

View File

@ -21,10 +21,13 @@
#include <openbsc/osmo_msc_data.h>
#include <openbsc/gsm_subscriber.h>
#include <stdbool.h>
struct gsm_network *gsm_network_init(uint16_t country_code, uint16_t network_code,
int (*mncc_recv)(struct gsm_network *, struct msgb *))
{
struct gsm_network *net;
const char *default_regexp = "*";
net = talloc_zero(tall_bsc_ctx, struct gsm_network);
if (!net)
@ -42,13 +45,18 @@ struct gsm_network *gsm_network_init(uint16_t country_code, uint16_t network_cod
return NULL;
}
if (gsm_parse_reg(net, &net->authorized_regexp, &net->authorized_reg_str, 1,
&default_regexp) != 0)
return NULL;
/* Init back pointer */
net->bsc_data->auto_off_timeout = -1;
net->bsc_data->network = net;
INIT_LLIST_HEAD(&net->bsc_data->mscs);
net->subscr_group->net = net;
net->subscr_creation_mode = GSM_SUBSCR_CREAT_W_RAND_EXT;
net->auto_create_subscr = true;
net->auto_assign_exten = true;
net->country_code = country_code;
net->network_code = network_code;

View File

@ -25,6 +25,8 @@
#include <openbsc/db.h>
#include <openbsc/debug.h>
#include <stdbool.h>
static bool alg_supported(const char *alg)
{
/*
@ -96,9 +98,7 @@ static int set_subscriber_modify(struct ctrl_cmd *cmd, void *data)
subscr = subscr_get_by_imsi(net->subscr_group, imsi);
if (!subscr)
subscr = subscr_create_subscriber(net->subscr_group, imsi,
net->ext_min,
net->ext_max);
subscr = subscr_create_subscriber(net->subscr_group, imsi);
if (!subscr)
goto fail;

View File

@ -23,6 +23,7 @@
#include <inttypes.h>
#include <libgen.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
@ -519,7 +520,7 @@ int db_fini(void)
}
struct gsm_subscriber *db_create_subscriber(const char *imsi, uint64_t smin,
uint64_t smax)
uint64_t smax, bool alloc_exten)
{
dbi_result result;
struct gsm_subscriber *subscr;
@ -551,7 +552,8 @@ struct gsm_subscriber *db_create_subscriber(const char *imsi, uint64_t smin,
strncpy(subscr->imsi, imsi, sizeof(subscr->imsi)-1);
dbi_result_free(result);
LOGP(DDB, LOGL_INFO, "New Subscriber: ID %llu, IMSI %s\n", subscr->id, subscr->imsi);
db_subscriber_alloc_exten(subscr, smin, smax);
if (alloc_exten)
db_subscriber_alloc_exten(subscr, smin, smax);
return subscr;
}
@ -956,8 +958,11 @@ int db_sync_subscriber(struct gsm_subscriber *subscriber)
dbi_conn_quote_string_copy(conn,
subscriber->name, &q_name);
dbi_conn_quote_string_copy(conn,
subscriber->extension, &q_extension);
if (subscriber->extension[0] != '\0')
dbi_conn_quote_string_copy(conn,
subscriber->extension, &q_extension);
else
q_extension = strdup("NULL");
if (subscriber->tmsi != GSM_RESERVED_TMSI) {
sprintf(tmsi, "%u", subscriber->tmsi);
@ -1062,15 +1067,17 @@ int db_subscriber_delete(struct gsm_subscriber *subscr)
}
dbi_result_free(result);
result = dbi_conn_queryf(conn,
"DELETE FROM SMS WHERE src_addr=%s OR dest_addr=%s",
subscr->extension, subscr->extension);
if (!result) {
LOGP(DDB, LOGL_ERROR,
"Failed to delete SMS for %llu\n", subscr->id);
return -1;
if (subscr->extension[0] != '\0') {
result = dbi_conn_queryf(conn,
"DELETE FROM SMS WHERE src_addr=%s OR dest_addr=%s",
subscr->extension, subscr->extension);
if (!result) {
LOGP(DDB, LOGL_ERROR,
"Failed to delete SMS for %llu\n", subscr->id);
return -1;
}
dbi_result_free(result);
}
dbi_result_free(result);
result = dbi_conn_queryf(conn,
"DELETE FROM VLR WHERE subscriber_id=%llu",

View File

@ -530,15 +530,13 @@ static int mm_tx_identity_req(struct gsm_subscriber_connection *conn, uint8_t id
static struct gsm_subscriber *subscr_create(const struct gsm_network *net,
const char *imsi)
{
if (net->subscr_creation_mode == GSM_SUBSCR_DONT_CREATE)
if (!net->auto_create_subscr)
return NULL;
if (net->subscr_creation_mode & GSM_SUBSCR_CREAT_W_REGEXP)
if (!subscr_regexp_check(net, imsi))
return NULL;
if (!subscr_regexp_check(net, imsi))
return NULL;
return subscr_create_subscriber(net->subscr_group, imsi, net->ext_min,
net->ext_max);
return subscr_create_subscriber(net->subscr_group, imsi);
}
/* Parse Chapter 9.2.11 Identity Response */

View File

@ -26,6 +26,7 @@
#include <string.h>
#include <assert.h>
#include <time.h>
#include <stdbool.h>
#include <osmocom/core/talloc.h>
@ -203,10 +204,12 @@ void subscr_remove_request(struct subscr_request *request)
}
struct gsm_subscriber *subscr_create_subscriber(struct gsm_subscriber_group *sgrp,
const char *imsi, uint64_t smin,
uint64_t smax)
const char *imsi)
{
struct gsm_subscriber *subscr = db_create_subscriber(imsi, smin, smax);
struct gsm_subscriber *subscr = db_create_subscriber(imsi,
sgrp->net->ext_min,
sgrp->net->ext_max,
sgrp->net->auto_assign_exten);
if (subscr)
subscr->group = sgrp;
return subscr;

View File

@ -242,9 +242,7 @@ DEFUN(subscriber_create,
if (subscr)
db_sync_subscriber(subscr);
else {
subscr = subscr_create_subscriber(gsmnet->subscr_group, argv[0],
gsmnet->ext_min,
gsmnet->ext_max);
subscr = subscr_create_subscriber(gsmnet->subscr_group, argv[0]);
if (!subscr) {
vty_out(vty, "%% No subscriber created for IMSI %s%s",
@ -1044,6 +1042,8 @@ DEFUN(cfg_nitb_subscr_random, cfg_nitb_subscr_random_cmd,
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
uint64_t mi = atoi(argv[0]), ma = atoi(argv[1]);
gsmnet->auto_create_subscr = true;
gsmnet->auto_assign_exten = true;
if (mi >= ma) {
vty_out(vty, "Incorrect range: %s >= %s, expected MIN < MAX%s",
argv[0], argv[1], VTY_NEWLINE);
@ -1055,15 +1055,13 @@ DEFUN(cfg_nitb_subscr_random, cfg_nitb_subscr_random_cmd,
}
DEFUN(cfg_nitb_subscr_create, cfg_nitb_subscr_create_cmd,
"subscriber-create-on-demand [regexp]",
"subscriber-create-on-demand [no-extension]",
"Make a new record when a subscriber is first seen.\n"
"Create subscribers only if IMSI matches the regexp specified in "
"authorized-regexp command\n")
"Do not automatically assign extension to created subscribers\n")
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
gsmnet->subscr_creation_mode = GSM_SUBSCR_CREAT_W_RAND_EXT;
if (argc)
gsmnet->subscr_creation_mode |= GSM_SUBSCR_CREAT_W_REGEXP;
gsmnet->auto_create_subscr = true;
gsmnet->auto_assign_exten = argc ? false : true;
return CMD_SUCCESS;
}
@ -1072,7 +1070,7 @@ DEFUN(cfg_nitb_no_subscr_create, cfg_nitb_no_subscr_create_cmd,
NO_STR "Make a new record when a subscriber is first seen.\n")
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
gsmnet->subscr_creation_mode = GSM_SUBSCR_DONT_CREATE;
gsmnet->auto_create_subscr = false;
return CMD_SUCCESS;
}
@ -1097,12 +1095,15 @@ DEFUN(cfg_nitb_no_assign_tmsi, cfg_nitb_no_assign_tmsi_cmd,
static int config_write_nitb(struct vty *vty)
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
enum gsm_subscr_creation_mode scm = gsmnet->subscr_creation_mode;
const char *reg = (scm & GSM_SUBSCR_CREAT_W_REGEXP) ? " regexp" : "",
*pref = scm ? "" : "no ";
vty_out(vty, "nitb%s", VTY_NEWLINE);
vty_out(vty, " %ssubscriber-create-on-demand%s%s",
pref, reg, VTY_NEWLINE);
if (!gsmnet->auto_create_subscr)
vty_out(vty, " no subscriber-create-on-demand%s", VTY_NEWLINE);
else
vty_out(vty, " subscriber-create-on-demand%s%s",
gsmnet->auto_assign_exten ? "" : " no-extension",
VTY_NEWLINE);
if (gsmnet->ext_min != GSM_MIN_EXTEN || gsmnet->ext_max != GSM_MAX_EXTEN)
vty_out(vty, " subscriber-create-on-demand random %"PRIu64" %"
PRIu64"%s", gsmnet->ext_min, gsmnet->ext_max,

View File

@ -28,6 +28,7 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <inttypes.h>
static struct gsm_network dummy_net;
@ -159,12 +160,13 @@ static void test_sms_migrate(void)
subscr_put(rcv_subscr);
}
static void test_subs(const char *alice_imsi, char *imei1, char *imei2)
static void test_subs(const char *imsi, char *imei1, char *imei2, bool make_ext)
{
struct gsm_subscriber *alice = NULL, *alice_db;
char scratch_str[256];
alice = db_create_subscriber(alice_imsi, GSM_MIN_EXTEN, GSM_MAX_EXTEN);
alice = db_create_subscriber(imsi, GSM_MIN_EXTEN, GSM_MAX_EXTEN,
make_ext);
db_subscriber_assoc_imei(alice, imei1);
if (imei2)
db_subscriber_assoc_imei(alice, imei2);
@ -177,7 +179,7 @@ static void test_subs(const char *alice_imsi, char *imei1, char *imei2)
COMPARE(alice, alice_db);
SUBSCR_PUT(alice_db);
/* Get by IMSI */
alice_db = db_get_subscriber(GSM_SUBSCRIBER_IMSI, alice_imsi);
alice_db = db_get_subscriber(GSM_SUBSCRIBER_IMSI, imsi);
COMPARE(alice, alice_db);
SUBSCR_PUT(alice_db);
/* Get by id */
@ -187,8 +189,14 @@ static void test_subs(const char *alice_imsi, char *imei1, char *imei2)
SUBSCR_PUT(alice_db);
/* Get by extension */
alice_db = db_get_subscriber(GSM_SUBSCRIBER_EXTENSION, alice->extension);
COMPARE(alice, alice_db);
SUBSCR_PUT(alice_db);
if (alice_db) {
if (!make_ext)
printf("FAIL: bogus extension created for IMSI %s\n",
imsi);
COMPARE(alice, alice_db);
SUBSCR_PUT(alice_db);
} else if (make_ext)
printf("FAIL: no subscriber extension for IMSI %s\n", imsi);
SUBSCR_PUT(alice);
}
@ -217,18 +225,22 @@ int main()
struct gsm_subscriber *alice_db;
char *alice_imsi = "3243245432345";
alice = db_create_subscriber(alice_imsi, GSM_MIN_EXTEN, GSM_MAX_EXTEN);
alice = db_create_subscriber(alice_imsi, GSM_MIN_EXTEN, GSM_MAX_EXTEN,
true);
db_sync_subscriber(alice);
alice_db = db_get_subscriber(GSM_SUBSCRIBER_IMSI, alice->imsi);
COMPARE(alice, alice_db);
SUBSCR_PUT(alice_db);
SUBSCR_PUT(alice);
test_subs("3693245423445", "1234567890", NULL);
test_subs("9993245423445", "1234567890", "6543560920");
test_subs("3693245423445", "1234567890", NULL, true);
test_subs("9993245423445", "1234567890", "6543560920", true);
test_subs("3123122223445", "1234567890", NULL, false);
test_subs("9123121223445", "1234567890", "6543560920", false);
/* create it again and see it fails */
alice = db_create_subscriber(alice_imsi, GSM_MIN_EXTEN, GSM_MAX_EXTEN);
alice = db_create_subscriber(alice_imsi, GSM_MIN_EXTEN, GSM_MAX_EXTEN,
true);
OSMO_ASSERT(!alice);
test_sms();

View File

@ -244,7 +244,7 @@ class TestVTYNITB(TestVTYGenericBSC):
self.vty.command("configure terminal")
self.vty.command("nitb")
self.assertTrue(self.vty.verify("subscriber-create-on-demand", ['']))
self.assertTrue(self.vty.verify("subscriber-create-on-demand regexp", ['']))
self.assertTrue(self.vty.verify("subscriber-create-on-demand no-extension", ['']))
self.vty.command("end")
def testSi2Q(self):
@ -400,6 +400,9 @@ class TestVTYNITB(TestVTYGenericBSC):
self.vty.enable()
imsi = "204300854013739"
imsi2 = "222301824913762"
imsi3 = "333500854113763"
imsi4 = "444583744053764"
# Initially we don't have this subscriber
self.vty.verify('show subscriber imsi '+imsi, ['% No subscriber found for imsi '+imsi])
@ -407,14 +410,60 @@ class TestVTYNITB(TestVTYGenericBSC):
# Lets create one
res = self.vty.command('subscriber create imsi '+imsi)
self.assert_(res.find(" IMSI: "+imsi) > 0)
self.assert_(res.find("Extension") > 0)
# Now we have it
res = self.vty.command('show subscriber imsi '+imsi)
self.assert_(res.find(" IMSI: "+imsi) > 0)
# With narrow random interval
self.vty.command("configure terminal")
self.vty.command("nitb")
self.assertTrue(self.vty.verify("subscriber-create-on-demand", ['']))
# wrong interval
res = self.vty.command("subscriber-create-on-demand random 221 122")
# error string will contain arguments
self.assert_(res.find("122") > 0)
self.assert_(res.find("221") > 0)
# correct interval - silent ok
self.assertTrue(self.vty.verify("subscriber-create-on-demand random 221 222", ['']))
self.vty.command("end")
res = self.vty.command('subscriber create imsi ' + imsi2)
self.assert_(res.find(" IMSI: " + imsi2) > 0)
self.assert_(res.find("221") > 0 or res.find("222") > 0)
self.assert_(res.find(" Extension: ") > 0)
# Without extension
self.vty.command("configure terminal")
self.vty.command("nitb")
self.assertTrue(self.vty.verify("subscriber-create-on-demand no-extension", ['']))
self.vty.command("end")
res = self.vty.command('subscriber create imsi ' + imsi3)
self.assert_(res.find(" IMSI: " + imsi3) > 0)
self.assertEquals(res.find("Extension"), -1)
# With extension again
self.vty.command("configure terminal")
self.vty.command("nitb")
self.assertTrue(self.vty.verify("no subscriber-create-on-demand", ['']))
self.assertTrue(self.vty.verify("subscriber-create-on-demand", ['']))
self.assertTrue(self.vty.verify("subscriber-create-on-demand random 221 666", ['']))
self.vty.command("end")
res = self.vty.command('subscriber create imsi ' + imsi4)
self.assert_(res.find(" IMSI: " + imsi4) > 0)
self.assert_(res.find(" Extension: ") > 0)
# Delete it
res = self.vty.command('subscriber imsi ' + imsi + ' delete')
self.assert_("" == res)
res = self.vty.command('subscriber imsi ' + imsi2 + ' delete')
self.assert_("" == res)
res = self.vty.command('subscriber imsi ' + imsi3 + ' delete')
self.assert_("" == res)
res = self.vty.command('subscriber imsi ' + imsi4 + ' delete')
self.assert_("" == res)
# Now it should not be there anymore
res = self.vty.command('show subscriber imsi '+imsi)