aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2016-06-19 18:06:02 +0200
committerNeels Hofmeyr <neels@hofmeyr.de>2017-08-27 17:40:52 +0200
commit9066b74c1fe5439c79e82cff9ab0fdb503a33332 (patch)
tree68489deee447c38f0d3efcfdfa715ec041b0be3f
parent9ccd6621fb236377c0b71d334f64bede02ec32e7 (diff)
Use libvlr in libmsc (large refactoring)
Original libvlr code is by Harald Welte <laforge@gnumonks.org>, polished and tweaked by Neels Hofmeyr <nhofmeyr@sysmocom.de>. This is a long series of trial-and-error development collapsed in one patch. This may be split in smaller commits if reviewers prefer that. If we can keep it as one, we have saved ourselves the additional separation work. SMS: The SQL based lookup of SMS for attached subscribers no longer works since the SQL database no longer has the subscriber data. Replace with a round-robin on the SMS recipient MSISDNs paired with a VLR subscriber RAM lookup whether the subscriber is currently attached. If there are many SMS for not-attached subscribers in the SMS database, this will become inefficient: a DB hit returns a pending SMS, the RAM lookup will reveal that the subscriber is not attached, after which the DB is hit for the next SMS. It would become more efficient e.g. by having an MSISDN based hash list for the VLR subscribers and by marking non-attached SMS recipients in the SMS database so that they can be excluded with the SQL query already. There is a sanity limit to do at most 100 db hits per attempt to find a pending SMS. So if there are more than 100 stored SMS waiting for their recipients to actually attach to the MSC, it may take more than one SMS queue trigger to deliver SMS for subscribers that are actually attached. This is not very beautiful, but is merely intended to carry us over to a time when we have a proper separate SMSC entity. Introduce gsm_subscriber_connection ref-counting in libmsc. Remove/Disable VTY and CTRL commands to create subscribers, which is now a task of the OsmoHLR. Adjust the python tests accordingly. Remove VTY cmd subscriber-keep-in-ram. Use OSMO_GSUP_PORT = 4222 instead of 2222. See I4222e21686c823985be8ff1f16b1182be8ad6175. So far use the LAC from conn->bts, will be replaced by conn->lac in Id3705236350d5f69e447046b0a764bbabc3d493c. Related: OS#1592 OS#1974 Change-Id: I639544a6cdda77a3aafc4e3446a55393f60e4050
-rw-r--r--configure.ac3
-rw-r--r--doc/examples/osmo-bsc/osmo-bsc.cfg1
-rw-r--r--doc/examples/osmo-nitb/rbs2308/openbsc.cfg1
-rw-r--r--include/openbsc/Makefile.am1
-rw-r--r--include/openbsc/abis_rsl.h1
-rw-r--r--include/openbsc/auth.h4
-rw-r--r--include/openbsc/bsc_api.h3
-rw-r--r--include/openbsc/chan_alloc.h2
-rw-r--r--include/openbsc/db.h45
-rw-r--r--include/openbsc/gsm_04_08.h6
-rw-r--r--include/openbsc/gsm_04_11.h10
-rw-r--r--include/openbsc/gsm_data.h69
-rw-r--r--include/openbsc/gsm_data_shared.h1
-rw-r--r--include/openbsc/gsm_subscriber.h92
-rw-r--r--include/openbsc/osmo_msc.h69
-rw-r--r--include/openbsc/signal.h4
-rw-r--r--include/openbsc/silent_call.h4
-rw-r--r--include/openbsc/token_auth.h7
-rw-r--r--include/openbsc/transaction.h7
-rw-r--r--include/openbsc/vlr.h3
-rw-r--r--include/openbsc/vty.h1
-rw-r--r--src/gprs/sgsn_main.c3
-rw-r--r--src/libbsc/abis_rsl.c4
-rw-r--r--src/libbsc/bsc_api.c10
-rw-r--r--src/libbsc/bsc_init.c7
-rw-r--r--src/libbsc/bsc_vty.c40
-rw-r--r--src/libbsc/handover_logic.c3
-rw-r--r--src/libbsc/paging.c1
-rw-r--r--src/libcommon-cs/common_cs.c12
-rw-r--r--src/libcommon-cs/common_cs_vty.c27
-rw-r--r--src/libcommon/debug.c23
-rw-r--r--src/libcommon/gsm_data.c10
-rw-r--r--src/libcommon/gsm_subscriber_base.c141
-rw-r--r--src/libcommon/gsup_client.c8
-rw-r--r--src/libcommon/gsup_test_client.c2
-rw-r--r--src/libmsc/Makefile.am2
-rw-r--r--src/libmsc/auth.c115
-rw-r--r--src/libmsc/ctrl_commands.c170
-rw-r--r--src/libmsc/db.c1025
-rw-r--r--src/libmsc/gsm_04_08.c1301
-rw-r--r--src/libmsc/gsm_04_11.c57
-rw-r--r--src/libmsc/gsm_subscriber.c313
-rw-r--r--src/libmsc/meas_feed.c11
-rw-r--r--src/libmsc/osmo_msc.c316
-rw-r--r--src/libmsc/rrlp.c6
-rw-r--r--src/libmsc/silent_call.c11
-rw-r--r--src/libmsc/smpp_openbsc.c65
-rw-r--r--src/libmsc/smpp_smsc.h2
-rw-r--r--src/libmsc/sms_queue.c116
-rw-r--r--src/libmsc/subscr_conn.c191
-rw-r--r--src/libmsc/token_auth.c160
-rw-r--r--src/libmsc/transaction.c81
-rw-r--r--src/libmsc/ussd.c18
-rw-r--r--src/libmsc/vty_interface_layer3.c561
-rw-r--r--src/libvlr/vlr_access_req_fsm.c1
-rw-r--r--src/osmo-nitb/bsc_hack.c18
-rw-r--r--tests/Makefile.am3
-rw-r--r--tests/channel/Makefile.am1
-rw-r--r--tests/channel/channel_test.c74
-rw-r--r--tests/channel/channel_test.ok2
-rw-r--r--tests/ctrl_test_runner.py46
-rw-r--r--tests/db/db_test.c7
-rw-r--r--tests/mm_auth/Makefile.am37
-rw-r--r--tests/mm_auth/mm_auth_test.c340
-rw-r--r--tests/mm_auth/mm_auth_test.ok40
-rw-r--r--tests/sms_queue/Makefile.am45
-rw-r--r--tests/sms_queue/sms_queue_test.c215
-rw-r--r--tests/sms_queue/sms_queue_test.err0
-rw-r--r--tests/sms_queue/sms_queue_test.ok98
-rw-r--r--tests/subscr/Makefile.am18
-rw-r--r--tests/subscr/subscr_test.c117
-rw-r--r--tests/subscr/subscr_test.ok3
-rw-r--r--tests/testsuite.at27
-rw-r--r--tests/vty_test_runner.py166
74 files changed, 2181 insertions, 4223 deletions
diff --git a/configure.ac b/configure.ac
index 63a087fc4..2ca1ba339 100644
--- a/configure.ac
+++ b/configure.ac
@@ -241,7 +241,6 @@ AC_OUTPUT(
tests/Makefile
tests/atlocal
tests/gsm0408/Makefile
- tests/db/Makefile
tests/channel/Makefile
tests/bsc/Makefile
tests/bsc-nat/Makefile
@@ -256,12 +255,12 @@ AC_OUTPUT(
tests/subscr/Makefile
tests/oap/Makefile
tests/gtphub/Makefile
- tests/mm_auth/Makefile
tests/xid/Makefile
tests/sndcp_xid/Makefile
tests/slhc/Makefile
tests/v42bis/Makefile
tests/nanobts_omlattr/Makefile
+ tests/sms_queue/Makefile
doc/Makefile
doc/examples/Makefile
contrib/Makefile
diff --git a/doc/examples/osmo-bsc/osmo-bsc.cfg b/doc/examples/osmo-bsc/osmo-bsc.cfg
index 56e47243b..b974b7635 100644
--- a/doc/examples/osmo-bsc/osmo-bsc.cfg
+++ b/doc/examples/osmo-bsc/osmo-bsc.cfg
@@ -40,7 +40,6 @@ network
timer t3119 0
timer t3122 0
timer t3141 0
- subscriber-keep-in-ram 0
bts 0
type nanobts
band DCS1800
diff --git a/doc/examples/osmo-nitb/rbs2308/openbsc.cfg b/doc/examples/osmo-nitb/rbs2308/openbsc.cfg
index 0226920e1..3a7bd21f2 100644
--- a/doc/examples/osmo-nitb/rbs2308/openbsc.cfg
+++ b/doc/examples/osmo-nitb/rbs2308/openbsc.cfg
@@ -37,7 +37,6 @@ network
timer t3119 0
timer t3122 0
timer t3141 0
- subscriber-keep-in-ram 0
bts 0
type rbs2000
band GSM900
diff --git a/include/openbsc/Makefile.am b/include/openbsc/Makefile.am
index 532328cb7..456a4a2d2 100644
--- a/include/openbsc/Makefile.am
+++ b/include/openbsc/Makefile.am
@@ -79,7 +79,6 @@ noinst_HEADERS = \
sms_queue.h \
socket.h \
system_information.h \
- token_auth.h \
transaction.h \
trau_mux.h \
trau_upqueue.h \
diff --git a/include/openbsc/abis_rsl.h b/include/openbsc/abis_rsl.h
index 400e09f67..f983fceec 100644
--- a/include/openbsc/abis_rsl.h
+++ b/include/openbsc/abis_rsl.h
@@ -30,7 +30,6 @@
struct gsm_bts;
struct gsm_lchan;
-struct gsm_subscriber;
struct gsm_bts_trx_ts;
#define GSM48_LEN2PLEN(a) (((a) << 2) | 1)
diff --git a/include/openbsc/auth.h b/include/openbsc/auth.h
index 61811316b..b314bbf19 100644
--- a/include/openbsc/auth.h
+++ b/include/openbsc/auth.h
@@ -4,7 +4,6 @@
#include <osmocom/core/utils.h>
struct gsm_auth_tuple;
-struct gsm_subscriber;
enum auth_action {
AUTH_ERROR = -1, /* Internal error */
@@ -20,7 +19,4 @@ static inline const char *auth_action_str(enum auth_action a)
return get_value_string(auth_action_names, a);
}
-int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple,
- struct gsm_subscriber *subscr, int key_seq);
-
#endif /* _AUTH_H */
diff --git a/include/openbsc/bsc_api.h b/include/openbsc/bsc_api.h
index 3a9311991..40068d6ef 100644
--- a/include/openbsc/bsc_api.h
+++ b/include/openbsc/bsc_api.h
@@ -41,6 +41,9 @@ struct bsc_api {
*/
void (*mr_config)(struct gsm_subscriber_connection *conn,
struct gsm_lchan *lchan, int full_rate);
+
+ /** Callback for additional actions during conn cleanup */
+ void (*conn_cleanup)(struct gsm_subscriber_connection *conn);
};
int bsc_api_init(struct gsm_network *network, struct bsc_api *api);
diff --git a/include/openbsc/chan_alloc.h b/include/openbsc/chan_alloc.h
index 78242e5b7..7388e14c5 100644
--- a/include/openbsc/chan_alloc.h
+++ b/include/openbsc/chan_alloc.h
@@ -25,7 +25,7 @@
struct gsm_subscriber_connection;
/* Find an allocated channel for a specified subscriber */
-struct gsm_subscriber_connection *connection_for_subscr(struct gsm_subscriber *subscr);
+struct gsm_subscriber_connection *connection_for_subscr(struct vlr_subscr *vsub);
/* Allocate a logical channel (SDCCH, TCH, ...) */
struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type, int allow_bigger);
diff --git a/include/openbsc/db.h b/include/openbsc/db.h
index bb90705ab..988c9bd6e 100644
--- a/include/openbsc/db.h
+++ b/include/openbsc/db.h
@@ -29,53 +29,26 @@ struct gsm_network;
struct gsm_auth_info;
struct gsm_auth_tuple;
struct gsm_sms;
-struct gsm_subscriber;
/* one time initialisation */
int db_init(const char *name);
int db_prepare(void);
int db_fini(void);
-/* subscriber management */
-struct gsm_subscriber *db_create_subscriber(const char *imsi, uint64_t smin,
- 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);
-int db_subscriber_expire(void *priv, void (*callback)(void *priv, long long unsigned int id));
-int db_subscriber_alloc_tmsi(struct gsm_subscriber *subscriber);
-int db_subscriber_alloc_exten(struct gsm_subscriber *subscriber, uint64_t smin,
- uint64_t smax);
-int db_subscriber_alloc_token(struct gsm_subscriber *subscriber, uint32_t* token);
-int db_subscriber_assoc_imei(struct gsm_subscriber *subscriber, char *imei);
-int db_subscriber_delete(struct gsm_subscriber *subscriber);
-int db_sync_equipment(struct gsm_equipment *equip);
-int db_subscriber_update(struct gsm_subscriber *subscriber);
-int db_subscriber_list_active(void (*list_cb)(struct gsm_subscriber*,void*), void*);
-
-/* auth info */
-int db_get_authinfo_for_subscr(struct gsm_auth_info *ainfo,
- struct gsm_subscriber *subscr);
-int db_sync_authinfo_for_subscr(struct gsm_auth_info *ainfo,
- struct gsm_subscriber *subscr);
-int db_get_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple,
- struct gsm_subscriber *subscr);
-int db_sync_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple,
- struct gsm_subscriber *subscr);
-
/* SMS store-and-forward */
int db_sms_store(struct gsm_sms *sms);
struct gsm_sms *db_sms_get(struct gsm_network *net, unsigned long long id);
-struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, unsigned long long min_id);
-struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net, unsigned long long min_subscr_id, unsigned int failed);
-struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr);
+struct gsm_sms *db_sms_get_next_unsent(struct gsm_network *net,
+ unsigned long long min_sms_id,
+ unsigned int max_failed);
+struct gsm_sms *db_sms_get_next_unsent_rr_msisdn(struct gsm_network *net,
+ const char *last_msisdn,
+ unsigned int max_failed);
+struct gsm_sms *db_sms_get_unsent_for_subscr(struct vlr_subscr *vsub,
+ unsigned int max_failed);
int db_sms_mark_delivered(struct gsm_sms *sms);
int db_sms_inc_deliver_attempts(struct gsm_sms *sms);
-
-/* APDU blob storage */
-int db_apdu_blob_store(struct gsm_subscriber *subscr,
- uint8_t apdu_id_flags, uint8_t len,
- uint8_t *apdu);
+int db_sms_delete_by_msisdn(const char *msisdn);
/* Statistics counter storage */
struct osmo_counter;
diff --git a/include/openbsc/gsm_04_08.h b/include/openbsc/gsm_04_08.h
index a8b2de958..c40d96d65 100644
--- a/include/openbsc/gsm_04_08.h
+++ b/include/openbsc/gsm_04_08.h
@@ -9,7 +9,6 @@
struct msgb;
struct gsm_bts;
-struct gsm_subscriber;
struct gsm_network;
struct gsm_trans;
struct gsm_subscriber_connection;
@@ -26,14 +25,15 @@ static inline struct msgb *gsm48_msgb_alloc_name(const char *name)
name);
}
+void cm_service_request_concludes(struct gsm_subscriber_connection *conn,
+ struct msgb *msg);
+
/* config options controlling the behaviour of the lower leves */
void gsm0408_allow_everyone(int allow);
-void gsm0408_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause);
void gsm0408_clear_all_trans(struct gsm_network *net, int protocol);
int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg);
int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id);
-int gsm0408_new_conn(struct gsm_subscriber_connection *conn);
enum gsm_chan_t get_ctype_by_chreq(struct gsm_network *bts, uint8_t ra);
/* don't use "enum gsm_chreq_reason_t" to avoid circular dependency */
int get_reason_by_chreq(uint8_t ra, int neci);
diff --git a/include/openbsc/gsm_04_11.h b/include/openbsc/gsm_04_11.h
index 017c88765..3305e3e61 100644
--- a/include/openbsc/gsm_04_11.h
+++ b/include/openbsc/gsm_04_11.h
@@ -3,6 +3,10 @@
#include <osmocom/gsm/protocol/gsm_04_11.h>
+struct vlr_subscr;
+struct gsm_subscriber_connection;
+struct gsm_trans;
+
#define UM_SAPI_SMS 3 /* See GSM 04.05/04.06 */
/* SMS deliver PDU */
@@ -29,10 +33,12 @@ int gsm0411_rcv_sms(struct gsm_subscriber_connection *conn, struct msgb *msg);
struct gsm_sms *sms_alloc(void);
void sms_free(struct gsm_sms *sms);
-struct gsm_sms *sms_from_text(struct gsm_subscriber *receiver, struct gsm_subscriber *sender, int dcs, const char *text);
+struct gsm_sms *sms_from_text(struct vlr_subscr *receiver,
+ struct vlr_subscr *sender,
+ int dcs, const char *text);
void _gsm411_sms_trans_free(struct gsm_trans *trans);
-int gsm411_send_sms_subscr(struct gsm_subscriber *subscr,
+int gsm411_send_sms_subscr(struct vlr_subscr *vsub,
struct gsm_sms *sms);
int gsm411_send_sms(struct gsm_subscriber_connection *conn,
struct gsm_sms *sms);
diff --git a/include/openbsc/gsm_data.h b/include/openbsc/gsm_data.h
index a97405105..fa2fed778 100644
--- a/include/openbsc/gsm_data.h
+++ b/include/openbsc/gsm_data.h
@@ -24,6 +24,8 @@
struct mncc_sock_state;
struct gsm_subscriber_group;
struct bsc_subscr;
+struct vlr_instance;
+struct vlr_subscr;
#define OBSC_LINKID_CB(__msgb) (__msgb)->cb[3]
@@ -70,20 +72,6 @@ struct gsm_auth_tuple {
#define GSM_KEY_SEQ_INVAL 7 /* GSM 04.08 - 10.5.1.2 */
/*
- * LOCATION UPDATING REQUEST state
- *
- * Our current operation is:
- * - Get imei/tmsi
- * - Accept/Reject according to global policy
- */
-struct gsm_loc_updating_operation {
- struct osmo_timer_list updating_timer;
- unsigned int waiting_for_imsi : 1;
- unsigned int waiting_for_imei : 1;
- unsigned int key_seq : 4;
-};
-
-/*
* AUTHENTICATION/CIPHERING state
*/
struct gsm_security_operation {
@@ -120,16 +108,34 @@ enum ran_type {
RAN_UTRAN_IU, /* 3G / Iu-interface (IuCS or IuPS) */
};
+struct gsm_classmark {
+ bool classmark1_set;
+ struct gsm48_classmark1 classmark1;
+ uint8_t classmark2_len;
+ uint8_t classmark2[3];
+ uint8_t classmark3_len;
+ uint8_t classmark3[14]; /* if cm3 gets extended by spec, it will be truncated */
+};
+
/* active radio connection of a mobile subscriber */
struct gsm_subscriber_connection {
+ /* global linked list of subscriber_connections */
struct llist_head entry;
- /* To whom we are allocated at the moment */
- struct gsm_subscriber *subscr;
+ /* usage count. If this drops to zero, we start the release
+ * towards A/Iu */
+ uint32_t use_count;
+
+ /* The MS has opened the conn with a CM Service Request, and we shall
+ * keep it open for an actual request (or until timeout). */
+ bool received_cm_service_request;
- /* libbsc subscriber information */
+ /* libbsc subscriber information (if available) */
struct bsc_subscr *bsub;
+ /* libmsc/libvlr subscriber information (if available) */
+ struct vlr_subscr *vsub;
+
/* LU expiration handling */
uint8_t expire_timer_stopped;
/* SMS helpers for libmsc */
@@ -138,10 +144,11 @@ struct gsm_subscriber_connection {
/*
* Operations that have a state and might be pending
*/
- struct gsm_loc_updating_operation *loc_operation;
struct gsm_security_operation *sec_operation;
struct gsm_anchor_operation *anch_operation;
+ struct osmo_fsm_inst *conn_fsm;
+
/* Are we part of a special "silent" call */
int silent_call;
@@ -156,7 +163,7 @@ struct gsm_subscriber_connection {
/* back pointers */
struct gsm_network *network;
- int in_release;
+ bool in_release;
struct gsm_lchan *lchan; /* BSC */
struct gsm_lchan *ho_lchan; /* BSC */
struct gsm_bts *bts; /* BSC */
@@ -167,6 +174,8 @@ struct gsm_subscriber_connection {
/* connected via 2G or 3G? */
enum ran_type via_ran;
+
+ struct gsm_classmark classmark;
};
@@ -312,6 +321,7 @@ struct gsm_network {
char *authorized_reg_str;
enum gsm48_reject_value reject_cause;
int a5_encryption;
+ bool authentication_required;
int neci;
int send_mm_info;
struct {
@@ -375,17 +385,8 @@ struct gsm_network {
/* MSC data in case we are a true BSC */
struct osmo_bsc_data *bsc_data;
- /* subscriber related features */
- bool auto_create_subscr;
- bool auto_assign_exten;
- uint64_t ext_min;
- uint64_t ext_max;
- struct gsm_subscriber_group *subscr_group;
struct gsm_sms_queue *sms_queue;
- /* nitb related control */
- int avoid_tmsi;
-
/* control interface */
struct ctrl_handle *ctrl;
@@ -409,6 +410,12 @@ struct gsm_network {
* not require gsm_data.h). In an MSC-without-BSC environment, this
* pointer is NULL to indicate absence of a bsc_subscribers list. */
struct llist_head *bsc_subscribers;
+
+ /* MSC: GSUP server address of the HLR */
+ const char *gsup_server_addr_str;
+ uint16_t gsup_server_port;
+
+ struct vlr_instance *vlr;
};
struct osmo_esme;
@@ -431,7 +438,7 @@ struct gsm_sms_addr {
struct gsm_sms {
unsigned long long id;
- struct gsm_subscriber *receiver;
+ struct vlr_subscr *receiver;
struct gsm_sms_addr src, dst;
enum gsm_sms_source_id source;
@@ -582,7 +589,7 @@ extern const struct value_string bts_type_descs[_NUM_GSM_BTS_TYPE+1];
/* control interface handling */
int bsc_base_ctrl_cmds_install(void);
-int msc_ctrl_cmds_install(void);
+int msc_ctrl_cmds_install(struct gsm_network *net);
/* dependency handling */
void bts_depend_mark(struct gsm_bts *bts, int dep);
@@ -593,4 +600,6 @@ int bts_depend_is_depedency(struct gsm_bts *base, struct gsm_bts *other);
int gsm_bts_get_radio_link_timeout(const struct gsm_bts *bts);
void gsm_bts_set_radio_link_timeout(struct gsm_bts *bts, int value);
+bool classmark_is_r99(struct gsm_classmark *cm);
+
#endif /* _GSM_DATA_H */
diff --git a/include/openbsc/gsm_data_shared.h b/include/openbsc/gsm_data_shared.h
index 4c71a075e..0790807b1 100644
--- a/include/openbsc/gsm_data_shared.h
+++ b/include/openbsc/gsm_data_shared.h
@@ -95,7 +95,6 @@ enum bts_gprs_mode {
};
struct gsm_lchan;
-struct gsm_subscriber;
struct gsm_mncc;
struct osmo_rtp_socket;
struct rtp_socket;
diff --git a/include/openbsc/gsm_subscriber.h b/include/openbsc/gsm_subscriber.h
index 7e656145a..c65b8a315 100644
--- a/include/openbsc/gsm_subscriber.h
+++ b/include/openbsc/gsm_subscriber.h
@@ -19,63 +19,8 @@
#define GSM_SUBSCRIBER_NO_EXPIRATION 0x0
-struct vty;
-struct sgsn_mm_ctx;
-struct sgsn_subscriber_data;
-
struct subscr_request;
-struct gsm_subscriber_group {
- struct gsm_network *net;
-
- int keep_subscr;
-};
-
-struct gsm_equipment {
- long long unsigned int id;
- char imei[GSM23003_IMEISV_NUM_DIGITS+1];
- char name[GSM_NAME_LENGTH];
-
- struct gsm48_classmark1 classmark1;
- uint8_t classmark2_len;
- uint8_t classmark2[3];
- uint8_t classmark3_len;
- uint8_t classmark3[14];
-};
-
-struct gsm_subscriber {
- struct gsm_subscriber_group *group;
- long long unsigned int id;
- char imsi[GSM23003_IMSI_MAX_DIGITS+1];
- uint32_t tmsi;
- uint16_t lac;
- char name[GSM_NAME_LENGTH];
- char extension[GSM_EXTENSION_LENGTH];
- int authorized;
- time_t expire_lu;
-
- /* Don't delete subscribers even if group->keep_subscr is not set */
- int keep_in_ram;
-
- /* Temporary field which is not stored in the DB/HLR */
- uint32_t flags;
-
- /* Every user can only have one equipment in use at any given
- * point in time */
- struct gsm_equipment equipment;
-
- /* for internal management */
- int use_count;
- struct llist_head entry;
-
- /* pending requests */
- int is_paging;
- struct llist_head requests;
-
- /* GPRS/SGSN related fields */
- struct sgsn_subscriber_data *sgsn_data;
-};
-
enum gsm_subscriber_field {
GSM_SUBSCRIBER_IMSI,
GSM_SUBSCRIBER_TMSI,
@@ -89,42 +34,17 @@ enum gsm_subscriber_update_reason {
GSM_SUBSCRIBER_UPDATE_EQUIPMENT,
};
-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);
-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,
- const char *imsi);
-struct gsm_subscriber *subscr_get_by_extension(struct gsm_subscriber_group *sgrp,
- const char *ext);
-struct gsm_subscriber *subscr_get_by_id(struct gsm_subscriber_group *sgrp,
- unsigned long long id);
-struct gsm_subscriber *subscr_get_or_create(struct gsm_subscriber_group *sgrp,
- const char *imsi);
-int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason);
-struct gsm_subscriber *subscr_active_by_tmsi(struct gsm_subscriber_group *sgrp,
- uint32_t tmsi);
-struct gsm_subscriber *subscr_active_by_imsi(struct gsm_subscriber_group *sgrp,
- const char *imsi);
-
-char *subscr_name(struct gsm_subscriber *subscr);
-
-int subscr_purge_inactive(struct gsm_subscriber_group *sgrp);
-void subscr_update_from_db(struct gsm_subscriber *subscr);
-void subscr_expire(struct gsm_subscriber_group *sgrp);
-int subscr_update_expire_lu(struct gsm_subscriber *subscr, struct gsm_bts *bts);
+int subscr_update(struct vlr_subscr *vsub, struct gsm_bts *bts, int reason);
/*
* Paging handling with authentication
*/
-struct subscr_request *subscr_request_channel(struct gsm_subscriber *subscr,
- int type, gsm_cbfn *cbfn, void *param);
+struct subscr_request *subscr_request_channel(struct vlr_subscr *vsub,
+ int channel_type,
+ gsm_cbfn *cbfn, void *param);
void subscr_remove_request(struct subscr_request *req);
-/* internal */
-struct gsm_subscriber *subscr_alloc(void);
-extern struct llist_head active_subscribers;
+int subscr_paging_dispatch(unsigned int hooknum, unsigned int event,
+ struct msgb *msg, void *data, void *param);
#endif /* _GSM_SUBSCR_H */
diff --git a/include/openbsc/osmo_msc.h b/include/openbsc/osmo_msc.h
index beb3f5e4c..0642c9109 100644
--- a/include/openbsc/osmo_msc.h
+++ b/include/openbsc/osmo_msc.h
@@ -3,9 +3,76 @@
#ifndef OSMO_MSC_H
#define OSMO_MSC_H
+#include <osmocom/core/fsm.h>
+#include <osmocom/gsm/gsup.h>
+
+#include <openbsc/gsm_data.h>
+
#include "bsc_api.h"
+#define MSC_HLR_REMOTE_IP_DEFAULT "127.0.0.1"
+#define MSC_HLR_REMOTE_PORT_DEFAULT OSMO_GSUP_PORT
+
+enum subscr_conn_fsm_event {
+ /* Mark 0 as invalid to catch uninitialized vars */
+ SUBSCR_CONN_E_INVALID = 0,
+ /* Timeout on connection establishment starts */
+ SUBSCR_CONN_E_START,
+ /* LU or Process Access FSM has determined that this conn is good */
+ SUBSCR_CONN_E_ACCEPTED,
+ /* received first reply from MS in "real" CC, SMS, USSD communication */
+ SUBSCR_CONN_E_COMMUNICATING,
+ /* Some async action has completed, check again whether all is done */
+ SUBSCR_CONN_E_BUMP,
+ /* MS/BTS/BSC originated close request */
+ SUBSCR_CONN_E_MO_CLOSE,
+ /* MSC originated close request, e.g. failed authentication */
+ SUBSCR_CONN_E_CN_CLOSE,
+};
+
+enum subscr_conn_fsm_state {
+ SUBSCR_CONN_S_INIT,
+ SUBSCR_CONN_S_NEW,
+ SUBSCR_CONN_S_ACCEPTED,
+ SUBSCR_CONN_S_COMMUNICATING,
+ SUBSCR_CONN_S_RELEASED,
+};
+
+enum subscr_conn_from {
+ SUBSCR_CONN_FROM_INVALID,
+ SUBSCR_CONN_FROM_LU,
+ SUBSCR_CONN_FROM_CM_SERVICE_REQ,
+ SUBSCR_CONN_FROM_PAGING_RESP,
+};
+
+extern const struct value_string subscr_conn_from_names[];
+static inline const char *subscr_conn_from_name(enum subscr_conn_from val)
+{
+ return get_value_string(subscr_conn_from_names, val);
+}
+
+
struct bsc_api *msc_bsc_api();
-void msc_release_connection(struct gsm_subscriber_connection *conn);
+
+int msc_create_conn_fsm(struct gsm_subscriber_connection *conn, const char *id);
+
+int msc_vlr_alloc(struct gsm_network *net);
+int msc_vlr_start(struct gsm_network *net);
+
+void msc_subscr_conn_init(void);
+bool msc_subscr_conn_is_accepted(struct gsm_subscriber_connection *conn);
+void msc_subscr_conn_communicating(struct gsm_subscriber_connection *conn);
+void msc_subscr_conn_close(struct gsm_subscriber_connection *conn,
+ uint32_t cause);
+
+#define msc_subscr_conn_get(conn) \
+ _msc_subscr_conn_get(conn, __BASE_FILE__, __LINE__)
+#define msc_subscr_conn_put(conn) \
+ _msc_subscr_conn_put(conn, __BASE_FILE__, __LINE__)
+struct gsm_subscriber_connection *
+_msc_subscr_conn_get(struct gsm_subscriber_connection *conn,
+ const char *file, int line);
+void _msc_subscr_conn_put(struct gsm_subscriber_connection *conn,
+ const char *file, int line);
#endif
diff --git a/include/openbsc/signal.h b/include/openbsc/signal.h
index d4ccf80da..49f86d612 100644
--- a/include/openbsc/signal.h
+++ b/include/openbsc/signal.h
@@ -138,10 +138,8 @@ enum signal_rf {
S_RF_GRACE,
};
-struct gsm_subscriber;
-
struct paging_signal_data {
- struct gsm_subscriber *subscr;
+ struct vlr_subscr *vsub;
struct gsm_bts *bts;
int paging_result;
diff --git a/include/openbsc/silent_call.h b/include/openbsc/silent_call.h
index 619a54327..5fec77b73 100644
--- a/include/openbsc/silent_call.h
+++ b/include/openbsc/silent_call.h
@@ -3,9 +3,9 @@
struct gsm_subscriber_connection;
-extern int gsm_silent_call_start(struct gsm_subscriber *subscr,
+extern int gsm_silent_call_start(struct vlr_subscr *vsub,
void *data, int type);
-extern int gsm_silent_call_stop(struct gsm_subscriber *subscr);
+extern int gsm_silent_call_stop(struct vlr_subscr *vsub);
#if 0
extern int silent_call_rx(struct gsm_subscriber_connection *conn, struct msgb *msg);
diff --git a/include/openbsc/token_auth.h b/include/openbsc/token_auth.h
deleted file mode 100644
index 47dc7aa94..000000000
--- a/include/openbsc/token_auth.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef _TOKEN_AUTH_H
-#define _TOKEN_AUTH_H
-
-void on_dso_load_token(void);
-
-#endif /* _TOKEN_AUTH_H */
-
diff --git a/include/openbsc/transaction.h b/include/openbsc/transaction.h
index 9a87d04e4..07ab7a7da 100644
--- a/include/openbsc/transaction.h
+++ b/include/openbsc/transaction.h
@@ -24,7 +24,7 @@ struct gsm_trans {
uint8_t transaction_id;
/* To whom we belong, unique identifier of remote MM entity */
- struct gsm_subscriber *subscr;
+ struct vlr_subscr *vsub;
/* The associated connection we are using to transmit messages */
struct gsm_subscriber_connection *conn;
@@ -67,13 +67,14 @@ struct gsm_trans *trans_find_by_callref(struct gsm_network *net,
uint32_t callref);
struct gsm_trans *trans_alloc(struct gsm_network *net,
- struct gsm_subscriber *subscr,
+ struct vlr_subscr *vsub,
uint8_t protocol, uint8_t trans_id,
uint32_t callref);
void trans_free(struct gsm_trans *trans);
-int trans_assign_trans_id(struct gsm_network *net, struct gsm_subscriber *subscr,
+int trans_assign_trans_id(struct gsm_network *net, struct vlr_subscr *vsub,
uint8_t protocol, uint8_t ti_flag);
int trans_has_conn(const struct gsm_subscriber_connection *conn);
+void trans_conn_closed(struct gsm_subscriber_connection *conn);
#endif
diff --git a/include/openbsc/vlr.h b/include/openbsc/vlr.h
index 90e3d7a11..17cb2ae35 100644
--- a/include/openbsc/vlr.h
+++ b/include/openbsc/vlr.h
@@ -11,6 +11,8 @@
// for GSM_NAME_LENGTH
#include <openbsc/gsm_subscriber.h>
+struct log_target;
+
/* from 3s to 10s */
#define GSM_29002_TIMER_S 10
/* from 15s to 30s */
@@ -20,7 +22,6 @@
/* from 28h to 38h */
#define GSM_29002_TIMER_L (32*60*60)
-
/* VLR subscriber authentication state */
enum vlr_subscr_auth_state {
/* subscriber needs to be autenticated */
diff --git a/include/openbsc/vty.h b/include/openbsc/vty.h
index 60b7d2d76..0cb0eec4f 100644
--- a/include/openbsc/vty.h
+++ b/include/openbsc/vty.h
@@ -38,6 +38,7 @@ enum bsc_vty_node {
SMPP_NODE,
SMPP_ESME_NODE,
GTPHUB_NODE,
+ HLR_NODE,
};
extern int bsc_vty_is_config_node(struct vty *vty, int node);
diff --git a/src/gprs/sgsn_main.c b/src/gprs/sgsn_main.c
index 04f2825f7..41cebefb2 100644
--- a/src/gprs/sgsn_main.c
+++ b/src/gprs/sgsn_main.c
@@ -40,6 +40,8 @@
#include <osmocom/core/logging.h>
#include <osmocom/core/stats.h>
+#include <osmocom/gsm/gsup.h>
+
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/gprs/gprs_bssgp.h>
@@ -83,6 +85,7 @@ static struct sgsn_instance sgsn_inst = {
.cfg = {
.gtp_statedir = "./",
.auth_policy = SGSN_AUTH_POLICY_CLOSED,
+ .gsup_server_port = OSMO_GSUP_PORT,
},
};
struct sgsn_instance *sgsn = &sgsn_inst;
diff --git a/src/libbsc/abis_rsl.c b/src/libbsc/abis_rsl.c
index 6ae790f6c..7ae3eebd9 100644
--- a/src/libbsc/abis_rsl.c
+++ b/src/libbsc/abis_rsl.c
@@ -160,7 +160,7 @@ static struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr,
gsm_ts_and_pchan_name(lchan->ts), log_name, chan_nr);
if (lchan->conn)
- log_set_context(LOG_CTX_VLR_SUBSCR, lchan->conn->subscr);
+ log_set_context(LOG_CTX_VLR_SUBSCR, lchan->conn->vsub);
return lchan;
}
@@ -1382,8 +1382,6 @@ static void print_meas_rep(struct gsm_lchan *lchan, struct gsm_meas_rep *mr)
if (lchan && lchan->conn) {
if (lchan->conn->bsub)
name = bsc_subscr_name(lchan->conn->bsub);
- else if (lchan->conn->subscr)
- name = lchan->conn->subscr->imsi;
else
name = lchan->name;
}
diff --git a/src/libbsc/bsc_api.c b/src/libbsc/bsc_api.c
index 7613cac90..947644eb3 100644
--- a/src/libbsc/bsc_api.c
+++ b/src/libbsc/bsc_api.c
@@ -32,6 +32,7 @@
#include <openbsc/debug.h>
#include <openbsc/gsm_04_08.h>
#include <openbsc/trau_mux.h>
+#include <openbsc/vlr.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/gsm48.h>
@@ -273,13 +274,14 @@ void bsc_subscr_con_free(struct gsm_subscriber_connection *conn)
if (!conn)
return;
+ if (conn->network->bsc_api->conn_cleanup)
+ conn->network->bsc_api->conn_cleanup(conn);
- if (conn->subscr) {
- subscr_put(conn->subscr);
- conn->subscr = NULL;
+ if (conn->vsub) {
+ LOGP(DNM, LOGL_ERROR, "conn->vsub should have been cleared.\n");
+ conn->vsub = NULL;
}
-
if (conn->ho_lchan) {
LOGP(DNM, LOGL_ERROR, "The ho_lchan should have been cleared.\n");
conn->ho_lchan->conn = NULL;
diff --git a/src/libbsc/bsc_init.c b/src/libbsc/bsc_init.c
index ec87a7bc7..b7135f1b1 100644
--- a/src/libbsc/bsc_init.c
+++ b/src/libbsc/bsc_init.c
@@ -38,6 +38,7 @@
#include <openbsc/e1_config.h>
#include <openbsc/common_bsc.h>
#include <openbsc/pcu_if.h>
+#include <openbsc/osmo_msc.h>
#include <limits.h>
/* global pointer to the gsm network data structure */
@@ -512,6 +513,12 @@ int bsc_network_alloc(mncc_recv_cb_t mncc_recv)
bsc_gsmnet->name_long = talloc_strdup(bsc_gsmnet, "OpenBSC");
bsc_gsmnet->name_short = talloc_strdup(bsc_gsmnet, "OpenBSC");
+ /* TODO: move to libmsc when gsm_network is split between libbsc and
+ * libmsc */
+ bsc_gsmnet->gsup_server_addr_str = talloc_strdup(bsc_gsmnet,
+ MSC_HLR_REMOTE_IP_DEFAULT);
+ bsc_gsmnet->gsup_server_port = MSC_HLR_REMOTE_PORT_DEFAULT;
+
return 0;
}
diff --git a/src/libbsc/bsc_vty.c b/src/libbsc/bsc_vty.c
index 9fc289503..3bd56eafb 100644
--- a/src/libbsc/bsc_vty.c
+++ b/src/libbsc/bsc_vty.c
@@ -54,8 +54,8 @@
#include <openbsc/bsc_msc_data.h>
#include <openbsc/osmo_bsc_rf.h>
#include <openbsc/pcu_if.h>
-
#include <openbsc/common_cs.h>
+#include <openbsc/vlr.h>
#include <inttypes.h>
@@ -173,10 +173,6 @@ 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,
@@ -801,6 +797,9 @@ static int config_write_net(struct vty *vty)
vty_out(vty, " location updating reject cause %u%s",
gsmnet->reject_cause, VTY_NEWLINE);
vty_out(vty, " encryption a5 %u%s", gsmnet->a5_encryption, VTY_NEWLINE);
+ vty_out(vty, " authentication %s%s",
+ gsmnet->authentication_required ? "required" : "optional",
+ VTY_NEWLINE);
vty_out(vty, " neci %u%s", gsmnet->neci, VTY_NEWLINE);
vty_out(vty, " paging any use tch %d%s", gsmnet->pag_any_tch, VTY_NEWLINE);
vty_out(vty, " rrlp mode %s%s", rrlp_mode_name(gsmnet->rrlp.mode),
@@ -833,8 +832,6 @@ static int config_write_net(struct vty *vty)
vty_out(vty, " timer t3141 %u%s", gsmnet->T3141, VTY_NEWLINE);
vty_out(vty, " dyn_ts_allow_tch_f %d%s",
gsmnet->dyn_ts_allow_tch_f ? 1 : 0, VTY_NEWLINE);
- vty_out(vty, " subscriber-keep-in-ram %d%s",
- gsmnet->subscr_group->keep_subscr, VTY_NEWLINE);
if (gsmnet->tz.override != 0) {
if (gsmnet->tz.dst)
vty_out(vty, " timezone %d %d %d%s",
@@ -1018,21 +1015,24 @@ DEFUN(show_ts,
return CMD_SUCCESS;
}
-static void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr)
+static void subscr_dump_vty(struct vty *vty, struct vlr_subscr *vsub)
{
- vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id,
- subscr->authorized, VTY_NEWLINE);
- if (strlen(subscr->name))
- vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE);
- if (strlen(subscr->extension))
- vty_out(vty, " Extension: %s%s", subscr->extension,
+ OSMO_ASSERT(vsub);
+ if (strlen(vsub->name))
+ vty_out(vty, " Name: '%s'%s", vsub->name, VTY_NEWLINE);
+ if (strlen(vsub->msisdn))
+ vty_out(vty, " Extension: %s%s", vsub->msisdn,
+ VTY_NEWLINE);
+ if (strlen(vsub->imsi))
+ vty_out(vty, " IMSI: %s%s", vsub->imsi, VTY_NEWLINE);
+ if (vsub->tmsi != GSM_RESERVED_TMSI)
+ vty_out(vty, " TMSI: %08X%s", vsub->tmsi,
VTY_NEWLINE);
- vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE);
- if (subscr->tmsi != GSM_RESERVED_TMSI)
- vty_out(vty, " TMSI: %08X%s", subscr->tmsi,
+ if (vsub->tmsi_new != GSM_RESERVED_TMSI)
+ vty_out(vty, " new TMSI: %08X%s", vsub->tmsi_new,
VTY_NEWLINE);
- vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE);
+ vty_out(vty, " Use count: %u%s", vsub->use_count, VTY_NEWLINE);
}
static void bsc_subscr_dump_vty(struct vty *vty, struct bsc_subscr *bsub)
@@ -1160,9 +1160,9 @@ static void lchan_dump_full_vty(struct vty *vty, struct gsm_lchan *lchan)
vty_out(vty, " Channel Mode / Codec: %s%s",
get_value_string(gsm48_cmode_names, lchan->tch_mode),
VTY_NEWLINE);
- if (lchan->conn && lchan->conn->subscr) {
+ if (lchan->conn && lchan->conn->vsub) {
vty_out(vty, " Subscriber:%s", VTY_NEWLINE);
- subscr_dump_vty(vty, lchan->conn->subscr);
+ subscr_dump_vty(vty, lchan->conn->vsub);
} else
vty_out(vty, " No Subscriber%s", VTY_NEWLINE);
if (is_ipaccess_bts(lchan->ts->trx->bts)) {
diff --git a/src/libbsc/handover_logic.c b/src/libbsc/handover_logic.c
index 4dd913b15..c03563f6b 100644
--- a/src/libbsc/handover_logic.c
+++ b/src/libbsc/handover_logic.c
@@ -40,6 +40,7 @@
#include <osmocom/core/talloc.h>
#include <openbsc/transaction.h>
#include <openbsc/trau_mux.h>
+#include <openbsc/vlr.h>
struct bsc_handover {
struct llist_head list;
@@ -260,7 +261,7 @@ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan)
net = new_lchan->ts->trx->bts->network;
LOGP(DHO, LOGL_INFO, "Subscriber %s HO from BTS %u->%u on ARFCN "
- "%u->%u\n", subscr_name(ho->old_lchan->conn->subscr),
+ "%u->%u\n", vlr_subscr_name(ho->old_lchan->conn->vsub),
ho->old_lchan->ts->trx->bts->nr, new_lchan->ts->trx->bts->nr,
ho->old_lchan->ts->trx->arfcn, new_lchan->ts->trx->arfcn);
diff --git a/src/libbsc/paging.c b/src/libbsc/paging.c
index 78e39c558..8c1445cc6 100644
--- a/src/libbsc/paging.c
+++ b/src/libbsc/paging.c
@@ -50,6 +50,7 @@
#include <openbsc/gsm_data.h>
#include <openbsc/chan_alloc.h>
#include <openbsc/bsc_api.h>
+#include <openbsc/vlr.h>
void *tall_paging_ctx;
diff --git a/src/libcommon-cs/common_cs.c b/src/libcommon-cs/common_cs.c
index 7905802bf..fc9caafa0 100644
--- a/src/libcommon-cs/common_cs.c
+++ b/src/libcommon-cs/common_cs.c
@@ -49,20 +49,10 @@ struct gsm_network *gsm_network_init(void *ctx,
if (!net)
return NULL;
- net->subscr_group = talloc_zero(net, struct gsm_subscriber_group);
- if (!net->subscr_group) {
- talloc_free(net);
- return NULL;
- }
-
if (gsm_parse_reg(net, &net->authorized_regexp, &net->authorized_reg_str, 1,
&default_regexp) != 0)
return NULL;
- net->subscr_group->net = net;
- net->auto_create_subscr = true;
- net->auto_assign_exten = true;
-
net->country_code = country_code;
net->network_code = network_code;
@@ -78,8 +68,6 @@ struct gsm_network *gsm_network_init(void *ctx,
net->active_calls = osmo_counter_alloc("msc.active_calls");
net->mncc_recv = mncc_recv;
- net->ext_min = GSM_MIN_EXTEN;
- net->ext_max = GSM_MAX_EXTEN;
net->dyn_ts_allow_tch_f = true;
diff --git a/src/libcommon-cs/common_cs_vty.c b/src/libcommon-cs/common_cs_vty.c
index bcc001d58..86b4c53e4 100644
--- a/src/libcommon-cs/common_cs_vty.c
+++ b/src/libcommon-cs/common_cs_vty.c
@@ -168,6 +168,20 @@ DEFUN(cfg_net_encryption,
return CMD_SUCCESS;
}
+DEFUN(cfg_net_authentication,
+ cfg_net_authentication_cmd,
+ "authentication (optional|required)",
+ "Whether to enforce MS authentication in 2G\n"
+ "Allow MS to attach via 2G BSC without authentication\n"
+ "Always do authentication\n")
+{
+ struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+
+ gsmnet->authentication_required = (argv[0][0] == 'r') ? true : false;
+
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_net_rrlp_mode, cfg_net_rrlp_mode_cmd,
"rrlp mode (none|ms-based|ms-preferred|ass-preferred)",
"Radio Resource Location Protocol\n"
@@ -209,17 +223,6 @@ DEFUN(cfg_net_dyn_ts_allow_tch_f,
return CMD_SUCCESS;
}
-DEFUN(cfg_net_subscr_keep,
- cfg_net_subscr_keep_cmd,
- "subscriber-keep-in-ram (0|1)",
- "Keep unused subscribers in RAM.\n"
- "Delete unused subscribers\n" "Keep unused subscribers\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->subscr_group->keep_subscr = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
DEFUN(cfg_net_timezone,
cfg_net_timezone_cmd,
"timezone <-19-19> (0|15|30|45)",
@@ -301,9 +304,9 @@ int common_cs_vty_init(struct gsm_network *network,
install_element(GSMNET_NODE, &cfg_net_authorize_regexp_cmd);
install_element(GSMNET_NODE, &cfg_net_reject_cause_cmd);
install_element(GSMNET_NODE, &cfg_net_encryption_cmd);
+ install_element(GSMNET_NODE, &cfg_net_authentication_cmd);
install_element(GSMNET_NODE, &cfg_net_rrlp_mode_cmd);
install_element(GSMNET_NODE, &cfg_net_mm_info_cmd);
- install_element(GSMNET_NODE, &cfg_net_subscr_keep_cmd);
install_element(GSMNET_NODE, &cfg_net_timezone_cmd);
install_element(GSMNET_NODE, &cfg_net_timezone_dst_cmd);
install_element(GSMNET_NODE, &cfg_net_no_timezone_cmd);
diff --git a/src/libcommon/debug.c b/src/libcommon/debug.c
index 088902a69..dc79a843e 100644
--- a/src/libcommon/debug.c
+++ b/src/libcommon/debug.c
@@ -32,7 +32,6 @@
#include <osmocom/core/logging.h>
#include <osmocom/gprs/gprs_msgb.h>
#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_subscriber.h>
#include <openbsc/debug.h>
/* default categories */
@@ -189,13 +188,13 @@ static const struct log_info_cat default_categories[] = {
static int filter_fn(const struct log_context *ctx, struct log_target *tar)
{
- const struct gsm_subscriber *subscr = ctx->ctx[LOG_CTX_VLR_SUBSCR];
+ const struct vlr_subscr *vsub = ctx->ctx[LOG_CTX_VLR_SUBSCR];
const struct bsc_subscr *bsub = ctx->ctx[LOG_CTX_BSC_SUBSCR];
const struct gprs_nsvc *nsvc = ctx->ctx[LOG_CTX_GB_NSVC];
const struct gprs_nsvc *bvc = ctx->ctx[LOG_CTX_GB_BVC];
if ((tar->filter_map & (1 << LOG_FLT_VLR_SUBSCR)) != 0
- && subscr && subscr == tar->filter_data[LOG_FLT_VLR_SUBSCR])
+ && vsub && vsub == tar->filter_data[LOG_FLT_VLR_SUBSCR])
return 1;
if ((tar->filter_map & (1 << LOG_FLT_BSC_SUBSCR)) != 0
@@ -220,21 +219,3 @@ const struct log_info log_info = {
.cat = default_categories,
.num_cat = ARRAY_SIZE(default_categories),
};
-
-void log_set_filter_vlr_subscr(struct log_target *target,
- struct gsm_subscriber *vlr_subscr)
-{
- struct gsm_subscriber **fsub = (void*)&target->filter_data[LOG_FLT_VLR_SUBSCR];
-
- /* free the old data */
- if (*fsub) {
- subscr_put(*fsub);
- *fsub = NULL;
- }
-
- if (vlr_subscr) {
- target->filter_map |= (1 << LOG_FLT_VLR_SUBSCR);
- *fsub = subscr_get(vlr_subscr);
- } else
- target->filter_map &= ~(1 << LOG_FLT_VLR_SUBSCR);
-}
diff --git a/src/libcommon/gsm_data.c b/src/libcommon/gsm_data.c
index db7de082d..7c717a40f 100644
--- a/src/libcommon/gsm_data.c
+++ b/src/libcommon/gsm_data.c
@@ -471,3 +471,13 @@ void gsm_bts_set_radio_link_timeout(struct gsm_bts *bts, int value)
cell_options->radio_link_timeout = (value >> 2) - 1;
}
}
+
+bool classmark_is_r99(struct gsm_classmark *cm)
+{
+ int rev_lev = 0;
+ if (cm->classmark1_set)
+ rev_lev = cm->classmark1.rev_lev;
+ else if (cm->classmark2_len > 0)
+ rev_lev = (cm->classmark2[0] >> 5) & 0x3;
+ return rev_lev >= 2;
+}
diff --git a/src/libcommon/gsm_subscriber_base.c b/src/libcommon/gsm_subscriber_base.c
index 1ecdee5a5..018ed210c 100644
--- a/src/libcommon/gsm_subscriber_base.c
+++ b/src/libcommon/gsm_subscriber_base.c
@@ -31,133 +31,34 @@
#include <osmocom/core/utils.h>
#include <openbsc/gsm_subscriber.h>
#include <openbsc/debug.h>
+#include <openbsc/vlr.h>
LLIST_HEAD(active_subscribers);
void *tall_subscr_ctx;
-/* for the gsm_subscriber.c */
-struct llist_head *subscr_bsc_active_subscribers(void)
+/* return static buffer with printable name of VLR subscriber */
+const char *vlr_subscr_name(struct vlr_subscr *vsub)
{
- return &active_subscribers;
-}
-
-
-char *subscr_name(struct gsm_subscriber *subscr)
-{
- if (!subscr)
+ static char buf[32];
+ if (!vsub)
return "unknown";
-
- if (strlen(subscr->name))
- return subscr->name;
-
- return subscr->imsi;
-}
-
-struct gsm_subscriber *subscr_alloc(void)
-{
- struct gsm_subscriber *s;
-
- s = talloc_zero(tall_subscr_ctx, struct gsm_subscriber);
- if (!s)
- return NULL;
-
- llist_add_tail(&s->entry, &active_subscribers);
- s->use_count = 1;
- s->tmsi = GSM_RESERVED_TMSI;
-
- INIT_LLIST_HEAD(&s->requests);
-
- return s;
-}
-
-static void subscr_free(struct gsm_subscriber *subscr)
-{
- llist_del(&subscr->entry);
- talloc_free(subscr);
-}
-
-void subscr_direct_free(struct gsm_subscriber *subscr)
-{
- OSMO_ASSERT(subscr->use_count == 1);
- subscr_free(subscr);
-}
-
-struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr)
-{
- subscr->use_count++;
- DEBUGP(DREF, "subscr %s usage increases usage to: %d\n",
- subscr->extension, subscr->use_count);
- return subscr;
-}
-
-struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr)
-{
- subscr->use_count--;
- DEBUGP(DREF, "subscr %s usage decreased usage to: %d\n",
- subscr->extension, subscr->use_count);
- if (subscr->use_count <= 0 &&
- !((subscr->group && subscr->group->keep_subscr) ||
- subscr->keep_in_ram))
- subscr_free(subscr);
- return NULL;
-}
-
-struct gsm_subscriber *subscr_get_or_create(struct gsm_subscriber_group *sgrp,
- const char *imsi)
-{
- struct gsm_subscriber *subscr;
-
- llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) {
- if (strcmp(subscr->imsi, imsi) == 0 && subscr->group == sgrp)
- return subscr_get(subscr);
- }
-
- subscr = subscr_alloc();
- if (!subscr)
- return NULL;
-
- osmo_strlcpy(subscr->imsi, imsi, sizeof(subscr->imsi));
- subscr->group = sgrp;
- return subscr;
-}
-
-struct gsm_subscriber *subscr_active_by_tmsi(struct gsm_subscriber_group *sgrp,
- uint32_t tmsi)
-{
- struct gsm_subscriber *subscr;
-
- llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) {
- if (subscr->tmsi == tmsi && subscr->group == sgrp)
- return subscr_get(subscr);
- }
-
- return NULL;
-}
-
-struct gsm_subscriber *subscr_active_by_imsi(struct gsm_subscriber_group *sgrp,
- const char *imsi)
-{
- struct gsm_subscriber *subscr;
-
- llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) {
- if (strcmp(subscr->imsi, imsi) == 0 && subscr->group == sgrp)
- return subscr_get(subscr);
- }
-
- return NULL;
+ if (vsub->msisdn[0])
+ snprintf(buf, sizeof(buf), "MSISDN:%s", vsub->msisdn);
+ else if (vsub->imsi[0])
+ snprintf(buf, sizeof(buf), "IMSI:%s", vsub->imsi);
+ else if (vsub->tmsi != GSM_RESERVED_TMSI)
+ snprintf(buf, sizeof(buf), "TMSI:0x%08x", vsub->tmsi);
+ else if (vsub->tmsi_new != GSM_RESERVED_TMSI)
+ snprintf(buf, sizeof(buf), "TMSI(new):0x%08x", vsub->tmsi_new);
+ else
+ return "unknown";
+ buf[sizeof(buf)-1] = '\0';
+ return buf;
}
-int subscr_purge_inactive(struct gsm_subscriber_group *sgrp)
+const char *vlr_subscr_msisdn_or_name(struct vlr_subscr *vsub)
{
- struct gsm_subscriber *subscr, *tmp;
- int purged = 0;
-
- llist_for_each_entry_safe(subscr, tmp, subscr_bsc_active_subscribers(), entry) {
- if (subscr->group == sgrp && subscr->use_count <= 0) {
- subscr_free(subscr);
- purged += 1;
- }
- }
-
- return purged;
+ if (!vsub || !vsub->msisdn[0])
+ return vlr_subscr_name(vsub);
+ return vsub->msisdn;
}
diff --git a/src/libcommon/gsup_client.c b/src/libcommon/gsup_client.c
index 46f25bb98..258f230cb 100644
--- a/src/libcommon/gsup_client.c
+++ b/src/libcommon/gsup_client.c
@@ -72,12 +72,12 @@ static int gsup_client_connect(struct gsup_client *gsupc)
rc = ipa_client_conn_open(gsupc->link);
if (rc >= 0) {
- LOGP(DLGSUP, LOGL_INFO, "GSUP connecting to %s:%d\n",
+ LOGP(DLGSUP, LOGL_NOTICE, "GSUP connecting to %s:%d\n",
gsupc->link->addr, gsupc->link->port);
return 0;
}
- LOGP(DLGSUP, LOGL_INFO, "GSUP failed to connect to %s:%d: %s\n",
+ LOGP(DLGSUP, LOGL_ERROR, "GSUP failed to connect to %s:%d: %s\n",
gsupc->link->addr, gsupc->link->port, strerror(-rc));
if (rc == -EBADF || rc == -ENOTSOCK || rc == -EAFNOSUPPORT ||
@@ -331,11 +331,15 @@ void gsup_client_destroy(struct gsup_client *gsupc)
int gsup_client_send(struct gsup_client *gsupc, struct msgb *msg)
{
if (!gsupc) {
+ LOGP(DGPRS, LOGL_NOTICE, "No GSUP client, unable to "
+ "send %s\n", msgb_hexdump(msg));
msgb_free(msg);
return -ENOTCONN;
}
if (!gsupc->is_connected) {
+ LOGP(DGPRS, LOGL_NOTICE, "GSUP not connected, unable to "
+ "send %s\n", msgb_hexdump(msg));
msgb_free(msg);
return -EAGAIN;
}
diff --git a/src/libcommon/gsup_test_client.c b/src/libcommon/gsup_test_client.c
index 1b39670de..b6a8d6b7d 100644
--- a/src/libcommon/gsup_test_client.c
+++ b/src/libcommon/gsup_test_client.c
@@ -271,7 +271,7 @@ int main(int argc, char **argv)
{
unsigned long long i;
char *server_host = "127.0.0.1";
- uint16_t server_port = 2222;
+ uint16_t server_port = OSMO_GSUP_PORT;
osmo_init_logging(&gsup_test_client_log_info);
diff --git a/src/libmsc/Makefile.am b/src/libmsc/Makefile.am
index 9d966dbc1..bb2a4a186 100644
--- a/src/libmsc/Makefile.am
+++ b/src/libmsc/Makefile.am
@@ -35,13 +35,13 @@ libmsc_a_SOURCES = \
rrlp.c \
silent_call.c \
sms_queue.c \
- token_auth.c \
ussd.c \
vty_interface_layer3.c \
transaction.c \
osmo_msc.c \
ctrl_commands.c \
meas_feed.c \
+ subscr_conn.c \
$(NULL)
if BUILD_SMPP
diff --git a/src/libmsc/auth.c b/src/libmsc/auth.c
index 19def1ec1..9064ce6c4 100644
--- a/src/libmsc/auth.c
+++ b/src/libmsc/auth.c
@@ -40,118 +40,3 @@ const struct value_string auth_action_names[] = {
OSMO_VALUE_STRING(AUTH_DO_AUTH),
{ 0, NULL }
};
-
-static int
-_use_xor(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple)
-{
- int i, l = ainfo->a3a8_ki_len;
-
- if ((l > A38_XOR_MAX_KEY_LEN) || (l < A38_XOR_MIN_KEY_LEN)) {
- LOGP(DMM, LOGL_ERROR, "Invalid XOR key (len=%d) %s\n",
- ainfo->a3a8_ki_len,
- osmo_hexdump(ainfo->a3a8_ki, ainfo->a3a8_ki_len));
- return -1;
- }
-
- for (i=0; i<4; i++)
- atuple->vec.sres[i] = atuple->vec.rand[i] ^ ainfo->a3a8_ki[i];
- for (i=4; i<12; i++)
- atuple->vec.kc[i-4] = atuple->vec.rand[i] ^ ainfo->a3a8_ki[i];
-
- return 0;
-}
-
-static int
-_use_comp128_v1(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple)
-{
- if (ainfo->a3a8_ki_len != A38_COMP128_KEY_LEN) {
- LOGP(DMM, LOGL_ERROR, "Invalid COMP128v1 key (len=%d) %s\n",
- ainfo->a3a8_ki_len,
- osmo_hexdump(ainfo->a3a8_ki, ainfo->a3a8_ki_len));
- return -1;
- }
-
- comp128(ainfo->a3a8_ki, atuple->vec.rand, atuple->vec.sres, atuple->vec.kc);
-
- return 0;
-}
-
-/* Return values
- * -1 -> Internal error
- * 0 -> Not available
- * 1 -> Tuple returned, need to do auth, then enable cipher
- * 2 -> Tuple returned, need to enable cipher
- */
-int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple,
- struct gsm_subscriber *subscr, int key_seq)
-{
- struct gsm_auth_info ainfo;
- int rc;
-
- /* Get subscriber info (if any) */
- rc = db_get_authinfo_for_subscr(&ainfo, subscr);
- if (rc < 0) {
- LOGP(DMM, LOGL_NOTICE,
- "No retrievable Ki for subscriber %s, skipping auth\n",
- subscr_name(subscr));
- return rc == -ENOENT ? AUTH_NOT_AVAIL : AUTH_ERROR;
- }
-
- /* If possible, re-use the last tuple and skip auth */
- rc = db_get_lastauthtuple_for_subscr(atuple, subscr);
- if ((rc == 0) &&
- (key_seq != GSM_KEY_SEQ_INVAL) &&
- (key_seq == atuple->key_seq) &&
- (atuple->use_count < 3))
- {
- atuple->use_count++;
- db_sync_lastauthtuple_for_subscr(atuple, subscr);
- DEBUGP(DMM, "Auth tuple use < 3, just doing ciphering\n");
- return AUTH_DO_CIPH;
- }
-
- /* Generate a new one */
- if (rc != 0) {
- /* If db_get_lastauthtuple_for_subscr() returned nothing, make
- * sure the atuple memory is initialized to zero and thus start
- * off with key_seq = 0. */
- memset(atuple, 0, sizeof(*atuple));
- } else {
- /* If db_get_lastauthtuple_for_subscr() returned a previous
- * tuple, use the next key_seq. */
- atuple->key_seq = (atuple->key_seq + 1) % 7;
- }
- atuple->use_count = 1;
-
- if (RAND_bytes(atuple->vec.rand, sizeof(atuple->vec.rand)) != 1) {
- LOGP(DMM, LOGL_NOTICE, "RAND_bytes failed, can't generate new auth tuple\n");
- return AUTH_ERROR;
- }
-
- switch (ainfo.auth_algo) {
- case AUTH_ALGO_NONE:
- DEBUGP(DMM, "No authentication for subscriber\n");
- return AUTH_NOT_AVAIL;
-
- case AUTH_ALGO_XOR:
- if (_use_xor(&ainfo, atuple))
- return AUTH_NOT_AVAIL;
- break;
-
- case AUTH_ALGO_COMP128v1:
- if (_use_comp128_v1(&ainfo, atuple))
- return AUTH_NOT_AVAIL;
- break;
-
- default:
- DEBUGP(DMM, "Unsupported auth type algo_id=%d\n",
- ainfo.auth_algo);
- return AUTH_NOT_AVAIL;
- }
-
- db_sync_lastauthtuple_for_subscr(atuple, subscr);
-
- DEBUGP(DMM, "Need to do authentication and ciphering\n");
- return AUTH_DO_AUTH_THEN_CIPH;
-}
-
diff --git a/src/libmsc/ctrl_commands.c b/src/libmsc/ctrl_commands.c
index c99dde44c..9d1f0d4fa 100644
--- a/src/libmsc/ctrl_commands.c
+++ b/src/libmsc/ctrl_commands.c
@@ -25,129 +25,20 @@
#include <openbsc/gsm_subscriber.h>
#include <openbsc/db.h>
#include <openbsc/debug.h>
+#include <openbsc/vlr.h>
#include <stdbool.h>
-static bool alg_supported(const char *alg)
-{
- /*
- * TODO: share this with the vty_interface and extend to all
- * algorithms supported by libosmocore now. Make it table based
- * as well.
- */
- if (strcasecmp(alg, "none") == 0)
- return true;
- if (strcasecmp(alg, "xor") == 0)
- return true;
- if (strcasecmp(alg, "comp128v1") == 0)
- return true;
- return false;
-}
+static struct gsm_network *msc_ctrl_net = NULL;
static int verify_subscriber_modify(struct ctrl_cmd *cmd, const char *value, void *d)
{
- char *tmp, *imsi, *msisdn, *alg, *ki, *saveptr = NULL;
- int rc = 0;
-
- tmp = talloc_strdup(cmd, value);
- if (!tmp)
- return 1;
-
- imsi = strtok_r(tmp, ",", &saveptr);
- msisdn = strtok_r(NULL, ",", &saveptr);
- alg = strtok_r(NULL, ",", &saveptr);
- ki = strtok_r(NULL, ",", &saveptr);
-
- if (!imsi || !msisdn)
- rc = 1;
- else if (strlen(imsi) > GSM23003_IMSI_MAX_DIGITS)
- rc = 1;
- else if (strlen(msisdn) >= GSM_EXTENSION_LENGTH)
- rc = 1;
- else if (alg) {
- if (!alg_supported(alg))
- rc = 1;
- else if (strcasecmp(alg, "none") != 0 && !ki)
- rc = 1;
- }
-
- talloc_free(tmp);
- return rc;
+ return 0;
}
static int set_subscriber_modify(struct ctrl_cmd *cmd, void *data)
{
- struct gsm_network *net = cmd->node;
- char *tmp, *imsi, *msisdn, *alg, *ki, *saveptr = NULL;
- struct gsm_subscriber* subscr;
- int rc;
-
- tmp = talloc_strdup(cmd, cmd->value);
- if (!tmp)
- return 1;
-
- imsi = strtok_r(tmp, ",", &saveptr);
- msisdn = strtok_r(NULL, ",", &saveptr);
- alg = strtok_r(NULL, ",", &saveptr);
- ki = strtok_r(NULL, ",", &saveptr);
-
- subscr = subscr_get_by_imsi(net->subscr_group, imsi);
- if (!subscr)
- subscr = subscr_create_subscriber(net->subscr_group, imsi);
- if (!subscr)
- goto fail;
-
- subscr->authorized = 1;
- osmo_strlcpy(subscr->extension, msisdn, sizeof(subscr->extension));
-
- /* put it back to the db */
- rc = db_sync_subscriber(subscr);
- db_subscriber_update(subscr);
-
- /* handle optional ciphering */
- if (alg) {
- if (strcasecmp(alg, "none") == 0)
- db_sync_authinfo_for_subscr(NULL, subscr);
- else {
- struct gsm_auth_info ainfo = { 0, };
- /* the verify should make sure that this is okay */
- OSMO_ASSERT(alg);
- OSMO_ASSERT(ki);
-
- if (strcasecmp(alg, "xor") == 0)
- ainfo.auth_algo = AUTH_ALGO_XOR;
- else if (strcasecmp(alg, "comp128v1") == 0)
- ainfo.auth_algo = AUTH_ALGO_COMP128v1;
-
- rc = osmo_hexparse(ki, ainfo.a3a8_ki, sizeof(ainfo.a3a8_ki));
- if (rc < 0) {
- subscr_put(subscr);
- talloc_free(tmp);
- cmd->reply = "Failed to parse KI";
- return CTRL_CMD_ERROR;
- }
-
- ainfo.a3a8_ki_len = rc;
- db_sync_authinfo_for_subscr(&ainfo, subscr);
- rc = 0;
- }
- db_sync_lastauthtuple_for_subscr(NULL, subscr);
- }
- subscr_put(subscr);
-
- talloc_free(tmp);
-
- if (rc != 0) {
- cmd->reply = "Failed to store the record in the DB";
- return CTRL_CMD_ERROR;
- }
-
- cmd->reply = "OK";
- return CTRL_CMD_REPLY;
-
-fail:
- talloc_free(tmp);
- cmd->reply = "Failed to create subscriber";
+ cmd->reply = "Command moved to osmo-hlr, no longer available here";
return CTRL_CMD_ERROR;
}
@@ -155,55 +46,40 @@ CTRL_CMD_DEFINE_WO(subscriber_modify, "subscriber-modify-v1");
static int set_subscriber_delete(struct ctrl_cmd *cmd, void *data)
{
- int was_used = 0;
- int rc;
- struct gsm_subscriber *subscr;
- struct gsm_network *net = cmd->node;
+ cmd->reply = "Command moved to osmo-hlr, no longer available here";
+ return CTRL_CMD_ERROR;
+}
+CTRL_CMD_DEFINE_WO_NOVRF(subscriber_delete, "subscriber-delete-v1");
- subscr = subscr_get_by_imsi(net->subscr_group, cmd->value);
- if (!subscr) {
- cmd->reply = "Failed to find subscriber";
- return CTRL_CMD_ERROR;
- }
+static int get_subscriber_list(struct ctrl_cmd *cmd, void *d)
+{
+ struct vlr_subscr *vsub;
- if (subscr->use_count != 1) {
- LOGP(DCTRL, LOGL_NOTICE, "Going to remove active subscriber.\n");
- was_used = 1;
+ if (!msc_ctrl_net) {
+ cmd->reply = "MSC CTRL commands not initialized";
+ return CTRL_CMD_ERROR;
}
- rc = db_subscriber_delete(subscr);
- subscr_put(subscr);
-
- if (rc != 0) {
- cmd->reply = "Failed to remove subscriber";
+ if (!msc_ctrl_net->vlr) {
+ cmd->reply = "VLR not initialized";
return CTRL_CMD_ERROR;
}
- cmd->reply = was_used ? "Removed active subscriber" : "Removed";
- return CTRL_CMD_REPLY;
-}
-CTRL_CMD_DEFINE_WO_NOVRF(subscriber_delete, "subscriber-delete-v1");
-
-static void list_cb(struct gsm_subscriber *subscr, void *d)
-{
- char **data = (char **) d;
- *data = talloc_asprintf_append(*data, "%s,%s\n",
- subscr->imsi, subscr->extension);
-}
-
-static int get_subscriber_list(struct ctrl_cmd *cmd, void *d)
-{
cmd->reply = talloc_strdup(cmd, "");
- db_subscriber_list_active(list_cb, &cmd->reply);
- printf("%s\n", cmd->reply);
+ llist_for_each_entry(vsub, &msc_ctrl_net->vlr->subscribers, list) {
+ cmd->reply = talloc_asprintf_append(cmd->reply, "%s,%s\n",
+ vsub->imsi, vsub->msisdn);
+ }
+ printf("%s\n", cmd->reply); /* <-- what? */
return CTRL_CMD_REPLY;
}
CTRL_CMD_DEFINE_RO(subscriber_list, "subscriber-list-active-v1");
-int msc_ctrl_cmds_install(void)
+int msc_ctrl_cmds_install(struct gsm_network *net)
{
int rc = 0;
+ msc_ctrl_net = net;
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_modify);
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_delete);
diff --git a/src/libmsc/db.c b/src/libmsc/db.c
index 5fe2a3c6b..28e978213 100644
--- a/src/libmsc/db.c
+++ b/src/libmsc/db.c
@@ -34,6 +34,7 @@
#include <openbsc/gsm_04_11.h>
#include <openbsc/db.h>
#include <openbsc/debug.h>
+#include <openbsc/vlr.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <osmocom/core/talloc.h>
@@ -43,9 +44,6 @@
#include <openssl/rand.h>
-/* Semi-Private-Interface (SPI) for the subscriber code */
-void subscr_direct_free(struct gsm_subscriber *subscr);
-
static char *db_basename = NULL;
static char *db_dirname = NULL;
static dbi_conn conn;
@@ -227,23 +225,33 @@ static struct gsm_sms *sms_from_result_v3(dbi_result result)
{
struct gsm_sms *sms = sms_alloc();
long long unsigned int sender_id;
- struct gsm_subscriber *sender;
const char *text, *daddr;
const unsigned char *user_data;
char buf[32];
+ char *quoted;
+ dbi_result result2;
+ const char *extension;
if (!sms)
return NULL;
sms->id = dbi_result_get_ulonglong(result, "id");
+ /* find extension by id, assuming that the subscriber still exists in
+ * the db */
sender_id = dbi_result_get_ulonglong(result, "sender_id");
snprintf(buf, sizeof(buf), "%llu", sender_id);
- sender = db_get_subscriber(GSM_SUBSCRIBER_ID, buf);
- OSMO_ASSERT(sender);
- osmo_strlcpy(sms->src.addr, sender->extension, sizeof(sms->src.addr));
- subscr_direct_free(sender);
- sender = NULL;
+
+ dbi_conn_quote_string_copy(conn, buf, &quoted);
+ result2 = dbi_conn_queryf(conn,
+ "SELECT extension FROM Subscriber "
+ "WHERE id = %s ", quoted);
+ free(quoted);
+ extension = dbi_result_get_string(result2, "extension");
+ if (extension)
+ osmo_strlcpy(sms->src.addr, extension, sizeof(sms->src.addr));
+ dbi_result_free(result2);
+ /* got the extension */
sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req");
sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req");
@@ -518,913 +526,6 @@ int db_fini(void)
return 0;
}
-struct gsm_subscriber *db_create_subscriber(const char *imsi, uint64_t smin,
- uint64_t smax, bool alloc_exten)
-{
- dbi_result result;
- struct gsm_subscriber *subscr;
-
- /* Is this subscriber known in the db? */
- subscr = db_get_subscriber(GSM_SUBSCRIBER_IMSI, imsi);
- if (subscr) {
- subscr_put(subscr);
- return NULL;
- }
-
- subscr = subscr_alloc();
- if (!subscr)
- return NULL;
- subscr->flags |= GSM_SUBSCRIBER_FIRST_CONTACT;
- result = dbi_conn_queryf(conn,
- "INSERT INTO Subscriber "
- "(imsi, created, updated) "
- "VALUES "
- "(%s, datetime('now'), datetime('now')) ",
- imsi
- );
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to create Subscriber by IMSI.\n");
- subscr_put(subscr);
- return NULL;
- }
- subscr->id = dbi_conn_sequence_last(conn, NULL);
- osmo_strlcpy(subscr->imsi, imsi, sizeof(subscr->imsi));
- dbi_result_free(result);
- LOGP(DDB, LOGL_INFO, "New Subscriber: ID %llu, IMSI %s\n", subscr->id, subscr->imsi);
- if (alloc_exten)
- db_subscriber_alloc_exten(subscr, smin, smax);
- return subscr;
-}
-
-osmo_static_assert(sizeof(unsigned char) == sizeof(struct gsm48_classmark1), classmark1_size);
-
-static int get_equipment_by_subscr(struct gsm_subscriber *subscr)
-{
- dbi_result result;
- const char *string;
- unsigned char cm1;
- const unsigned char *cm2, *cm3;
- struct gsm_equipment *equip = &subscr->equipment;
-
- result = dbi_conn_queryf(conn,
- "SELECT Equipment.* "
- "FROM Equipment JOIN EquipmentWatch ON "
- "EquipmentWatch.equipment_id=Equipment.id "
- "WHERE EquipmentWatch.subscriber_id = %llu "
- "ORDER BY EquipmentWatch.updated DESC", subscr->id);
- if (!result)
- return -EIO;
-
- if (!dbi_result_next_row(result)) {
- dbi_result_free(result);
- return -ENOENT;
- }
-
- equip->id = dbi_result_get_ulonglong(result, "id");
-
- string = dbi_result_get_string(result, "imei");
- if (string)
- osmo_strlcpy(equip->imei, string, sizeof(equip->imei));
-
- string = dbi_result_get_string(result, "classmark1");
- if (string) {
- cm1 = atoi(string) & 0xff;
- memcpy(&equip->classmark1, &cm1, sizeof(equip->classmark1));
- }
-
- equip->classmark2_len = dbi_result_get_field_length(result, "classmark2");
- cm2 = dbi_result_get_binary(result, "classmark2");
- if (equip->classmark2_len > sizeof(equip->classmark2))
- equip->classmark2_len = sizeof(equip->classmark2);
- if (cm2)
- memcpy(equip->classmark2, cm2, equip->classmark2_len);
-
- equip->classmark3_len = dbi_result_get_field_length(result, "classmark3");
- cm3 = dbi_result_get_binary(result, "classmark3");
- if (equip->classmark3_len > sizeof(equip->classmark3))
- equip->classmark3_len = sizeof(equip->classmark3);
- if (cm3)
- memcpy(equip->classmark3, cm3, equip->classmark3_len);
-
- dbi_result_free(result);
-
- return 0;
-}
-
-int db_get_authinfo_for_subscr(struct gsm_auth_info *ainfo,
- struct gsm_subscriber *subscr)
-{
- dbi_result result;
- const unsigned char *a3a8_ki;
-
- result = dbi_conn_queryf(conn,
- "SELECT * FROM AuthKeys WHERE subscriber_id=%llu",
- subscr->id);
- if (!result)
- return -EIO;
-
- if (!dbi_result_next_row(result)) {
- dbi_result_free(result);
- return -ENOENT;
- }
-
- ainfo->auth_algo = dbi_result_get_ulonglong(result, "algorithm_id");
- ainfo->a3a8_ki_len = dbi_result_get_field_length(result, "a3a8_ki");
- a3a8_ki = dbi_result_get_binary(result, "a3a8_ki");
- if (ainfo->a3a8_ki_len > sizeof(ainfo->a3a8_ki))
- ainfo->a3a8_ki_len = sizeof(ainfo->a3a8_ki);
- memcpy(ainfo->a3a8_ki, a3a8_ki, ainfo->a3a8_ki_len);
-
- dbi_result_free(result);
-
- return 0;
-}
-
-int db_sync_authinfo_for_subscr(struct gsm_auth_info *ainfo,
- struct gsm_subscriber *subscr)
-{
- dbi_result result;
- struct gsm_auth_info ainfo_old;
- int rc, upd;
- unsigned char *ki_str;
-
- /* Deletion ? */
- if (ainfo == NULL) {
- result = dbi_conn_queryf(conn,
- "DELETE FROM AuthKeys WHERE subscriber_id=%llu",
- subscr->id);
-
- if (!result)
- return -EIO;
-
- dbi_result_free(result);
-
- return 0;
- }
-
- /* Check if already existing */
- rc = db_get_authinfo_for_subscr(&ainfo_old, subscr);
- if (rc && rc != -ENOENT)
- return rc;
- upd = rc ? 0 : 1;
-
- /* Update / Insert */
- dbi_conn_quote_binary_copy(conn,
- ainfo->a3a8_ki, ainfo->a3a8_ki_len, &ki_str);
-
- if (!upd) {
- result = dbi_conn_queryf(conn,
- "INSERT INTO AuthKeys "
- "(subscriber_id, algorithm_id, a3a8_ki) "
- "VALUES (%llu, %u, %s)",
- subscr->id, ainfo->auth_algo, ki_str);
- } else {
- result = dbi_conn_queryf(conn,
- "UPDATE AuthKeys "
- "SET algorithm_id=%u, a3a8_ki=%s "
- "WHERE subscriber_id=%llu",
- ainfo->auth_algo, ki_str, subscr->id);
- }
-
- free(ki_str);
-
- if (!result)
- return -EIO;
-
- dbi_result_free(result);
-
- return 0;
-}
-
-int db_get_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple,
- struct gsm_subscriber *subscr)
-{
- dbi_result result;
- int len;
- const unsigned char *blob;
-
- result = dbi_conn_queryf(conn,
- "SELECT * FROM AuthLastTuples WHERE subscriber_id=%llu",
- subscr->id);
- if (!result)
- return -EIO;
-
- if (!dbi_result_next_row(result)) {
- dbi_result_free(result);
- return -ENOENT;
- }
-
- memset(atuple, 0, sizeof(*atuple));
-
- atuple->use_count = dbi_result_get_ulonglong(result, "use_count");
- atuple->key_seq = dbi_result_get_ulonglong(result, "key_seq");
-
- len = dbi_result_get_field_length(result, "rand");
- if (len != sizeof(atuple->vec.rand))
- goto err_size;
-
- blob = dbi_result_get_binary(result, "rand");
- memcpy(atuple->vec.rand, blob, len);
-
- len = dbi_result_get_field_length(result, "sres");
- if (len != sizeof(atuple->vec.sres))
- goto err_size;
-
- blob = dbi_result_get_binary(result, "sres");
- memcpy(atuple->vec.sres, blob, len);
-
- len = dbi_result_get_field_length(result, "kc");
- if (len != sizeof(atuple->vec.kc))
- goto err_size;
-
- blob = dbi_result_get_binary(result, "kc");
- memcpy(atuple->vec.kc, blob, len);
-
- dbi_result_free(result);
-
- return 0;
-
-err_size:
- dbi_result_free(result);
- return -EIO;
-}
-
-int db_sync_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple,
- struct gsm_subscriber *subscr)
-{
- dbi_result result;
- int rc, upd;
- struct gsm_auth_tuple atuple_old;
- unsigned char *rand_str, *sres_str, *kc_str;
-
- /* Deletion ? */
- if (atuple == NULL) {
- result = dbi_conn_queryf(conn,
- "DELETE FROM AuthLastTuples WHERE subscriber_id=%llu",
- subscr->id);
-
- if (!result)
- return -EIO;
-
- dbi_result_free(result);
-
- return 0;
- }
-
- /* Check if already existing */
- rc = db_get_lastauthtuple_for_subscr(&atuple_old, subscr);
- if (rc && rc != -ENOENT)
- return rc;
- upd = rc ? 0 : 1;
-
- /* Update / Insert */
- dbi_conn_quote_binary_copy(conn,
- atuple->vec.rand, sizeof(atuple->vec.rand), &rand_str);
- dbi_conn_quote_binary_copy(conn,
- atuple->vec.sres, sizeof(atuple->vec.sres), &sres_str);
- dbi_conn_quote_binary_copy(conn,
- atuple->vec.kc, sizeof(atuple->vec.kc), &kc_str);
-
- if (!upd) {
- result = dbi_conn_queryf(conn,
- "INSERT INTO AuthLastTuples "
- "(subscriber_id, issued, use_count, "
- "key_seq, rand, sres, kc) "
- "VALUES (%llu, datetime('now'), %u, "
- "%u, %s, %s, %s ) ",
- subscr->id, atuple->use_count, atuple->key_seq,
- rand_str, sres_str, kc_str);
- } else {
- char *issued = atuple->key_seq == atuple_old.key_seq ?
- "issued" : "datetime('now')";
- result = dbi_conn_queryf(conn,
- "UPDATE AuthLastTuples "
- "SET issued=%s, use_count=%u, "
- "key_seq=%u, rand=%s, sres=%s, kc=%s "
- "WHERE subscriber_id = %llu",
- issued, atuple->use_count, atuple->key_seq,
- rand_str, sres_str, kc_str, subscr->id);
- }
-
- free(rand_str);
- free(sres_str);
- free(kc_str);
-
- if (!result)
- return -EIO;
-
- dbi_result_free(result);
-
- return 0;
-}
-
-static void db_set_from_query(struct gsm_subscriber *subscr, dbi_conn result)
-{
- const char *string;
- string = dbi_result_get_string(result, "imsi");
- if (string)
- osmo_strlcpy(subscr->imsi, string, sizeof(subscr->imsi));
-
- string = dbi_result_get_string(result, "tmsi");
- if (string)
- subscr->tmsi = tmsi_from_string(string);
-
- string = dbi_result_get_string(result, "name");
- if (string)
- osmo_strlcpy(subscr->name, string, sizeof(subscr->name));
-
- string = dbi_result_get_string(result, "extension");
- if (string)
- osmo_strlcpy(subscr->extension, string, sizeof(subscr->extension));
-
- subscr->lac = dbi_result_get_ulonglong(result, "lac");
-
- if (!dbi_result_field_is_null(result, "expire_lu"))
- subscr->expire_lu = dbi_result_get_datetime(result, "expire_lu");
- else
- subscr->expire_lu = GSM_SUBSCRIBER_NO_EXPIRATION;
-
- subscr->authorized = dbi_result_get_ulonglong(result, "authorized");
-
-}
-
-#define BASE_QUERY "SELECT * FROM Subscriber "
-struct gsm_subscriber *db_get_subscriber(enum gsm_subscriber_field field,
- const char *id)
-{
- dbi_result result;
- char *quoted;
- struct gsm_subscriber *subscr;
-
- switch (field) {
- case GSM_SUBSCRIBER_IMSI:
- dbi_conn_quote_string_copy(conn, id, &quoted);
- result = dbi_conn_queryf(conn,
- BASE_QUERY
- "WHERE imsi = %s ",
- quoted
- );
- free(quoted);
- break;
- case GSM_SUBSCRIBER_TMSI:
- dbi_conn_quote_string_copy(conn, id, &quoted);
- result = dbi_conn_queryf(conn,
- BASE_QUERY
- "WHERE tmsi = %s ",
- quoted
- );
- free(quoted);
- break;
- case GSM_SUBSCRIBER_EXTENSION:
- dbi_conn_quote_string_copy(conn, id, &quoted);
- result = dbi_conn_queryf(conn,
- BASE_QUERY
- "WHERE extension = %s ",
- quoted
- );
- free(quoted);
- break;
- case GSM_SUBSCRIBER_ID:
- dbi_conn_quote_string_copy(conn, id, &quoted);
- result = dbi_conn_queryf(conn,
- BASE_QUERY
- "WHERE id = %s ", quoted);
- free(quoted);
- break;
- default:
- LOGP(DDB, LOGL_NOTICE, "Unknown query selector for Subscriber.\n");
- return NULL;
- }
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber.\n");
- return NULL;
- }
- if (!dbi_result_next_row(result)) {
- DEBUGP(DDB, "Failed to find the Subscriber. '%u' '%s'\n",
- field, id);
- dbi_result_free(result);
- return NULL;
- }
-
- subscr = subscr_alloc();
- subscr->id = dbi_result_get_ulonglong(result, "id");
-
- db_set_from_query(subscr, result);
- DEBUGP(DDB, "Found Subscriber: ID %llu, IMSI %s, NAME '%s', TMSI %x, EXTEN '%s', LAC %hu, AUTH %u\n",
- subscr->id, subscr->imsi, subscr->name, subscr->tmsi, subscr->extension,
- subscr->lac, subscr->authorized);
- dbi_result_free(result);
-
- get_equipment_by_subscr(subscr);
-
- return subscr;
-}
-
-int db_subscriber_update(struct gsm_subscriber *subscr)
-{
- char buf[32];
- dbi_result result;
-
- /* Copy the id to a string as queryf with %llu is failing */
- sprintf(buf, "%llu", subscr->id);
- result = dbi_conn_queryf(conn,
- BASE_QUERY
- "WHERE id = %s", buf);
-
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber: %llu\n", subscr->id);
- return -EIO;
- }
- if (!dbi_result_next_row(result)) {
- DEBUGP(DDB, "Failed to find the Subscriber. %llu\n",
- subscr->id);
- dbi_result_free(result);
- return -EIO;
- }
-
- db_set_from_query(subscr, result);
- dbi_result_free(result);
- get_equipment_by_subscr(subscr);
-
- return 0;
-}
-
-int db_sync_subscriber(struct gsm_subscriber *subscriber)
-{
- dbi_result result;
- char tmsi[14];
- char *q_tmsi, *q_name, *q_extension;
-
- dbi_conn_quote_string_copy(conn,
- subscriber->name, &q_name);
- 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);
- dbi_conn_quote_string_copy(conn,
- tmsi,
- &q_tmsi);
- } else
- q_tmsi = strdup("NULL");
-
- if (subscriber->expire_lu == GSM_SUBSCRIBER_NO_EXPIRATION) {
- result = dbi_conn_queryf(conn,
- "UPDATE Subscriber "
- "SET updated = datetime('now'), "
- "name = %s, "
- "extension = %s, "
- "authorized = %i, "
- "tmsi = %s, "
- "lac = %i, "
- "expire_lu = NULL "
- "WHERE imsi = %s ",
- q_name,
- q_extension,
- subscriber->authorized,
- q_tmsi,
- subscriber->lac,
- subscriber->imsi);
- } else {
- result = dbi_conn_queryf(conn,
- "UPDATE Subscriber "
- "SET updated = datetime('now'), "
- "name = %s, "
- "extension = %s, "
- "authorized = %i, "
- "tmsi = %s, "
- "lac = %i, "
- "expire_lu = datetime(%i, 'unixepoch') "
- "WHERE imsi = %s ",
- q_name,
- q_extension,
- subscriber->authorized,
- q_tmsi,
- subscriber->lac,
- (int) subscriber->expire_lu,
- subscriber->imsi);
- }
-
- free(q_tmsi);
- free(q_name);
- free(q_extension);
-
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to update Subscriber (by IMSI).\n");
- return 1;
- }
-
- dbi_result_free(result);
-
- return 0;
-}
-
-int db_subscriber_delete(struct gsm_subscriber *subscr)
-{
- dbi_result result;
-
- result = dbi_conn_queryf(conn,
- "DELETE FROM AuthKeys WHERE subscriber_id=%llu",
- subscr->id);
- if (!result) {
- LOGP(DDB, LOGL_ERROR,
- "Failed to delete Authkeys for %llu\n", subscr->id);
- return -1;
- }
- dbi_result_free(result);
-
- result = dbi_conn_queryf(conn,
- "DELETE FROM AuthLastTuples WHERE subscriber_id=%llu",
- subscr->id);
- if (!result) {
- LOGP(DDB, LOGL_ERROR,
- "Failed to delete AuthLastTuples for %llu\n", subscr->id);
- return -1;
- }
- dbi_result_free(result);
-
- result = dbi_conn_queryf(conn,
- "DELETE FROM AuthToken WHERE subscriber_id=%llu",
- subscr->id);
- if (!result) {
- LOGP(DDB, LOGL_ERROR,
- "Failed to delete AuthToken for %llu\n", subscr->id);
- return -1;
- }
- dbi_result_free(result);
-
- result = dbi_conn_queryf(conn,
- "DELETE FROM EquipmentWatch WHERE subscriber_id=%llu",
- subscr->id);
- if (!result) {
- LOGP(DDB, LOGL_ERROR,
- "Failed to delete EquipmentWatch for %llu\n", subscr->id);
- return -1;
- }
- dbi_result_free(result);
-
- 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);
- }
-
- result = dbi_conn_queryf(conn,
- "DELETE FROM VLR WHERE subscriber_id=%llu",
- subscr->id);
- if (!result) {
- LOGP(DDB, LOGL_ERROR,
- "Failed to delete VLR for %llu\n", subscr->id);
- return -1;
- }
- dbi_result_free(result);
-
- result = dbi_conn_queryf(conn,
- "DELETE FROM ApduBlobs WHERE subscriber_id=%llu",
- subscr->id);
- if (!result) {
- LOGP(DDB, LOGL_ERROR,
- "Failed to delete ApduBlobs for %llu\n", subscr->id);
- return -1;
- }
- dbi_result_free(result);
-
- result = dbi_conn_queryf(conn,
- "DELETE FROM Subscriber WHERE id=%llu",
- subscr->id);
- if (!result) {
- LOGP(DDB, LOGL_ERROR,
- "Failed to delete Subscriber for %llu\n", subscr->id);
- return -1;
- }
- dbi_result_free(result);
-
- return 0;
-}
-
-/**
- * List all the authorized and non-expired subscribers. The callback will
- * be called one by one. The subscr argument is not fully initialize and
- * subscr_get/subscr_put must not be called. The passed in pointer will be
- * deleted after the callback by the database call.
- */
-int db_subscriber_list_active(void (*cb)(struct gsm_subscriber*,void*), void *closure)
-{
- dbi_result result;
-
- result = dbi_conn_query(conn,
- "SELECT * from Subscriber WHERE LAC != 0 AND authorized = 1");
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to list active subscribers\n");
- return -1;
- }
-
- while (dbi_result_next_row(result)) {
- struct gsm_subscriber *subscr;
-
- subscr = subscr_alloc();
- subscr->id = dbi_result_get_ulonglong(result, "id");
- db_set_from_query(subscr, result);
- cb(subscr, closure);
- OSMO_ASSERT(subscr->use_count == 1);
- llist_del(&subscr->entry);
- talloc_free(subscr);
- }
-
- dbi_result_free(result);
- return 0;
-}
-
-int db_sync_equipment(struct gsm_equipment *equip)
-{
- dbi_result result;
- unsigned char *cm2, *cm3;
- char *q_imei;
- uint8_t classmark1;
-
- memcpy(&classmark1, &equip->classmark1, sizeof(classmark1));
- DEBUGP(DDB, "Sync Equipment IMEI=%s, classmark1=%02x",
- equip->imei, classmark1);
- if (equip->classmark2_len)
- DEBUGPC(DDB, ", classmark2=%s",
- osmo_hexdump(equip->classmark2, equip->classmark2_len));
- if (equip->classmark3_len)
- DEBUGPC(DDB, ", classmark3=%s",
- osmo_hexdump(equip->classmark3, equip->classmark3_len));
- DEBUGPC(DDB, "\n");
-
- dbi_conn_quote_binary_copy(conn, equip->classmark2,
- equip->classmark2_len, &cm2);
- dbi_conn_quote_binary_copy(conn, equip->classmark3,
- equip->classmark3_len, &cm3);
- dbi_conn_quote_string_copy(conn, equip->imei, &q_imei);
-
- result = dbi_conn_queryf(conn,
- "UPDATE Equipment SET "
- "updated = datetime('now'), "
- "classmark1 = %u, "
- "classmark2 = %s, "
- "classmark3 = %s "
- "WHERE imei = %s ",
- classmark1, cm2, cm3, q_imei);
-
- free(cm2);
- free(cm3);
- free(q_imei);
-
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to update Equipment\n");
- return -EIO;
- }
-
- dbi_result_free(result);
- return 0;
-}
-
-int db_subscriber_expire(void *priv, void (*callback)(void *priv, long long unsigned int id))
-{
- dbi_result result;
-
- result = dbi_conn_query(conn,
- "SELECT id "
- "FROM Subscriber "
- "WHERE lac != 0 AND "
- "( expire_lu is NOT NULL "
- "AND expire_lu < datetime('now') ) "
- "LIMIT 1");
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to get expired subscribers\n");
- return -EIO;
- }
-
- while (dbi_result_next_row(result))
- callback(priv, dbi_result_get_ulonglong(result, "id"));
-
- dbi_result_free(result);
- return 0;
-}
-
-int db_subscriber_alloc_tmsi(struct gsm_subscriber *subscriber)
-{
- dbi_result result = NULL;
- char tmsi[14];
- char *tmsi_quoted;
-
- for (;;) {
- if (RAND_bytes((uint8_t *) &subscriber->tmsi, sizeof(subscriber->tmsi)) != 1) {
- LOGP(DDB, LOGL_ERROR, "RAND_bytes failed\n");
- return 1;
- }
- if (subscriber->tmsi == GSM_RESERVED_TMSI)
- continue;
-
- sprintf(tmsi, "%u", subscriber->tmsi);
- dbi_conn_quote_string_copy(conn, tmsi, &tmsi_quoted);
- result = dbi_conn_queryf(conn,
- "SELECT * FROM Subscriber "
- "WHERE tmsi = %s ",
- tmsi_quoted);
-
- free(tmsi_quoted);
-
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber "
- "while allocating new TMSI.\n");
- return 1;
- }
- if (dbi_result_get_numrows(result)) {
- dbi_result_free(result);
- continue;
- }
- if (!dbi_result_next_row(result)) {
- dbi_result_free(result);
- DEBUGP(DDB, "Allocated TMSI %u for IMSI %s.\n",
- subscriber->tmsi, subscriber->imsi);
- return db_sync_subscriber(subscriber);
- }
- dbi_result_free(result);
- }
- return 0;
-}
-
-int db_subscriber_alloc_exten(struct gsm_subscriber *subscriber, uint64_t smin,
- uint64_t smax)
-{
- dbi_result result = NULL;
- uint64_t try;
-
- for (;;) {
- try = (rand() % (smax - smin + 1) + smin);
- result = dbi_conn_queryf(conn,
- "SELECT * FROM Subscriber "
- "WHERE extension = %"PRIu64,
- try
- );
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber "
- "while allocating new extension.\n");
- return 1;
- }
- if (dbi_result_get_numrows(result)){
- dbi_result_free(result);
- continue;
- }
- if (!dbi_result_next_row(result)) {
- dbi_result_free(result);
- break;
- }
- dbi_result_free(result);
- }
- sprintf(subscriber->extension, "%"PRIu64, try);
- DEBUGP(DDB, "Allocated extension %"PRIu64 " for IMSI %s.\n", try, subscriber->imsi);
- return db_sync_subscriber(subscriber);
-}
-/*
- * try to allocate a new unique token for this subscriber and return it
- * via a parameter. if the subscriber already has a token, return
- * an error.
- */
-
-int db_subscriber_alloc_token(struct gsm_subscriber *subscriber, uint32_t *token)
-{
- dbi_result result;
- uint32_t try;
-
- for (;;) {
- if (RAND_bytes((uint8_t *) &try, sizeof(try)) != 1) {
- LOGP(DDB, LOGL_ERROR, "RAND_bytes failed\n");
- return 1;
- }
- if (!try) /* 0 is an invalid token */
- continue;
- result = dbi_conn_queryf(conn,
- "SELECT * FROM AuthToken "
- "WHERE subscriber_id = %llu OR token = \"%08X\" ",
- subscriber->id, try);
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to query AuthToken "
- "while allocating new token.\n");
- return 1;
- }
- if (dbi_result_get_numrows(result)) {
- dbi_result_free(result);
- continue;
- }
- if (!dbi_result_next_row(result)) {
- dbi_result_free(result);
- break;
- }
- dbi_result_free(result);
- }
- result = dbi_conn_queryf(conn,
- "INSERT INTO AuthToken "
- "(subscriber_id, created, token) "
- "VALUES "
- "(%llu, datetime('now'), \"%08X\") ",
- subscriber->id, try);
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to create token %08X for "
- "IMSI %s.\n", try, subscriber->imsi);
- return 1;
- }
- dbi_result_free(result);
- *token = try;
- DEBUGP(DDB, "Allocated token %08X for IMSI %s.\n", try, subscriber->imsi);
-
- return 0;
-}
-
-int db_subscriber_assoc_imei(struct gsm_subscriber *subscriber, char imei[GSM23003_IMEISV_NUM_DIGITS])
-{
- unsigned long long equipment_id, watch_id;
- dbi_result result;
-
- osmo_strlcpy(subscriber->equipment.imei, imei, sizeof(subscriber->equipment.imei));
-
- result = dbi_conn_queryf(conn,
- "INSERT OR IGNORE INTO Equipment "
- "(imei, created, updated) "
- "VALUES "
- "(%s, datetime('now'), datetime('now')) ",
- imei);
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to create Equipment by IMEI.\n");
- return 1;
- }
-
- equipment_id = 0;
- if (dbi_result_get_numrows_affected(result)) {
- equipment_id = dbi_conn_sequence_last(conn, NULL);
- }
- dbi_result_free(result);
-
- if (equipment_id)
- DEBUGP(DDB, "New Equipment: ID %llu, IMEI %s\n", equipment_id, imei);
- else {
- result = dbi_conn_queryf(conn,
- "SELECT id FROM Equipment "
- "WHERE imei = %s ",
- imei
- );
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to query Equipment by IMEI.\n");
- return 1;
- }
- if (!dbi_result_next_row(result)) {
- LOGP(DDB, LOGL_ERROR, "Failed to find the Equipment.\n");
- dbi_result_free(result);
- return 1;
- }
- equipment_id = dbi_result_get_ulonglong(result, "id");
- dbi_result_free(result);
- }
-
- result = dbi_conn_queryf(conn,
- "INSERT OR IGNORE INTO EquipmentWatch "
- "(subscriber_id, equipment_id, created, updated) "
- "VALUES "
- "(%llu, %llu, datetime('now'), datetime('now')) ",
- subscriber->id, equipment_id);
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to create EquipmentWatch.\n");
- return 1;
- }
-
- watch_id = 0;
- if (dbi_result_get_numrows_affected(result))
- watch_id = dbi_conn_sequence_last(conn, NULL);
-
- dbi_result_free(result);
- if (watch_id)
- DEBUGP(DDB, "New EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n",
- equipment_id, subscriber->imsi, imei);
- else {
- result = dbi_conn_queryf(conn,
- "UPDATE EquipmentWatch "
- "SET updated = datetime('now') "
- "WHERE subscriber_id = %llu AND equipment_id = %llu ",
- subscriber->id, equipment_id);
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to update EquipmentWatch.\n");
- return 1;
- }
- dbi_result_free(result);
- DEBUGP(DDB, "Updated EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n",
- equipment_id, subscriber->imsi, imei);
- }
-
- return 0;
-}
-
/* store an [unsent] SMS to the database */
int db_sms_store(struct gsm_sms *sms)
{
@@ -1500,7 +601,7 @@ static struct gsm_sms *sms_from_result(struct gsm_network *net, dbi_result resul
daddr = dbi_result_get_string(result, "dest_addr");
if (daddr)
osmo_strlcpy(sms->dst.addr, daddr, sizeof(sms->dst.addr));
- sms->receiver = subscr_get_by_extension(net->subscr_group, sms->dst.addr);
+ sms->receiver = vlr_subscr_find_by_msisdn(net->vlr, sms->dst.addr);
sms->src.npi = dbi_result_get_ulonglong(result, "src_npi");
sms->src.ton = dbi_result_get_ulonglong(result, "src_ton");
@@ -1542,20 +643,21 @@ struct gsm_sms *db_sms_get(struct gsm_network *net, unsigned long long id)
return sms;
}
-/* retrieve the next unsent SMS with ID >= min_id */
-struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, unsigned long long min_id)
+struct gsm_sms *db_sms_get_next_unsent(struct gsm_network *net,
+ unsigned long long min_sms_id,
+ unsigned int max_failed)
{
dbi_result result;
struct gsm_sms *sms;
result = dbi_conn_queryf(conn,
- "SELECT SMS.* "
- "FROM SMS JOIN Subscriber ON "
- "SMS.dest_addr = Subscriber.extension "
- "WHERE SMS.id >= %llu AND SMS.sent IS NULL "
- "AND Subscriber.lac > 0 "
- "ORDER BY SMS.id LIMIT 1",
- min_id);
+ "SELECT * FROM SMS"
+ " WHERE sent IS NULL"
+ " AND id >= %llu"
+ " AND deliver_attempts <= %u"
+ " ORDER BY id LIMIT 1",
+ min_sms_id, max_failed);
+
if (!result)
return NULL;
@@ -1571,21 +673,24 @@ struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, unsigned long long mi
return sms;
}
-struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net,
- unsigned long long min_subscr_id,
- unsigned int failed)
+/* retrieve the next unsent SMS for a given subscriber */
+struct gsm_sms *db_sms_get_unsent_for_subscr(struct vlr_subscr *vsub,
+ unsigned int max_failed)
{
+ struct gsm_network *net = vsub->vlr->user_ctx;
dbi_result result;
struct gsm_sms *sms;
+ if (!vsub->lu_complete)
+ return NULL;
+
result = dbi_conn_queryf(conn,
- "SELECT SMS.* "
- "FROM SMS JOIN Subscriber ON "
- "SMS.dest_addr = Subscriber.extension "
- "WHERE Subscriber.id >= %llu AND SMS.sent IS NULL "
- "AND Subscriber.lac > 0 AND SMS.deliver_attempts < %u "
- "ORDER BY Subscriber.id, SMS.id LIMIT 1",
- min_subscr_id, failed);
+ "SELECT * FROM SMS"
+ " WHERE sent IS NULL"
+ " AND dest_addr=%s"
+ " AND deliver_attempts <= %u"
+ " ORDER BY id LIMIT 1",
+ vsub->msisdn, max_failed);
if (!result)
return NULL;
@@ -1601,20 +706,20 @@ struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net,
return sms;
}
-/* retrieve the next unsent SMS for a given subscriber */
-struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr)
+struct gsm_sms *db_sms_get_next_unsent_rr_msisdn(struct gsm_network *net,
+ const char *last_msisdn,
+ unsigned int max_failed)
{
dbi_result result;
struct gsm_sms *sms;
result = dbi_conn_queryf(conn,
- "SELECT SMS.* "
- "FROM SMS JOIN Subscriber ON "
- "SMS.dest_addr = Subscriber.extension "
- "WHERE Subscriber.id = %llu AND SMS.sent IS NULL "
- "AND Subscriber.lac > 0 "
- "ORDER BY SMS.id LIMIT 1",
- subscr->id);
+ "SELECT * FROM SMS"
+ " WHERE sent IS NULL"
+ " AND dest_addr > '%s'"
+ " AND deliver_attempts <= %u"
+ " ORDER BY dest_addr, id LIMIT 1",
+ last_msisdn, max_failed);
if (!result)
return NULL;
@@ -1623,7 +728,7 @@ struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr)
return NULL;
}
- sms = sms_from_result(subscr->group->net, result);
+ sms = sms_from_result(net, result);
dbi_result_free(result);
@@ -1667,26 +772,20 @@ int db_sms_inc_deliver_attempts(struct gsm_sms *sms)
return 0;
}
-int db_apdu_blob_store(struct gsm_subscriber *subscr,
- uint8_t apdu_id_flags, uint8_t len,
- uint8_t *apdu)
+/* Drop all pending SMS to or from the given extension */
+int db_sms_delete_by_msisdn(const char *msisdn)
{
dbi_result result;
- unsigned char *q_apdu;
-
- dbi_conn_quote_binary_copy(conn, apdu, len, &q_apdu);
-
+ if (!msisdn || !*msisdn)
+ return 0;
result = dbi_conn_queryf(conn,
- "INSERT INTO ApduBlobs "
- "(created,subscriber_id,apdu_id_flags,apdu) VALUES "
- "(datetime('now'),%llu,%u,%s)",
- subscr->id, apdu_id_flags, q_apdu);
-
- free(q_apdu);
-
- if (!result)
- return -EIO;
-
+ "DELETE FROM SMS WHERE src_addr=%s OR dest_addr=%s",
+ msisdn, msisdn);
+ if (!result) {
+ LOGP(DDB, LOGL_ERROR,
+ "Failed to delete SMS for %s\n", msisdn);
+ return -1;
+ }
dbi_result_free(result);
return 0;
}
diff --git a/src/libmsc/gsm_04_08.c b/src/libmsc/gsm_04_08.c
index fb02de206..6cea2420c 100644
--- a/src/libmsc/gsm_04_08.c
+++ b/src/libmsc/gsm_04_08.c
@@ -1,7 +1,7 @@
/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface
* 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
-/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+/* (C) 2008-2016 by Harald Welte <laforge@gnumonks.org>
* (C) 2008-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
*
* All Rights Reserved
@@ -58,6 +58,7 @@
#include <openbsc/mncc_int.h>
#include <osmocom/abis/e1_input.h>
#include <osmocom/core/bitvec.h>
+#include <openbsc/vlr.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/gsm/gsm0480.h>
@@ -75,11 +76,10 @@ void *tall_authciphop_ctx;
static int tch_rtp_signal(struct gsm_lchan *lchan, int signal);
-static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn);
+static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn,
+ uint32_t send_tmsi);
static int gsm48_tx_simple(struct gsm_subscriber_connection *conn,
uint8_t pdisc, uint8_t msg_type);
-static void schedule_reject(struct gsm_subscriber_connection *conn);
-static void release_anchor(struct gsm_subscriber_connection *conn);
struct gsm_lai {
uint16_t mcc;
@@ -169,298 +169,7 @@ int gsm48_cc_tx_notify_ss(struct gsm_trans *trans, const char *message)
return gsm48_conn_sendmsg(ss_notify, trans->conn, trans);
}
-void release_security_operation(struct gsm_subscriber_connection *conn)
-{
- if (!conn->sec_operation)
- return;
-
- talloc_free(conn->sec_operation);
- conn->sec_operation = NULL;
- msc_release_connection(conn);
-}
-
-void allocate_security_operation(struct gsm_subscriber_connection *conn)
-{
- conn->sec_operation = talloc_zero(tall_authciphop_ctx,
- struct gsm_security_operation);
-}
-
-int gsm48_secure_channel(struct gsm_subscriber_connection *conn, int key_seq,
- gsm_cbfn *cb, void *cb_data)
-{
- struct gsm_network *net = conn->network;
- struct gsm_subscriber *subscr = conn->subscr;
- struct gsm_security_operation *op;
- struct gsm_auth_tuple atuple;
- int status = -1, rc;
-
- /* Check if we _can_ enable encryption. Cases where we can't:
- * - Encryption disabled in config
- * - Channel already secured (nothing to do)
- * - Subscriber equipment doesn't support configured encryption
- */
- if (!net->a5_encryption) {
- status = GSM_SECURITY_NOAVAIL;
- } else if (conn->lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) {
- DEBUGP(DMM, "Requesting to secure an already secure channel");
- status = GSM_SECURITY_ALREADY;
- } else if (!ms_cm2_a5n_support(subscr->equipment.classmark2,
- net->a5_encryption)) {
- DEBUGP(DMM, "Subscriber equipment doesn't support requested encryption");
- status = GSM_SECURITY_NOAVAIL;
- }
-
- /* If not done yet, try to get info for this user */
- if (status < 0) {
- rc = auth_get_tuple_for_subscr(&atuple, subscr, key_seq);
- if (rc <= 0)
- status = GSM_SECURITY_NOAVAIL;
- }
-
- /* Are we done yet ? */
- if (status >= 0)
- return cb ?
- cb(GSM_HOOK_RR_SECURITY, status, NULL, conn, cb_data) :
- 0;
-
- /* Start an operation (can't have more than one pending !!!) */
- if (conn->sec_operation)
- return -EBUSY;
-
- allocate_security_operation(conn);
- op = conn->sec_operation;
- op->cb = cb;
- op->cb_data = cb_data;
- memcpy(&op->atuple, &atuple, sizeof(struct gsm_auth_tuple));
-
- /* FIXME: Should start a timer for completion ... */
-
- /* Then do whatever is needed ... */
- if (rc == AUTH_DO_AUTH_THEN_CIPH) {
- /* Start authentication */
- return gsm48_tx_mm_auth_req(conn, op->atuple.vec.rand, NULL,
- op->atuple.key_seq);
- } else if (rc == AUTH_DO_CIPH) {
- /* Start ciphering directly */
- return gsm0808_cipher_mode(conn, net->a5_encryption,
- op->atuple.vec.kc, 8, 0);
- }
-
- return -EINVAL; /* not reached */
-}
-
-static bool subscr_regexp_check(const struct gsm_network *net, const char *imsi)
-{
- if (!net->authorized_reg_str)
- return false;
-
- if (regexec(&net->authorized_regexp, imsi, 0, NULL, 0) != REG_NOMATCH)
- return true;
-
- return false;
-}
-
-static int authorize_subscriber(struct gsm_loc_updating_operation *loc,
- struct gsm_subscriber *subscriber)
-{
- if (!subscriber)
- return 0;
-
- /*
- * Do not send accept yet as more information should arrive. Some
- * phones will not send us the information and we will have to check
- * what we want to do with that.
- */
- if (loc && (loc->waiting_for_imsi || loc->waiting_for_imei))
- return 0;
-
- switch (subscriber->group->net->auth_policy) {
- case GSM_AUTH_POLICY_CLOSED:
- return subscriber->authorized;
- case GSM_AUTH_POLICY_REGEXP:
- if (subscriber->authorized)
- return 1;
- if (subscr_regexp_check(subscriber->group->net,
- subscriber->imsi))
- subscriber->authorized = 1;
- return subscriber->authorized;
- case GSM_AUTH_POLICY_TOKEN:
- if (subscriber->authorized)
- return subscriber->authorized;
- return (subscriber->flags & GSM_SUBSCRIBER_FIRST_CONTACT);
- case GSM_AUTH_POLICY_ACCEPT_ALL:
- return 1;
- default:
- return 0;
- }
-}
-
-static void _release_loc_updating_req(struct gsm_subscriber_connection *conn, int release)
-{
- if (!conn->loc_operation)
- return;
-
- /* No need to keep the connection up */
- release_anchor(conn);
-
- osmo_timer_del(&conn->loc_operation->updating_timer);
- talloc_free(conn->loc_operation);
- conn->loc_operation = NULL;
- if (release)
- msc_release_connection(conn);
-}
-
-static void loc_updating_failure(struct gsm_subscriber_connection *conn, int release)
-{
- if (!conn->loc_operation)
- return;
- LOGP(DMM, LOGL_ERROR, "Location Updating failed for %s\n",
- subscr_name(conn->subscr));
- rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_FAILED]);
- _release_loc_updating_req(conn, release);
-}
-
-static void loc_updating_success(struct gsm_subscriber_connection *conn, int release)
-{
- if (!conn->loc_operation)
- return;
- LOGP(DMM, LOGL_INFO, "Location Updating completed for %s\n",
- subscr_name(conn->subscr));
- rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_COMPLETED]);
- _release_loc_updating_req(conn, release);
-}
-
-static void allocate_loc_updating_req(struct gsm_subscriber_connection *conn)
-{
- if (conn->loc_operation)
- LOGP(DMM, LOGL_ERROR, "Connection already had operation.\n");
- loc_updating_failure(conn, 0);
-
- conn->loc_operation = talloc_zero(tall_locop_ctx,
- struct gsm_loc_updating_operation);
-}
-
-static int finish_lu(struct gsm_subscriber_connection *conn)
-{
- int rc = 0;
- int avoid_tmsi = conn->network->avoid_tmsi;
-
- /* We're all good */
- if (avoid_tmsi) {
- conn->subscr->tmsi = GSM_RESERVED_TMSI;
- db_sync_subscriber(conn->subscr);
- } else {
- db_subscriber_alloc_tmsi(conn->subscr);
- }
-
- rc = gsm0408_loc_upd_acc(conn);
- if (conn->network->send_mm_info) {
- /* send MM INFO with network name */
- rc = gsm48_tx_mm_info(conn);
- }
-
- /* call subscr_update after putting the loc_upd_acc
- * in the transmit queue, since S_SUBSCR_ATTACHED might
- * trigger further action like SMS delivery */
- subscr_update(conn->subscr, conn->bts,
- GSM_SUBSCRIBER_UPDATE_ATTACHED);
-
- /*
- * The gsm0408_loc_upd_acc sends a MI with the TMSI. The
- * MS needs to respond with a TMSI REALLOCATION COMPLETE
- * (even if the TMSI is the same).
- * If avoid_tmsi == true, we don't send a TMSI, we don't
- * expect a reply and Location Updating is done.
- */
- if (avoid_tmsi)
- loc_updating_success(conn, 1);
-
- return rc;
-}
-
-static int _gsm0408_authorize_sec_cb(unsigned int hooknum, unsigned int event,
- struct msgb *msg, void *data, void *param)
-{
- struct gsm_subscriber_connection *conn = data;
- int rc = 0;
-
- switch (event) {
- case GSM_SECURITY_AUTH_FAILED:
- loc_updating_failure(conn, 1);
- break;
-
- case GSM_SECURITY_ALREADY:
- LOGP(DMM, LOGL_ERROR, "We don't expect LOCATION "
- "UPDATING after CM SERVICE REQUEST\n");
- /* fall through */
-
- case GSM_SECURITY_NOAVAIL:
- case GSM_SECURITY_SUCCEEDED:
- rc = finish_lu(conn);
- break;
-
- default:
- rc = -EINVAL;
- };
-
- return rc;
-}
-
-static int gsm0408_authorize(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- if (!conn->loc_operation)
- return 0;
-
- if (authorize_subscriber(conn->loc_operation, conn->subscr))
- return gsm48_secure_channel(conn,
- conn->loc_operation->key_seq,
- _gsm0408_authorize_sec_cb, NULL);
- return 0;
-}
-
-void gsm0408_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause)
-{
- struct gsm_trans *trans, *temp;
-
- /* avoid someone issuing a clear */
- conn->in_release = 1;
-
- /*
- * Cancel any outstanding location updating request
- * operation taking place on the subscriber connection.
- */
- loc_updating_failure(conn, 0);
-
- /* We might need to cancel the paging response or such. */
- if (conn->sec_operation && conn->sec_operation->cb) {
- conn->sec_operation->cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_AUTH_FAILED,
- NULL, conn, conn->sec_operation->cb_data);
- }
-
- release_security_operation(conn);
- release_anchor(conn);
-
- /*
- * Free all transactions that are associated with the released
- * connection. The transaction code will inform the CC or SMS
- * facilities that will send the release indications. As part of
- * the CC REL_IND the remote leg might be released and this will
- * trigger the call to trans_free. This is something the llist
- * macro can not handle and we will need to re-iterate the list.
- *
- * TODO: Move the trans_list into the subscriber connection and
- * create a pending list for MT transactions. These exist before
- * we have a subscriber connection.
- */
-restart:
- llist_for_each_entry_safe(trans, temp, &conn->network->trans_list, entry) {
- if (trans->conn == conn) {
- trans_free(trans);
- goto restart;
- }
- }
-}
-
+/* clear all transactions globally; used in case of MNCC socket disconnect */
void gsm0408_clear_all_trans(struct gsm_network *net, int protocol)
{
struct gsm_trans *trans, *temp;
@@ -490,14 +199,15 @@ int gsm0408_loc_upd_rej(struct gsm_subscriber_connection *conn, uint8_t cause)
msg->lchan = conn->lchan;
LOGP(DMM, LOGL_INFO, "Subscriber %s: LOCATION UPDATING REJECT "
- "LAC=%u BTS=%u\n", subscr_name(conn->subscr),
+ "LAC=%u BTS=%u\n", vlr_subscr_name(conn->vsub),
bts->location_area_code, bts->nr);
return gsm48_conn_sendmsg(msg, conn, NULL);
}
/* Chapter 9.2.13 : Send LOCATION UPDATE ACCEPT */
-static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn)
+static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn,
+ uint32_t send_tmsi)
{
struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 LOC UPD ACC");
struct gsm48_hdr *gh;
@@ -515,16 +225,27 @@ static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn)
conn->network->network_code,
conn->bts->location_area_code);
- if (conn->subscr->tmsi == GSM_RESERVED_TMSI) {
+ if (send_tmsi == GSM_RESERVED_TMSI) {
+ /* we did not allocate a TMSI to the MS, so we need to
+ * include the IMSI in order for the MS to delete any
+ * old TMSI that might still be allocated */
uint8_t mi[10];
int len;
- len = gsm48_generate_mid_from_imsi(mi, conn->subscr->imsi);
+ len = gsm48_generate_mid_from_imsi(mi, conn->vsub->imsi);
mid = msgb_put(msg, len);
memcpy(mid, mi, len);
} else {
+ /* Include the TMSI, which means that the MS will send a
+ * TMSI REALLOCATION COMPLETE, and we should wait for
+ * that until T3250 expiration */
mid = msgb_put(msg, GSM48_MID_TMSI_LEN);
- gsm48_generate_mid_from_tmsi(mid, conn->subscr->tmsi);
+ gsm48_generate_mid_from_tmsi(mid, send_tmsi);
}
+ /* TODO: Follow-on proceed */
+ /* TODO: CTS permission */
+ /* TODO: Equivalent PLMNs */
+ /* TODO: Emergency Number List */
+ /* TODO: Per-MS T3312 */
DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n");
@@ -547,82 +268,29 @@ static int mm_tx_identity_req(struct gsm_subscriber_connection *conn, uint8_t id
return gsm48_conn_sendmsg(msg, conn, NULL);
}
-static struct gsm_subscriber *subscr_create(const struct gsm_network *net,
- const char *imsi)
-{
- if (!net->auto_create_subscr)
- return NULL;
-
- if (!subscr_regexp_check(net, imsi))
- return NULL;
-
- return subscr_create_subscriber(net->subscr_group, imsi);
-}
-
/* Parse Chapter 9.2.11 Identity Response */
static int mm_rx_id_resp(struct gsm_subscriber_connection *conn, struct msgb *msg)
{
struct gsm48_hdr *gh = msgb_l3(msg);
- struct gsm_network *net = conn->network;
uint8_t mi_type = gh->data[1] & GSM_MI_TYPE_MASK;
char mi_string[GSM48_MI_SIZE];
+ if (!conn->vsub) {
+ LOGP(DMM, LOGL_ERROR,
+ "Rx MM Identity Response: invalid: no subscriber\n");
+ return -EINVAL;
+ }
+
gsm48_mi_to_string(mi_string, sizeof(mi_string), &gh->data[1], gh->data[0]);
DEBUGP(DMM, "IDENTITY RESPONSE: MI(%s)=%s\n",
gsm48_mi_type_name(mi_type), mi_string);
osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, gh->data);
- switch (mi_type) {
- case GSM_MI_TYPE_IMSI:
- /* look up subscriber based on IMSI, create if not found */
- if (!conn->subscr) {
- conn->subscr = subscr_get_by_imsi(net->subscr_group,
- mi_string);
- if (!conn->subscr)
- conn->subscr = subscr_create(net, mi_string);
- }
- if (!conn->subscr && conn->loc_operation) {
- gsm0408_loc_upd_rej(conn, net->reject_cause);
- loc_updating_failure(conn, 1);
- return 0;
- }
- if (conn->loc_operation)
- conn->loc_operation->waiting_for_imsi = 0;
- break;
- case GSM_MI_TYPE_IMEI:
- case GSM_MI_TYPE_IMEISV:
- /* update subscribe <-> IMEI mapping */
- if (conn->subscr) {
- db_subscriber_assoc_imei(conn->subscr, mi_string);
- db_sync_equipment(&conn->subscr->equipment);
- }
- if (conn->loc_operation)
- conn->loc_operation->waiting_for_imei = 0;
- break;
- }
-
- /* Check if we can let the mobile station enter */
- return gsm0408_authorize(conn, msg);
-}
-
-
-static void loc_upd_rej_cb(void *data)
-{
- struct gsm_subscriber_connection *conn = data;
-
- LOGP(DMM, LOGL_DEBUG, "Location Updating Request procedure timedout.\n");
- gsm0408_loc_upd_rej(conn, conn->network->reject_cause);
- loc_updating_failure(conn, 1);
-}
-
-static void schedule_reject(struct gsm_subscriber_connection *conn)
-{
- osmo_timer_setup(&conn->loc_operation->updating_timer, loc_upd_rej_cb,
- conn);
- osmo_timer_schedule(&conn->loc_operation->updating_timer, 5, 0);
+ return vlr_subscr_rx_id_resp(conn->vsub, gh->data+1, gh->data[0]);
}
+/* FIXME: to libosmogsm */
static const struct value_string lupd_names[] = {
{ GSM48_LUPD_NORMAL, "NORMAL" },
{ GSM48_LUPD_PERIODIC, "PERIODIC" },
@@ -630,14 +298,23 @@ static const struct value_string lupd_names[] = {
{ 0, NULL }
};
-/* Chapter 9.2.15: Receive Location Updating Request */
-static int mm_rx_loc_upd_req(struct gsm_subscriber_connection *conn, struct msgb *msg)
+/* Chapter 9.2.15: Receive Location Updating Request.
+ * Keep this function non-static for direct invocation by unit tests. */
+int mm_rx_loc_upd_req(struct gsm_subscriber_connection *conn, struct msgb *msg)
{
+ static const enum subscr_conn_from conn_from_lu = SUBSCR_CONN_FROM_LU;
+ struct gsm_network *net = conn->network;
struct gsm48_hdr *gh = msgb_l3(msg);
struct gsm48_loc_upd_req *lu;
- struct gsm_subscriber *subscr = NULL;
uint8_t mi_type;
char mi_string[GSM48_MI_SIZE];
+ enum vlr_lu_type vlr_lu_type = VLR_LU_TYPE_REGULAR;
+
+ uint32_t tmsi;
+ char *imsi;
+ struct osmo_location_area_id old_lai, new_lai;
+ struct osmo_fsm_inst *lu_fsm;
+ int rc;
lu = (struct gsm48_loc_upd_req *) gh->data;
@@ -645,97 +322,95 @@ static int mm_rx_loc_upd_req(struct gsm_subscriber_connection *conn, struct msgb
gsm48_mi_to_string(mi_string, sizeof(mi_string), lu->mi, lu->mi_len);
- DEBUGPC(DMM, "MI(%s)=%s type=%s ", gsm48_mi_type_name(mi_type),
- mi_string, get_value_string(lupd_names, lu->type));
+ rc = msc_create_conn_fsm(conn, mi_string);
+ if (rc)
+ /* logging already happened in msc_create_conn_fsm() */
+ return rc;
+
+ conn->classmark.classmark1 = lu->classmark1;
+ conn->classmark.classmark1_set = true;
+
+ DEBUGP(DMM, "LOCATION UPDATING REQUEST: MI(%s)=%s type=%s\n",
+ gsm48_mi_type_name(mi_type), mi_string,
+ get_value_string(lupd_names, lu->type));
osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, &lu->mi_len);
switch (lu->type) {
case GSM48_LUPD_NORMAL:
rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_NORMAL]);
+ vlr_lu_type = VLR_LU_TYPE_REGULAR;
break;
case GSM48_LUPD_IMSI_ATT:
rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_ATTACH]);
+ vlr_lu_type = VLR_LU_TYPE_IMSI_ATTACH;
break;
case GSM48_LUPD_PERIODIC:
rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_PERIODIC]);
+ vlr_lu_type = VLR_LU_TYPE_PERIODIC;
break;
}
- /*
- * Pseudo Spoof detection: Just drop a second/concurrent
- * location updating request.
- */
- if (conn->loc_operation) {
- DEBUGPC(DMM, "ignoring request due an existing one: %p.\n",
- conn->loc_operation);
- gsm0408_loc_upd_rej(conn, GSM48_REJECT_PROTOCOL_ERROR);
- return 0;
- }
-
- allocate_loc_updating_req(conn);
-
- conn->loc_operation->key_seq = lu->key_seq;
+ /* TODO: 10.5.1.6 MS Classmark for UMTS / Classmark 2 */
+ /* TODO: 10.5.3.14 Aditional update parameters (CS fallback calls) */
+ /* TODO: 10.5.7.8 Device properties */
+ /* TODO: 10.5.1.15 MS network feature support */
switch (mi_type) {
case GSM_MI_TYPE_IMSI:
- DEBUGPC(DMM, "\n");
- /* we always want the IMEI, too */
- mm_tx_identity_req(conn, GSM_MI_TYPE_IMEI);
- conn->loc_operation->waiting_for_imei = 1;
-
- /* look up subscriber based on IMSI, create if not found */
- subscr = subscr_get_by_imsi(conn->network->subscr_group, mi_string);
- if (!subscr)
- subscr = subscr_create(conn->network, mi_string);
- if (!subscr) {
- gsm0408_loc_upd_rej(conn, conn->network->reject_cause);
- loc_updating_failure(conn, 0); /* FIXME: set release == true? */
- return 0;
- }
+ tmsi = GSM_RESERVED_TMSI;
+ imsi = mi_string;
break;
case GSM_MI_TYPE_TMSI:
- DEBUGPC(DMM, "\n");
- /* look up the subscriber based on TMSI, request IMSI if it fails */
- subscr = subscr_get_by_tmsi(conn->network->subscr_group,
- tmsi_from_string(mi_string));
- if (!subscr) {
- /* send IDENTITY REQUEST message to get IMSI */
- mm_tx_identity_req(conn, GSM_MI_TYPE_IMSI);
- conn->loc_operation->waiting_for_imsi = 1;
- }
- /* we always want the IMEI, too */
- mm_tx_identity_req(conn, GSM_MI_TYPE_IMEI);
- conn->loc_operation->waiting_for_imei = 1;
- break;
- case GSM_MI_TYPE_IMEI:
- case GSM_MI_TYPE_IMEISV:
- /* no sim card... FIXME: what to do ? */
- DEBUGPC(DMM, "unimplemented mobile identity type\n");
+ tmsi = tmsi_from_string(mi_string);
+ imsi = NULL;
break;
default:
DEBUGPC(DMM, "unknown mobile identity type\n");
+ tmsi = GSM_RESERVED_TMSI;
+ imsi = NULL;
break;
}
- /* schedule the reject timer */
- schedule_reject(conn);
-
- if (!subscr) {
- DEBUGPC(DRR, "<- Can't find any subscriber for this ID\n");
- /* FIXME: request id? close channel? */
- return -EINVAL;
+ gsm48_decode_lai(&lu->lai, &old_lai.plmn.mcc,
+ &old_lai.plmn.mnc, &old_lai.lac);
+ new_lai.plmn.mcc = conn->network->country_code;
+ new_lai.plmn.mnc = conn->network->network_code;
+ new_lai.lac = conn->bts->location_area_code;
+ DEBUGP(DMM, "LU/new-LAC: %u/%u\n", old_lai.lac, new_lai.lac);
+
+ lu_fsm = vlr_loc_update(conn->conn_fsm,
+ SUBSCR_CONN_E_ACCEPTED,
+ SUBSCR_CONN_E_CN_CLOSE,
+ (void*)&conn_from_lu,
+ net->vlr, conn, vlr_lu_type, tmsi, imsi,
+ &old_lai, &new_lai,
+ conn->network->authentication_required,
+ conn->network->a5_encryption,
+ classmark_is_r99(&conn->classmark),
+ conn->via_ran == RAN_UTRAN_IU,
+ net->vlr->cfg.assign_tmsi);
+ if (!lu_fsm) {
+ DEBUGP(DRR, "%s: Can't start LU FSM\n", mi_string);
+ return 0;
}
- conn->subscr = subscr;
- conn->subscr->equipment.classmark1 = lu->classmark1;
+ /* From vlr_loc_update() we expect an implicit dispatch of
+ * VLR_ULA_E_UPDATE_LA, and thus we expect msc_vlr_subscr_assoc() to
+ * already have been called and completed. Has an error occured? */
+
+ if (!conn->vsub || conn->vsub->lu_fsm != lu_fsm) {
+ LOGP(DRR, LOGL_ERROR,
+ "%s: internal error during Location Updating attempt\n",
+ mi_string);
+ return -EIO;
+ }
- /* check if we can let the subscriber into our network immediately
- * or if we need to wait for identity responses. */
- return gsm0408_authorize(conn, msg);
+ return 0;
}
/* Turn int into semi-octet representation: 98 => 0x89 */
+/* FIXME: libosmocore/libosmogsm */
static uint8_t bcdify(uint8_t value)
{
uint8_t ret;
@@ -939,53 +614,46 @@ int gsm48_tx_mm_auth_rej(struct gsm_subscriber_connection *conn)
return gsm48_tx_simple(conn, GSM48_PDISC_MM, GSM48_MT_MM_AUTH_REJ);
}
-/*
- * At the 30C3 phones miss their periodic update
- * interval a lot and then remain unreachable. In case
- * we still know the TMSI we can just attach it again.
- */
-static void implit_attach(struct gsm_subscriber_connection *conn)
-{
- if (conn->subscr->lac != GSM_LAC_RESERVED_DETACHED)
- return;
-
- subscr_update(conn->subscr, conn->bts,
- GSM_SUBSCRIBER_UPDATE_ATTACHED);
-}
-
+static int msc_vlr_tx_cm_serv_acc(void *msc_conn_ref);
+static int msc_vlr_tx_cm_serv_rej(void *msc_conn_ref, enum vlr_proc_arq_result result);
-static int _gsm48_rx_mm_serv_req_sec_cb(
- unsigned int hooknum, unsigned int event,
- struct msgb *msg, void *data, void *param)
+static int cm_serv_reuse_conn(struct gsm_subscriber_connection *conn, const uint8_t *mi_lv)
{
- struct gsm_subscriber_connection *conn = data;
- int rc = 0;
-
- /* auth failed or succeeded, the timer was stopped */
- conn->expire_timer_stopped = 1;
-
- switch (event) {
- case GSM_SECURITY_AUTH_FAILED:
- /* Nothing to do */
- break;
+ uint8_t mi_type;
+ char mi_string[GSM48_MI_SIZE];
+ uint32_t tmsi;
- case GSM_SECURITY_NOAVAIL:
- case GSM_SECURITY_ALREADY:
- rc = gsm48_tx_mm_serv_ack(conn);
- implit_attach(conn);
- break;
+ gsm48_mi_to_string(mi_string, sizeof(mi_string), mi_lv+1, mi_lv[0]);
+ mi_type = mi_lv[1] & GSM_MI_TYPE_MASK;
- case GSM_SECURITY_SUCCEEDED:
- /* nothing to do. CIPHER MODE COMMAND is
- * implicit CM SERV ACK */
- implit_attach(conn);
- break;
+ switch (mi_type) {
+ case GSM_MI_TYPE_IMSI:
+ if (vlr_subscr_matches_imsi(conn->vsub, mi_string))
+ goto accept_reuse;
+ break;
+ case GSM_MI_TYPE_TMSI:
+ tmsi = osmo_load32be(mi_lv+2);
+ if (vlr_subscr_matches_tmsi(conn->vsub, tmsi))
+ goto accept_reuse;
+ break;
+ case GSM_MI_TYPE_IMEI:
+ if (vlr_subscr_matches_imei(conn->vsub, mi_string))
+ goto accept_reuse;
+ break;
+ default:
+ break;
+ }
- default:
- rc = -EINVAL;
- };
+ LOGP(DMM, LOGL_ERROR, "%s: CM Service Request with mismatching mobile identity: %s %s\n",
+ vlr_subscr_name(conn->vsub), gsm48_mi_type_name(mi_type), mi_string);
+ msc_vlr_tx_cm_serv_rej(conn, VLR_PR_ARQ_RES_ILLEGAL_SUBSCR);
+ return -EINVAL;
- return rc;
+accept_reuse:
+ DEBUGP(DMM, "%s: re-using already accepted connection\n",
+ vlr_subscr_name(conn->vsub));
+ conn->received_cm_service_request = true;
+ return conn->network->vlr->ops.tx_cm_serv_acc(conn);
}
/*
@@ -996,14 +664,17 @@ static int _gsm48_rx_mm_serv_req_sec_cb(
* c) Check that we know the subscriber with the TMSI otherwise reject
* with a HLR cause
* d) Set the subscriber on the gsm_lchan and accept
+ *
+ * Keep this function non-static for direct invocation by unit tests.
*/
-static int gsm48_rx_mm_serv_req(struct gsm_subscriber_connection *conn, struct msgb *msg)
+int gsm48_rx_mm_serv_req(struct gsm_subscriber_connection *conn, struct msgb *msg)
{
+ static const enum subscr_conn_from conn_from_cm_service_req =
+ SUBSCR_CONN_FROM_CM_SERVICE_REQ;
+ struct gsm_network *net = conn->network;
uint8_t mi_type;
char mi_string[GSM48_MI_SIZE];
- struct gsm_network *network = conn->network;
- struct gsm_subscriber *subscr;
struct gsm48_hdr *gh = msgb_l3(msg);
struct gsm48_service_request *req =
(struct gsm48_service_request *)gh->data;
@@ -1012,6 +683,12 @@ static int gsm48_rx_mm_serv_req(struct gsm_subscriber_connection *conn, struct m
uint8_t *classmark2 = gh->data+2;
uint8_t mi_len = *(classmark2 + classmark2_len);
uint8_t *mi = (classmark2 + classmark2_len + 1);
+ struct osmo_location_area_id lai;
+ int rc;
+
+ lai.plmn.mcc = conn->network->country_code;
+ lai.plmn.mnc = conn->network->network_code;
+ lai.lac = conn->bts->location_area_code;
DEBUGP(DMM, "<- CM SERVICE REQUEST ");
if (msg->data_len < sizeof(struct gsm48_service_request*)) {
@@ -1033,14 +710,10 @@ static int gsm48_rx_mm_serv_req(struct gsm_subscriber_connection *conn, struct m
DEBUGPC(DMM, "serv_type=0x%02x MI(%s)=%s\n",
req->cm_service_type, gsm48_mi_type_name(mi_type),
mi_string);
- subscr = subscr_get_by_imsi(network->subscr_group,
- mi_string);
} else if (mi_type == GSM_MI_TYPE_TMSI) {
DEBUGPC(DMM, "serv_type=0x%02x MI(%s)=%s\n",
req->cm_service_type, gsm48_mi_type_name(mi_type),
mi_string);
- subscr = subscr_get_by_tmsi(network->subscr_group,
- tmsi_from_string(mi_string));
} else {
DEBUGPC(DMM, "mi_type is not expected: %d\n", mi_type);
return gsm48_tx_mm_serv_rej(conn,
@@ -1048,34 +721,40 @@ static int gsm48_rx_mm_serv_req(struct gsm_subscriber_connection *conn, struct m
}
osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, (classmark2 + classmark2_len));
+ memcpy(conn->classmark.classmark2, classmark2, classmark2_len);
+ conn->classmark.classmark2_len = classmark2_len;
+
+ if (conn->conn_fsm) {
+ if (msc_subscr_conn_is_accepted(conn))
+ return cm_serv_reuse_conn(conn, mi-1);
+ LOGP(DMM, LOGL_ERROR, "%s: connection already in use\n",
+ vlr_subscr_name(conn->vsub));
+ msc_vlr_tx_cm_serv_rej(conn, VLR_PR_ARQ_RES_UNKNOWN_ERROR);
+ return -EINVAL;
+ }
- if (is_siemens_bts(conn->bts))
- send_siemens_mrpci(msg->lchan, classmark2-1);
-
-
- /* FIXME: if we don't know the TMSI, inquire abit IMSI and allocate new TMSI */
- if (!subscr)
- return gsm48_tx_mm_serv_rej(conn,
- GSM48_REJECT_IMSI_UNKNOWN_IN_VLR);
-
- if (!conn->subscr)
- conn->subscr = subscr;
- else if (conn->subscr == subscr)
- subscr_put(subscr); /* lchan already has a ref, don't need another one */
- else {
- DEBUGP(DMM, "<- CM Channel already owned by someone else?\n");
- subscr_put(subscr);
+ rc = msc_create_conn_fsm(conn, mi_string);
+ if (rc) {
+ msc_vlr_tx_cm_serv_rej(conn, VLR_PR_ARQ_RES_UNKNOWN_ERROR);
+ /* logging already happened in msc_create_conn_fsm() */
+ return rc;
}
- subscr->equipment.classmark2_len = classmark2_len;
- memcpy(subscr->equipment.classmark2, classmark2, classmark2_len);
- db_sync_equipment(&subscr->equipment);
+ if (is_siemens_bts(conn->bts))
+ send_siemens_mrpci(msg->lchan, classmark2-1);
- /* we will send a MM message soon */
- conn->expire_timer_stopped = 1;
+ vlr_proc_acc_req(conn->conn_fsm,
+ SUBSCR_CONN_E_ACCEPTED,
+ SUBSCR_CONN_E_CN_CLOSE,
+ (void*)&conn_from_cm_service_req,
+ net->vlr, conn,
+ VLR_PR_ARQ_T_CM_SERV_REQ, mi-1, &lai,
+ conn->network->authentication_required,
+ conn->network->a5_encryption,
+ classmark_is_r99(&conn->classmark),
+ conn->via_ran == RAN_UTRAN_IU);
- return gsm48_secure_channel(conn, req->cipher_key_seq,
- _gsm48_rx_mm_serv_req_sec_cb, NULL);
+ return 0;
}
static int gsm48_rx_mm_imsi_detach_ind(struct gsm_subscriber_connection *conn, struct msgb *msg)
@@ -1086,51 +765,51 @@ static int gsm48_rx_mm_imsi_detach_ind(struct gsm_subscriber_connection *conn, s
(struct gsm48_imsi_detach_ind *) gh->data;
uint8_t mi_type = idi->mi[0] & GSM_MI_TYPE_MASK;
char mi_string[GSM48_MI_SIZE];
- struct gsm_subscriber *subscr = NULL;
+ struct vlr_subscr *vsub = NULL;
gsm48_mi_to_string(mi_string, sizeof(mi_string), idi->mi, idi->mi_len);
- DEBUGP(DMM, "IMSI DETACH INDICATION: MI(%s)=%s",
- gsm48_mi_type_name(mi_type), mi_string);
+ DEBUGP(DMM, "IMSI DETACH INDICATION: MI(%s)=%s\n",
+ gsm48_mi_type_name(mi_type), mi_string);
rate_ctr_inc(&network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_DETACH]);
switch (mi_type) {
case GSM_MI_TYPE_TMSI:
- DEBUGPC(DMM, "\n");
- subscr = subscr_get_by_tmsi(network->subscr_group,
- tmsi_from_string(mi_string));
+ vsub = vlr_subscr_find_by_tmsi(network->vlr,
+ tmsi_from_string(mi_string));
break;
case GSM_MI_TYPE_IMSI:
- DEBUGPC(DMM, "\n");
- subscr = subscr_get_by_imsi(network->subscr_group,
- mi_string);
+ vsub = vlr_subscr_find_by_imsi(network->vlr, mi_string);
break;
case GSM_MI_TYPE_IMEI:
case GSM_MI_TYPE_IMEISV:
/* no sim card... FIXME: what to do ? */
- DEBUGPC(DMM, ": unimplemented mobile identity type\n");
+ LOGP(DMM, LOGL_ERROR, "MI(%s)=%s: unimplemented mobile identity type\n",
+ gsm48_mi_type_name(mi_type), mi_string);
break;
default:
- DEBUGPC(DMM, ": unknown mobile identity type\n");
+ LOGP(DMM, LOGL_ERROR, "MI(%s)=%s: unknown mobile identity type\n",
+ gsm48_mi_type_name(mi_type), mi_string);
break;
}
- if (subscr) {
- subscr_update(subscr, conn->bts,
- GSM_SUBSCRIBER_UPDATE_DETACHED);
- DEBUGP(DMM, "Subscriber: %s\n", subscr_name(subscr));
-
- subscr->equipment.classmark1 = idi->classmark1;
- db_sync_equipment(&subscr->equipment);
- subscr_put(subscr);
- } else
- DEBUGP(DMM, "Unknown Subscriber ?!?\n");
+ /* TODO? We used to remember the subscriber's classmark1 here and
+ * stored it in the old sqlite db, but now we store it in a conn that
+ * will be discarded anyway: */
+ conn->classmark.classmark1 = idi->classmark1;
- /* FIXME: iterate over all transactions and release them,
- * imagine an IMSI DETACH happening during an active call! */
+ if (!vsub) {
+ LOGP(DMM, LOGL_ERROR, "IMSI DETACH for unknown subscriber MI(%s)=%s\n",
+ gsm48_mi_type_name(mi_type), mi_string);
+ } else {
+ LOGP(DMM, LOGL_INFO, "IMSI DETACH for %s\n", vlr_subscr_name(vsub));
+ vlr_subscr_rx_imsi_detach(vsub);
+ osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_DETACHED, vsub);
+ vlr_subscr_put(vsub);
+ }
- release_anchor(conn);
+ msc_subscr_conn_close(conn, 0);
return 0;
}
@@ -1154,7 +833,7 @@ static int parse_gsm_auth_resp(uint8_t *res, uint8_t *res_len,
LOGP(DMM, LOGL_ERROR,
"%s: MM AUTHENTICATION RESPONSE:"
" l3 length invalid: %u\n",
- subscr_name(conn->subscr), msgb_l3len(msg));
+ vlr_subscr_name(conn->vsub), msgb_l3len(msg));
return -EINVAL;
}
@@ -1187,7 +866,7 @@ static int parse_umts_auth_resp(uint8_t *res, uint8_t *res_len,
LOGP(DMM, LOGL_ERROR,
"%s: MM AUTHENTICATION RESPONSE:"
" l3 length invalid: %u\n",
- subscr_name(conn->subscr), msgb_l3len(msg));
+ vlr_subscr_name(conn->vsub), msgb_l3len(msg));
return -EINVAL;
}
@@ -1197,7 +876,7 @@ static int parse_umts_auth_resp(uint8_t *res, uint8_t *res_len,
LOGP(DMM, LOGL_ERROR,
"%s: MM R99 AUTHENTICATION RESPONSE:"
" expected IEI 0x%02x, got 0x%02x\n",
- subscr_name(conn->subscr),
+ vlr_subscr_name(conn->vsub),
GSM48_IE_AUTH_RES_EXT, iei);
return -EINVAL;
}
@@ -1206,7 +885,7 @@ static int parse_umts_auth_resp(uint8_t *res, uint8_t *res_len,
LOGP(DMM, LOGL_ERROR,
"%s: MM R99 AUTHENTICATION RESPONSE:"
" extended Auth Resp IE 0x%02x is too large: %u bytes\n",
- subscr_name(conn->subscr), GSM48_IE_AUTH_RES_EXT, ie_len);
+ vlr_subscr_name(conn->vsub), GSM48_IE_AUTH_RES_EXT, ie_len);
return -EINVAL;
}
@@ -1218,17 +897,15 @@ static int parse_umts_auth_resp(uint8_t *res, uint8_t *res_len,
/* Chapter 9.2.3: Authentication Response */
static int gsm48_rx_mm_auth_resp(struct gsm_subscriber_connection *conn, struct msgb *msg)
{
- struct gsm_network *net = conn->network;
uint8_t res[16];
uint8_t res_len;
int rc;
bool is_r99;
- if (!conn->subscr) {
+ if (!conn->vsub) {
LOGP(DMM, LOGL_ERROR,
"MM AUTHENTICATION RESPONSE: invalid: no subscriber\n");
- gsm48_tx_mm_auth_rej(conn);
- release_security_operation(conn);
+ msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED);
return -EINVAL;
}
@@ -1242,56 +919,18 @@ static int gsm48_rx_mm_auth_resp(struct gsm_subscriber_connection *conn, struct
}
if (rc) {
- gsm48_tx_mm_auth_rej(conn);
- release_security_operation(conn);
+ msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED);
return -EINVAL;
}
DEBUGP(DMM, "%s: MM %s AUTHENTICATION RESPONSE (%s = %s)\n",
- subscr_name(conn->subscr),
+ vlr_subscr_name(conn->vsub),
is_r99 ? "R99" : "GSM", is_r99 ? "res" : "sres",
osmo_hexdump_nospc(res, res_len));
- /* Future: vlr_sub_rx_auth_resp(conn->vsub, is_r99,
- * conn->via_ran == RAN_UTRAN_IU,
- * res, res_len);
- */
-
- if (res_len != 4) {
- LOGP(DMM, LOGL_ERROR,
- "%s: MM AUTHENTICATION RESPONSE:"
- " UMTS authentication not supported\n",
- subscr_name(conn->subscr));
- }
-
- /* Safety check */
- if (!conn->sec_operation) {
- DEBUGP(DMM, "No authentication/cipher operation in progress !!!\n");
- return -EIO;
- }
-
- /* Validate SRES */
- if (memcmp(conn->sec_operation->atuple.vec.sres, res, 4)) {
- int rc;
- gsm_cbfn *cb = conn->sec_operation->cb;
-
- DEBUGPC(DMM, "Invalid (expected %s)\n",
- osmo_hexdump(conn->sec_operation->atuple.vec.sres, 4));
-
- if (cb)
- cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_AUTH_FAILED,
- NULL, conn, conn->sec_operation->cb_data);
-
- rc = gsm48_tx_mm_auth_rej(conn);
- release_security_operation(conn);
- return rc;
- }
-
- DEBUGPC(DMM, "OK\n");
-
- /* Start ciphering */
- return gsm0808_cipher_mode(conn, net->a5_encryption,
- conn->sec_operation->atuple.vec.kc, 8, 0);
+ return vlr_subscr_rx_auth_resp(conn->vsub, is_r99,
+ conn->via_ran == RAN_UTRAN_IU,
+ res, res_len);
}
static int gsm48_rx_mm_auth_fail(struct gsm_subscriber_connection *conn, struct msgb *msg)
@@ -1301,20 +940,11 @@ static int gsm48_rx_mm_auth_fail(struct gsm_subscriber_connection *conn, struct
uint8_t auts_tag;
uint8_t auts_len;
uint8_t *auts;
- int rc;
-
- if (!conn->sec_operation) {
- DEBUGP(DMM, "%s: MM R99 AUTHENTICATION FAILURE:"
- " No authentication/cipher operation in progress\n",
- subscr_name(conn->subscr));
- return -EINVAL;
- }
- if (!conn->subscr) {
+ if (!conn->vsub) {
LOGP(DMM, LOGL_ERROR,
"MM R99 AUTHENTICATION FAILURE: invalid: no subscriber\n");
- gsm48_tx_mm_auth_rej(conn);
- release_security_operation(conn);
+ msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED);
return -EINVAL;
}
@@ -1322,9 +952,8 @@ static int gsm48_rx_mm_auth_fail(struct gsm_subscriber_connection *conn, struct
LOGP(DMM, LOGL_ERROR,
"%s: MM R99 AUTHENTICATION FAILURE:"
" l3 length invalid: %u\n",
- subscr_name(conn->subscr), msgb_l3len(msg));
- gsm48_tx_mm_auth_rej(conn);
- release_security_operation(conn);
+ vlr_subscr_name(conn->vsub), msgb_l3len(msg));
+ msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED);
return -EINVAL;
}
@@ -1333,10 +962,9 @@ static int gsm48_rx_mm_auth_fail(struct gsm_subscriber_connection *conn, struct
if (cause != GSM48_REJECT_SYNCH_FAILURE) {
LOGP(DMM, LOGL_INFO,
"%s: MM R99 AUTHENTICATION FAILURE: cause 0x%0x\n",
- subscr_name(conn->subscr), cause);
- rc = gsm48_tx_mm_auth_rej(conn);
- release_security_operation(conn);
- return rc;
+ vlr_subscr_name(conn->vsub), cause);
+ vlr_subscr_rx_auth_fail(conn->vsub, NULL);
+ return 0;
}
/* This is a Synch Failure procedure, which should pass an AUTS to
@@ -1347,9 +975,8 @@ static int gsm48_rx_mm_auth_fail(struct gsm_subscriber_connection *conn, struct
LOGP(DMM, LOGL_INFO,
"%s: MM R99 AUTHENTICATION FAILURE:"
" invalid Synch Failure: missing AUTS IE\n",
- subscr_name(conn->subscr));
- gsm48_tx_mm_auth_rej(conn);
- release_security_operation(conn);
+ vlr_subscr_name(conn->vsub));
+ msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED);
return -EINVAL;
}
@@ -1364,10 +991,9 @@ static int gsm48_rx_mm_auth_fail(struct gsm_subscriber_connection *conn, struct
" invalid Synch Failure:"
" expected AUTS IE 0x%02x of 14 bytes,"
" got IE 0x%02x of %u bytes\n",
- subscr_name(conn->subscr),
+ vlr_subscr_name(conn->vsub),
GSM48_IE_AUTS, auts_tag, auts_len);
- gsm48_tx_mm_auth_rej(conn);
- release_security_operation(conn);
+ msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED);
return -EINVAL;
}
@@ -1375,9 +1001,8 @@ static int gsm48_rx_mm_auth_fail(struct gsm_subscriber_connection *conn, struct
LOGP(DMM, LOGL_INFO,
"%s: MM R99 AUTHENTICATION FAILURE:"
" invalid Synch Failure msg: message truncated (%u)\n",
- subscr_name(conn->subscr), msgb_l3len(msg));
- gsm48_tx_mm_auth_rej(conn);
- release_security_operation(conn);
+ vlr_subscr_name(conn->vsub), msgb_l3len(msg));
+ msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED);
return -EINVAL;
}
@@ -1385,15 +1010,21 @@ static int gsm48_rx_mm_auth_fail(struct gsm_subscriber_connection *conn, struct
* large enough. */
DEBUGP(DMM, "%s: MM R99 AUTHENTICATION SYNCH (AUTS = %s)\n",
- subscr_name(conn->subscr), osmo_hexdump_nospc(auts, 14));
+ vlr_subscr_name(conn->vsub), osmo_hexdump_nospc(auts, 14));
- /* Future: vlr_sub_rx_auth_fail(conn->vsub, auts); */
+ return vlr_subscr_rx_auth_fail(conn->vsub, auts);
+}
- LOGP(DMM, LOGL_ERROR, "%s: MM R99 AUTHENTICATION not supported\n",
- subscr_name(conn->subscr));
- rc = gsm48_tx_mm_auth_rej(conn);
- release_security_operation(conn);
- return rc;
+static int gsm48_rx_mm_tmsi_reall_compl(struct gsm_subscriber_connection *conn)
+{
+ DEBUGP(DMM, "TMSI Reallocation Completed. Subscriber: %s\n",
+ vlr_subscr_name(conn->vsub));
+ if (!conn->vsub) {
+ LOGP(DMM, LOGL_ERROR,
+ "Rx MM TMSI Reallocation Complete: invalid: no subscriber\n");
+ return -EINVAL;
+ }
+ return vlr_subscr_rx_tmsi_reall_compl(conn->vsub);
}
/* Receive a GSM 04.08 Mobility Management (MM) message */
@@ -1404,7 +1035,6 @@ static int gsm0408_rcv_mm(struct gsm_subscriber_connection *conn, struct msgb *m
switch (gsm48_hdr_msg_type(gh)) {
case GSM48_MT_MM_LOC_UPD_REQUEST:
- DEBUGP(DMM, "LOCATION UPDATING REQUEST: ");
rc = mm_rx_loc_upd_req(conn, msg);
break;
case GSM48_MT_MM_ID_RESP:
@@ -1417,9 +1047,7 @@ static int gsm0408_rcv_mm(struct gsm_subscriber_connection *conn, struct msgb *m
rc = gsm48_rx_mm_status(msg);
break;
case GSM48_MT_MM_TMSI_REALL_COMPL:
- DEBUGP(DMM, "TMSI Reallocation Completed. Subscriber: %s\n",
- subscr_name(conn->subscr));
- loc_updating_success(conn, 1);
+ rc = gsm48_rx_mm_tmsi_reall_compl(conn);
break;
case GSM48_MT_MM_IMSI_DETACH_IND:
rc = gsm48_rx_mm_imsi_detach_ind(conn, msg);
@@ -1442,18 +1070,37 @@ static int gsm0408_rcv_mm(struct gsm_subscriber_connection *conn, struct msgb *m
return rc;
}
+static uint8_t *gsm48_cm2_get_mi(uint8_t *classmark2_lv, unsigned int tot_len)
+{
+ /* Check the size for the classmark */
+ if (tot_len < 1 + *classmark2_lv)
+ return NULL;
+
+ uint8_t *mi_lv = classmark2_lv + *classmark2_lv + 1;
+ if (tot_len < 2 + *classmark2_lv + mi_lv[0])
+ return NULL;
+
+ return mi_lv;
+}
+
/* Receive a PAGING RESPONSE message from the MS */
static int gsm48_rx_rr_pag_resp(struct gsm_subscriber_connection *conn, struct msgb *msg)
{
+ static const enum subscr_conn_from conn_from_paging_resp =
+ SUBSCR_CONN_FROM_PAGING_RESP;
+ struct gsm_network *net = conn->network;
struct gsm48_hdr *gh = msgb_l3(msg);
struct gsm48_pag_resp *resp;
uint8_t *classmark2_lv = gh->data + 1;
+ uint8_t *mi_lv;
uint8_t mi_type;
char mi_string[GSM48_MI_SIZE];
- struct gsm_subscriber *subscr = NULL;
- struct bsc_subscr *bsub;
- uint32_t tmsi;
int rc = 0;
+ struct osmo_location_area_id lai;
+
+ lai.plmn.mcc = conn->network->country_code;
+ lai.plmn.mnc = conn->network->network_code;
+ lai.lac = conn->bts->location_area_code; /* (will be replaced by conn->lac soon) */
resp = (struct gsm48_pag_resp *) &gh->data[0];
gsm48_paging_extract_mi(resp, msgb_l3len(msg) - sizeof(*gh),
@@ -1461,55 +1108,31 @@ static int gsm48_rx_rr_pag_resp(struct gsm_subscriber_connection *conn, struct m
DEBUGP(DRR, "PAGING RESPONSE: MI(%s)=%s\n",
gsm48_mi_type_name(mi_type), mi_string);
- switch (mi_type) {
- case GSM_MI_TYPE_TMSI:
- tmsi = tmsi_from_string(mi_string);
- subscr = subscr_get_by_tmsi(conn->network->subscr_group, tmsi);
- break;
- case GSM_MI_TYPE_IMSI:
- subscr = subscr_get_by_imsi(conn->network->subscr_group,
- mi_string);
- break;
- }
-
- if (!subscr) {
- DEBUGP(DRR, "<- Can't find any subscriber for this ID\n");
- /* FIXME: request id? close channel? */
- return -EINVAL;
- }
-
- if (!conn->subscr) {
- conn->subscr = subscr;
- } else if (conn->subscr != subscr) {
- LOGP(DRR, LOGL_ERROR, "<- Channel already owned by someone else?\n");
- subscr_put(subscr);
- return -EINVAL;
- } else {
- DEBUGP(DRR, "<- Channel already owned by us\n");
- subscr_put(subscr);
- subscr = conn->subscr;
+ mi_lv = gsm48_cm2_get_mi(classmark2_lv, msgb_l3len(msg) - sizeof(*gh));
+ if (!mi_lv) {
+ /* FIXME */
+ return -1;
}
- log_set_context(LOG_CTX_VLR_SUBSCR, subscr);
- DEBUGP(DRR, "<- Channel was requested by %s\n",
- subscr->name && strlen(subscr->name) ? subscr->name : subscr->imsi);
-
- subscr->equipment.classmark2_len = *classmark2_lv;
- memcpy(subscr->equipment.classmark2, classmark2_lv+1, *classmark2_lv);
- db_sync_equipment(&subscr->equipment);
+ rc = msc_create_conn_fsm(conn, mi_string);
+ if (rc)
+ /* logging already happened in msc_create_conn_fsm() */
+ return rc;
- /* TODO MSC split -- creating a BSC subscriber directly from MSC data
- * structures in RAM. At some point the MSC will send a message to the
- * BSC instead. */
- bsub = bsc_subscr_find_or_create_by_imsi(conn->network->bsc_subscribers,
- subscr->imsi);
- bsub->tmsi = subscr->tmsi;
- bsub->lac = subscr->lac;
+ memcpy(conn->classmark.classmark2, classmark2_lv+1, *classmark2_lv);
+ conn->classmark.classmark2_len = *classmark2_lv;
- /* We received a paging */
- conn->expire_timer_stopped = 1;
+ vlr_proc_acc_req(conn->conn_fsm,
+ SUBSCR_CONN_E_ACCEPTED,
+ SUBSCR_CONN_E_CN_CLOSE,
+ (void*)&conn_from_paging_resp,
+ net->vlr, conn,
+ VLR_PR_ARQ_T_PAGING_RESP, mi_lv, &lai,
+ conn->network->authentication_required,
+ conn->network->a5_encryption,
+ classmark_is_r99(&conn->classmark),
+ conn->via_ran == RAN_UTRAN_IU);
- rc = gsm48_handle_paging_resp(conn, msg, bsub);
return rc;
}
@@ -1527,7 +1150,12 @@ static int gsm48_rx_rr_app_info(struct gsm_subscriber_connection *conn, struct m
DEBUGP(DRR, "RX APPLICATION INFO id/flags=0x%02x apdu_len=%u apdu=%s\n",
apdu_id_flags, apdu_len, osmo_hexdump(apdu_data, apdu_len));
+ /* we're not using the app info blob anywhere, so ignore. */
+#if 0
return db_apdu_blob_store(conn->subscr, apdu_id_flags, apdu_len, apdu_data);
+#else
+ return 0;
+#endif
}
/* Receive a GSM 04.08 Radio Resource (RR) message */
@@ -1681,12 +1309,12 @@ static int mncc_recvmsg(struct gsm_network *net, struct gsm_trans *trans,
trans->conn->lchan->ts->trx->bts->nr,
trans->conn->lchan->ts->trx->nr,
trans->conn->lchan->ts->nr, trans->transaction_id,
- (trans->subscr)?(trans->subscr->extension):"-",
+ vlr_subscr_msisdn_or_name(trans->vsub),
get_mncc_name(msg_type));
else
DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) "
"Sending '%s' to MNCC.\n",
- (trans->subscr)?(trans->subscr->extension):"-",
+ vlr_subscr_msisdn_or_name(trans->vsub),
get_mncc_name(msg_type));
else
DEBUGP(DCC, "(bts - trx - ts - ti -- sub -) "
@@ -1752,7 +1380,8 @@ static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event,
/* check all tranactions (without lchan) for subscriber */
switch (event) {
case GSM_PAGING_SUCCEEDED:
- DEBUGP(DCC, "Paging subscr %s succeeded!\n", transt->subscr->extension);
+ DEBUGP(DCC, "Paging subscr %s succeeded!\n",
+ vlr_subscr_msisdn_or_name(transt->vsub));
OSMO_ASSERT(conn);
/* Assign lchan */
transt->conn = conn;
@@ -1762,7 +1391,7 @@ static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event,
case GSM_PAGING_EXPIRED:
case GSM_PAGING_BUSY:
DEBUGP(DCC, "Paging subscr %s expired!\n",
- transt->subscr->extension);
+ vlr_subscr_msisdn_or_name(transt->vsub));
/* Temporarily out of order */
mncc_release_ind(transt->net, transt,
transt->callref,
@@ -2023,7 +1652,7 @@ static int tch_bridge(struct gsm_network *net, struct gsm_mncc_bridge *bridge)
return -EIO;
/* Which subscriber do we want to track trans1 or trans2? */
- log_set_context(LOG_CTX_VLR_SUBSCR, trans1->subscr);
+ log_set_context(LOG_CTX_VLR_SUBSCR, trans1->vsub);
/* through-connect channel */
return tch_map(trans1->conn->lchan, trans2->conn->lchan);
@@ -2044,7 +1673,7 @@ static int tch_recv_mncc(struct gsm_network *net, uint32_t callref, int enable)
if (!trans->conn)
return 0;
- log_set_context(LOG_CTX_VLR_SUBSCR, trans->subscr);
+ log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub);
lchan = trans->conn->lchan;
bts = lchan->ts->trx->bts;
@@ -2247,9 +1876,8 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg)
/* use subscriber as calling party number */
setup.fields |= MNCC_F_CALLING;
- osmo_strlcpy(setup.calling.number, trans->subscr->extension,
- sizeof(setup.calling.number));
- osmo_strlcpy(setup.imsi, trans->subscr->imsi, sizeof(setup.imsi));
+ osmo_strlcpy(setup.calling.number, trans->vsub->msisdn, sizeof(setup.calling.number));
+ osmo_strlcpy(setup.imsi, trans->vsub->imsi, sizeof(setup.imsi));
/* bearer capability */
if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) {
@@ -2298,7 +1926,7 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg)
new_cc_state(trans, GSM_CSTATE_INITIATED);
LOGP(DCC, LOGL_INFO, "Subscriber %s (%s) sends SETUP to %s\n",
- subscr_name(trans->subscr), trans->subscr->extension,
+ vlr_subscr_name(trans->vsub), trans->vsub->msisdn,
setup.called.number);
rate_ctr_inc(&trans->net->msc_ctrs->ctr[MSC_CTR_CALL_MO_SETUP]);
@@ -2335,7 +1963,7 @@ static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg)
}
/* Get free transaction_id */
- trans_id = trans_assign_trans_id(trans->net, trans->subscr,
+ trans_id = trans_assign_trans_id(trans->net, trans->vsub,
GSM48_PDISC_CC, 0);
if (trans_id < 0) {
/* no free transaction ID */
@@ -2426,8 +2054,7 @@ static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg)
}
/* IMSI of called subscriber */
- osmo_strlcpy(call_conf.imsi, trans->subscr->imsi,
- sizeof(call_conf.imsi));
+ osmo_strlcpy(call_conf.imsi, trans->vsub->imsi, sizeof(call_conf.imsi));
new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF);
@@ -2580,9 +2207,8 @@ static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg)
tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
/* use subscriber as connected party number */
connect.fields |= MNCC_F_CONNECTED;
- osmo_strlcpy(connect.connected.number, trans->subscr->extension,
- sizeof(connect.connected.number));
- osmo_strlcpy(connect.imsi, trans->subscr->imsi, sizeof(connect.imsi));
+ osmo_strlcpy(connect.connected.number, trans->vsub->msisdn, sizeof(connect.connected.number));
+ osmo_strlcpy(connect.imsi, trans->vsub->imsi, sizeof(connect.imsi));
/* facility */
if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
@@ -3379,7 +3005,7 @@ static int tch_rtp_create(struct gsm_network *net, uint32_t callref)
mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE);
return -EIO;
}
- log_set_context(LOG_CTX_VLR_SUBSCR, trans->subscr);
+ log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub);
if (!trans->conn) {
LOGP(DMNCC, LOGL_NOTICE, "RTP create for trans without conn\n");
mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE);
@@ -3435,7 +3061,7 @@ static int tch_rtp_connect(struct gsm_network *net, void *arg)
mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT);
return -EIO;
}
- log_set_context(LOG_CTX_VLR_SUBSCR, trans->subscr);
+ log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub);
if (!trans->conn) {
LOGP(DMNCC, LOGL_ERROR, "RTP connect for trans without conn\n");
mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT);
@@ -3612,7 +3238,7 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg)
LOGP(DMNCC, LOGL_ERROR, "TCH frame for non-existing trans\n");
return -EIO;
}
- log_set_context(LOG_CTX_VLR_SUBSCR, trans->subscr);
+ log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub);
if (!trans->conn) {
LOGP(DMNCC, LOGL_NOTICE, "TCH frame for trans without conn\n");
return 0;
@@ -3656,7 +3282,7 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg)
/* Callref unknown */
if (!trans) {
- struct gsm_subscriber *subscr;
+ struct vlr_subscr *vsub;
if (msg_type != MNCC_SETUP_REQ) {
DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) "
@@ -3679,17 +3305,16 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg)
}
/* New transaction due to setup, find subscriber */
if (data->called.number[0])
- subscr = subscr_get_by_extension(net->subscr_group,
- data->called.number);
+ vsub = vlr_subscr_find_by_msisdn(net->vlr,
+ data->called.number);
else
- subscr = subscr_get_by_imsi(net->subscr_group,
- data->imsi);
+ vsub = vlr_subscr_find_by_imsi(net->vlr, data->imsi);
/* update the subscriber we deal with */
- log_set_context(LOG_CTX_VLR_SUBSCR, subscr);
+ log_set_context(LOG_CTX_VLR_SUBSCR, vsub);
/* If subscriber is not found */
- if (!subscr) {
+ if (!vsub) {
DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) "
"Received '%s' from MNCC with "
"unknown subscriber %s\n", data->called.number,
@@ -3700,22 +3325,22 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg)
GSM48_CC_CAUSE_UNASSIGNED_NR);
}
/* If subscriber is not "attached" */
- if (!subscr->lac) {
+ if (!vsub->lac) {
DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) "
"Received '%s' from MNCC with "
"detached subscriber %s\n", data->called.number,
get_mncc_name(msg_type), data->called.number);
- subscr_put(subscr);
+ vlr_subscr_put(vsub);
/* Temporarily out of order */
return mncc_release_ind(net, NULL, data->callref,
GSM48_CAUSE_LOC_PRN_S_LU,
GSM48_CC_CAUSE_DEST_OOO);
}
/* Create transaction */
- trans = trans_alloc(net, subscr, GSM48_PDISC_CC, 0xff, data->callref);
+ trans = trans_alloc(net, vsub, GSM48_PDISC_CC, 0xff, data->callref);
if (!trans) {
DEBUGP(DCC, "No memory for trans.\n");
- subscr_put(subscr);
+ vlr_subscr_put(vsub);
/* Ressource unavailable */
mncc_release_ind(net, NULL, data->callref,
GSM48_CAUSE_LOC_PRN_S_LU,
@@ -3723,7 +3348,7 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg)
return -ENOMEM;
}
/* Find lchan */
- conn = connection_for_subscr(subscr);
+ conn = connection_for_subscr(vsub);
/* If subscriber has no lchan */
if (!conn) {
@@ -3731,15 +3356,15 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg)
llist_for_each_entry(transt, &net->trans_list, entry) {
/* Transaction of our lchan? */
if (transt == trans ||
- transt->subscr != subscr)
+ transt->vsub != vsub)
continue;
DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) "
"Received '%s' from MNCC with "
"unallocated channel, paging already "
"started for lac %d.\n",
data->called.number,
- get_mncc_name(msg_type), subscr->lac);
- subscr_put(subscr);
+ get_mncc_name(msg_type), vsub->lac);
+ vlr_subscr_put(vsub);
trans_free(trans);
return 0;
}
@@ -3747,24 +3372,26 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg)
memcpy(&trans->cc.msg, data, sizeof(struct gsm_mncc));
/* Request a channel */
- trans->paging_request = subscr_request_channel(subscr,
- RSL_CHANNEED_TCH_F, setup_trig_pag_evt,
+ trans->paging_request = subscr_request_channel(
+ vsub,
+ RSL_CHANNEED_TCH_F,
+ setup_trig_pag_evt,
trans);
if (!trans->paging_request) {
LOGP(DCC, LOGL_ERROR, "Failed to allocate paging token.\n");
- subscr_put(subscr);
+ vlr_subscr_put(vsub);
trans_free(trans);
return 0;
}
- subscr_put(subscr);
+ vlr_subscr_put(vsub);
return 0;
}
/* Assign lchan */
- trans->conn = conn;
- subscr_put(subscr);
+ trans->conn = msc_subscr_conn_get(conn);
+ vlr_subscr_put(vsub);
} else {
/* update the subscriber we deal with */
- log_set_context(LOG_CTX_VLR_SUBSCR, trans->subscr);
+ log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub);
}
if (trans->conn)
@@ -3774,7 +3401,7 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg)
if (!conn) {
DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) "
"Received '%s' from MNCC in paging state\n",
- (trans->subscr)?(trans->subscr->extension):"-",
+ vlr_subscr_msisdn_or_name(trans->vsub),
get_mncc_name(msg_type));
mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU,
GSM48_CC_CAUSE_NORM_CALL_CLEAR);
@@ -3791,7 +3418,7 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg)
"Received '%s' from MNCC in state %d (%s)\n",
conn->bts->nr, conn->lchan->ts->trx->nr, conn->lchan->ts->nr,
trans->transaction_id,
- (trans->conn->subscr)?(trans->conn->subscr->extension):"-",
+ vlr_subscr_msisdn_or_name(trans->conn->vsub),
get_mncc_name(msg_type), trans->cc.state,
gsm48_cc_state_name(trans->cc.state));
@@ -3878,8 +3505,8 @@ static int gsm0408_rcv_cc(struct gsm_subscriber_connection *conn, struct msgb *m
return -EINVAL;
}
- if (!conn->subscr) {
- LOGP(DCC, LOGL_ERROR, "Invalid conn, no subscriber\n");
+ if (!conn->vsub) {
+ LOGP(DCC, LOGL_ERROR, "Invalid conn: no subscriber\n");
return -EINVAL;
}
@@ -3889,7 +3516,7 @@ static int gsm0408_rcv_cc(struct gsm_subscriber_connection *conn, struct msgb *m
DEBUGP(DCC, "(bts %d trx %d ts %d ti %x sub %s) "
"Received '%s' from MS in state %d (%s)\n",
conn->bts->nr, conn->lchan->ts->trx->nr, conn->lchan->ts->nr,
- transaction_id, (conn->subscr)?(conn->subscr->extension):"-",
+ transaction_id, vlr_subscr_msisdn_or_name(conn->vsub),
gsm48_cc_msg_name(msg_type), trans?(trans->cc.state):0,
gsm48_cc_state_name(trans?(trans->cc.state):0));
@@ -3898,7 +3525,7 @@ static int gsm0408_rcv_cc(struct gsm_subscriber_connection *conn, struct msgb *m
DEBUGP(DCC, "Unknown transaction ID %x, "
"creating new trans.\n", transaction_id);
/* Create transaction */
- trans = trans_alloc(conn->network, conn->subscr,
+ trans = trans_alloc(conn->network, conn->vsub,
GSM48_PDISC_CC,
transaction_id, new_callref++);
if (!trans) {
@@ -3909,7 +3536,8 @@ static int gsm0408_rcv_cc(struct gsm_subscriber_connection *conn, struct msgb *m
return -ENOMEM;
}
/* Assign transaction */
- trans->conn = conn;
+ trans->conn = msc_subscr_conn_get(conn);
+ cm_service_request_concludes(conn, msg);
}
/* find function for current state and message */
@@ -3922,70 +3550,74 @@ static int gsm0408_rcv_cc(struct gsm_subscriber_connection *conn, struct msgb *m
return 0;
}
- assert(trans->subscr);
+ assert(trans->vsub);
rc = datastatelist[i].rout(trans, msg);
+ msc_subscr_conn_communicating(conn);
return rc;
}
-/* Create a dummy to wait five seconds */
-static void release_anchor(struct gsm_subscriber_connection *conn)
+static bool msg_is_initially_permitted(const struct gsm48_hdr *hdr)
{
- if (!conn->anch_operation)
- return;
+ uint8_t pdisc = gsm48_hdr_pdisc(hdr);
+ uint8_t msg_type = gsm48_hdr_msg_type(hdr);
- osmo_timer_del(&conn->anch_operation->timeout);
- talloc_free(conn->anch_operation);
- conn->anch_operation = NULL;
-}
-
-static void anchor_timeout(void *_data)
-{
- struct gsm_subscriber_connection *con = _data;
+ switch (pdisc) {
+ case GSM48_PDISC_MM:
+ switch (msg_type) {
+ case GSM48_MT_MM_LOC_UPD_REQUEST:
+ case GSM48_MT_MM_CM_SERV_REQ:
+ case GSM48_MT_MM_AUTH_RESP:
+ case GSM48_MT_MM_AUTH_FAIL:
+ case GSM48_MT_MM_ID_RESP:
+ case GSM48_MT_MM_TMSI_REALL_COMPL:
+ case GSM48_MT_MM_IMSI_DETACH_IND:
+ return true;
+ default:
+ break;
+ }
+ break;
+ case GSM48_PDISC_RR:
+ switch (msg_type) {
+ case GSM48_MT_RR_CIPH_M_COMPL:
+ case GSM48_MT_RR_PAG_RESP:
+ return true;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
- release_anchor(con);
- msc_release_connection(con);
+ return false;
}
-int gsm0408_new_conn(struct gsm_subscriber_connection *conn)
+void cm_service_request_concludes(struct gsm_subscriber_connection *conn,
+ struct msgb *msg)
{
- conn->anch_operation = talloc_zero(conn, struct gsm_anchor_operation);
- if (!conn->anch_operation)
- return -1;
-
- osmo_timer_setup(&conn->anch_operation->timeout, anchor_timeout, conn);
- osmo_timer_schedule(&conn->anch_operation->timeout, 5, 0);
- return 0;
-}
-struct gsm_subscriber_connection *msc_subscr_con_allocate(struct gsm_network *network)
-{
- struct gsm_subscriber_connection *conn;
+ /* If a CM Service Request was received before, this is the request the
+ * conn was opened for. No need to wait for further messages. */
- conn = talloc_zero(network, struct gsm_subscriber_connection);
- if (!conn)
- return NULL;
-
- conn->network = network;
- llist_add_tail(&conn->entry, &network->subscr_conns);
- return conn;
-}
-
-void msc_subscr_con_free(struct gsm_subscriber_connection *conn)
-{
- if (!conn)
+ if (!conn->received_cm_service_request)
return;
- if (conn->subscr) {
- subscr_put(conn->subscr);
- conn->subscr = NULL;
- }
+ if (log_check_level(DMM, LOGL_DEBUG)) {
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ uint8_t pdisc = gsm48_hdr_pdisc(gh);
+ uint8_t msg_type = gsm48_hdr_msg_type(gh);
- llist_del(&conn->entry);
- talloc_free(conn);
+ DEBUGP(DMM, "%s pdisc=%d msg_type=0x%02x:"
+ " received_cm_service_request changes to false\n",
+ vlr_subscr_name(conn->vsub),
+ pdisc, msg_type);
+ }
+ conn->received_cm_service_request = false;
}
+
/* Main entry point for GSM 04.08/44.008 Layer 3 data (e.g. from the BSC). */
int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg)
{
@@ -3997,6 +3629,16 @@ int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg)
OSMO_ASSERT(msg);
LOGP(DRLL, LOGL_DEBUG, "Dispatching 04.08 message, pdisc=%d\n", pdisc);
+
+ if (!msc_subscr_conn_is_accepted(conn)
+ && !msg_is_initially_permitted(gh)) {
+ LOGP(DRLL, LOGL_ERROR,
+ "subscr %s: Message not permitted for initial conn:"
+ " pdisc=0x%02x msg_type=0x%02x\n",
+ vlr_subscr_name(conn->vsub), gh->proto_discr, gh->msg_type);
+ return -EACCES;
+ }
+
#if 0
if (silent_call_reroute(conn, msg))
return silent_call_rx(conn, msg);
@@ -4004,7 +3646,6 @@ int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg)
switch (pdisc) {
case GSM48_PDISC_CC:
- release_anchor(conn);
rc = gsm0408_rcv_cc(conn, msg);
break;
case GSM48_PDISC_MM:
@@ -4014,7 +3655,6 @@ int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg)
rc = gsm0408_rcv_rr(conn, msg);
break;
case GSM48_PDISC_SMS:
- release_anchor(conn);
rc = gsm0411_rcv_sms(conn, msg);
break;
case GSM48_PDISC_MM_GPRS:
@@ -4024,7 +3664,6 @@ int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg)
rc = -ENOTSUP;
break;
case GSM48_PDISC_NC_SS:
- release_anchor(conn);
rc = handle_rcv_ussd(conn, msg);
break;
default:
@@ -4037,6 +3676,166 @@ int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg)
return rc;
}
+/***********************************************************************
+ * VLR integration
+ ***********************************************************************/
+
+/* VLR asks us to send an authentication request */
+static int msc_vlr_tx_auth_req(void *msc_conn_ref, struct gsm_auth_tuple *at,
+ bool send_autn)
+{
+ struct gsm_subscriber_connection *conn = msc_conn_ref;
+ return gsm48_tx_mm_auth_req(conn, at->vec.rand,
+ send_autn? at->vec.autn : NULL,
+ at->key_seq);
+}
+
+/* VLR asks us to send an authentication reject */
+static int msc_vlr_tx_auth_rej(void *msc_conn_ref)
+{
+ struct gsm_subscriber_connection *conn = msc_conn_ref;
+ return gsm48_tx_mm_auth_rej(conn);
+}
+
+/* VLR asks us to transmit an Identity Request of given type */
+static int msc_vlr_tx_id_req(void *msc_conn_ref, uint8_t mi_type)
+{
+ struct gsm_subscriber_connection *conn = msc_conn_ref;
+ return mm_tx_identity_req(conn, mi_type);
+}
+
+/* VLR asks us to transmit a Location Update Accept */
+static int msc_vlr_tx_lu_acc(void *msc_conn_ref, uint32_t send_tmsi)
+{
+ struct gsm_subscriber_connection *conn = msc_conn_ref;
+ return gsm0408_loc_upd_acc(conn, send_tmsi);
+}
+
+/* VLR asks us to transmit a Location Update Reject */
+static int msc_vlr_tx_lu_rej(void *msc_conn_ref, uint8_t cause)
+{
+ struct gsm_subscriber_connection *conn = msc_conn_ref;
+ return gsm0408_loc_upd_rej(conn, cause);
+}
+
+/* VLR asks us to transmit a CM Service Accept */
+static int msc_vlr_tx_cm_serv_acc(void *msc_conn_ref)
+{
+ struct gsm_subscriber_connection *conn = msc_conn_ref;
+ return gsm48_tx_mm_serv_ack(conn);
+}
+
+/* VLR asks us to transmit a CM Service Reject */
+static int msc_vlr_tx_cm_serv_rej(void *msc_conn_ref, enum vlr_proc_arq_result result)
+{
+ uint8_t cause;
+ struct gsm_subscriber_connection *conn = msc_conn_ref;
+ conn->received_cm_service_request = false;
+
+ switch (result) {
+ default:
+ case VLR_PR_ARQ_RES_NONE:
+ case VLR_PR_ARQ_RES_SYSTEM_FAILURE:
+ case VLR_PR_ARQ_RES_UNKNOWN_ERROR:
+ cause = GSM48_REJECT_NETWORK_FAILURE;
+ break;
+ case VLR_PR_ARQ_RES_ILLEGAL_SUBSCR:
+ cause = GSM48_REJECT_LOC_NOT_ALLOWED;
+ break;
+ case VLR_PR_ARQ_RES_UNIDENT_SUBSCR:
+ cause = GSM48_REJECT_INVALID_MANDANTORY_INF;
+ break;
+ case VLR_PR_ARQ_RES_ROAMING_NOTALLOWED:
+ cause = GSM48_REJECT_ROAMING_NOT_ALLOWED;
+ break;
+ case VLR_PR_ARQ_RES_ILLEGAL_EQUIP:
+ cause = GSM48_REJECT_ILLEGAL_MS;
+ break;
+ case VLR_PR_ARQ_RES_TIMEOUT:
+ cause = GSM48_REJECT_CONGESTION;
+ break;
+ };
+
+ return gsm48_tx_mm_serv_rej(conn, cause);
+}
+
+/* VLR asks us to start using ciphering */
+static int msc_vlr_set_ciph_mode(void *msc_conn_ref,
+ enum vlr_ciph ciph,
+ bool retrieve_imeisv)
+{
+ struct gsm_subscriber_connection *conn = msc_conn_ref;
+ struct vlr_subscr *vsub;
+ struct gsm_auth_tuple *tuple;
+
+ if (!conn || !conn->vsub) {
+ LOGP(DMM, LOGL_ERROR, "Cannot send Ciphering Mode Command to"
+ " NULL conn/subscriber");
+ return -EINVAL;
+ }
+
+ vsub = conn->vsub;
+ tuple = vsub->last_tuple;
+
+ if (!tuple) {
+ LOGP(DMM, LOGL_ERROR, "subscr %s: Cannot send Ciphering Mode"
+ " Command: no auth tuple available\n",
+ vlr_subscr_name(vsub));
+ return -EINVAL;
+ }
+
+ /* TODO: MSCSPLIT: don't directly push BSC buttons */
+ return gsm0808_cipher_mode(conn, ciph, tuple->vec.kc, 8,
+ retrieve_imeisv);
+}
+
+/* VLR informs us that the subscriber data has somehow been modified */
+static void msc_vlr_subscr_update(struct vlr_subscr *subscr)
+{
+ /* FIXME */
+}
+
+/* VLR informs us that the subscriber has been associated with a conn */
+static void msc_vlr_subscr_assoc(void *msc_conn_ref,
+ struct vlr_subscr *vsub)
+{
+ struct gsm_subscriber_connection *conn = msc_conn_ref;
+ OSMO_ASSERT(!conn->vsub);
+ conn->vsub = vlr_subscr_get(vsub);
+}
+
+/* operations that we need to implement for libvlr */
+static const struct vlr_ops msc_vlr_ops = {
+ .tx_auth_req = msc_vlr_tx_auth_req,
+ .tx_auth_rej = msc_vlr_tx_auth_rej,
+ .tx_id_req = msc_vlr_tx_id_req,
+ .tx_lu_acc = msc_vlr_tx_lu_acc,
+ .tx_lu_rej = msc_vlr_tx_lu_rej,
+ .tx_cm_serv_acc = msc_vlr_tx_cm_serv_acc,
+ .tx_cm_serv_rej = msc_vlr_tx_cm_serv_rej,
+ .set_ciph_mode = msc_vlr_set_ciph_mode,
+ .subscr_update = msc_vlr_subscr_update,
+ .subscr_assoc = msc_vlr_subscr_assoc,
+};
+
+/* Allocate net->vlr so that the VTY may configure the VLR's data structures */
+int msc_vlr_alloc(struct gsm_network *net)
+{
+ net->vlr = vlr_alloc(net, &msc_vlr_ops);
+ if (!net->vlr)
+ return -ENOMEM;
+ net->vlr->user_ctx = net;
+ return 0;
+}
+
+/* Launch the VLR, i.e. its GSUP connection */
+int msc_vlr_start(struct gsm_network *net)
+{
+ OSMO_ASSERT(net->vlr);
+ return vlr_start("MSC", net->vlr, net->gsup_server_addr_str,
+ net->gsup_server_port);
+}
+
/*
* This will be run by the linker when loading the DSO. We use it to
* do system initialization, e.g. registration of signal handlers.
diff --git a/src/libmsc/gsm_04_11.c b/src/libmsc/gsm_04_11.c
index aa2030f80..6ad944b50 100644
--- a/src/libmsc/gsm_04_11.c
+++ b/src/libmsc/gsm_04_11.c
@@ -56,6 +56,8 @@
#include <openbsc/bsc_rll.h>
#include <openbsc/chan_alloc.h>
#include <openbsc/bsc_api.h>
+#include <openbsc/osmo_msc.h>
+#include <openbsc/vlr.h>
#ifdef BUILD_SMPP
#include "smpp_smsc.h"
@@ -74,7 +76,7 @@ void sms_free(struct gsm_sms *sms)
{
/* drop references to subscriber structure */
if (sms->receiver)
- subscr_put(sms->receiver);
+ vlr_subscr_put(sms->receiver);
#ifdef BUILD_SMPP
if (sms->smpp.esme)
smpp_esme_put(sms->smpp.esme);
@@ -83,8 +85,8 @@ void sms_free(struct gsm_sms *sms)
talloc_free(sms);
}
-struct gsm_sms *sms_from_text(struct gsm_subscriber *receiver,
- struct gsm_subscriber *sender,
+struct gsm_sms *sms_from_text(struct vlr_subscr *receiver,
+ struct vlr_subscr *sender,
int dcs, const char *text)
{
struct gsm_sms *sms = sms_alloc();
@@ -92,16 +94,16 @@ struct gsm_sms *sms_from_text(struct gsm_subscriber *receiver,
if (!sms)
return NULL;
- sms->receiver = subscr_get(receiver);
+ sms->receiver = vlr_subscr_get(receiver);
osmo_strlcpy(sms->text, text, sizeof(sms->text));
- osmo_strlcpy(sms->src.addr, sender->extension, sizeof(sms->src.addr));
+ osmo_strlcpy(sms->src.addr, sender->msisdn, sizeof(sms->src.addr));
sms->reply_path_req = 0;
sms->status_rep_req = 0;
sms->ud_hdr_ind = 0;
sms->protocol_id = 0; /* implicit */
sms->data_coding_scheme = dcs;
- osmo_strlcpy(sms->dst.addr, receiver->extension, sizeof(sms->dst.addr));
+ osmo_strlcpy(sms->dst.addr, receiver->msisdn, sizeof(sms->dst.addr));
/* Generate user_data */
sms->user_data_len = gsm_7bit_encode_n(sms->user_data, sizeof(sms->user_data),
sms->text, NULL);
@@ -297,7 +299,7 @@ int sms_route_mt_sms(struct gsm_subscriber_connection *conn, struct msgb *msg,
goto try_local;
if (rc < 0) {
LOGP(DLSMS, LOGL_ERROR, "%s: SMS delivery error: %d.",
- subscr_name(conn->subscr), rc);
+ vlr_subscr_name(conn->vsub), rc);
rc = GSM411_RP_CAUSE_MO_TEMP_FAIL;
/* rc will be logged by gsm411_send_rp_error() */
rate_ctr_inc(&conn->bts->network->msc_ctrs->ctr[
@@ -310,8 +312,8 @@ try_local:
#endif
/* determine gsms->receiver based on dialled number */
- gsms->receiver = subscr_get_by_extension(conn->network->subscr_group,
- gsms->dst.addr);
+ gsms->receiver = vlr_subscr_find_by_msisdn(conn->network->vlr,
+ gsms->dst.addr);
if (!gsms->receiver) {
#ifdef BUILD_SMPP
/* Avoid a second look-up */
@@ -325,7 +327,7 @@ try_local:
rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_SMS_NO_RECEIVER]);
} else if (rc < 0) {
LOGP(DLSMS, LOGL_ERROR, "%s: SMS delivery error: %d.",
- subscr_name(conn->subscr), rc);
+ vlr_subscr_name(conn->vsub), rc);
rc = GSM411_RP_CAUSE_MO_TEMP_FAIL;
/* rc will be logged by gsm411_send_rp_error() */
rate_ctr_inc(&conn->bts->network->msc_ctrs->ctr[
@@ -469,13 +471,12 @@ static int gsm340_rx_tpdu(struct gsm_trans *trans, struct msgb *msg,
}
}
- osmo_strlcpy(gsms->src.addr, conn->subscr->extension,
- sizeof(gsms->src.addr));
+ osmo_strlcpy(gsms->src.addr, conn->vsub->msisdn, sizeof(gsms->src.addr));
LOGP(DLSMS, LOGL_INFO, "RX SMS: Sender: %s, MTI: 0x%02x, VPF: 0x%02x, "
"MR: 0x%02x PID: 0x%02x, DCS: 0x%02x, DA: %s, "
"UserDataLength: 0x%02x, UserData: \"%s\"\n",
- subscr_name(conn->subscr), sms_mti, sms_vpf, gsms->msg_ref,
+ vlr_subscr_name(conn->vsub), sms_mti, sms_vpf, gsms->msg_ref,
gsms->protocol_id, gsms->data_coding_scheme, gsms->dst.addr,
gsms->user_data_len,
sms_alphabet == DCS_7BIT_DEFAULT ? gsms->text :
@@ -634,7 +635,7 @@ static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans,
* the cause and take action depending on it */
LOGP(DLSMS, LOGL_NOTICE, "%s: RX SMS RP-ERROR, cause %d:%d (%s)\n",
- subscr_name(trans->conn->subscr), cause_len, cause,
+ vlr_subscr_name(trans->conn->vsub), cause_len, cause,
get_value_string(gsm411_rp_cause_strs, cause));
if (!sms) {
@@ -804,7 +805,7 @@ int gsm0411_rcv_sms(struct gsm_subscriber_connection *conn,
int new_trans = 0;
int rc = 0;
- if (!conn->subscr)
+ if (!conn->vsub)
return -EIO;
/* FIXME: send some error message */
@@ -824,7 +825,7 @@ int gsm0411_rcv_sms(struct gsm_subscriber_connection *conn,
if (!trans) {
DEBUGP(DLSMS, " -> (new transaction)\n");
- trans = trans_alloc(conn->network, conn->subscr,
+ trans = trans_alloc(conn->network, conn->vsub,
GSM48_PDISC_SMS,
transaction_id, new_callref++);
if (!trans) {
@@ -837,9 +838,10 @@ int gsm0411_rcv_sms(struct gsm_subscriber_connection *conn,
gsm411_smr_init(&trans->sms.smr_inst, 0, 1,
gsm411_rl_recv, gsm411_mn_send);
- trans->conn = conn;
+ trans->conn = msc_subscr_conn_get(conn);
new_trans = 1;
+ cm_service_request_concludes(conn, msg);
}
/* 5.4: For MO, if a CP-DATA is received for a new
@@ -866,6 +868,8 @@ int gsm0411_rcv_sms(struct gsm_subscriber_connection *conn,
}
}
+ msc_subscr_conn_communicating(conn);
+
gsm411_smc_recv(&trans->sms.smc_inst,
(new_trans) ? GSM411_MMSMS_EST_IND : GSM411_MMSMS_DATA_IND,
msg, msg_type);
@@ -886,7 +890,7 @@ int gsm411_send_sms(struct gsm_subscriber_connection *conn, struct gsm_sms *sms)
int rc;
transaction_id =
- trans_assign_trans_id(conn->network, conn->subscr,
+ trans_assign_trans_id(conn->network, conn->vsub,
GSM48_PDISC_SMS, 0);
if (transaction_id == -1) {
LOGP(DLSMS, LOGL_ERROR, "No available transaction ids\n");
@@ -899,7 +903,7 @@ int gsm411_send_sms(struct gsm_subscriber_connection *conn, struct gsm_sms *sms)
DEBUGP(DLSMS, "%s()\n", __func__);
/* FIXME: allocate transaction with message reference */
- trans = trans_alloc(conn->network, conn->subscr,
+ trans = trans_alloc(conn->network, conn->vsub,
GSM48_PDISC_SMS,
transaction_id, new_callref++);
if (!trans) {
@@ -916,7 +920,7 @@ int gsm411_send_sms(struct gsm_subscriber_connection *conn, struct gsm_sms *sms)
gsm411_rl_recv, gsm411_mn_send);
trans->sms.sms = sms;
- trans->conn = conn;
+ trans->conn = msc_subscr_conn_get(conn);
/* Hardcode SMSC Originating Address for now */
data = (uint8_t *)msgb_put(msg, 8);
@@ -994,7 +998,7 @@ static int paging_cb_send_sms(unsigned int hooknum, unsigned int event,
/* high-level function to send a SMS to a given subscriber. The function
* will take care of paging the subscriber, establishing the RLL SAPI3
* connection, etc. */
-int gsm411_send_sms_subscr(struct gsm_subscriber *subscr,
+int gsm411_send_sms_subscr(struct vlr_subscr *vsub,
struct gsm_sms *sms)
{
struct gsm_subscriber_connection *conn;
@@ -1002,18 +1006,18 @@ int gsm411_send_sms_subscr(struct gsm_subscriber *subscr,
/* check if we already have an open lchan to the subscriber.
* if yes, send the SMS this way */
- conn = connection_for_subscr(subscr);
+ conn = connection_for_subscr(vsub);
if (conn) {
LOGP(DLSMS, LOGL_DEBUG, "Sending SMS via already open connection %p to %s\n",
- conn, subscr_name(subscr));
+ conn, vlr_subscr_name(vsub));
return gsm411_send_sms(conn, sms);
}
/* if not, we have to start paging */
LOGP(DLSMS, LOGL_DEBUG, "Sending SMS: no connection open, start paging %s\n",
- subscr_name(subscr));
- res = subscr_request_channel(subscr, RSL_CHANNEED_SDCCH,
- paging_cb_send_sms, sms);
+ vlr_subscr_name(vsub));
+ res = subscr_request_channel(vsub, RSL_CHANNEED_SDCCH,
+ paging_cb_send_sms, sms);
if (!res) {
send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, GSM_PAGING_BUSY);
sms_free(sms);
@@ -1040,6 +1044,7 @@ void _gsm411_sms_trans_free(struct gsm_trans *trans)
}
}
+/* Process incoming SAPI N-REJECT from BSC */
void gsm411_sapi_n_reject(struct gsm_subscriber_connection *conn)
{
struct gsm_network *net;
diff --git a/src/libmsc/gsm_subscriber.c b/src/libmsc/gsm_subscriber.c
index 1a03cf76e..e9b2e0e5d 100644
--- a/src/libmsc/gsm_subscriber.c
+++ b/src/libmsc/gsm_subscriber.c
@@ -39,11 +39,10 @@
#include <openbsc/signal.h>
#include <openbsc/db.h>
#include <openbsc/chan_alloc.h>
+#include <openbsc/vlr.h>
void *tall_sub_req_ctx;
-extern struct llist_head *subscr_bsc_active_subscribers(void);
-
int gsm48_secure_channel(struct gsm_subscriber_connection *conn, int key_seq,
gsm_cbfn *cb, void *cb_data);
@@ -62,31 +61,35 @@ struct subscr_request {
void *param;
};
-static struct gsm_subscriber *get_subscriber(struct gsm_subscriber_group *sgrp,
- int type, const char *ident)
+static struct bsc_subscr *vlr_subscr_to_bsc_sub(struct llist_head *bsc_subscribers,
+ struct vlr_subscr *vsub)
{
- struct gsm_subscriber *subscr = db_get_subscriber(type, ident);
- if (subscr)
- subscr->group = sgrp;
- return subscr;
+ struct bsc_subscr *sub;
+ /* TODO MSC split -- creating a BSC subscriber directly from MSC data
+ * structures in RAM. At some point the MSC will send a message to the
+ * BSC instead. */
+ sub = bsc_subscr_find_or_create_by_imsi(bsc_subscribers, vsub->imsi);
+ sub->tmsi = vsub->tmsi;
+ sub->lac = vsub->lac;
+ return sub;
}
/*
* We got the channel assigned and can now hand this channel
* over to one of our callbacks.
*/
-static int subscr_paging_dispatch(unsigned int hooknum, unsigned int event,
- struct msgb *msg, void *data, void *param)
+int subscr_paging_dispatch(unsigned int hooknum, unsigned int event,
+ struct msgb *msg, void *data, void *param)
{
struct subscr_request *request, *tmp;
struct gsm_subscriber_connection *conn = data;
- struct gsm_subscriber *subscr = param;
+ struct vlr_subscr *vsub = param;
struct paging_signal_data sig_data;
struct bsc_subscr *bsub;
struct gsm_network *net;
- OSMO_ASSERT(subscr && subscr->is_paging);
- net = subscr->group->net;
+ OSMO_ASSERT(vsub && vsub->cs.is_paging);
+ net = vsub->vlr->user_ctx;
/*
* Stop paging on all other BTS. E.g. if this is
@@ -95,18 +98,12 @@ static int subscr_paging_dispatch(unsigned int hooknum, unsigned int event,
* and forget we wanted to page.
*/
- /* TODO MSC split -- creating a BSC subscriber directly from MSC data
- * structures in RAM. At some point the MSC will send a message to the
- * BSC instead. */
- bsub = bsc_subscr_find_or_create_by_imsi(net->bsc_subscribers,
- subscr->imsi);
- bsub->tmsi = subscr->tmsi;
- bsub->lac = subscr->lac;
+ bsub = vlr_subscr_to_bsc_sub(conn->network->bsc_subscribers, vsub);
paging_request_stop(&net->bts_list, NULL, bsub, NULL, NULL);
bsc_subscr_put(bsub);
/* Inform parts of the system we don't know */
- sig_data.subscr = subscr;
+ sig_data.vsub = vsub;
sig_data.bts = conn ? conn->bts : NULL;
sig_data.conn = conn;
sig_data.paging_result = event;
@@ -117,105 +114,52 @@ static int subscr_paging_dispatch(unsigned int hooknum, unsigned int event,
&sig_data
);
- llist_for_each_entry_safe(request, tmp, &subscr->requests, entry) {
+ llist_for_each_entry_safe(request, tmp, &vsub->cs.requests, entry) {
llist_del(&request->entry);
request->cbfn(hooknum, event, msg, data, request->param);
talloc_free(request);
}
/* balanced with the moment we start paging */
- subscr->is_paging = 0;
- subscr_put(subscr);
+ vsub->cs.is_paging = false;
+ vlr_subscr_put(vsub);
return 0;
}
-static int subscr_paging_sec_cb(unsigned int hooknum, unsigned int event,
- struct msgb *msg, void *data, void *param)
-{
- int rc;
-
- switch (event) {
- case GSM_SECURITY_AUTH_FAILED:
- /* Dispatch as paging failure */
- rc = subscr_paging_dispatch(
- GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED,
- msg, data, param);
- break;
-
- case GSM_SECURITY_NOAVAIL:
- case GSM_SECURITY_SUCCEEDED:
- /* Dispatch as paging failure */
- rc = subscr_paging_dispatch(
- GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED,
- msg, data, param);
- break;
-
- default:
- rc = -EINVAL;
- }
-
- return rc;
-}
-
-static int subscr_paging_cb(unsigned int hooknum, unsigned int event,
- struct msgb *msg, void *data, void *param)
-{
- struct gsm_subscriber_connection *conn = data;
- struct gsm48_hdr *gh;
- struct gsm48_pag_resp *pr;
-
- /* Other cases mean problem, dispatch direclty */
- if (event != GSM_PAGING_SUCCEEDED)
- return subscr_paging_dispatch(hooknum, event, msg, data, param);
-
- /* Get paging response */
- gh = msgb_l3(msg);
- pr = (struct gsm48_pag_resp *)gh->data;
-
- /* We _really_ have a channel, secure it now ! */
- return gsm48_secure_channel(conn, pr->key_seq, subscr_paging_sec_cb, param);
-}
-
-struct subscr_request *subscr_request_channel(struct gsm_subscriber *subscr,
- int channel_type, gsm_cbfn *cbfn, void *param)
+struct subscr_request *subscr_request_channel(struct vlr_subscr *vsub,
+ int channel_type,
+ gsm_cbfn *cbfn, void *param)
{
int rc;
struct subscr_request *request;
struct bsc_subscr *bsub;
- struct gsm_network *net = subscr->group->net;
+ struct gsm_network *net = vsub->vlr->user_ctx;
/* Start paging.. we know it is async so we can do it before */
- if (!subscr->is_paging) {
+ if (!vsub->cs.is_paging) {
LOGP(DMM, LOGL_DEBUG, "Subscriber %s not paged yet.\n",
- subscr_name(subscr));
- /* TODO MSC split -- creating a BSC subscriber directly from
- * MSC data structures in RAM. At some point the MSC will send
- * a message to the BSC instead. */
- bsub = bsc_subscr_find_or_create_by_imsi(net->bsc_subscribers,
- subscr->imsi);
- bsub->tmsi = subscr->tmsi;
- bsub->lac = subscr->lac;
- rc = paging_request(net, bsub, channel_type, subscr_paging_cb,
- subscr);
+ vlr_subscr_name(vsub));
+ bsub = vlr_subscr_to_bsc_sub(net->bsc_subscribers, vsub);
+ rc = paging_request(net, bsub, channel_type, NULL, NULL);
bsc_subscr_put(bsub);
if (rc <= 0) {
LOGP(DMM, LOGL_ERROR, "Subscriber %s paging failed: %d\n",
- subscr_name(subscr), rc);
+ vlr_subscr_name(vsub), rc);
return NULL;
}
/* reduced on the first paging callback */
- subscr_get(subscr);
- subscr->is_paging = 1;
+ vlr_subscr_get(vsub);
+ vsub->cs.is_paging = true;
}
/* TODO: Stop paging in case of memory allocation failure */
- request = talloc_zero(subscr, struct subscr_request);
+ request = talloc_zero(vsub, struct subscr_request);
if (!request)
return NULL;
request->cbfn = cbfn;
request->param = param;
- llist_add_tail(&request->entry, &subscr->requests);
+ llist_add_tail(&request->entry, &vsub->cs.requests);
return request;
}
@@ -225,196 +169,13 @@ void subscr_remove_request(struct subscr_request *request)
talloc_free(request);
}
-struct gsm_subscriber *subscr_create_subscriber(struct gsm_subscriber_group *sgrp,
- const char *imsi)
-{
- 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;
-}
-
-struct gsm_subscriber *subscr_get_by_tmsi(struct gsm_subscriber_group *sgrp,
- uint32_t tmsi)
-{
- char tmsi_string[14];
- struct gsm_subscriber *subscr;
-
- /* we might have a record in memory already */
- llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) {
- if (tmsi == subscr->tmsi)
- return subscr_get(subscr);
- }
-
- sprintf(tmsi_string, "%u", tmsi);
- return get_subscriber(sgrp, GSM_SUBSCRIBER_TMSI, tmsi_string);
-}
-
-struct gsm_subscriber *subscr_get_by_imsi(struct gsm_subscriber_group *sgrp,
- const char *imsi)
-{
- struct gsm_subscriber *subscr;
-
- llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) {
- if (strcmp(subscr->imsi, imsi) == 0)
- return subscr_get(subscr);
- }
-
- return get_subscriber(sgrp, GSM_SUBSCRIBER_IMSI, imsi);
-}
-
-struct gsm_subscriber *subscr_get_by_extension(struct gsm_subscriber_group *sgrp,
- const char *ext)
-{
- struct gsm_subscriber *subscr;
-
- llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) {
- if (strcmp(subscr->extension, ext) == 0)
- return subscr_get(subscr);
- }
-
- return get_subscriber(sgrp, GSM_SUBSCRIBER_EXTENSION, ext);
-}
-
-struct gsm_subscriber *subscr_get_by_id(struct gsm_subscriber_group *sgrp,
- unsigned long long id)
-{
- struct gsm_subscriber *subscr;
- char buf[32];
- sprintf(buf, "%llu", id);
-
- llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) {
- if (subscr->id == id)
- return subscr_get(subscr);
- }
-
- return get_subscriber(sgrp, GSM_SUBSCRIBER_ID, buf);
-}
-
-int subscr_update_expire_lu(struct gsm_subscriber *s, struct gsm_bts *bts)
-{
- int rc;
-
- if (!s) {
- LOGP(DMM, LOGL_ERROR, "LU Expiration but NULL subscriber\n");
- return -1;
- }
- if (!bts) {
- LOGP(DMM, LOGL_ERROR, "%s: LU Expiration but NULL bts\n",
- subscr_name(s));
- return -1;
- }
-
- /* Table 10.5.33: The T3212 timeout value field is coded as the
- * binary representation of the timeout value for
- * periodic updating in decihours. Mark the subscriber as
- * inactive if it missed two consecutive location updates.
- * Timeout is twice the t3212 value plus one minute */
-
- /* Is expiration handling enabled? */
- if (bts->si_common.chan_desc.t3212 == 0)
- s->expire_lu = GSM_SUBSCRIBER_NO_EXPIRATION;
- else
- s->expire_lu = time(NULL) +
- (bts->si_common.chan_desc.t3212 * 60 * 6 * 2) + 60;
-
- rc = db_sync_subscriber(s);
- db_subscriber_update(s);
- return rc;
-}
-
-int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason)
-{
- int rc;
-
- /* FIXME: Migrate pending requests from one BSC to another */
- switch (reason) {
- case GSM_SUBSCRIBER_UPDATE_ATTACHED:
- s->group = bts->network->subscr_group;
- /* Indicate "attached to LAC" */
- s->lac = bts->location_area_code;
-
- LOGP(DMM, LOGL_INFO, "Subscriber %s ATTACHED LAC=%u\n",
- subscr_name(s), s->lac);
-
- /*
- * The below will set a new expire_lu but as a side-effect
- * the new lac will be saved in the database.
- */
- rc = subscr_update_expire_lu(s, bts);
- osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_ATTACHED, s);
- break;
- case GSM_SUBSCRIBER_UPDATE_DETACHED:
- /* Only detach if we are currently in this area */
- if (bts->location_area_code == s->lac)
- s->lac = GSM_LAC_RESERVED_DETACHED;
- LOGP(DMM, LOGL_INFO, "Subscriber %s DETACHED\n", subscr_name(s));
- rc = db_sync_subscriber(s);
- db_subscriber_update(s);
- osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_DETACHED, s);
- break;
- default:
- fprintf(stderr, "subscr_update with unknown reason: %d\n",
- reason);
- rc = db_sync_subscriber(s);
- db_subscriber_update(s);
- break;
- };
-
- return rc;
-}
-
-void subscr_update_from_db(struct gsm_subscriber *sub)
-{
- db_subscriber_update(sub);
-}
-
-static void subscr_expire_callback(void *data, long long unsigned int id)
-{
- struct gsm_network *net = data;
- struct gsm_subscriber *s = subscr_get_by_id(net->subscr_group, id);
- struct gsm_subscriber_connection *conn = connection_for_subscr(s);
-
- /*
- * The subscriber is active and the phone stopped the timer. As
- * we don't want to periodically update the database for active
- * subscribers we will just do it when the subscriber was selected
- * for expiration. This way on the next around another subscriber
- * will be selected.
- */
- if (conn && conn->expire_timer_stopped) {
- LOGP(DMM, LOGL_DEBUG, "Not expiring subscriber %s (ID %llu)\n",
- subscr_name(s), id);
- subscr_update_expire_lu(s, conn->bts);
- subscr_put(s);
- return;
- }
-
-
- LOGP(DMM, LOGL_NOTICE, "Expiring inactive subscriber %s (ID %llu)\n",
- subscr_name(s), id);
- s->lac = GSM_LAC_RESERVED_DETACHED;
- db_sync_subscriber(s);
-
- subscr_put(s);
-}
-
-void subscr_expire(struct gsm_subscriber_group *sgrp)
-{
- db_subscriber_expire(sgrp->net, subscr_expire_callback);
-}
-
-struct gsm_subscriber_connection *connection_for_subscr(struct gsm_subscriber *subscr)
+struct gsm_subscriber_connection *connection_for_subscr(struct vlr_subscr *vsub)
{
- /* FIXME: replace this with a backpointer in gsm_subscriber? */
- struct gsm_network *net = subscr->group->net;
+ struct gsm_network *net = vsub->vlr->user_ctx;
struct gsm_subscriber_connection *conn;
llist_for_each_entry(conn, &net->subscr_conns, entry) {
- if (conn->subscr == subscr)
+ if (conn->vsub == vsub)
return conn;
}
diff --git a/src/libmsc/meas_feed.c b/src/libmsc/meas_feed.c
index 3ddcdc39c..1e7b4cd51 100644
--- a/src/libmsc/meas_feed.c
+++ b/src/libmsc/meas_feed.c
@@ -18,6 +18,7 @@
#include <openbsc/gsm_subscriber.h>
#include <openbsc/meas_feed.h>
#include <openbsc/vty.h>
+#include <openbsc/vlr.h>
#include "meas_feed.h"
@@ -35,13 +36,13 @@ static int process_meas_rep(struct gsm_meas_rep *mr)
{
struct msgb *msg;
struct meas_feed_meas *mfm;
- struct gsm_subscriber *subscr;
+ struct vlr_subscr *vsub;
/* ignore measurements as long as we don't know who it is */
- if (!mr->lchan || !mr->lchan->conn || !mr->lchan->conn->subscr)
+ if (!mr->lchan || !mr->lchan->conn || !mr->lchan->conn->vsub)
return 0;
- subscr = mr->lchan->conn->subscr;
+ vsub = mr->lchan->conn->vsub;
msg = msgb_alloc(sizeof(struct meas_feed_meas), "Meas. Feed");
if (!msg)
@@ -53,8 +54,8 @@ static int process_meas_rep(struct gsm_meas_rep *mr)
mfm->hdr.version = MEAS_FEED_VERSION;
/* fill in MEAS_FEED_MEAS specific header */
- osmo_strlcpy(mfm->imsi, subscr->imsi, sizeof(mfm->imsi));
- osmo_strlcpy(mfm->name, subscr->name, sizeof(mfm->name));
+ osmo_strlcpy(mfm->imsi, vsub->imsi, sizeof(mfm->imsi));
+ osmo_strlcpy(mfm->name, vsub->name, sizeof(mfm->name));
osmo_strlcpy(mfm->scenario, g_mfs.scenario, sizeof(mfm->scenario));
/* copy the entire measurement report */
diff --git a/src/libmsc/osmo_msc.c b/src/libmsc/osmo_msc.c
index 2389980d3..95e58182c 100644
--- a/src/libmsc/osmo_msc.c
+++ b/src/libmsc/osmo_msc.c
@@ -25,9 +25,12 @@
#include <openbsc/debug.h>
#include <openbsc/transaction.h>
#include <openbsc/db.h>
+#include <openbsc/vlr.h>
+#include <openbsc/osmo_msc.h>
#include <openbsc/gsm_04_11.h>
+/* Receive a SAPI-N-REJECT from BSC */
static void msc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci)
{
int sapi = dlci & 0x7;
@@ -36,18 +39,66 @@ static void msc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci)
gsm411_sapi_n_reject(conn);
}
-static int msc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause)
+static bool keep_conn(struct gsm_subscriber_connection *conn)
{
- gsm0408_clear_request(conn, cause);
- return 1;
+ /* TODO: what about a silent call? */
+
+ if (!conn->conn_fsm) {
+ DEBUGP(DMM, "No conn_fsm, release conn\n");
+ return false;
+ }
+
+ switch (conn->conn_fsm->state) {
+ case SUBSCR_CONN_S_NEW:
+ case SUBSCR_CONN_S_ACCEPTED:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void subscr_conn_bump(struct gsm_subscriber_connection *conn)
+{
+ if (!conn)
+ return;
+ if (!conn->conn_fsm)
+ return;
+ if (!(conn->conn_fsm->state == SUBSCR_CONN_S_ACCEPTED
+ || conn->conn_fsm->state == SUBSCR_CONN_S_COMMUNICATING))
+ return;
+ osmo_fsm_inst_dispatch(conn->conn_fsm, SUBSCR_CONN_E_BUMP, NULL);
}
+/* Receive a COMPLETE LAYER3 INFO from BSC */
static int msc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg,
uint16_t chosen_channel)
{
- gsm0408_new_conn(conn);
+ /* Ownership of the gsm_subscriber_connection is still a bit mucky
+ * between libbsc and libmsc. In libmsc, we use ref counting, but not
+ * in libbsc. This will become simpler with the MSCSPLIT. */
+
+ /* reserve for the duration of this function */
+ msc_subscr_conn_get(conn);
+
gsm0408_dispatch(conn, msg);
+ if (!keep_conn(conn)) {
+ DEBUGP(DMM, "compl_l3: Discarding conn\n");
+ /* keep the use_count reserved, libbsc will discard. If we
+ * released the ref count and discarded here, libbsc would
+ * double-free. And we will not change bsc_api semantics. */
+ return BSC_API_CONN_POL_REJECT;
+ }
+ DEBUGP(DMM, "compl_l3: Keeping conn\n");
+
+ /* Bump whether the conn wants to be closed */
+ subscr_conn_bump(conn);
+
+ /* If this should be kept, the conn->conn_fsm has placed a use_count */
+ msc_subscr_conn_put(conn);
+ return BSC_API_CONN_POL_ACCEPT;
+
+#if 0
/*
* If this is a silent call we want the channel to remain open as long as
* possible and this is why we accept this connection regardless of any
@@ -55,20 +106,28 @@ static int msc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg
*/
if (conn->silent_call)
return BSC_API_CONN_POL_ACCEPT;
- if (conn->loc_operation || conn->sec_operation || conn->anch_operation)
+ if (conn->sec_operation || conn->anch_operation)
return BSC_API_CONN_POL_ACCEPT;
if (trans_has_conn(conn))
return BSC_API_CONN_POL_ACCEPT;
LOGP(DRR, LOGL_INFO, "MSC Complete L3: Rejecting connection.\n");
return BSC_API_CONN_POL_REJECT;
+#endif
}
+/* Receive a DTAP message from BSC */
static void msc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg)
{
+ msc_subscr_conn_get(conn);
gsm0408_dispatch(conn, msg);
+
+ /* Bump whether the conn wants to be closed */
+ subscr_conn_bump(conn);
+ msc_subscr_conn_put(conn);
}
+/* Receive an ASSIGNMENT COMPLETE from BSC */
static void msc_assign_compl(struct gsm_subscriber_connection *conn,
uint8_t rr_cause, uint8_t chosen_channel,
uint8_t encr_alg_id, uint8_t speec)
@@ -76,58 +135,150 @@ static void msc_assign_compl(struct gsm_subscriber_connection *conn,
LOGP(DRR, LOGL_DEBUG, "MSC assign complete (do nothing).\n");
}
+/* Receive an ASSIGNMENT FAILURE from BSC */
static void msc_assign_fail(struct gsm_subscriber_connection *conn,
uint8_t cause, uint8_t *rr_cause)
{
LOGP(DRR, LOGL_DEBUG, "MSC assign failure (do nothing).\n");
}
+/* Receive a CLASSMARK CHANGE from BSC */
static void msc_classmark_chg(struct gsm_subscriber_connection *conn,
const uint8_t *cm2, uint8_t cm2_len,
const uint8_t *cm3, uint8_t cm3_len)
{
- struct gsm_subscriber *subscr = conn->subscr;
-
- if (subscr) {
- subscr->equipment.classmark2_len = cm2_len;
- memcpy(subscr->equipment.classmark2, cm2, cm2_len);
- if (cm3) {
- subscr->equipment.classmark3_len = cm3_len;
- memcpy(subscr->equipment.classmark3, cm3, cm3_len);
+ if (cm2 && cm2_len) {
+ if (cm2_len > sizeof(conn->classmark.classmark2)) {
+ LOGP(DRR, LOGL_NOTICE, "%s: classmark2 is %u bytes, truncating at %zu bytes\n",
+ vlr_subscr_name(conn->vsub), cm2_len, sizeof(conn->classmark.classmark2));
+ cm2_len = sizeof(conn->classmark.classmark2);
}
- db_sync_equipment(&subscr->equipment);
+ conn->classmark.classmark2_len = cm2_len;
+ memcpy(conn->classmark.classmark2, cm2, cm2_len);
+ }
+ if (cm3 && cm3_len) {
+ if (cm3_len > sizeof(conn->classmark.classmark3)) {
+ LOGP(DRR, LOGL_NOTICE, "%s: classmark3 is %u bytes, truncating at %zu bytes\n",
+ vlr_subscr_name(conn->vsub), cm3_len, sizeof(conn->classmark.classmark3));
+ cm3_len = sizeof(conn->classmark.classmark3);
+ }
+ conn->classmark.classmark3_len = cm3_len;
+ memcpy(conn->classmark.classmark3, cm3, cm3_len);
}
}
+/* Receive a CIPHERING MODE COMPLETE from BSC */
static void msc_ciph_m_compl(struct gsm_subscriber_connection *conn,
struct msgb *msg, uint8_t alg_id)
{
- gsm_cbfn *cb;
-
- DEBUGP(DRR, "CIPHERING MODE COMPLETE\n");
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ uint8_t mi_type;
+ char imeisv[GSM48_MI_SIZE] = "";
+ struct vlr_ciph_result ciph_res = { .cause = VLR_CIPH_REJECT };
+
+ if (!gh) {
+ LOGP(DRR, LOGL_ERROR, "invalid: msgb without l3 header\n");
+ return;
+ }
- /* Safety check */
- if (!conn->sec_operation) {
- DEBUGP(DRR, "No authentication/cipher operation in progress !!!\n");
+ if (!conn) {
+ LOGP(DRR, LOGL_ERROR,
+ "invalid: rx Ciphering Mode Complete on NULL conn\n");
+ return;
+ }
+ if (!conn->vsub) {
+ LOGP(DRR, LOGL_ERROR,
+ "invalid: rx Ciphering Mode Complete for NULL subscr\n");
return;
}
- /* FIXME: check for MI (if any) */
+ DEBUGP(DRR, "%s: CIPHERING MODE COMPLETE\n",
+ vlr_subscr_name(conn->vsub));
- /* Call back whatever was in progress (if anything) ... */
- cb = conn->sec_operation->cb;
- if (cb) {
- cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_SUCCEEDED,
- NULL, conn, conn->sec_operation->cb_data);
+ tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
+ /* bearer capability */
+ if (TLVP_PRESENT(&tp, GSM48_IE_MOBILE_ID)) {
+ mi_type = TLVP_VAL(&tp, GSM48_IE_MOBILE_ID)[0] & GSM_MI_TYPE_MASK;
+ if (mi_type == GSM_MI_TYPE_IMEISV
+ && TLVP_LEN(&tp, GSM48_IE_MOBILE_ID) > 0) {
+ gsm48_mi_to_string(imeisv, sizeof(imeisv),
+ TLVP_VAL(&tp, GSM48_IE_MOBILE_ID),
+ TLVP_LEN(&tp, GSM48_IE_MOBILE_ID));
+ ciph_res.imeisv = imeisv;
+ }
}
- /* Complete the operation */
- release_security_operation(conn);
+ ciph_res.cause = VLR_CIPH_COMPL;
+ vlr_subscr_rx_ciph_res(conn->vsub, &ciph_res);
+}
+
+struct gsm_subscriber_connection *msc_subscr_con_allocate(struct gsm_network *network)
+{
+ struct gsm_subscriber_connection *conn;
+
+ conn = talloc_zero(network, struct gsm_subscriber_connection);
+ if (!conn)
+ return NULL;
+
+ conn->network = network;
+ llist_add_tail(&conn->entry, &network->subscr_conns);
+ return conn;
+}
+
+void msc_subscr_cleanup(struct vlr_subscr *vsub)
+{
+ if (!vsub)
+ return;
+ vsub->lu_fsm = NULL;
+}
+
+void msc_subscr_con_cleanup(struct gsm_subscriber_connection *conn)
+{
+ if (!conn)
+ return;
+
+ if (conn->vsub) {
+ DEBUGP(DRLL, "subscr %s: Freeing subscriber connection\n",
+ vlr_subscr_name(conn->vsub));
+ msc_subscr_cleanup(conn->vsub);
+ vlr_subscr_put(conn->vsub);
+ conn->vsub = NULL;
+ } else
+ DEBUGP(DRLL, "Freeing subscriber connection"
+ " with NULL subscriber\n");
+
+ if (!conn->conn_fsm)
+ return;
+
+ osmo_fsm_inst_term(conn->conn_fsm,
+ (conn->conn_fsm->state == SUBSCR_CONN_S_RELEASED)
+ ? OSMO_FSM_TERM_REGULAR
+ : OSMO_FSM_TERM_ERROR,
+ NULL);
}
+void msc_subscr_con_free(struct gsm_subscriber_connection *conn)
+{
+ if (!conn)
+ return;
+
+ msc_subscr_con_cleanup(conn);
+ llist_del(&conn->entry);
+ talloc_free(conn);
+}
+
+/* Receive a CLEAR REQUEST from BSC */
+static int msc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause)
+{
+ msc_subscr_conn_close(conn, cause);
+ return 1;
+}
+/* MSC-level operations to be called by libbsc in NITB */
static struct bsc_api msc_handler = {
.sapi_n_reject = msc_sapi_n_reject,
.compl_l3 = msc_compl_l3,
@@ -137,41 +288,108 @@ static struct bsc_api msc_handler = {
.assign_fail = msc_assign_fail,
.classmark_chg = msc_classmark_chg,
.cipher_mode_compl = msc_ciph_m_compl,
+ .conn_cleanup = msc_subscr_con_cleanup,
};
struct bsc_api *msc_bsc_api() {
return &msc_handler;
}
-/* lchan release handling */
-void msc_release_connection(struct gsm_subscriber_connection *conn)
+static void msc_subscr_conn_release_all(struct gsm_subscriber_connection *conn, uint32_t cause)
{
- /* skip when we are in release, e.g. due an error */
if (conn->in_release)
return;
+ conn->in_release = true;
+
+ /* If we're closing in a middle of a trans, we need to clean up */
+ trans_conn_closed(conn);
+
+ switch (conn->via_ran) {
+ case RAN_UTRAN_IU:
+ /* future: iu_tx_release(conn->iu.ue_ctx, NULL); */
+ break;
+ case RAN_GERAN_A:
+ /* future: a_iface_tx_clear_cmd(conn); */
+ break;
+ default:
+ LOGP(DMM, LOGL_ERROR, "%s: Unknown RAN type, cannot tx release/clear\n",
+ vlr_subscr_name(conn->vsub));
+ break;
+ }
+}
- /* skip releasing of silent calls as they have no transaction */
- if (conn->silent_call)
+/* If the conn->conn_fsm is still present, dispatch SUBSCR_CONN_E_CN_CLOSE
+ * event to gracefully terminate the connection. If the conn_fsm is already
+ * cleared, call msc_subscr_conn_release_all() to take release actions.
+ * \param cause a GSM_CAUSE_* constant, e.g. GSM_CAUSE_AUTH_FAILED.
+ */
+void msc_subscr_conn_close(struct gsm_subscriber_connection *conn,
+ uint32_t cause)
+{
+ if (!conn)
return;
-
- /* check if there is a pending operation */
- if (conn->loc_operation || conn->sec_operation || conn->anch_operation)
+ if (conn->in_release) {
+ DEBUGP(DMM, "msc_subscr_conn_close(vsub=%s, cause=%u):"
+ " already dispatching release, ignore.\n",
+ vlr_subscr_name(conn->vsub), cause);
return;
-
- if (trans_has_conn(conn))
+ }
+ if (!conn->conn_fsm) {
+ DEBUGP(DMM, "msc_subscr_conn_close(vsub=%s, cause=%u): no conn fsm,"
+ " releasing directly without release event.\n",
+ vlr_subscr_name(conn->vsub), cause);
+ /* In case of an IMSI Detach, we don't have conn_fsm. Release
+ * anyway to ensure a timely Iu Release / BSSMAP Clear. */
+ msc_subscr_conn_release_all(conn, cause);
return;
+ }
+ if (conn->conn_fsm->state == SUBSCR_CONN_S_RELEASED) {
+ DEBUGP(DMM, "msc_subscr_conn_close(vsub=%s, cause=%u):"
+ " conn fsm already releasing, ignore.\n",
+ vlr_subscr_name(conn->vsub), cause);
+ return;
+ }
+ osmo_fsm_inst_dispatch(conn->conn_fsm, SUBSCR_CONN_E_CN_CLOSE, &cause);
+}
- /* no more connections, asking to release the channel */
+/* increment the ref-count. Needs to be called by every user */
+struct gsm_subscriber_connection *
+_msc_subscr_conn_get(struct gsm_subscriber_connection *conn,
+ const char *file, int line)
+{
+ OSMO_ASSERT(conn);
- /*
- * We had stopped the LU expire timer T3212. Now we are about
- * to send the MS back to the idle state and this should lead
- * to restarting the timer. Set the new expiration time.
- */
- if (conn->expire_timer_stopped)
- subscr_update_expire_lu(conn->subscr, conn->bts);
+ if (conn->in_release)
+ return NULL;
- conn->in_release = 1;
- gsm0808_clear(conn);
- msc_subscr_con_free(conn);
+ conn->use_count++;
+ LOGPSRC(DREF, LOGL_DEBUG, file, line,
+ "%s: MSC conn use + 1 == %u\n",
+ vlr_subscr_name(conn->vsub), conn->use_count);
+
+ return conn;
+}
+
+/* decrement the ref-count. Once it reaches zero, we release */
+void _msc_subscr_conn_put(struct gsm_subscriber_connection *conn,
+ const char *file, int line)
+{
+ OSMO_ASSERT(conn);
+
+ if (conn->use_count == 0) {
+ LOGPSRC(DREF, LOGL_ERROR, file, line,
+ "%s: MSC conn use - 1 failed: is already 0\n",
+ vlr_subscr_name(conn->vsub));
+ return;
+ }
+
+ conn->use_count--;
+ LOGPSRC(DREF, LOGL_DEBUG, file, line,
+ "%s: MSC conn use - 1 == %u\n",
+ vlr_subscr_name(conn->vsub), conn->use_count);
+
+ if (conn->use_count == 0) {
+ gsm0808_clear(conn);
+ bsc_subscr_con_free(conn);
+ }
}
diff --git a/src/libmsc/rrlp.c b/src/libmsc/rrlp.c
index e695daac7..cd3da066b 100644
--- a/src/libmsc/rrlp.c
+++ b/src/libmsc/rrlp.c
@@ -65,14 +65,14 @@ static int send_rrlp_req(struct gsm_subscriber_connection *conn)
static int subscr_sig_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
- struct gsm_subscriber *subscr;
+ struct vlr_subscr *vsub;
struct gsm_subscriber_connection *conn;
switch (signal) {
case S_SUBSCR_ATTACHED:
/* A subscriber has attached. */
- subscr = signal_data;
- conn = connection_for_subscr(subscr);
+ vsub = signal_data;
+ conn = connection_for_subscr(vsub);
if (!conn)
break;
send_rrlp_req(conn);
diff --git a/src/libmsc/silent_call.c b/src/libmsc/silent_call.c
index 590d01bbf..6f3fbf264 100644
--- a/src/libmsc/silent_call.c
+++ b/src/libmsc/silent_call.c
@@ -55,6 +55,7 @@ static int paging_cb_silent(unsigned int hooknum, unsigned int event,
DEBUGPC(DLSMS, "success, using Timeslot %u on ARFCN %u\n",
conn->lchan->ts->nr, conn->lchan->ts->trx->arfcn);
conn->silent_call = 1;
+ msc_subscr_conn_get(conn);
/* increment lchan reference count */
osmo_signal_dispatch(SS_SCALL, S_SCALL_SUCCESS, &sigdata);
break;
@@ -121,20 +122,20 @@ int silent_call_reroute(struct gsm_subscriber_connection *conn, struct msgb *msg
/* initiate a silent call with a given subscriber */
-int gsm_silent_call_start(struct gsm_subscriber *subscr, void *data, int type)
+int gsm_silent_call_start(struct vlr_subscr *vsub, void *data, int type)
{
struct subscr_request *req;
- req = subscr_request_channel(subscr, type, paging_cb_silent, data);
+ req = subscr_request_channel(vsub, type, paging_cb_silent, data);
return req != NULL;
}
/* end a silent call with a given subscriber */
-int gsm_silent_call_stop(struct gsm_subscriber *subscr)
+int gsm_silent_call_stop(struct vlr_subscr *vsub)
{
struct gsm_subscriber_connection *conn;
- conn = connection_for_subscr(subscr);
+ conn = connection_for_subscr(vsub);
if (!conn)
return -EINVAL;
@@ -146,7 +147,7 @@ int gsm_silent_call_stop(struct gsm_subscriber *subscr)
conn->lchan->ts->nr, conn->lchan->ts->trx->arfcn);
conn->silent_call = 0;
- msc_release_connection(conn);
+ msc_subscr_conn_put(conn);
return 0;
}
diff --git a/src/libmsc/smpp_openbsc.c b/src/libmsc/smpp_openbsc.c
index f94968a3f..f06eb7d93 100644
--- a/src/libmsc/smpp_openbsc.c
+++ b/src/libmsc/smpp_openbsc.c
@@ -45,31 +45,32 @@
#include <openbsc/transaction.h>
#include <openbsc/gsm_subscriber.h>
#include <openbsc/chan_alloc.h>
+#include <openbsc/vlr.h>
#include "smpp_smsc.h"
-/*! \brief find gsm_subscriber for a given SMPP NPI/TON/Address */
-static struct gsm_subscriber *subscr_by_dst(struct gsm_network *net,
- uint8_t npi, uint8_t ton, const char *addr)
+/*! \brief find vlr_subscr for a given SMPP NPI/TON/Address */
+static struct vlr_subscr *subscr_by_dst(struct gsm_network *net,
+ uint8_t npi, uint8_t ton,
+ const char *addr)
{
- struct gsm_subscriber *subscr = NULL;
+ struct vlr_subscr *vsub = NULL;
switch (npi) {
case NPI_Land_Mobile_E212:
- subscr = subscr_get_by_imsi(net->subscr_group, addr);
+ vsub = vlr_subscr_find_by_imsi(net->vlr, addr);
break;
case NPI_ISDN_E163_E164:
case NPI_Private:
- subscr = subscr_get_by_extension(net->subscr_group, addr);
+ vsub = vlr_subscr_find_by_msisdn(net->vlr, addr);
break;
default:
LOGP(DSMPP, LOGL_NOTICE, "Unsupported NPI: %u\n", npi);
break;
}
- /* tag the context in case we know it */
- log_set_context(LOG_CTX_VLR_SUBSCR, subscr);
- return subscr;
+ log_set_context(LOG_CTX_VLR_SUBSCR, vsub);
+ return vsub;
}
/*! \brief find a TLV with given tag in list of libsmpp34 TLVs */
@@ -88,7 +89,7 @@ static struct tlv_t *find_tlv(struct tlv_t *head, uint16_t tag)
static int submit_to_sms(struct gsm_sms **psms, struct gsm_network *net,
const struct submit_sm_t *submit)
{
- struct gsm_subscriber *dest;
+ struct vlr_subscr *dest;
struct gsm_sms *sms;
struct tlv_t *t;
const uint8_t *sms_msg;
@@ -111,7 +112,7 @@ static int submit_to_sms(struct gsm_sms **psms, struct gsm_network *net,
/* ERROR: we cannot have both! */
LOGP(DLSMS, LOGL_ERROR, "SMPP Cannot have payload in "
"TLV _and_ in the header\n");
- subscr_put(dest);
+ vlr_subscr_put(dest);
return ESME_ROPTPARNOTALLWD;
}
sms_msg = t->value.octet;
@@ -122,7 +123,7 @@ static int submit_to_sms(struct gsm_sms **psms, struct gsm_network *net,
} else {
LOGP(DLSMS, LOGL_ERROR,
"SMPP neither message payload nor valid sm_length.\n");
- subscr_put(dest);
+ vlr_subscr_put(dest);
return ESME_RINVPARLEN;
}
@@ -134,7 +135,7 @@ static int submit_to_sms(struct gsm_sms **psms, struct gsm_network *net,
sms->receiver = dest;
sms->dst.ton = submit->dest_addr_ton;
sms->dst.npi = submit->dest_addr_npi;
- osmo_strlcpy(sms->dst.addr, dest->extension, sizeof(sms->dst.addr));
+ osmo_strlcpy(sms->dst.addr, dest->msisdn, sizeof(sms->dst.addr));
/* fill in the source address */
sms->src.ton = submit->source_addr_ton;
@@ -252,7 +253,7 @@ int handle_smpp_submit(struct osmo_esme *esme, struct submit_sm_t *submit,
return rc;
}
-static void alert_all_esme(struct smsc *smsc, struct gsm_subscriber *subscr,
+static void alert_all_esme(struct smsc *smsc, struct vlr_subscr *vsub,
uint8_t smpp_avail_status)
{
struct osmo_esme *esme;
@@ -265,11 +266,11 @@ static void alert_all_esme(struct smsc *smsc, struct gsm_subscriber *subscr,
if (esme->acl && esme->acl->deliver_src_imsi) {
smpp_tx_alert(esme, TON_Subscriber_Number,
NPI_Land_Mobile_E212,
- subscr->imsi, smpp_avail_status);
+ vsub->imsi, smpp_avail_status);
} else {
smpp_tx_alert(esme, TON_Network_Specific,
NPI_ISDN_E163_E164,
- subscr->extension, smpp_avail_status);
+ vsub->msisdn, smpp_avail_status);
}
}
}
@@ -316,7 +317,7 @@ static int smpp_sms_cb(unsigned int subsys, unsigned int signal,
}
break;
case S_SMS_SMMA:
- if (!sig_sms->trans || !sig_sms->trans->subscr) {
+ if (!sig_sms->trans || !sig_sms->trans->vsub) {
/* SMMA without a subscriber? strange... */
LOGP(DLSMS, LOGL_NOTICE, "SMMA without subscriber?\n");
break;
@@ -325,7 +326,7 @@ static int smpp_sms_cb(unsigned int subsys, unsigned int signal,
/* There's no real 1:1 match for SMMA in SMPP. However,
* an ALERT NOTIFICATION seems to be the most logical
* choice */
- alert_all_esme(smsc, sig_sms->trans->subscr, 0);
+ alert_all_esme(smsc, sig_sms->trans->vsub, 0);
break;
}
@@ -336,7 +337,7 @@ static int smpp_sms_cb(unsigned int subsys, unsigned int signal,
static int smpp_subscr_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
- struct gsm_subscriber *subscr = signal_data;
+ struct vlr_subscr *vsub = signal_data;
struct smsc *smsc = handler_data;
uint8_t smpp_avail_status;
@@ -352,7 +353,7 @@ static int smpp_subscr_cb(unsigned int subsys, unsigned int signal,
return 0;
}
- alert_all_esme(smsc, subscr, smpp_avail_status);
+ alert_all_esme(smsc, vsub, smpp_avail_status);
return 0;
}
@@ -452,12 +453,12 @@ static void append_osmo_tlvs(tlv_t **req_tlv, const struct gsm_lchan *lchan)
dl_meas->full.rx_qual);
}
- if (lchan->conn && lchan->conn->subscr) {
- struct gsm_subscriber *subscr = lchan->conn->subscr;
- size_t imei_len = strlen(subscr->equipment.imei);
+ if (lchan->conn && lchan->conn->vsub) {
+ struct vlr_subscr *vsub = lchan->conn->vsub;
+ size_t imei_len = strlen(vsub->imei);
if (imei_len)
append_tlv(req_tlv, TLVID_osmo_imei,
- (uint8_t *)subscr->equipment.imei, imei_len+1);
+ (uint8_t *)vsub->imei, imei_len+1);
}
}
@@ -496,7 +497,7 @@ static void smpp_cmd_free(struct osmo_smpp_cmd *cmd)
{
osmo_timer_del(&cmd->response_timer);
llist_del(&cmd->list);
- subscr_put(cmd->subscr);
+ vlr_subscr_put(cmd->vsub);
sms_free(cmd->sms);
talloc_free(cmd);
}
@@ -514,7 +515,7 @@ void smpp_cmd_ack(struct osmo_smpp_cmd *cmd)
struct gsm_subscriber_connection *conn;
struct gsm_trans *trans;
- conn = connection_for_subscr(cmd->subscr);
+ conn = connection_for_subscr(cmd->vsub);
if (!conn) {
LOGP(DSMPP, LOGL_ERROR, "No connection to subscriber anymore\n");
return;
@@ -538,7 +539,7 @@ void smpp_cmd_err(struct osmo_smpp_cmd *cmd, uint32_t status)
struct gsm_trans *trans;
int gsm411_cause;
- conn = connection_for_subscr(cmd->subscr);
+ conn = connection_for_subscr(cmd->vsub);
if (!conn) {
LOGP(DSMPP, LOGL_ERROR, "No connection to subscriber anymore\n");
return;
@@ -566,7 +567,7 @@ static void smpp_deliver_sm_cb(void *data)
}
static int smpp_cmd_enqueue(struct osmo_esme *esme,
- struct gsm_subscriber *subscr, struct gsm_sms *sms,
+ struct vlr_subscr *vsub, struct gsm_sms *sms,
uint32_t sequence_number, bool *deferred)
{
struct osmo_smpp_cmd *cmd;
@@ -577,7 +578,7 @@ static int smpp_cmd_enqueue(struct osmo_esme *esme,
cmd->sequence_nr = sequence_number;
cmd->sms = sms;
- cmd->subscr = subscr_get(subscr);
+ cmd->vsub = vlr_subscr_get(vsub);
/* FIXME: No predefined value for this response_timer as specified by
* SMPP 3.4 specs, section 7.2. Make this configurable? Don't forget
@@ -623,13 +624,13 @@ static int deliver_to_esme(struct osmo_esme *esme, struct gsm_sms *sms,
deliver.source_addr_npi = NPI_Land_Mobile_E212;
snprintf((char *)deliver.source_addr,
sizeof(deliver.source_addr), "%s",
- conn->subscr->imsi);
+ conn->vsub->imsi);
} else {
deliver.source_addr_ton = TON_Network_Specific;
deliver.source_addr_npi = NPI_ISDN_E163_E164;
snprintf((char *)deliver.source_addr,
sizeof(deliver.source_addr), "%s",
- conn->subscr->extension);
+ conn->vsub->msisdn);
}
deliver.dest_addr_ton = sms->dst.ton;
@@ -686,7 +687,7 @@ static int deliver_to_esme(struct osmo_esme *esme, struct gsm_sms *sms,
if (ret < 0)
return ret;
- return smpp_cmd_enqueue(esme, conn->subscr, sms,
+ return smpp_cmd_enqueue(esme, conn->vsub, sms,
deliver.sequence_number, deferred);
}
diff --git a/src/libmsc/smpp_smsc.h b/src/libmsc/smpp_smsc.h
index d8e82e421..4bee59b6b 100644
--- a/src/libmsc/smpp_smsc.h
+++ b/src/libmsc/smpp_smsc.h
@@ -88,7 +88,7 @@ struct osmo_smpp_route {
struct osmo_smpp_cmd {
struct llist_head list;
- struct gsm_subscriber *subscr;
+ struct vlr_subscr *vsub;
struct gsm_sms *sms;
uint32_t sequence_nr;
struct osmo_timer_list response_timer;
diff --git a/src/libmsc/sms_queue.c b/src/libmsc/sms_queue.c
index dc7f6e8c6..fe7a608be 100644
--- a/src/libmsc/sms_queue.c
+++ b/src/libmsc/sms_queue.c
@@ -28,6 +28,8 @@
* things up by collecting data from other parts of the system.
*/
+#include <limits.h>
+
#include <openbsc/sms_queue.h>
#include <openbsc/chan_alloc.h>
#include <openbsc/db.h>
@@ -36,6 +38,7 @@
#include <openbsc/gsm_04_11.h>
#include <openbsc/gsm_subscriber.h>
#include <openbsc/signal.h>
+#include <openbsc/vlr.h>
#include <osmocom/core/talloc.h>
@@ -47,7 +50,7 @@
struct gsm_sms_pending {
struct llist_head entry;
- struct gsm_subscriber *subscr;
+ struct vlr_subscr *vsub;
unsigned long long sms_id;
int failed_attempts;
int resend;
@@ -62,7 +65,8 @@ struct gsm_sms_queue {
int pending;
struct llist_head pending_sms;
- unsigned long long last_subscr_id;
+
+ char last_msisdn[GSM_EXTENSION_LENGTH+1];
};
static int sms_subscr_cb(unsigned int, unsigned int, void *, void *);
@@ -88,12 +92,12 @@ static int sms_is_in_pending(struct gsm_sms_queue *smsq, struct gsm_sms *sms)
static struct gsm_sms_pending *sms_subscriber_find_pending(
struct gsm_sms_queue *smsq,
- struct gsm_subscriber *subscr)
+ struct vlr_subscr *vsub)
{
struct gsm_sms_pending *pending;
llist_for_each_entry(pending, &smsq->pending_sms, entry) {
- if (pending->subscr == subscr)
+ if (pending->vsub == vsub)
return pending;
}
@@ -101,9 +105,9 @@ static struct gsm_sms_pending *sms_subscriber_find_pending(
}
static int sms_subscriber_is_pending(struct gsm_sms_queue *smsq,
- struct gsm_subscriber *subscr)
+ struct vlr_subscr *vsub)
{
- return sms_subscriber_find_pending(smsq, subscr) != NULL;
+ return sms_subscriber_find_pending(smsq, vsub) != NULL;
}
static struct gsm_sms_pending *sms_pending_from(struct gsm_sms_queue *smsq,
@@ -115,27 +119,28 @@ static struct gsm_sms_pending *sms_pending_from(struct gsm_sms_queue *smsq,
if (!pending)
return NULL;
- pending->subscr = subscr_get(sms->receiver);
+ pending->vsub = vlr_subscr_get(sms->receiver);
pending->sms_id = sms->id;
return pending;
}
static void sms_pending_free(struct gsm_sms_pending *pending)
{
- subscr_put(pending->subscr);
+ vlr_subscr_put(pending->vsub);
llist_del(&pending->entry);
talloc_free(pending);
}
static void sms_pending_resend(struct gsm_sms_pending *pending)
{
+ struct gsm_network *net = pending->vsub->vlr->user_ctx;
struct gsm_sms_queue *smsq;
LOGP(DLSMS, LOGL_DEBUG,
"Scheduling resend of SMS %llu.\n", pending->sms_id);
pending->resend = 1;
- smsq = pending->subscr->group->net->sms_queue;
+ smsq = net->sms_queue;
if (osmo_timer_pending(&smsq->resend_pending))
return;
@@ -144,12 +149,13 @@ static void sms_pending_resend(struct gsm_sms_pending *pending)
static void sms_pending_failed(struct gsm_sms_pending *pending, int paging_error)
{
+ struct gsm_network *net = pending->vsub->vlr->user_ctx;
struct gsm_sms_queue *smsq;
LOGP(DLSMS, LOGL_NOTICE, "Sending SMS %llu failed %d times.\n",
pending->sms_id, pending->failed_attempts);
- smsq = pending->subscr->group->net->sms_queue;
+ smsq = net->sms_queue;
if (++pending->failed_attempts < smsq->max_fail)
return sms_pending_resend(pending);
@@ -186,23 +192,49 @@ static void sms_resend_pending(void *_data)
}
}
-static struct gsm_sms *take_next_sms(struct gsm_sms_queue *smsq)
+/* Find the next pending SMS by cycling through the recipients. We could also
+ * cycle through the pending SMS, but that might cause us to keep trying to
+ * send SMS to the same few subscribers repeatedly while not servicing other
+ * subscribers for a long time. By walking the list of recipient MSISDNs, we
+ * ensure that all subscribers get their fair time to receive SMS. */
+struct gsm_sms *smsq_take_next_sms(struct gsm_network *net,
+ char *last_msisdn,
+ size_t last_msisdn_buflen)
{
struct gsm_sms *sms;
+ int wrapped = 0;
+ int sanity = 100;
+ char started_with_msisdn[last_msisdn_buflen];
+
+ osmo_strlcpy(started_with_msisdn, last_msisdn,
+ sizeof(started_with_msisdn));
+
+ while (wrapped < 2 && (--sanity)) {
+ /* If we wrapped around and passed the first msisdn, we're
+ * through the entire SMS DB; end it. */
+ if (wrapped && strcmp(last_msisdn, started_with_msisdn) >= 0)
+ break;
+
+ sms = db_sms_get_next_unsent_rr_msisdn(net, last_msisdn, 9);
+ if (!sms) {
+ last_msisdn[0] = '\0';
+ wrapped ++;
+ continue;
+ }
+
+ /* Whatever happens, next time around service another recipient
+ */
+ osmo_strlcpy(last_msisdn, sms->dst.addr, last_msisdn_buflen);
+
+ /* Is the subscriber attached? If not, go to next SMS */
+ if (!sms->receiver || !sms->receiver->lu_complete)
+ continue;
- sms = db_sms_get_unsent_by_subscr(smsq->network, smsq->last_subscr_id, 10);
- if (sms) {
- smsq->last_subscr_id = sms->receiver->id + 1;
return sms;
}
- /* need to wrap around */
- smsq->last_subscr_id = 0;
- sms = db_sms_get_unsent_by_subscr(smsq->network,
- smsq->last_subscr_id, 10);
- if (sms)
- smsq->last_subscr_id = sms->receiver->id + 1;
- return sms;
+ DEBUGP(DLSMS, "SMS queue: no SMS to be sent\n");
+ return NULL;
}
/**
@@ -224,7 +256,8 @@ static void sms_submit_pending(void *_data)
struct gsm_sms *sms;
- sms = take_next_sms(smsq);
+ sms = smsq_take_next_sms(smsq->network, smsq->last_msisdn,
+ sizeof(smsq->last_msisdn));
if (!sms) {
LOGP(DLSMS, LOGL_DEBUG, "Sending SMS done (%d attempted)\n",
attempted);
@@ -289,21 +322,22 @@ static void sms_submit_pending(void *_data)
/**
* Send the next SMS or trigger the queue
*/
-static void sms_send_next(struct gsm_subscriber *subscr)
+static void sms_send_next(struct vlr_subscr *vsub)
{
- struct gsm_sms_queue *smsq = subscr->group->net->sms_queue;
+ struct gsm_network *net = vsub->vlr->user_ctx;
+ struct gsm_sms_queue *smsq = net->sms_queue;
struct gsm_sms_pending *pending;
struct gsm_sms *sms;
/* the subscriber should not be in the queue */
- OSMO_ASSERT(!sms_subscriber_is_pending(smsq, subscr));
+ OSMO_ASSERT(!sms_subscriber_is_pending(smsq, vsub));
/* check for more messages for this subscriber */
- sms = db_sms_get_unsent_for_subscr(subscr);
+ sms = db_sms_get_unsent_for_subscr(vsub, UINT_MAX);
if (!sms)
goto no_pending_sms;
- /* No sms should be scheduled right now */
+ /* The sms should not be scheduled right now */
OSMO_ASSERT(!sms_is_in_pending(smsq, sms));
/* Remember that we deliver this SMS and send it */
@@ -322,7 +356,7 @@ static void sms_send_next(struct gsm_subscriber *subscr)
no_pending_sms:
/* Try to send the SMS to avoid the queue being stuck */
- sms_submit_pending(subscr->group->net->sms_queue);
+ sms_submit_pending(net->sms_queue);
}
/*
@@ -362,7 +396,7 @@ int sms_queue_start(struct gsm_network *network, int max_pending)
return 0;
}
-static int sub_ready_for_sm(struct gsm_network *net, struct gsm_subscriber *subscr)
+static int sub_ready_for_sm(struct gsm_network *net, struct vlr_subscr *vsub)
{
struct gsm_sms *sms;
struct gsm_sms_pending *pending;
@@ -383,20 +417,20 @@ static int sub_ready_for_sm(struct gsm_network *net, struct gsm_subscriber *subs
*/
/* check if we have pending requests */
- pending = sms_subscriber_find_pending(net->sms_queue, subscr);
+ pending = sms_subscriber_find_pending(net->sms_queue, vsub);
if (pending) {
LOGP(DMSC, LOGL_NOTICE,
"Pending paging while subscriber %llu attached.\n",
- subscr->id);
+ vsub->id);
return 0;
}
- conn = connection_for_subscr(subscr);
+ conn = connection_for_subscr(vsub);
if (!conn)
return -1;
/* Now try to deliver any pending SMS to this sub */
- sms = db_sms_get_unsent_for_subscr(subscr);
+ sms = db_sms_get_unsent_for_subscr(vsub, UINT_MAX);
if (!sms)
return -1;
gsm411_send_sms(conn, sms);
@@ -406,13 +440,13 @@ static int sub_ready_for_sm(struct gsm_network *net, struct gsm_subscriber *subs
static int sms_subscr_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
- struct gsm_subscriber *subscr = signal_data;
+ struct vlr_subscr *vsub = signal_data;
if (signal != S_SUBSCR_ATTACHED)
return 0;
/* this is readyForSM */
- return sub_ready_for_sm(handler_data, subscr);
+ return sub_ready_for_sm(handler_data, vsub);
}
static int sms_sms_cb(unsigned int subsys, unsigned int signal,
@@ -421,7 +455,7 @@ static int sms_sms_cb(unsigned int subsys, unsigned int signal,
struct gsm_network *network = handler_data;
struct sms_signal_data *sig_sms = signal_data;
struct gsm_sms_pending *pending;
- struct gsm_subscriber *subscr;
+ struct vlr_subscr *vsub;
/* We got a new SMS and maybe should launch the queue again. */
if (signal == S_SMS_SUBMITTED || signal == S_SMS_SMMA) {
@@ -447,11 +481,11 @@ static int sms_sms_cb(unsigned int subsys, unsigned int signal,
case S_SMS_DELIVERED:
/* Remember the subscriber and clear the pending entry */
network->sms_queue->pending -= 1;
- subscr = subscr_get(pending->subscr);
+ vsub = vlr_subscr_get(pending->vsub);
sms_pending_free(pending);
/* Attempt to send another SMS to this subscriber */
- sms_send_next(subscr);
- subscr_put(subscr);
+ sms_send_next(vsub);
+ vlr_subscr_put(vsub);
break;
case S_SMS_MEM_EXCEEDED:
network->sms_queue->pending -= 1;
@@ -508,7 +542,7 @@ int sms_queue_stats(struct gsm_sms_queue *smsq, struct vty *vty)
llist_for_each_entry(pending, &smsq->pending_sms, entry)
vty_out(vty, " SMS Pending for Subscriber: %llu SMS: %llu Failed: %d.%s",
- pending->subscr->id, pending->sms_id,
+ pending->vsub->id, pending->sms_id,
pending->failed_attempts, VTY_NEWLINE);
return 0;
}
@@ -535,7 +569,7 @@ int sms_queue_clear(struct gsm_sms_queue *smsq)
llist_for_each_entry_safe(pending, tmp, &smsq->pending_sms, entry) {
LOGP(DLSMS, LOGL_NOTICE,
- "SMSqueue clearing for sub %llu\n", pending->subscr->id);
+ "SMSqueue clearing for sub %llu\n", pending->vsub->id);
sms_pending_free(pending);
}
diff --git a/src/libmsc/subscr_conn.c b/src/libmsc/subscr_conn.c
index 91ffe4069..b28a51128 100644
--- a/src/libmsc/subscr_conn.c
+++ b/src/libmsc/subscr_conn.c
@@ -23,19 +23,24 @@
#include <osmocom/core/logging.h>
#include <osmocom/core/fsm.h>
+#include <osmocom/core/signal.h>
#include <openbsc/osmo_msc.h>
#include <openbsc/vlr.h>
#include <openbsc/debug.h>
#include <openbsc/transaction.h>
+#include <openbsc/signal.h>
+
+#define SUBSCR_CONN_TIMEOUT 5 /* seconds */
static const struct value_string subscr_conn_fsm_event_names[] = {
OSMO_VALUE_STRING(SUBSCR_CONN_E_INVALID),
+ OSMO_VALUE_STRING(SUBSCR_CONN_E_START),
OSMO_VALUE_STRING(SUBSCR_CONN_E_ACCEPTED),
+ OSMO_VALUE_STRING(SUBSCR_CONN_E_COMMUNICATING),
OSMO_VALUE_STRING(SUBSCR_CONN_E_BUMP),
OSMO_VALUE_STRING(SUBSCR_CONN_E_MO_CLOSE),
OSMO_VALUE_STRING(SUBSCR_CONN_E_CN_CLOSE),
- OSMO_VALUE_STRING(SUBSCR_CONN_E_CLOSE_CONF),
{ 0, NULL }
};
@@ -50,14 +55,21 @@ const struct value_string subscr_conn_from_names[] = {
static void paging_resp(struct gsm_subscriber_connection *conn,
enum gsm_paging_event pe)
{
- subscr_paging_dispatch(GSM_HOOK_RR_PAGING, pe, NULL, conn, conn->subscr);
+ subscr_paging_dispatch(GSM_HOOK_RR_PAGING, pe, NULL, conn, conn->vsub);
+}
+
+void subscr_conn_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ OSMO_ASSERT(event == SUBSCR_CONN_E_START);
+ osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_NEW,
+ SUBSCR_CONN_TIMEOUT, 0);
}
void subscr_conn_fsm_new(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_subscriber_connection *conn = fi->priv;
enum subscr_conn_from from = SUBSCR_CONN_FROM_INVALID;
- enum gsm_paging_event pe;
+ bool success;
if (data) {
from = *(enum subscr_conn_from*)data;
@@ -67,12 +79,12 @@ void subscr_conn_fsm_new(struct osmo_fsm_inst *fi, uint32_t event, void *data)
/* If accepted, transition the state, all other cases mean failure. */
switch (event) {
case SUBSCR_CONN_E_ACCEPTED:
- osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_ACCEPTED, 0, 0);
+ osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_ACCEPTED,
+ SUBSCR_CONN_TIMEOUT, 0);
break;
case SUBSCR_CONN_E_MO_CLOSE:
case SUBSCR_CONN_E_CN_CLOSE:
- case SUBSCR_CONN_E_CLOSE_CONF:
break;
default:
@@ -81,23 +93,27 @@ void subscr_conn_fsm_new(struct osmo_fsm_inst *fi, uint32_t event, void *data)
break;
}
- /* if appropriate, signal paging success or failure */
- if (from == SUBSCR_CONN_FROM_PAGING_RESP) {
- pe = (fi->state == SUBSCR_CONN_S_ACCEPTED)?
- GSM_PAGING_SUCCEEDED : GSM_PAGING_EXPIRED;
- paging_resp(conn, pe);
- }
+ success = (fi->state == SUBSCR_CONN_S_ACCEPTED);
+
+ if (from == SUBSCR_CONN_FROM_LU)
+ rate_ctr_inc(&conn->network->msc_ctrs->ctr[
+ success ? MSC_CTR_LOC_UPDATE_COMPLETED
+ : MSC_CTR_LOC_UPDATE_FAILED]);
+
+ /* signal paging success or failure in case this was a paging */
+ if (from == SUBSCR_CONN_FROM_PAGING_RESP)
+ paging_resp(conn,
+ success ? GSM_PAGING_SUCCEEDED
+ : GSM_PAGING_EXPIRED);
/* On failure, discard the conn */
- if (fi->state != SUBSCR_CONN_S_ACCEPTED) {
+ if (!success) {
/* TODO: on MO_CLOSE or CN_CLOSE, first go to RELEASING and
* await BSC confirmation? */
osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
return;
}
- /* On success, handle pending requests and/or close conn */
-
if (from == SUBSCR_CONN_FROM_CM_SERVICE_REQ) {
conn->received_cm_service_request = true;
LOGPFSM(fi, "received_cm_service_request = true\n");
@@ -106,33 +122,6 @@ void subscr_conn_fsm_new(struct osmo_fsm_inst *fi, uint32_t event, void *data)
osmo_fsm_inst_dispatch(fi, SUBSCR_CONN_E_BUMP, data);
}
-#if 0
- case SUBSCR_CONN_E_PARQ_SUCCESS:
- osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_ACCEPTED, 0, 0);
- accept_conn = true;
- /* fall through */
- case SUBSCR_CONN_E_PARQ_FAILURE:
- parq_type = data ? *(enum vlr_parq_type*)data : VLR_PR_ARQ_T_INVALID;
- switch (parq_type) {
-
- case VLR_PR_ARQ_T_CM_SERV_REQ:
- accept_conn = handle_cm_serv_result(fi, accept_conn);
- break;
-
- case VLR_PR_ARQ_T_PAGING_RESP:
- accept_conn = handle_paging_result(fi, accept_conn);
- break;
-
- default:
- LOGPFSML(fi, LOGL_ERROR,
- "Invalid VLR Process Access Request type"
- " %d\n", parq_type);
- accept_conn = false;
- break;
- }
- break;
-#endif
-
static void subscr_conn_fsm_bump(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_subscriber_connection *conn = fi->priv;
@@ -143,8 +132,7 @@ static void subscr_conn_fsm_bump(struct osmo_fsm_inst *fi, uint32_t event, void
if (conn->received_cm_service_request)
return;
- /* is this needed? */
- if (conn->subscr && !llist_empty(&conn->subscr->requests))
+ if (conn->vsub && !llist_empty(&conn->vsub->cs.requests))
return;
if (trans_has_conn(conn))
@@ -153,9 +141,19 @@ static void subscr_conn_fsm_bump(struct osmo_fsm_inst *fi, uint32_t event, void
osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
}
+static void subscr_conn_fsm_accepted_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_ATTACHED, conn->vsub);
+}
+
static void subscr_conn_fsm_accepted(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
+ case SUBSCR_CONN_E_COMMUNICATING:
+ osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_COMMUNICATING, 0, 0);
+ return;
+
case SUBSCR_CONN_E_BUMP:
subscr_conn_fsm_bump(fi, event, data);
return;
@@ -169,34 +167,68 @@ static void subscr_conn_fsm_accepted(struct osmo_fsm_inst *fi, uint32_t event, v
osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
}
-static void subscr_conn_fsm_release(struct osmo_fsm_inst *fi, uint32_t prev_state)
+static void subscr_conn_fsm_communicating(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
- struct gsm_subscriber_connection *conn = fi->priv;
- if (!conn)
+ switch (event) {
+ case SUBSCR_CONN_E_COMMUNICATING:
+ /* no-op */
return;
- /* temporary hack, see owned_by_msc */
- if (!conn->owned_by_msc) {
- DEBUGP(DMM, "%s leaving bsc_subscr_con_free() to bsc_api.c, owned_by_msc = false\n",
- subscr_name(conn->subscr));
+ case SUBSCR_CONN_E_BUMP:
+ subscr_conn_fsm_bump(fi, event, data);
return;
+
+ default:
+ break;
}
+ /* Whatever unexpected happens in the accepted state, it means release.
+ * Even if an unexpected event is passed, the safest thing to do is
+ * discard the conn. We don't expect another SUBSCR_CONN_E_ACCEPTED. */
+ osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
+}
+
+static void subscr_conn_fsm_cleanup(struct osmo_fsm_inst *fi,
+ enum osmo_fsm_term_cause cause)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ fi->priv = NULL;
+
+ if (!conn)
+ return;
+
+ conn->conn_fsm = NULL;
+ msc_subscr_conn_close(conn, cause);
+ msc_subscr_conn_put(conn);
+}
+
+int subscr_conn_fsm_timeout(struct osmo_fsm_inst *fi)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ if (conn)
+ vlr_subscr_conn_timeout(conn->vsub);
+ osmo_fsm_inst_dispatch(fi, SUBSCR_CONN_E_CN_CLOSE, NULL);
+ return 0;
+}
- DEBUGP(DMM, "%s calling bsc_subscr_con_free(), owned_by_msc = true\n",
- subscr_name(conn->subscr));
- gsm0808_clear(conn);
- bsc_subscr_con_free(conn);
+static void subscr_conn_fsm_release(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
}
#define S(x) (1 << (x))
static const struct osmo_fsm_state subscr_conn_fsm_states[] = {
+ [SUBSCR_CONN_S_INIT] = {
+ .name = OSMO_STRINGIFY(SUBSCR_CONN_S_INIT),
+ .in_event_mask = S(SUBSCR_CONN_E_START),
+ .out_state_mask = S(SUBSCR_CONN_S_NEW),
+ .action = subscr_conn_fsm_init,
+ },
[SUBSCR_CONN_S_NEW] = {
.name = OSMO_STRINGIFY(SUBSCR_CONN_S_NEW),
.in_event_mask = S(SUBSCR_CONN_E_ACCEPTED) |
S(SUBSCR_CONN_E_MO_CLOSE) |
- S(SUBSCR_CONN_E_CN_CLOSE) |
- S(SUBSCR_CONN_E_CLOSE_CONF),
+ S(SUBSCR_CONN_E_CN_CLOSE),
.out_state_mask = S(SUBSCR_CONN_S_ACCEPTED) |
S(SUBSCR_CONN_S_RELEASED),
.action = subscr_conn_fsm_new,
@@ -204,14 +236,27 @@ static const struct osmo_fsm_state subscr_conn_fsm_states[] = {
[SUBSCR_CONN_S_ACCEPTED] = {
.name = OSMO_STRINGIFY(SUBSCR_CONN_S_ACCEPTED),
/* allow everything to release for any odd behavior */
- .in_event_mask = S(SUBSCR_CONN_E_ACCEPTED) |
- S(SUBSCR_CONN_E_BUMP) |
+ .in_event_mask = S(SUBSCR_CONN_E_COMMUNICATING) |
+ S(SUBSCR_CONN_E_BUMP) |
+ S(SUBSCR_CONN_E_ACCEPTED) |
S(SUBSCR_CONN_E_MO_CLOSE) |
- S(SUBSCR_CONN_E_CN_CLOSE) |
- S(SUBSCR_CONN_E_CLOSE_CONF),
- .out_state_mask = S(SUBSCR_CONN_S_RELEASED),
+ S(SUBSCR_CONN_E_CN_CLOSE),
+ .out_state_mask = S(SUBSCR_CONN_S_RELEASED) |
+ S(SUBSCR_CONN_S_COMMUNICATING),
+ .onenter = subscr_conn_fsm_accepted_enter,
.action = subscr_conn_fsm_accepted,
},
+ [SUBSCR_CONN_S_COMMUNICATING] = {
+ .name = OSMO_STRINGIFY(SUBSCR_CONN_S_COMMUNICATING),
+ /* allow everything to release for any odd behavior */
+ .in_event_mask = S(SUBSCR_CONN_E_BUMP) |
+ S(SUBSCR_CONN_E_ACCEPTED) |
+ S(SUBSCR_CONN_E_COMMUNICATING) |
+ S(SUBSCR_CONN_E_MO_CLOSE) |
+ S(SUBSCR_CONN_E_CN_CLOSE),
+ .out_state_mask = S(SUBSCR_CONN_S_RELEASED),
+ .action = subscr_conn_fsm_communicating,
+ },
[SUBSCR_CONN_S_RELEASED] = {
.name = OSMO_STRINGIFY(SUBSCR_CONN_S_RELEASED),
.onenter = subscr_conn_fsm_release,
@@ -226,6 +271,8 @@ static struct osmo_fsm subscr_conn_fsm = {
.allstate_action = NULL,
.log_subsys = DVLR,
.event_names = subscr_conn_fsm_event_names,
+ .cleanup = subscr_conn_fsm_cleanup,
+ .timer_cb = subscr_conn_fsm_timeout,
};
int msc_create_conn_fsm(struct gsm_subscriber_connection *conn, const char *id)
@@ -239,7 +286,14 @@ int msc_create_conn_fsm(struct gsm_subscriber_connection *conn, const char *id)
return -EINVAL;
}
- fi = osmo_fsm_inst_alloc(&subscr_conn_fsm, conn, conn, LOGL_DEBUG, id);
+ /* Allocate the FSM not with the subscr_conn. Semantically it would
+ * make sense, but in subscr_conn_fsm_cleanup(), we want to discard the
+ * subscriber connection. If the FSM is freed along with the subscriber
+ * connection, then in _osmo_fsm_inst_term() the osmo_fsm_inst_free()
+ * that follows the cleanup() call would run into a double free. */
+ fi = osmo_fsm_inst_alloc(&subscr_conn_fsm, conn->network,
+ msc_subscr_conn_get(conn),
+ LOGL_DEBUG, id);
if (!fi) {
LOGP(DMM, LOGL_ERROR,
@@ -247,6 +301,7 @@ int msc_create_conn_fsm(struct gsm_subscriber_connection *conn, const char *id)
return -ENOMEM;
}
conn->conn_fsm = fi;
+ osmo_fsm_inst_dispatch(conn->conn_fsm, SUBSCR_CONN_E_START, NULL);
return 0;
}
@@ -254,15 +309,23 @@ bool msc_subscr_conn_is_accepted(struct gsm_subscriber_connection *conn)
{
if (!conn)
return false;
- if (!conn->subscr)
+ if (!conn->vsub)
return false;
if (!conn->conn_fsm)
return false;
- if (conn->conn_fsm->state != SUBSCR_CONN_S_ACCEPTED)
+ if (!(conn->conn_fsm->state == SUBSCR_CONN_S_ACCEPTED
+ || conn->conn_fsm->state == SUBSCR_CONN_S_COMMUNICATING))
return false;
return true;
}
+void msc_subscr_conn_communicating(struct gsm_subscriber_connection *conn)
+{
+ OSMO_ASSERT(conn);
+ osmo_fsm_inst_dispatch(conn->conn_fsm, SUBSCR_CONN_E_COMMUNICATING,
+ NULL);
+}
+
void msc_subscr_conn_init(void)
{
osmo_fsm_register(&subscr_conn_fsm);
diff --git a/src/libmsc/token_auth.c b/src/libmsc/token_auth.c
deleted file mode 100644
index 5af1e980b..000000000
--- a/src/libmsc/token_auth.c
+++ /dev/null
@@ -1,160 +0,0 @@
-/* SMS based token authentication for ad-hoc GSM networks */
-
-/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
- *
- * All Rights Reserved
- *
- * 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 Affero 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 <osmocom/core/talloc.h>
-#include <openbsc/signal.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_04_11.h>
-#include <openbsc/gsm_04_08.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/chan_alloc.h>
-#include <openbsc/db.h>
-
-#define TOKEN_SMS_TEXT "HAR 2009 GSM. Register at http://har2009.gnumonks.org/ " \
- "Your IMSI is %s, auth token is %08X, phone no is %s."
-
-static char *build_sms_string(struct gsm_subscriber *subscr, uint32_t token)
-{
- char *sms_str;
- unsigned int len;
-
- len = strlen(subscr->imsi) + 8 + strlen(TOKEN_SMS_TEXT);
- sms_str = talloc_size(tall_bsc_ctx, len);
- if (!sms_str)
- return NULL;
-
- snprintf(sms_str, len, TOKEN_SMS_TEXT, subscr->imsi, token,
- subscr->extension);
- sms_str[len-1] = '\0';
-
- return sms_str;
-}
-
-static int token_subscr_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct gsm_subscriber *subscr = signal_data;
- struct gsm_sms *sms;
- int rc = 0;
-
- if (signal != S_SUBSCR_ATTACHED)
- return 0;
-
- if (subscr->group->net->auth_policy != GSM_AUTH_POLICY_TOKEN)
- return 0;
-
- if (subscr->flags & GSM_SUBSCRIBER_FIRST_CONTACT) {
- struct gsm_subscriber *sender;
- uint32_t token;
- char *sms_str;
-
- /* we've seen this subscriber for the first time. */
- rc = db_subscriber_alloc_token(subscr, &token);
- if (rc != 0) {
- rc = -EIO;
- goto unauth;
- }
-
- sms_str = build_sms_string(subscr, token);
- if (!sms_str) {
- rc = -ENOMEM;
- goto unauth;
- }
-
-
- /* FIXME: don't use ID 1 static */
- sender = subscr_get_by_id(subscr->group, 1);
-
- sms = sms_from_text(subscr, sender, 0, sms_str);
-
- subscr_put(sender);
- talloc_free(sms_str);
- if (!sms) {
- rc = -ENOMEM;
- goto unauth;
- }
-
- rc = gsm411_send_sms_subscr(subscr, sms);
-
- /* FIXME: else, delete the subscirber from database */
-unauth:
-
- /* make sure we don't allow him in again unless he clicks the web UI */
- subscr->authorized = 0;
- db_sync_subscriber(subscr);
- if (rc) {
- struct gsm_subscriber_connection *conn = connection_for_subscr(subscr);
- if (conn) {
- uint8_t auth_rand[16];
- /* kick the subscriber off the network */
- gsm48_tx_mm_auth_req(conn, auth_rand, NULL, 0);
- gsm48_tx_mm_auth_rej(conn);
- /* FIXME: close the channel early ?*/
- //gsm48_send_rr_Release(lchan);
- }
- }
- }
-
- return rc;
-}
-
-static int token_sms_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct sms_signal_data *sig = signal_data;
- struct gsm_sms *sms = sig->sms;;
- struct gsm_subscriber_connection *conn;
- uint8_t auth_rand[16];
-
-
- if (signal != S_SMS_DELIVERED)
- return 0;
-
-
- /* these are not the droids we've been looking for */
- if (!sms->receiver ||
- !(sms->receiver->flags & GSM_SUBSCRIBER_FIRST_CONTACT))
- return 0;
-
-
- if (sms->receiver->group->net->auth_policy != GSM_AUTH_POLICY_TOKEN)
- return 0;
-
-
- conn = connection_for_subscr(sms->receiver);
- if (conn) {
- /* kick the subscriber off the network */
- gsm48_tx_mm_auth_req(conn, auth_rand, NULL, 0);
- gsm48_tx_mm_auth_rej(conn);
- /* FIXME: close the channel early ?*/
- //gsm48_send_rr_Release(lchan);
- }
-
- return 0;
-}
-
-//static __attribute__((constructor)) void on_dso_load_token(void)
-void on_dso_load_token(void)
-{
- osmo_signal_register_handler(SS_SUBSCR, token_subscr_cb, NULL);
- osmo_signal_register_handler(SS_SMS, token_sms_cb, NULL);
-}
diff --git a/src/libmsc/transaction.c b/src/libmsc/transaction.c
index dba4bed17..d157f5469 100644
--- a/src/libmsc/transaction.c
+++ b/src/libmsc/transaction.c
@@ -23,25 +23,31 @@
#include <openbsc/mncc.h>
#include <openbsc/debug.h>
#include <osmocom/core/talloc.h>
-#include <openbsc/gsm_subscriber.h>
#include <openbsc/gsm_04_08.h>
#include <openbsc/mncc.h>
#include <openbsc/paging.h>
#include <openbsc/osmo_msc.h>
+#include <openbsc/vlr.h>
void *tall_trans_ctx;
void _gsm48_cc_trans_free(struct gsm_trans *trans);
+/*! Find a transaction in connection for given protocol + transaction ID
+ * \param[in] conn Connection in whihc we want to find transaction
+ * \param[in] proto Protocol of transaction
+ * \param[in] trans_id Transaction ID of transaction
+ * \returns Matching transaction, if any
+ */
struct gsm_trans *trans_find_by_id(struct gsm_subscriber_connection *conn,
uint8_t proto, uint8_t trans_id)
{
struct gsm_trans *trans;
struct gsm_network *net = conn->network;
- struct gsm_subscriber *subscr = conn->subscr;
+ struct vlr_subscr *vsub = conn->vsub;
llist_for_each_entry(trans, &net->trans_list, entry) {
- if (trans->subscr == subscr &&
+ if (trans->vsub == vsub &&
trans->protocol == proto &&
trans->transaction_id == trans_id)
return trans;
@@ -49,6 +55,11 @@ struct gsm_trans *trans_find_by_id(struct gsm_subscriber_connection *conn,
return NULL;
}
+/*! Find a transaction by call reference
+ * \param[in] net Network in which we should search
+ * \param[in] callref Call Reference of transaction
+ * \returns Matching transaction, if any
+ */
struct gsm_trans *trans_find_by_callref(struct gsm_network *net,
uint32_t callref)
{
@@ -61,21 +72,27 @@ struct gsm_trans *trans_find_by_callref(struct gsm_network *net,
return NULL;
}
+/*! Allocate a new transaction and add it to network list
+ * \param[in] net Netwokr in which we allocate transaction
+ * \param[in] subscr Subscriber for which we allocate transaction
+ * \param[in] protocol Protocol (CC/SMS/...)
+ * \param[in] callref Call Reference
+ * \returns Transaction
+ */
struct gsm_trans *trans_alloc(struct gsm_network *net,
- struct gsm_subscriber *subscr,
+ struct vlr_subscr *vsub,
uint8_t protocol, uint8_t trans_id,
uint32_t callref)
{
struct gsm_trans *trans;
- DEBUGP(DCC, "subscr=%p, net=%p\n", subscr, net);
+ DEBUGP(DCC, "subscr=%p, net=%p\n", vsub, net);
trans = talloc_zero(tall_trans_ctx, struct gsm_trans);
if (!trans)
return NULL;
- trans->subscr = subscr;
- subscr_get(trans->subscr);
+ trans->vsub = vlr_subscr_get(vsub);
trans->protocol = protocol;
trans->transaction_id = trans_id;
@@ -87,6 +104,9 @@ struct gsm_trans *trans_alloc(struct gsm_network *net,
return trans;
}
+/*! Release a transaction
+ * \param[in] trans Transaction to be released
+ */
void trans_free(struct gsm_trans *trans)
{
switch (trans->protocol) {
@@ -103,23 +123,28 @@ void trans_free(struct gsm_trans *trans)
trans->paging_request = NULL;
}
- if (trans->subscr) {
- subscr_put(trans->subscr);
- trans->subscr = NULL;
+ if (trans->vsub) {
+ vlr_subscr_put(trans->vsub);
+ trans->vsub = NULL;
}
llist_del(&trans->entry);
if (trans->conn)
- msc_release_connection(trans->conn);
+ msc_subscr_conn_put(trans->conn);
trans->conn = NULL;
talloc_free(trans);
}
-/* allocate an unused transaction ID for the given subscriber
- * in the given protocol using the ti_flag specified */
-int trans_assign_trans_id(struct gsm_network *net, struct gsm_subscriber *subscr,
+/*! allocate an unused transaction ID for the given subscriber
+ * in the given protocol using the ti_flag specified
+ * \param[in] net GSM network
+ * \param[in] subscr Subscriber for which to find ID
+ * \param[in] protocol Protocol for whihc to find ID
+ * \param[in] ti_flag FIXME
+ */
+int trans_assign_trans_id(struct gsm_network *net, struct vlr_subscr *vsub,
uint8_t protocol, uint8_t ti_flag)
{
struct gsm_trans *trans;
@@ -131,7 +156,7 @@ int trans_assign_trans_id(struct gsm_network *net, struct gsm_subscriber *subscr
/* generate bitmask of already-used TIDs for this (subscr,proto) */
llist_for_each_entry(trans, &net->trans_list, entry) {
- if (trans->subscr != subscr ||
+ if (trans->vsub != vsub ||
trans->protocol != protocol ||
trans->transaction_id == 0xff)
continue;
@@ -151,6 +176,10 @@ int trans_assign_trans_id(struct gsm_network *net, struct gsm_subscriber *subscr
return -1;
}
+/*! Check if we have any transaction for given connection
+ * \param[in] conn Connection to check
+ * \returns 1 in case there is a transaction, 0 otherwise
+ */
int trans_has_conn(const struct gsm_subscriber_connection *conn)
{
struct gsm_trans *trans;
@@ -161,3 +190,25 @@ int trans_has_conn(const struct gsm_subscriber_connection *conn)
return 0;
}
+
+/*! Free all transactions associated with a connection, presumably when the
+ * conn is being closed. The transaction code will inform the CC or SMS
+ * facilities, which will then send the necessary release indications.
+ * \param[in] conn Connection that is going to be closed.
+ */
+void trans_conn_closed(struct gsm_subscriber_connection *conn)
+{
+ struct gsm_trans *trans;
+
+ /* As part of the CC REL_IND the remote leg might be released and this
+ * will trigger another call to trans_free. This is something the llist
+ * macro can not handle and we need to re-iterate the list every time.
+ */
+restart:
+ llist_for_each_entry(trans, &conn->network->trans_list, entry) {
+ if (trans->conn == conn) {
+ trans_free(trans);
+ goto restart;
+ }
+ }
+}
diff --git a/src/libmsc/ussd.c b/src/libmsc/ussd.c
index f12c1f281..81a356690 100644
--- a/src/libmsc/ussd.c
+++ b/src/libmsc/ussd.c
@@ -33,6 +33,7 @@
#include <openbsc/gsm_subscriber.h>
#include <openbsc/debug.h>
#include <openbsc/osmo_msc.h>
+#include <openbsc/vlr.h>
/* Declarations of USSD strings to be recognised */
const char USSD_TEXT_OWN_NUMBER[] = "*#100#";
@@ -48,13 +49,19 @@ int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg)
struct ss_request req;
struct gsm48_hdr *gh;
+ /* TODO: Use subscriber_connection ref-counting if we ever want
+ * to keep the connection alive due ot ongoing USSD exchange.
+ * As we answer everytying synchronously so far, there's no need
+ * yet */
+
+ cm_service_request_concludes(conn, msg);
+
memset(&req, 0, sizeof(req));
gh = msgb_l3(msg);
rc = gsm0480_decode_ss_request(gh, msgb_l3len(msg), &req);
if (!rc) {
DEBUGP(DMM, "Unhandled SS\n");
rc = gsm0480_send_ussd_reject(conn, msg, &req);
- msc_release_connection(conn);
return rc;
}
@@ -63,13 +70,13 @@ int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg)
if (req.ss_code > 0) {
/* Assume interrogateSS or modification of it and reject */
rc = gsm0480_send_ussd_reject(conn, msg, &req);
- msc_release_connection(conn);
return rc;
}
/* Still assuming a Release-Complete and returning */
return 0;
}
+ msc_subscr_conn_communicating(conn);
if (!strcmp(USSD_TEXT_OWN_NUMBER, (const char *)req.ussd_text)) {
DEBUGP(DMM, "USSD: Own number requested\n");
rc = send_own_number(conn, msg, &req);
@@ -78,17 +85,18 @@ int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg)
rc = gsm0480_send_ussd_reject(conn, msg, &req);
}
- /* check if we can release it */
- msc_release_connection(conn);
return rc;
}
/* A network-specific handler function */
static int send_own_number(struct gsm_subscriber_connection *conn, const struct msgb *msg, const struct ss_request *req)
{
- char *own_number = conn->subscr->extension;
+ char *own_number = conn->vsub->msisdn;
char response_string[GSM_EXTENSION_LENGTH + 20];
+ DEBUGP(DMM, "%s: MSISDN = %s\n", vlr_subscr_name(conn->vsub),
+ own_number);
+
/* Need trailing CR as EOT character */
snprintf(response_string, sizeof(response_string), "Your extension is %s\r", own_number);
return gsm0480_send_ussd_response(conn, msg, response_string, req);
diff --git a/src/libmsc/vty_interface_layer3.c b/src/libmsc/vty_interface_layer3.c
index 99d7fb94f..c393a8fa6 100644
--- a/src/libmsc/vty_interface_layer3.c
+++ b/src/libmsc/vty_interface_layer3.c
@@ -51,74 +51,78 @@
#include <openbsc/sms_queue.h>
#include <openbsc/mncc_int.h>
#include <openbsc/handover.h>
+#include <openbsc/vlr.h>
#include <osmocom/vty/logging.h>
+#include <openbsc/osmo_msc.h>
+
#include "meas_feed.h"
extern struct gsm_network *gsmnet_from_vty(struct vty *v);
-static void subscr_dump_full_vty(struct vty *vty, struct gsm_subscriber *subscr)
+static void subscr_dump_full_vty(struct vty *vty, struct vlr_subscr *vsub)
{
- int rc;
int reqs;
- struct gsm_auth_info ainfo;
- struct gsm_auth_tuple atuple;
struct llist_head *entry;
char expire_time[200];
- vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id,
- subscr->authorized, VTY_NEWLINE);
- if (strlen(subscr->name))
- vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE);
- if (strlen(subscr->extension))
- vty_out(vty, " Extension: %s%s", subscr->extension,
+ if (strlen(vsub->name))
+ vty_out(vty, " Name: '%s'%s", vsub->name, VTY_NEWLINE);
+ if (strlen(vsub->msisdn))
+ vty_out(vty, " Extension: %s%s", vsub->msisdn,
VTY_NEWLINE);
vty_out(vty, " LAC: %d/0x%x%s",
- subscr->lac, subscr->lac, VTY_NEWLINE);
- vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE);
- if (subscr->tmsi != GSM_RESERVED_TMSI)
- vty_out(vty, " TMSI: %08X%s", subscr->tmsi,
+ vsub->lac, vsub->lac, VTY_NEWLINE);
+ vty_out(vty, " IMSI: %s%s", vsub->imsi, VTY_NEWLINE);
+ if (vsub->tmsi != GSM_RESERVED_TMSI)
+ vty_out(vty, " TMSI: %08X%s", vsub->tmsi,
+ VTY_NEWLINE);
+ if (vsub->tmsi_new != GSM_RESERVED_TMSI)
+ vty_out(vty, " new TMSI: %08X%s", vsub->tmsi_new,
VTY_NEWLINE);
- rc = db_get_authinfo_for_subscr(&ainfo, subscr);
- if (!rc) {
+#if 0
+ /* TODO: add this to vlr_subscr? */
+ if (vsub->auth_info.auth_algo != AUTH_ALGO_NONE) {
+ struct gsm_auth_info *i = &vsub->auth_info;
vty_out(vty, " A3A8 algorithm id: %d%s",
- ainfo.auth_algo, VTY_NEWLINE);
+ i->auth_algo, VTY_NEWLINE);
vty_out(vty, " A3A8 Ki: %s%s",
- osmo_hexdump(ainfo.a3a8_ki, ainfo.a3a8_ki_len),
+ osmo_hexdump(i->a3a8_ki, i->a3a8_ki_len),
VTY_NEWLINE);
}
+#endif
- rc = db_get_lastauthtuple_for_subscr(&atuple, subscr);
- if (!rc) {
+ if (vsub->last_tuple) {
+ struct gsm_auth_tuple *t = vsub->last_tuple;
vty_out(vty, " A3A8 last tuple (used %d times):%s",
- atuple.use_count, VTY_NEWLINE);
+ t->use_count, VTY_NEWLINE);
vty_out(vty, " seq # : %d%s",
- atuple.key_seq, VTY_NEWLINE);
+ t->key_seq, VTY_NEWLINE);
vty_out(vty, " RAND : %s%s",
- osmo_hexdump(atuple.vec.rand, sizeof(atuple.vec.rand)),
+ osmo_hexdump(t->vec.rand, sizeof(t->vec.rand)),
VTY_NEWLINE);
vty_out(vty, " SRES : %s%s",
- osmo_hexdump(atuple.vec.sres, sizeof(atuple.vec.sres)),
+ osmo_hexdump(t->vec.sres, sizeof(t->vec.sres)),
VTY_NEWLINE);
vty_out(vty, " Kc : %s%s",
- osmo_hexdump(atuple.vec.kc, sizeof(atuple.vec.kc)),
+ osmo_hexdump(t->vec.kc, sizeof(t->vec.kc)),
VTY_NEWLINE);
}
/* print the expiration time of a subscriber */
strftime(expire_time, sizeof(expire_time),
- "%a, %d %b %Y %T %z", localtime(&subscr->expire_lu));
+ "%a, %d %b %Y %T %z", localtime(&vsub->expire_lu));
expire_time[sizeof(expire_time) - 1] = '\0';
vty_out(vty, " Expiration Time: %s%s", expire_time, VTY_NEWLINE);
reqs = 0;
- llist_for_each(entry, &subscr->requests)
+ llist_for_each(entry, &vsub->cs.requests)
reqs += 1;
- vty_out(vty, " Paging: %s paging Requests: %d%s",
- subscr->is_paging ? "is" : "not", reqs, VTY_NEWLINE);
- vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE);
+ vty_out(vty, " Paging: %s paging for %d requests%s",
+ vsub->cs.is_paging ? "is" : "not", reqs, VTY_NEWLINE);
+ vty_out(vty, " Use count: %u%s", vsub->use_count, VTY_NEWLINE);
}
@@ -129,11 +133,18 @@ DEFUN(show_subscr_cache,
SHOW_STR "Show information about subscribers\n"
"Display contents of subscriber cache\n")
{
- struct gsm_subscriber *subscr;
+ struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+ struct vlr_subscr *vsub;
+ int count = 0;
- llist_for_each_entry(subscr, &active_subscribers, entry) {
+ llist_for_each_entry(vsub, &gsmnet->vlr->subscribers, list) {
+ if (++count > 100) {
+ vty_out(vty, "%% More than %d subscribers in cache,"
+ " stopping here.%s", count-1, VTY_NEWLINE);
+ break;
+ }
vty_out(vty, " Subscriber:%s", VTY_NEWLINE);
- subscr_dump_full_vty(vty, subscr);
+ subscr_dump_full_vty(vty, vsub);
}
return CMD_SUCCESS;
@@ -147,25 +158,27 @@ DEFUN(sms_send_pend,
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
struct gsm_sms *sms;
- int id = 0;
+ unsigned long long sms_id = 0;
while (1) {
- sms = db_sms_get_unsent_by_subscr(gsmnet, id, UINT_MAX);
+ sms = db_sms_get_next_unsent(gsmnet, sms_id, UINT_MAX);
if (!sms)
break;
- gsm411_send_sms_subscr(sms->receiver, sms);
+ if (sms->receiver)
+ gsm411_send_sms_subscr(sms->receiver, sms);
- id = sms->receiver->id + 1;
+ sms_id = sms->id + 1;
}
return CMD_SUCCESS;
}
-static int _send_sms_str(struct gsm_subscriber *receiver,
- struct gsm_subscriber *sender,
- char *str, uint8_t tp_pid)
+static int _send_sms_str(struct vlr_subscr *receiver,
+ struct vlr_subscr *sender,
+ char *str, uint8_t tp_pid)
{
+ struct gsm_network *net = receiver->vlr->user_ctx;
struct gsm_sms *sms;
sms = sms_from_text(receiver, sender, 0, str);
@@ -180,22 +193,20 @@ static int _send_sms_str(struct gsm_subscriber *receiver,
LOGP(DLSMS, LOGL_DEBUG, "SMS stored in DB\n");
sms_free(sms);
- sms_queue_trigger(receiver->group->net->sms_queue);
+ sms_queue_trigger(net->sms_queue);
return CMD_SUCCESS;
}
-static struct gsm_subscriber *get_subscr_by_argv(struct gsm_network *gsmnet,
- const char *type,
- const char *id)
+static struct vlr_subscr *get_vsub_by_argv(struct gsm_network *gsmnet,
+ const char *type,
+ const char *id)
{
- if (!strcmp(type, "extension"))
- return subscr_get_by_extension(gsmnet->subscr_group, id);
- else if (!strcmp(type, "imsi"))
- return subscr_get_by_imsi(gsmnet->subscr_group, id);
+ if (!strcmp(type, "extension") || !strcmp(type, "msisdn"))
+ return vlr_subscr_find_by_msisdn(gsmnet->vlr, id);
+ else if (!strcmp(type, "imsi") || !strcmp(type, "id"))
+ return vlr_subscr_find_by_imsi(gsmnet->vlr, id);
else if (!strcmp(type, "tmsi"))
- return subscr_get_by_tmsi(gsmnet->subscr_group, atoi(id));
- else if (!strcmp(type, "id"))
- return subscr_get_by_id(gsmnet->subscr_group, atoi(id));
+ return vlr_subscr_find_by_tmsi(gsmnet->vlr, atoi(id));
return NULL;
}
@@ -213,18 +224,18 @@ DEFUN(show_subscr,
SHOW_STR SUBSCR_HELP)
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr =
- get_subscr_by_argv(gsmnet, argv[0], argv[1]);
+ struct vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0],
+ argv[1]);
- if (!subscr) {
+ if (!vsub) {
vty_out(vty, "%% No subscriber found for %s %s%s",
argv[0], argv[1], VTY_NEWLINE);
return CMD_WARNING;
}
- subscr_dump_full_vty(vty, subscr);
+ subscr_dump_full_vty(vty, vsub);
- subscr_put(subscr);
+ vlr_subscr_put(vsub);
return CMD_SUCCESS;
}
@@ -237,28 +248,9 @@ DEFUN(subscriber_create,
"Identify the subscriber by his IMSI\n" \
"Identifier for the subscriber\n")
{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr;
-
- subscr = subscr_get_by_imsi(gsmnet->subscr_group, argv[0]);
- if (subscr)
- db_sync_subscriber(subscr);
- else {
- subscr = subscr_create_subscriber(gsmnet->subscr_group, argv[0]);
-
- if (!subscr) {
- vty_out(vty, "%% No subscriber created for IMSI %s%s",
- argv[0], VTY_NEWLINE);
- return CMD_WARNING;
- }
- }
-
- /* Show info about the created subscriber. */
- subscr_dump_full_vty(vty, subscr);
-
- subscr_put(subscr);
-
- return CMD_SUCCESS;
+ vty_out(vty, "%% 'subscriber create' now needs to be done at osmo-hlr%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
}
DEFUN(subscriber_send_pending_sms,
@@ -267,21 +259,21 @@ DEFUN(subscriber_send_pending_sms,
SUBSCR_HELP "SMS Operations\n" "Send pending SMS\n")
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr;
+ struct vlr_subscr *vsub;
struct gsm_sms *sms;
- subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]);
- if (!subscr) {
+ vsub = get_vsub_by_argv(gsmnet, argv[0], argv[1]);
+ if (!vsub) {
vty_out(vty, "%% No subscriber found for %s %s%s",
argv[0], argv[1], VTY_NEWLINE);
return CMD_WARNING;
}
- sms = db_sms_get_unsent_by_subscr(gsmnet, subscr->id, UINT_MAX);
+ sms = db_sms_get_unsent_for_subscr(vsub, UINT_MAX);
if (sms)
gsm411_send_sms_subscr(sms->receiver, sms);
- subscr_put(subscr);
+ vlr_subscr_put(vsub);
return CMD_SUCCESS;
}
@@ -292,12 +284,12 @@ DEFUN(subscriber_send_sms,
SUBSCR_HELP "SMS Operations\n" SUBSCR_HELP "Send SMS\n" "Actual SMS Text\n")
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]);
- struct gsm_subscriber *sender = get_subscr_by_argv(gsmnet, argv[2], argv[3]);
+ struct vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0], argv[1]);
+ struct vlr_subscr *sender = get_vsub_by_argv(gsmnet, argv[2], argv[3]);
char *str;
int rc;
- if (!subscr) {
+ if (!vsub) {
vty_out(vty, "%% No subscriber found for %s %s%s",
argv[0], argv[1], VTY_NEWLINE);
rc = CMD_WARNING;
@@ -312,15 +304,15 @@ DEFUN(subscriber_send_sms,
}
str = argv_concat(argv, argc, 4);
- rc = _send_sms_str(subscr, sender, str, 0);
+ rc = _send_sms_str(vsub, sender, str, 0);
talloc_free(str);
err:
if (sender)
- subscr_put(sender);
+ vlr_subscr_put(sender);
- if (subscr)
- subscr_put(subscr);
+ if (vsub)
+ vlr_subscr_put(vsub);
return rc;
}
@@ -332,12 +324,12 @@ DEFUN(subscriber_silent_sms,
SUBSCR_HELP "Silent SMS Operations\n" SUBSCR_HELP "Send SMS\n" "Actual SMS Text\n")
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]);
- struct gsm_subscriber *sender = get_subscr_by_argv(gsmnet, argv[2], argv[3]);
+ struct vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0], argv[1]);
+ struct vlr_subscr *sender = get_vsub_by_argv(gsmnet, argv[2], argv[3]);
char *str;
int rc;
- if (!subscr) {
+ if (!vsub) {
vty_out(vty, "%% No subscriber found for %s %s%s",
argv[0], argv[1], VTY_NEWLINE);
rc = CMD_WARNING;
@@ -352,15 +344,15 @@ DEFUN(subscriber_silent_sms,
}
str = argv_concat(argv, argc, 4);
- rc = _send_sms_str(subscr, sender, str, 64);
+ rc = _send_sms_str(vsub, sender, str, 64);
talloc_free(str);
err:
if (sender)
- subscr_put(sender);
+ vlr_subscr_put(sender);
- if (subscr)
- subscr_put(subscr);
+ if (vsub)
+ vlr_subscr_put(vsub);
return rc;
}
@@ -379,10 +371,10 @@ DEFUN(subscriber_silent_call_start,
CHAN_TYPE_HELP)
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]);
+ struct vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0], argv[1]);
int rc, type;
- if (!subscr) {
+ if (!vsub) {
vty_out(vty, "%% No subscriber found for %s %s%s",
argv[0], argv[1], VTY_NEWLINE);
return CMD_WARNING;
@@ -397,15 +389,15 @@ DEFUN(subscriber_silent_call_start,
else
type = RSL_CHANNEED_ANY; /* Defaults to ANY */
- rc = gsm_silent_call_start(subscr, vty, type);
+ rc = gsm_silent_call_start(vsub, vty, type);
if (rc <= 0) {
vty_out(vty, "%% Subscriber not attached%s",
VTY_NEWLINE);
- subscr_put(subscr);
+ vlr_subscr_put(vsub);
return CMD_WARNING;
}
- subscr_put(subscr);
+ vlr_subscr_put(vsub);
return CMD_SUCCESS;
}
@@ -417,22 +409,22 @@ DEFUN(subscriber_silent_call_stop,
CHAN_TYPE_HELP)
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]);
+ struct vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0], argv[1]);
int rc;
- if (!subscr) {
+ if (!vsub) {
vty_out(vty, "%% No subscriber found for %s %s%s",
argv[0], argv[1], VTY_NEWLINE);
return CMD_WARNING;
}
- rc = gsm_silent_call_stop(subscr);
+ rc = gsm_silent_call_stop(vsub);
if (rc < 0) {
- subscr_put(subscr);
+ vlr_subscr_put(vsub);
return CMD_WARNING;
}
- subscr_put(subscr);
+ vlr_subscr_put(vsub);
return CMD_SUCCESS;
}
@@ -449,10 +441,10 @@ DEFUN(subscriber_ussd_notify,
char *text;
struct gsm_subscriber_connection *conn;
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]);
+ struct vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0], argv[1]);
int level;
- if (!subscr) {
+ if (!vsub) {
vty_out(vty, "%% No subscriber found for %s %s%s",
argv[0], argv[1], VTY_NEWLINE);
return CMD_WARNING;
@@ -461,15 +453,15 @@ DEFUN(subscriber_ussd_notify,
level = atoi(argv[2]);
text = argv_concat(argv, argc, 3);
if (!text) {
- subscr_put(subscr);
+ vlr_subscr_put(vsub);
return CMD_WARNING;
}
- conn = connection_for_subscr(subscr);
+ conn = connection_for_subscr(vsub);
if (!conn) {
vty_out(vty, "%% An active connection is required for %s %s%s",
argv[0], argv[1], VTY_NEWLINE);
- subscr_put(subscr);
+ vlr_subscr_put(vsub);
talloc_free(text);
return CMD_WARNING;
}
@@ -477,7 +469,7 @@ DEFUN(subscriber_ussd_notify,
msc_send_ussd_notify(conn, level, text);
msc_send_ussd_release_complete(conn);
- subscr_put(subscr);
+ vlr_subscr_put(vsub);
talloc_free(text);
return CMD_SUCCESS;
}
@@ -485,32 +477,12 @@ DEFUN(subscriber_ussd_notify,
DEFUN(ena_subscr_delete,
ena_subscr_delete_cmd,
"subscriber " SUBSCR_TYPES " ID delete",
- SUBSCR_HELP "Delete subscriber in HLR\n")
+ SUBSCR_HELP "Delete subscriber in VLR\n")
{
- int rc;
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr =
- get_subscr_by_argv(gsmnet, argv[0], argv[1]);
-
- if (!subscr) {
- vty_out(vty, "%% No subscriber found for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (subscr->use_count != 1) {
- vty_out(vty, "Removing active subscriber%s", VTY_NEWLINE);
- }
-
- rc = db_subscriber_delete(subscr);
- subscr_put(subscr);
-
- if (rc != 0) {
- vty_out(vty, "Failed to remove subscriber%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
+ vty_out(vty, "%% 'subscriber delete' is no longer supported.%s"
+ "%% This is now up to osmo-hlr.%s",
+ VTY_NEWLINE, VTY_NEWLINE);
+ return CMD_WARNING;
}
DEFUN(ena_subscr_expire,
@@ -519,19 +491,28 @@ DEFUN(ena_subscr_expire,
SUBSCR_HELP "Expire the subscriber Now\n")
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr =
- get_subscr_by_argv(gsmnet, argv[0], argv[1]);
+ struct vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0],
+ argv[1]);
- if (!subscr) {
+ if (!vsub) {
vty_out(vty, "%% No subscriber found for %s %s%s",
argv[0], argv[1], VTY_NEWLINE);
return CMD_WARNING;
}
- subscr->expire_lu = time(0);
- db_sync_subscriber(subscr);
- subscr_put(subscr);
+ if (vsub->lu_complete) {
+ vsub->lu_complete = false;
+ vlr_subscr_put(vsub);
+ vty_out(vty, "%% VLR released subscriber %s%s",
+ vlr_subscr_name(vsub), VTY_NEWLINE);
+ }
+
+ if (vsub->use_count > 1)
+ vty_out(vty, "%% Subscriber %s is still in use,"
+ " should be released soon%s",
+ vlr_subscr_name(vsub), VTY_NEWLINE);
+ vlr_subscr_put(vsub);
return CMD_SUCCESS;
}
@@ -542,22 +523,10 @@ DEFUN(ena_subscr_authorized,
"Subscriber should NOT be authorized\n"
"Subscriber should be authorized\n")
{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr =
- get_subscr_by_argv(gsmnet, argv[0], argv[1]);
-
- if (!subscr) {
- vty_out(vty, "%% No subscriber found for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- subscr->authorized = atoi(argv[2]);
- db_sync_subscriber(subscr);
-
- subscr_put(subscr);
-
- return CMD_SUCCESS;
+ vty_out(vty, "%% 'subscriber authorized' is no longer supported.%s"
+ "%% Authorization is now up to osmo-hlr.%s",
+ VTY_NEWLINE, VTY_NEWLINE);
+ return CMD_WARNING;
}
DEFUN(ena_subscr_name,
@@ -566,38 +535,10 @@ DEFUN(ena_subscr_name,
SUBSCR_HELP "Set the name of the subscriber\n"
"Name of the Subscriber\n")
{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr =
- get_subscr_by_argv(gsmnet, argv[0], argv[1]);
- char *name;
-
- if (!subscr) {
- vty_out(vty, "%% No subscriber found for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- name = argv_concat(argv, argc, 2);
- if (!name) {
- subscr_put(subscr);
- return CMD_WARNING;
- }
-
- if (strlen(name) > sizeof(subscr->name)-1) {
- vty_out(vty,
- "%% NAME is too long, max. %zu characters are allowed%s",
- sizeof(subscr->name)-1, VTY_NEWLINE);
- subscr_put(subscr);
- return CMD_WARNING;
- }
-
- osmo_strlcpy(subscr->name, name, sizeof(subscr->name));
- talloc_free(name);
- db_sync_subscriber(subscr);
-
- subscr_put(subscr);
-
- return CMD_SUCCESS;
+ vty_out(vty, "%% 'subscriber name' is no longer supported.%s"
+ "%% This is now up to osmo-hlr.%s",
+ VTY_NEWLINE, VTY_NEWLINE);
+ return CMD_WARNING;
}
DEFUN(ena_subscr_extension,
@@ -606,30 +547,10 @@ DEFUN(ena_subscr_extension,
SUBSCR_HELP "Set the extension (phone number) of the subscriber\n"
"Extension (phone number)\n")
{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr =
- get_subscr_by_argv(gsmnet, argv[0], argv[1]);
- const char *ext = argv[2];
-
- if (!subscr) {
- vty_out(vty, "%% No subscriber found for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (strlen(ext) > sizeof(subscr->extension)-1) {
- vty_out(vty,
- "%% EXTENSION is too long, max. %zu characters are allowed%s",
- sizeof(subscr->extension)-1, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- osmo_strlcpy(subscr->extension, ext, sizeof(subscr->extension));
- db_sync_subscriber(subscr);
-
- subscr_put(subscr);
-
- return CMD_SUCCESS;
+ vty_out(vty, "%% 'subscriber extension' is no longer supported.%s"
+ "%% This is now up to osmo-hlr.%s",
+ VTY_NEWLINE, VTY_NEWLINE);
+ return CMD_WARNING;
}
DEFUN(ena_subscr_handover,
@@ -642,20 +563,20 @@ DEFUN(ena_subscr_handover,
struct gsm_subscriber_connection *conn;
struct gsm_bts *bts;
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr =
- get_subscr_by_argv(gsmnet, argv[0], argv[1]);
+ struct vlr_subscr *vsub =
+ get_vsub_by_argv(gsmnet, argv[0], argv[1]);
- if (!subscr) {
+ if (!vsub) {
vty_out(vty, "%% No subscriber found for %s %s.%s",
argv[0], argv[1], VTY_NEWLINE);
return CMD_WARNING;
}
- conn = connection_for_subscr(subscr);
+ conn = connection_for_subscr(vsub);
if (!conn) {
vty_out(vty, "%% No active connection for subscriber %s %s.%s",
argv[0], argv[1], VTY_NEWLINE);
- subscr_put(subscr);
+ vlr_subscr_put(vsub);
return CMD_WARNING;
}
@@ -663,7 +584,7 @@ DEFUN(ena_subscr_handover,
if (!bts) {
vty_out(vty, "%% BTS with number(%d) could not be found.%s",
atoi(argv[2]), VTY_NEWLINE);
- subscr_put(subscr);
+ vlr_subscr_put(vsub);
return CMD_WARNING;
}
@@ -679,7 +600,7 @@ DEFUN(ena_subscr_handover,
VTY_NEWLINE);
}
- subscr_put(subscr);
+ vlr_subscr_put(vsub);
return CMD_SUCCESS;
}
@@ -695,69 +616,10 @@ DEFUN(ena_subscr_a3a8,
SUBSCR_HELP "Set a3a8 parameters for the subscriber\n"
A3A8_ALG_HELP "Encryption Key Ki\n")
{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr =
- get_subscr_by_argv(gsmnet, argv[0], argv[1]);
- const char *alg_str = argv[2];
- const char *ki_str = argc == 4 ? argv[3] : NULL;
- struct gsm_auth_info ainfo;
- int rc, minlen, maxlen;
-
- if (!subscr) {
- vty_out(vty, "%% No subscriber found for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (!strcasecmp(alg_str, "none")) {
- ainfo.auth_algo = AUTH_ALGO_NONE;
- minlen = maxlen = 0;
- } else if (!strcasecmp(alg_str, "xor")) {
- ainfo.auth_algo = AUTH_ALGO_XOR;
- minlen = A38_XOR_MIN_KEY_LEN;
- maxlen = A38_XOR_MAX_KEY_LEN;
- } else if (!strcasecmp(alg_str, "comp128v1")) {
- ainfo.auth_algo = AUTH_ALGO_COMP128v1;
- minlen = maxlen = A38_COMP128_KEY_LEN;
- } else {
- /* Unknown method */
- subscr_put(subscr);
- vty_out(vty, "%% Unknown auth method %s%s",
- alg_str, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (ki_str) {
- rc = osmo_hexparse(ki_str, ainfo.a3a8_ki, sizeof(ainfo.a3a8_ki));
- if ((rc > maxlen) || (rc < minlen)) {
- subscr_put(subscr);
- vty_out(vty, "%% Wrong Ki `%s'%s",
- ki_str, VTY_NEWLINE);
- return CMD_WARNING;
- }
- ainfo.a3a8_ki_len = rc;
- } else {
- ainfo.a3a8_ki_len = 0;
- if (minlen) {
- subscr_put(subscr);
- vty_out(vty, "%% Missing Ki argument%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- }
-
- rc = db_sync_authinfo_for_subscr(
- ainfo.auth_algo == AUTH_ALGO_NONE ? NULL : &ainfo,
- subscr);
-
- /* the last tuple probably invalid with the new auth settings */
- db_sync_lastauthtuple_for_subscr(NULL, subscr);
- subscr_put(subscr);
-
- if (rc) {
- vty_out(vty, "%% Operation has failed%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- return CMD_SUCCESS;
+ vty_out(vty, "%% 'subscriber a3a8' is no longer supported.%s"
+ "%% This is now up to osmo-hlr.%s",
+ VTY_NEWLINE, VTY_NEWLINE);
+ return CMD_WARNING;
}
DEFUN(subscriber_purge,
@@ -765,12 +627,11 @@ DEFUN(subscriber_purge,
"subscriber purge-inactive",
"Operations on a Subscriber\n" "Purge subscribers with a zero use count.\n")
{
- struct gsm_network *net = gsmnet_from_vty(vty);
- int purged;
-
- purged = subscr_purge_inactive(net->subscr_group);
- vty_out(vty, "%d subscriber(s) were purged.%s", purged, VTY_NEWLINE);
- return CMD_SUCCESS;
+ /* TODO: does this still have a use with the VLR? */
+ vty_out(vty, "%% 'subscriber purge-inactive' is no longer supported.%s"
+ "%% This is now up to osmo-hlr.%s",
+ VTY_NEWLINE, VTY_NEWLINE);
+ return CMD_WARNING;
}
DEFUN(subscriber_update,
@@ -778,18 +639,9 @@ DEFUN(subscriber_update,
"subscriber " SUBSCR_TYPES " ID update",
SUBSCR_HELP "Update the subscriber data from the dabase.\n")
{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]);
-
- if (!subscr) {
- vty_out(vty, "%% No subscriber found for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- subscr_update_from_db(subscr);
- subscr_put(subscr);
- return CMD_SUCCESS;
+ vty_out(vty, "%% 'subscriber update' is no longer supported.%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
}
static int scall_cbfn(unsigned int subsys, unsigned int signal,
@@ -1035,7 +887,7 @@ DEFUN(logging_fltr_imsi,
LOGGING_STR FILTER_STR
"Filter log messages by IMSI\n" "IMSI to be used as filter\n")
{
- struct gsm_subscriber *vlr_subscr;
+ struct vlr_subscr *vlr_subscr;
struct bsc_subscr *bsc_subscr;
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
struct log_target *tgt = osmo_log_vty2tgt(vty);
@@ -1044,7 +896,7 @@ DEFUN(logging_fltr_imsi,
if (!tgt)
return CMD_WARNING;
- vlr_subscr = subscr_get_by_imsi(gsmnet->subscr_group, imsi);
+ vlr_subscr = vlr_subscr_find_by_imsi(gsmnet->vlr, imsi);
bsc_subscr = bsc_subscr_find_by_imsi(gsmnet->bsc_subscribers, imsi);
if (!vlr_subscr && !bsc_subscr) {
@@ -1053,10 +905,55 @@ DEFUN(logging_fltr_imsi,
return CMD_WARNING;
}
+ log_set_filter_vlr_subscr(tgt, vlr_subscr);
log_set_filter_bsc_subscr(tgt, bsc_subscr);
return CMD_SUCCESS;
}
+static struct cmd_node hlr_node = {
+ HLR_NODE,
+ "%s(config-hlr)# ",
+ 1,
+};
+
+DEFUN(cfg_hlr, cfg_hlr_cmd,
+ "hlr", "Configure connection to the HLR")
+{
+ vty->node = HLR_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_hlr_remote_ip, cfg_hlr_remote_ip_cmd, "remote-ip A.B.C.D",
+ "Remote GSUP address of the HLR\n"
+ "Remote GSUP address (default: " MSC_HLR_REMOTE_IP_DEFAULT ")")
+{
+ struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+ talloc_free((void*)gsmnet->gsup_server_addr_str);
+ gsmnet->gsup_server_addr_str = talloc_strdup(gsmnet, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_hlr_remote_port, cfg_hlr_remote_port_cmd, "remote-port <1-65535>",
+ "Remote GSUP port of the HLR\n"
+ "Remote GSUP port (default: " OSMO_STRINGIFY(MSC_HLR_REMOTE_PORT_DEFAULT) ")")
+{
+ struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+ gsmnet->gsup_server_port = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+static int config_write_hlr(struct vty *vty)
+{
+ struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+
+ vty_out(vty, "hlr%s", VTY_NEWLINE);
+ vty_out(vty, " remote-ip %s%s",
+ gsmnet->gsup_server_addr_str, VTY_NEWLINE);
+ vty_out(vty, " remote-port %u%s",
+ gsmnet->gsup_server_port, VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
static struct cmd_node nitb_node = {
NITB_NODE,
"%s(config-nitb)# ",
@@ -1077,18 +974,10 @@ DEFUN(cfg_nitb_subscr_random, cfg_nitb_subscr_random_cmd,
"Set random parameters for a new record when a subscriber is first seen.\n"
"Minimum for subscriber extension\n""Maximum for subscriber extension\n")
{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- uint64_t mi = atoll(argv[0]), ma = atoll(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);
- return CMD_WARNING;
- }
- gsmnet->ext_min = mi;
- gsmnet->ext_max = ma;
- return CMD_SUCCESS;
+ vty_out(vty, "%% 'subscriber-create-on-demand' is no longer supported.%s"
+ "%% This is now up to osmo-hlr.%s",
+ VTY_NEWLINE, VTY_NEWLINE);
+ return CMD_WARNING;
}
DEFUN(cfg_nitb_subscr_create, cfg_nitb_subscr_create_cmd,
@@ -1096,19 +985,20 @@ DEFUN(cfg_nitb_subscr_create, cfg_nitb_subscr_create_cmd,
"Make a new record when a subscriber is first seen.\n"
"Do not automatically assign extension to created subscribers\n")
{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->auto_create_subscr = true;
- gsmnet->auto_assign_exten = argc ? false : true;
- return CMD_SUCCESS;
+ vty_out(vty, "%% 'subscriber-create-on-demand' is no longer supported.%s"
+ "%% This is now up to osmo-hlr.%s",
+ VTY_NEWLINE, VTY_NEWLINE);
+ return CMD_WARNING;
}
DEFUN(cfg_nitb_no_subscr_create, cfg_nitb_no_subscr_create_cmd,
"no subscriber-create-on-demand",
NO_STR "Make a new record when a subscriber is first seen.\n")
{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->auto_create_subscr = false;
- return CMD_SUCCESS;
+ vty_out(vty, "%% 'subscriber-create-on-demand' is no longer supported.%s"
+ "%% This is now up to osmo-hlr.%s",
+ VTY_NEWLINE, VTY_NEWLINE);
+ return CMD_WARNING;
}
DEFUN(cfg_nitb_assign_tmsi, cfg_nitb_assign_tmsi_cmd,
@@ -1116,7 +1006,7 @@ DEFUN(cfg_nitb_assign_tmsi, cfg_nitb_assign_tmsi_cmd,
"Assign TMSI during Location Updating.\n")
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->avoid_tmsi = 0;
+ gsmnet->vlr->cfg.assign_tmsi = true;
return CMD_SUCCESS;
}
@@ -1125,7 +1015,7 @@ DEFUN(cfg_nitb_no_assign_tmsi, cfg_nitb_no_assign_tmsi_cmd,
NO_STR "Assign TMSI during Location Updating.\n")
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->avoid_tmsi = 1;
+ gsmnet->vlr->cfg.assign_tmsi = false;
return CMD_SUCCESS;
}
@@ -1134,19 +1024,8 @@ static int config_write_nitb(struct vty *vty)
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
vty_out(vty, "nitb%s", 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,
- VTY_NEWLINE);
vty_out(vty, " %sassign-tmsi%s",
- gsmnet->avoid_tmsi ? "no " : "", VTY_NEWLINE);
+ gsmnet->vlr->cfg.assign_tmsi? "" : "no ", VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -1196,6 +1075,10 @@ int bsc_vty_init_extra(void)
install_element(CFG_LOG_NODE, &log_level_sms_cmd);
install_element(CFG_LOG_NODE, &logging_fltr_imsi_cmd);
+ install_element(CONFIG_NODE, &cfg_hlr_cmd);
+ install_node(&hlr_node, config_write_hlr);
+ install_element(HLR_NODE, &cfg_hlr_remote_ip_cmd);
+ install_element(HLR_NODE, &cfg_hlr_remote_port_cmd);
install_element(CONFIG_NODE, &cfg_nitb_cmd);
install_node(&nitb_node, config_write_nitb);
diff --git a/src/libvlr/vlr_access_req_fsm.c b/src/libvlr/vlr_access_req_fsm.c
index 4e63e299f..279e3d49a 100644
--- a/src/libvlr/vlr_access_req_fsm.c
+++ b/src/libvlr/vlr_access_req_fsm.c
@@ -343,6 +343,7 @@ static void proc_arq_vlr_fn_init(struct osmo_fsm_inst *fi,
vsub = vlr_subscr_find_by_tmsi(par->vlr, par->tmsi);
}
if (vsub) {
+ log_set_context(LOG_CTX_VLR_SUBSCR, vsub);
if (vsub->proc_arq_fsm && fi != vsub->proc_arq_fsm) {
LOGPFSML(fi, LOGL_ERROR,
"Another proc_arq_fsm is already"
diff --git a/src/osmo-nitb/bsc_hack.c b/src/osmo-nitb/bsc_hack.c
index 17b23b2b9..89bbb5e68 100644
--- a/src/osmo-nitb/bsc_hack.c
+++ b/src/osmo-nitb/bsc_hack.c
@@ -44,7 +44,6 @@
#include <openbsc/vty.h>
#include <openbsc/bss.h>
#include <openbsc/mncc.h>
-#include <openbsc/token_auth.h>
#include <openbsc/handover_decision.h>
#include <openbsc/rrlp.h>
#include <osmocom/ctrl/control_if.h>
@@ -240,7 +239,7 @@ static void db_sync_timer_cb(void *data)
static void subscr_expire_cb(void *data)
{
- subscr_expire(bsc_gsmnet->subscr_group);
+ /* TODO expire vlr_subscrs? */
osmo_timer_schedule(&bsc_gsmnet->subscr_expire_timer, EXPIRE_INTERVAL);
}
@@ -261,7 +260,6 @@ int main(int argc, char **argv)
tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc");
talloc_ctx_init(tall_bsc_ctx);
- on_dso_load_token();
on_dso_load_rrlp();
on_dso_load_ho_dec();
@@ -285,6 +283,10 @@ int main(int argc, char **argv)
/* Initialize VTY */
bsc_vty_init(bsc_gsmnet);
ctrl_vty_init(tall_bsc_ctx);
+ if (msc_vlr_alloc(bsc_gsmnet)) {
+ fprintf(stderr, "Failed to allocate VLR\n");
+ exit(1);
+ }
#ifdef BUILD_SMPP
if (smpp_openbsc_alloc_init(tall_bsc_ctx) < 0)
@@ -342,7 +344,7 @@ int main(int argc, char **argv)
return -1;
}
- if (msc_ctrl_cmds_install() != 0) {
+ if (msc_ctrl_cmds_install(bsc_gsmnet) != 0) {
printf("Failed to initialize the MSC control commands.\n");
return -1;
}
@@ -356,6 +358,14 @@ int main(int argc, char **argv)
exit(1);
}
+ osmo_fsm_log_addr(true);
+ if (msc_vlr_start(bsc_gsmnet)) {
+ fprintf(stderr, "Failed to start VLR\n");
+ exit(1);
+ }
+
+ msc_subscr_conn_init();
+
if (db_init(database_name)) {
printf("DB: Failed to init database. Please check the option settings.\n");
return -1;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 72161eeea..acfe7d297 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,6 +1,5 @@
SUBDIRS = \
gsm0408 \
- db \
channel \
mgcp \
gprs \
@@ -8,8 +7,8 @@ SUBDIRS = \
gbproxy \
trau \
subscr \
- mm_auth \
nanobts_omlattr \
+ sms_queue \
$(NULL)
if BUILD_NAT
diff --git a/tests/channel/Makefile.am b/tests/channel/Makefile.am
index ca470ace4..c7164b475 100644
--- a/tests/channel/Makefile.am
+++ b/tests/channel/Makefile.am
@@ -26,6 +26,7 @@ channel_test_SOURCES = \
channel_test_LDADD = \
$(top_builddir)/src/libmsc/libmsc.a \
$(top_builddir)/src/libbsc/libbsc.a \
+ $(top_builddir)/src/libvlr/libvlr.a \
$(top_builddir)/src/libcommon-cs/libcommon-cs.a \
$(top_builddir)/src/libcommon/libcommon.a \
$(LIBOSMOCORE_LIBS) \
diff --git a/tests/channel/channel_test.c b/tests/channel/channel_test.c
index 88293d0a8..f6869691c 100644
--- a/tests/channel/channel_test.c
+++ b/tests/channel/channel_test.c
@@ -29,68 +29,7 @@
#include <openbsc/abis_rsl.h>
#include <openbsc/debug.h>
#include <openbsc/gsm_subscriber.h>
-
-static int s_end = 0;
-static struct gsm_subscriber_connection s_conn;
-static void *s_data;
-static gsm_cbfn *s_cbfn = NULL;
-
-/* our handler */
-static int subscr_cb(unsigned int hook, unsigned int event, struct msgb *msg, void *data, void *param)
-{
- assert(hook == 101);
- assert(event == 200);
- assert(msg == (void*)0x1323L);
- assert(data == &s_conn);
- assert(param == (void*)0x2342L);
- printf("Reached, didn't crash, test passed\n");
- s_end = true;
- return 0;
-}
-
-/* mock object for testing, directly invoke the cb... maybe later through the timer */
-int paging_request(struct gsm_bts *bts, struct bsc_subscr *bsub, int type, gsm_cbfn *cbfn, void *data)
-{
- s_data = data;
- s_cbfn = cbfn;
-
- /* claim we have patched */
- return 1;
-}
-
-
-void test_request_chan(void)
-{
- struct gsm_network *network;
- struct gsm_bts *bts;
-
- printf("Testing the gsm_subscriber chan logic\n");
-
- /* Create a dummy network */
- network = bsc_network_init(tall_bsc_ctx, 1, 1, NULL);
- if (!network)
- exit(1);
- bts = gsm_bts_alloc(network);
- bts->location_area_code = 23;
- s_conn.network = network;
-
- /* Create a dummy subscriber */
- struct gsm_subscriber *subscr = subscr_alloc();
- subscr->lac = 23;
- subscr->group = network->subscr_group;
-
- OSMO_ASSERT(subscr->group);
- OSMO_ASSERT(subscr->group->net == network);
-
- /* Ask for a channel... */
- struct subscr_request *sr;
- sr = subscr_request_channel(subscr, RSL_CHANNEED_TCH_F, subscr_cb, (void*)0x2342L);
- OSMO_ASSERT(sr);
- OSMO_ASSERT(s_cbfn);
- s_cbfn(101, 200, (void*)0x1323L, &s_conn, s_data);
-
- OSMO_ASSERT(s_end);
-}
+#include <openbsc/vlr.h>
void test_dyn_ts_subslots(void)
{
@@ -126,7 +65,6 @@ int main(int argc, char **argv)
{
osmo_init_logging(&log_info);
- test_request_chan();
test_dyn_ts_subslots();
return EXIT_SUCCESS;
@@ -140,5 +78,13 @@ void gsm48_secure_channel() {}
void paging_request_stop() {}
void vty_out() {}
-struct tlv_definition nm_att_tlvdef;
+void ipa_client_conn_clear_queue() {}
+void ipa_client_conn_close() {}
+void ipa_client_conn_create() {}
+void ipa_client_conn_destroy() {}
+void ipa_client_conn_open() {}
+void ipa_client_conn_send() {}
+void ipa_msg_push_header() {}
+void ipaccess_bts_handle_ccm() {}
+struct tlv_definition nm_att_tlvdef;
diff --git a/tests/channel/channel_test.ok b/tests/channel/channel_test.ok
index 33c819350..e4d625a65 100644
--- a/tests/channel/channel_test.ok
+++ b/tests/channel/channel_test.ok
@@ -1,3 +1 @@
-Testing the gsm_subscriber chan logic
-Reached, didn't crash, test passed
Testing subslot numbers for pchan types
diff --git a/tests/ctrl_test_runner.py b/tests/ctrl_test_runner.py
index 0a99c8992..75961a1de 100644
--- a/tests/ctrl_test_runner.py
+++ b/tests/ctrl_test_runner.py
@@ -474,54 +474,14 @@ class TestCtrlNITB(TestCtrlBase):
self.assertEquals(r['var'], 'number-of-bts')
self.assertEquals(r['value'], '1')
- def testSubscriberAddWithKi(self):
- """Test that we can set the algorithm to none, xor, comp128v1"""
-
- r = self.do_set('subscriber-modify-v1', '2620345,445566')
- self.assertEquals(r['mtype'], 'SET_REPLY')
- self.assertEquals(r['var'], 'subscriber-modify-v1')
- self.assertEquals(r['value'], 'OK')
-
- r = self.do_set('subscriber-modify-v1', '2620345,445566,none')
- self.assertEquals(r['mtype'], 'SET_REPLY')
- self.assertEquals(r['var'], 'subscriber-modify-v1')
- self.assertEquals(r['value'], 'OK')
-
- r = self.do_set('subscriber-modify-v1', '2620345,445566,xor')
- self.assertEquals(r['mtype'], 'ERROR')
- self.assertEquals(r['error'], 'Value failed verification.')
-
- r = self.do_set('subscriber-modify-v1', '2620345,445566,comp128v1,00112233445566778899AABBCCDDEEFF')
- self.assertEquals(r['mtype'], 'SET_REPLY')
- self.assertEquals(r['var'], 'subscriber-modify-v1')
- self.assertEquals(r['value'], 'OK')
-
- r = self.do_set('subscriber-modify-v1', '2620345,445566,none')
- self.assertEquals(r['mtype'], 'SET_REPLY')
- self.assertEquals(r['var'], 'subscriber-modify-v1')
- self.assertEquals(r['value'], 'OK')
-
def testSubscriberAddRemove(self):
r = self.do_set('subscriber-modify-v1', '2620345,445566')
- self.assertEquals(r['mtype'], 'SET_REPLY')
- self.assertEquals(r['var'], 'subscriber-modify-v1')
- self.assertEquals(r['value'], 'OK')
-
- r = self.do_set('subscriber-modify-v1', '2620345,445567')
- self.assertEquals(r['mtype'], 'SET_REPLY')
- self.assertEquals(r['var'], 'subscriber-modify-v1')
- self.assertEquals(r['value'], 'OK')
-
- # TODO. verify that the entry has been created and modified? Invoke
- # the sqlite3 CLI or do it through the DB libraries?
-
- r = self.do_set('subscriber-delete-v1', '2620345')
- self.assertEquals(r['mtype'], 'SET_REPLY')
- self.assertEquals(r['value'], 'Removed')
+ self.assertEquals(r['mtype'], 'ERROR')
+ self.assertEquals(r['error'], 'Command moved to osmo-hlr, no longer available here')
r = self.do_set('subscriber-delete-v1', '2620345')
self.assertEquals(r['mtype'], 'ERROR')
- self.assertEquals(r['error'], 'Failed to find subscriber')
+ self.assertEquals(r['error'], 'Command moved to osmo-hlr, no longer available here')
def testSubscriberList(self):
# TODO. Add command to mark a subscriber as active
diff --git a/tests/db/db_test.c b/tests/db/db_test.c
index 755a6e9eb..a54f11b26 100644
--- a/tests/db/db_test.c
+++ b/tests/db/db_test.c
@@ -254,3 +254,10 @@ int main()
/* stubs */
void vty_out() {}
+void vlr_subscr_disconnected() {}
+void vlr_subscr_rx_tmsi_reall_compl() {}
+void vlr_subscr_rx_id_resp() {}
+void vlr_subscr_rx_auth_resp() {}
+void vlr_loc_update() {}
+void vlr_proc_acc_req() {}
+void vlr_init() {}
diff --git a/tests/mm_auth/Makefile.am b/tests/mm_auth/Makefile.am
deleted file mode 100644
index cb351988b..000000000
--- a/tests/mm_auth/Makefile.am
+++ /dev/null
@@ -1,37 +0,0 @@
-AM_CPPFLAGS = \
- $(all_includes) \
- -I$(top_srcdir)/include \
- $(NULL)
-
-AM_CFLAGS = \
- -Wall \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOABIS_CFLAGS) \
- $(LIBOSMOGSM_CFLAGS) \
- $(LIBCRYPTO_CFLAGS) \
- $(NULL)
-
-noinst_PROGRAMS = \
- mm_auth_test \
- $(NULL)
-
-EXTRA_DIST = \
- mm_auth_test.ok \
- $(NULL)
-
-mm_auth_test_SOURCES = \
- mm_auth_test.c \
- $(NULL)
-
-mm_auth_test_LDFLAGS = \
- -Wl,--wrap=db_get_authinfo_for_subscr \
- -Wl,--wrap=db_get_lastauthtuple_for_subscr \
- -Wl,--wrap=db_sync_lastauthtuple_for_subscr \
- $(NULL)
-
-mm_auth_test_LDADD = \
- $(top_builddir)/src/libmsc/libmsc.a \
- $(top_builddir)/src/libcommon/libcommon.a \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- $(NULL)
diff --git a/tests/mm_auth/mm_auth_test.c b/tests/mm_auth/mm_auth_test.c
deleted file mode 100644
index b8777a8c5..000000000
--- a/tests/mm_auth/mm_auth_test.c
+++ /dev/null
@@ -1,340 +0,0 @@
-#include <stdbool.h>
-
-#include <osmocom/core/application.h>
-#include <osmocom/core/logging.h>
-
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/auth.h>
-
-#define min(A,B) ((A)>(B)? (B) : (A))
-
-static char *auth_tuple_str(struct gsm_auth_tuple *atuple)
-{
- static char buf[256];
- char *pos = buf;
- int len = sizeof(buf);
- int l;
-
-#define print2buf(FMT, args...) do {\
- l = snprintf(pos, len, FMT, ## args); \
- pos += l;\
- len -= l;\
- } while (0)
-
- print2buf("gsm_auth_tuple {\n");
- print2buf(" .use_count = %d\n", atuple->use_count);
- print2buf(" .key_seq = %d\n", atuple->key_seq);
- print2buf(" .rand = %s\n", osmo_hexdump(atuple->vec.rand, sizeof(atuple->vec.rand)));
- print2buf(" .sres = %s\n", osmo_hexdump(atuple->vec.sres, sizeof(atuple->vec.sres)));
- print2buf(" .kc = %s\n", osmo_hexdump(atuple->vec.kc, sizeof(atuple->vec.kc)));
- print2buf("}\n");
-#undef print2buf
-
- return buf;
-}
-
-static bool auth_tuple_is(struct gsm_auth_tuple *atuple,
- const char *expect_str)
-{
- int l, l1, l2;
- int i;
- char *tuple_str = auth_tuple_str(atuple);
- bool same = (strcmp(expect_str, tuple_str) == 0);
- if (!same) {
- l1 = strlen(expect_str);
- l2 = strlen(tuple_str);
- printf("Expected %d:\n%s\nGot %d:\n%s\n",
- l1, expect_str, l2, tuple_str);
- l = min(l1, l2);
- for (i = 0; i < l; i++) {
- if (expect_str[i] != tuple_str[i]) {
- printf("Difference at pos %d"
- " (%c 0x%0x != %c 0x%0x)\n",
- i, expect_str[i], expect_str[i],
- tuple_str[i], tuple_str[i]);
- break;
- }
- }
- }
- return same;
-}
-
-/* override, requires '-Wl,--wrap=db_get_authinfo_for_subscr' */
-int __real_db_get_authinfo_for_subscr(struct gsm_auth_info *ainfo,
- struct gsm_subscriber *subscr);
-
-int test_get_authinfo_rc = 0;
-struct gsm_auth_info test_auth_info = {0};
-struct gsm_auth_info default_auth_info = {
- .auth_algo = AUTH_ALGO_COMP128v1,
- .a3a8_ki_len = 16,
- .a3a8_ki = { 0 }
-};
-
-int __wrap_db_get_authinfo_for_subscr(struct gsm_auth_info *ainfo,
- struct gsm_subscriber *subscr)
-{
- *ainfo = test_auth_info;
- printf("wrapped: db_get_authinfo_for_subscr(): rc = %d\n", test_get_authinfo_rc);
- return test_get_authinfo_rc;
-}
-
-/* override, requires '-Wl,--wrap=db_get_lastauthtuple_for_subscr' */
-int __real_db_get_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple,
- struct gsm_subscriber *subscr);
-
-int test_get_lastauthtuple_rc = 0;
-struct gsm_auth_tuple test_last_auth_tuple = { 0 };
-struct gsm_auth_tuple default_auth_tuple = { 0 };
-
-int __wrap_db_get_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple,
- struct gsm_subscriber *subscr)
-{
- *atuple = test_last_auth_tuple;
- printf("wrapped: db_get_lastauthtuple_for_subscr(): rc = %d\n", test_get_lastauthtuple_rc);
- return test_get_lastauthtuple_rc;
-}
-
-/* override, requires '-Wl,--wrap=db_sync_lastauthtuple_for_subscr' */
-int __real_db_sync_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple,
- struct gsm_subscriber *subscr);
-int test_sync_lastauthtuple_rc = 0;
-int __wrap_db_sync_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple,
- struct gsm_subscriber *subscr)
-{
- test_last_auth_tuple = *atuple;
- printf("wrapped: db_sync_lastauthtuple_for_subscr(): rc = %d\n", test_sync_lastauthtuple_rc);
- return test_sync_lastauthtuple_rc;
-}
-
-int auth_get_tuple_for_subscr_verbose(struct gsm_auth_tuple *atuple,
- struct gsm_subscriber *subscr,
- int key_seq)
-{
- int auth_action;
- auth_action = auth_get_tuple_for_subscr(atuple, subscr, key_seq);
- printf("auth_get_tuple_for_subscr(key_seq=%d) --> auth_action == %s\n",
- key_seq, auth_action_str(auth_action));
- return auth_action;
-}
-
-/* override libssl RAND_bytes() to get testable crypto results */
-int RAND_bytes(uint8_t *rand, int len)
-{
- memset(rand, 23, len);
- return 1;
-}
-
-static void test_error()
-{
- int auth_action;
-
- struct gsm_auth_tuple atuple = {0};
- struct gsm_subscriber subscr = {0};
- int key_seq = 0;
-
- printf("\n* test_error()\n");
-
- /* any error (except -ENOENT) */
- test_get_authinfo_rc = -EIO;
- auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr,
- key_seq);
- OSMO_ASSERT(auth_action == AUTH_ERROR);
-}
-
-static void test_auth_not_avail()
-{
- int auth_action;
-
- struct gsm_auth_tuple atuple = {0};
- struct gsm_subscriber subscr = {0};
- int key_seq = 0;
-
- printf("\n* test_auth_not_avail()\n");
-
- /* no entry */
- test_get_authinfo_rc = -ENOENT;
- auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr,
- key_seq);
- OSMO_ASSERT(auth_action == AUTH_NOT_AVAIL);
-}
-
-static void test_auth_then_ciph1()
-{
- int auth_action;
-
- struct gsm_auth_tuple atuple = {0};
- struct gsm_subscriber subscr = {0};
- int key_seq;
-
- printf("\n* test_auth_then_ciph1()\n");
-
- /* Ki entry, but no auth tuple negotiated yet */
- test_auth_info = default_auth_info;
- test_last_auth_tuple = default_auth_tuple;
- test_get_authinfo_rc = 0;
- test_get_lastauthtuple_rc = -ENOENT;
- key_seq = 0;
- auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr,
- key_seq);
- OSMO_ASSERT(auth_action == AUTH_DO_AUTH_THEN_CIPH);
- OSMO_ASSERT(auth_tuple_is(&atuple,
- "gsm_auth_tuple {\n"
- " .use_count = 1\n"
- " .key_seq = 0\n"
- " .rand = 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 \n"
- " .sres = a1 ab c6 90 \n"
- " .kc = 0f 27 ed f3 ac 97 ac 00 \n"
- "}\n"
- ));
-
- /* With a different last saved key_seq stored in the out-arg of
- * db_get_lastauthtuple_for_subscr() by coincidence, expect absolutely
- * the same as above. */
- test_auth_info = default_auth_info;
- test_last_auth_tuple = default_auth_tuple;
- test_last_auth_tuple.key_seq = 3;
- test_get_authinfo_rc = 0;
- test_get_lastauthtuple_rc = -ENOENT;
- key_seq = 0;
- auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr,
- key_seq);
- OSMO_ASSERT(auth_action == AUTH_DO_AUTH_THEN_CIPH);
- OSMO_ASSERT(auth_tuple_is(&atuple,
- "gsm_auth_tuple {\n"
- " .use_count = 1\n"
- " .key_seq = 0\n"
- " .rand = 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 \n"
- " .sres = a1 ab c6 90 \n"
- " .kc = 0f 27 ed f3 ac 97 ac 00 \n"
- "}\n"
- ));
-}
-
-static void test_auth_then_ciph2()
-{
- int auth_action;
-
- struct gsm_auth_tuple atuple = {0};
- struct gsm_subscriber subscr = {0};
- int key_seq;
-
- printf("\n* test_auth_then_ciph2()\n");
-
- /* Ki entry, auth tuple negotiated, but invalid incoming key_seq */
- test_auth_info = default_auth_info;
- test_last_auth_tuple = default_auth_tuple;
- test_last_auth_tuple.key_seq = 2;
- test_get_authinfo_rc = 0;
- test_get_lastauthtuple_rc = 0;
- key_seq = GSM_KEY_SEQ_INVAL;
- auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr,
- key_seq);
- OSMO_ASSERT(auth_action == AUTH_DO_AUTH_THEN_CIPH);
- OSMO_ASSERT(auth_tuple_is(&atuple,
- "gsm_auth_tuple {\n"
- " .use_count = 1\n"
- " .key_seq = 3\n"
- " .rand = 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 \n"
- " .sres = a1 ab c6 90 \n"
- " .kc = 0f 27 ed f3 ac 97 ac 00 \n"
- "}\n"
- ));
-
- /* Change the last saved key_seq, expect last_auth_tuple.key_seq + 1 */
- test_auth_info = default_auth_info;
- test_last_auth_tuple = default_auth_tuple;
- test_last_auth_tuple.key_seq = 3;
- test_get_authinfo_rc = 0;
- test_get_lastauthtuple_rc = 0;
- key_seq = GSM_KEY_SEQ_INVAL;
- auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr,
- key_seq);
- OSMO_ASSERT(auth_action == AUTH_DO_AUTH_THEN_CIPH);
- OSMO_ASSERT(auth_tuple_is(&atuple,
- "gsm_auth_tuple {\n"
- " .use_count = 1\n"
- " .key_seq = 4\n"
- " .rand = 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 \n"
- " .sres = a1 ab c6 90 \n"
- " .kc = 0f 27 ed f3 ac 97 ac 00 \n"
- "}\n"
- ));
-}
-
-static void test_auth_reuse()
-{
- int auth_action;
- struct gsm_auth_tuple atuple = {0};
- struct gsm_subscriber subscr = {0};
- int key_seq;
-
- printf("\n* test_auth_reuse()\n");
-
- /* Ki entry, auth tuple negotiated, valid+matching incoming key_seq */
- test_auth_info = default_auth_info;
- test_last_auth_tuple = default_auth_tuple;
- test_last_auth_tuple.key_seq = key_seq = 3;
- test_last_auth_tuple.use_count = 1;
- test_get_authinfo_rc = 0;
- test_get_lastauthtuple_rc = 0;
- auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr,
- key_seq);
- OSMO_ASSERT(auth_action == AUTH_DO_CIPH);
- OSMO_ASSERT(auth_tuple_is(&atuple,
- "gsm_auth_tuple {\n"
- " .use_count = 2\n"
- " .key_seq = 3\n"
- " .rand = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n"
- " .sres = 00 00 00 00 \n"
- " .kc = 00 00 00 00 00 00 00 00 \n"
- "}\n"
- ));
-}
-
-static void test_auth_reuse_key_seq_mismatch()
-{
- int auth_action;
- struct gsm_auth_tuple atuple = {0};
- struct gsm_subscriber subscr = {0};
- int key_seq;
-
- printf("\n* test_auth_reuse_key_seq_mismatch()\n");
-
- /* Ki entry, auth tuple negotiated, valid+matching incoming key_seq */
- test_auth_info = default_auth_info;
- test_last_auth_tuple = default_auth_tuple;
- test_last_auth_tuple.key_seq = 3;
- key_seq = 4;
- test_last_auth_tuple.use_count = 1;
- test_get_authinfo_rc = 0;
- test_get_lastauthtuple_rc = 0;
- auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr,
- key_seq);
- OSMO_ASSERT(auth_action == AUTH_DO_AUTH_THEN_CIPH);
- OSMO_ASSERT(auth_tuple_is(&atuple,
- "gsm_auth_tuple {\n"
- " .use_count = 1\n"
- " .key_seq = 4\n"
- " .rand = 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 \n"
- " .sres = a1 ab c6 90 \n"
- " .kc = 0f 27 ed f3 ac 97 ac 00 \n"
- "}\n"
- ));
-}
-
-int main(void)
-{
- osmo_init_logging(&log_info);
- log_set_log_level(osmo_stderr_target, LOGL_INFO);
-
- test_error();
- test_auth_not_avail();
- test_auth_then_ciph1();
- test_auth_then_ciph2();
- test_auth_reuse();
- test_auth_reuse_key_seq_mismatch();
- return 0;
-}
diff --git a/tests/mm_auth/mm_auth_test.ok b/tests/mm_auth/mm_auth_test.ok
deleted file mode 100644
index 6c49f97b7..000000000
--- a/tests/mm_auth/mm_auth_test.ok
+++ /dev/null
@@ -1,40 +0,0 @@
-
-* test_error()
-wrapped: db_get_authinfo_for_subscr(): rc = -5
-auth_get_tuple_for_subscr(key_seq=0) --> auth_action == AUTH_ERROR
-
-* test_auth_not_avail()
-wrapped: db_get_authinfo_for_subscr(): rc = -2
-auth_get_tuple_for_subscr(key_seq=0) --> auth_action == AUTH_NOT_AVAIL
-
-* test_auth_then_ciph1()
-wrapped: db_get_authinfo_for_subscr(): rc = 0
-wrapped: db_get_lastauthtuple_for_subscr(): rc = -2
-wrapped: db_sync_lastauthtuple_for_subscr(): rc = 0
-auth_get_tuple_for_subscr(key_seq=0) --> auth_action == AUTH_DO_AUTH_THEN_CIPH
-wrapped: db_get_authinfo_for_subscr(): rc = 0
-wrapped: db_get_lastauthtuple_for_subscr(): rc = -2
-wrapped: db_sync_lastauthtuple_for_subscr(): rc = 0
-auth_get_tuple_for_subscr(key_seq=0) --> auth_action == AUTH_DO_AUTH_THEN_CIPH
-
-* test_auth_then_ciph2()
-wrapped: db_get_authinfo_for_subscr(): rc = 0
-wrapped: db_get_lastauthtuple_for_subscr(): rc = 0
-wrapped: db_sync_lastauthtuple_for_subscr(): rc = 0
-auth_get_tuple_for_subscr(key_seq=7) --> auth_action == AUTH_DO_AUTH_THEN_CIPH
-wrapped: db_get_authinfo_for_subscr(): rc = 0
-wrapped: db_get_lastauthtuple_for_subscr(): rc = 0
-wrapped: db_sync_lastauthtuple_for_subscr(): rc = 0
-auth_get_tuple_for_subscr(key_seq=7) --> auth_action == AUTH_DO_AUTH_THEN_CIPH
-
-* test_auth_reuse()
-wrapped: db_get_authinfo_for_subscr(): rc = 0
-wrapped: db_get_lastauthtuple_for_subscr(): rc = 0
-wrapped: db_sync_lastauthtuple_for_subscr(): rc = 0
-auth_get_tuple_for_subscr(key_seq=3) --> auth_action == AUTH_DO_CIPH
-
-* test_auth_reuse_key_seq_mismatch()
-wrapped: db_get_authinfo_for_subscr(): rc = 0
-wrapped: db_get_lastauthtuple_for_subscr(): rc = 0
-wrapped: db_sync_lastauthtuple_for_subscr(): rc = 0
-auth_get_tuple_for_subscr(key_seq=4) --> auth_action == AUTH_DO_AUTH_THEN_CIPH
diff --git a/tests/sms_queue/Makefile.am b/tests/sms_queue/Makefile.am
new file mode 100644
index 000000000..b2266ebb8
--- /dev/null
+++ b/tests/sms_queue/Makefile.am
@@ -0,0 +1,45 @@
+AM_CPPFLAGS = \
+ $(all_includes) \
+ -I$(top_srcdir)/include \
+ $(NULL)
+
+AM_CFLAGS = \
+ -Wall \
+ -ggdb3 \
+ $(LIBOSMOCORE_CFLAGS) \
+ $(LIBOSMOGSM_CFLAGS) \
+ $(NULL)
+
+EXTRA_DIST = \
+ sms_queue_test.ok \
+ sms_queue_test.err \
+ $(NULL)
+
+noinst_PROGRAMS = \
+ sms_queue_test \
+ $(NULL)
+
+sms_queue_test_SOURCES = \
+ sms_queue_test.c \
+ $(NULL)
+
+sms_queue_test_LDADD = \
+ $(top_builddir)/src/libmsc/libmsc.a \
+ $(top_builddir)/src/libvlr/libvlr.a \
+ $(top_builddir)/src/libbsc/libbsc.a \
+ $(top_builddir)/src/libtrau/libtrau.a \
+ $(top_builddir)/src/libcommon/libcommon.a \
+ $(top_builddir)/src/libcommon-cs/libcommon-cs.a \
+ $(LIBSMPP34_LIBS) \
+ $(LIBOSMOCORE_LIBS) \
+ $(LIBOSMOGSM_LIBS) \
+ $(LIBCRYPTO_LIBS) \
+ $(LIBOSMOVTY_LIBS) \
+ $(LIBOSMOABIS_LIBS) \
+ -ldbi \
+ -lrt \
+ $(NULL)
+
+sms_queue_test_LDFLAGS = \
+ -Wl,--wrap=db_sms_get_next_unsent_rr_msisdn \
+ $(NULL)
diff --git a/tests/sms_queue/sms_queue_test.c b/tests/sms_queue/sms_queue_test.c
new file mode 100644
index 000000000..af25b0645
--- /dev/null
+++ b/tests/sms_queue/sms_queue_test.c
@@ -0,0 +1,215 @@
+/* Test Osmocom SMS queue */
+
+/*
+ * (C) 2017 by sysmocom s.f.m.c. GmbH
+ * 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 Affero 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 <osmocom/core/application.h>
+
+#include <openbsc/debug.h>
+#include <openbsc/vlr.h>
+
+static void *talloc_ctx = NULL;
+
+struct gsm_sms *smsq_take_next_sms(struct gsm_network *net,
+ char *last_msisdn,
+ size_t last_msisdn_buflen);
+
+static void _test_take_next_sms_print(int i,
+ struct gsm_sms *sms,
+ const char *last_msisdn)
+{
+ printf("#%d: ", i);
+ if (sms)
+ printf("sending SMS to %s", sms->text);
+ else
+ printf("no SMS to send");
+ printf(" (last_msisdn='%s')\n", last_msisdn? last_msisdn : "NULL");
+}
+
+static struct gsm_sms fake_sms = { 0 };
+
+struct {
+ const char *msisdn;
+ int nr_of_sms;
+ int failed_attempts;
+ bool vsub_attached;
+} fake_sms_db[] = {
+ {
+ .msisdn = "1111",
+ .nr_of_sms = 0,
+ .vsub_attached = true,
+ },
+ {
+ .msisdn = "2222",
+ .nr_of_sms = 2,
+ .failed_attempts = 2,
+ .vsub_attached = true,
+ },
+ {
+ .msisdn = "3333",
+ .nr_of_sms = 2,
+ .failed_attempts = 3,
+ .vsub_attached = true,
+ },
+ {
+ .msisdn = "4444",
+ .nr_of_sms = 0,
+ .vsub_attached = true,
+ },
+ {
+ .msisdn = "5555",
+ .nr_of_sms = 2,
+ .failed_attempts = 5,
+ .vsub_attached = false,
+ },
+};
+
+/* override, requires '-Wl,--wrap=db_sms_get_next_unsent_rr_msisdn' */
+struct gsm_sms *__real_db_sms_get_next_unsent_rr_msisdn(struct gsm_network *net,
+ const char *last_msisdn,
+ unsigned int max_failed);
+struct gsm_sms *__wrap_db_sms_get_next_unsent_rr_msisdn(struct gsm_network *net,
+ const char *last_msisdn,
+ unsigned int max_failed)
+{
+ static struct vlr_subscr arbitrary_vsub = { .lu_complete = true };
+ int i;
+ printf(" hitting database: looking for MSISDN > '%s', failed_attempts <= %d\n",
+ last_msisdn, max_failed);
+
+ for (i = 0; i < ARRAY_SIZE(fake_sms_db); i++) {
+ if (!fake_sms_db[i].nr_of_sms)
+ continue;
+ if (strcmp(fake_sms_db[i].msisdn, last_msisdn) <= 0)
+ continue;
+ if (fake_sms_db[i].failed_attempts > max_failed)
+ continue;
+ osmo_strlcpy(fake_sms.dst.addr, fake_sms_db[i].msisdn,
+ sizeof(fake_sms.dst.addr));
+ fake_sms.receiver = fake_sms_db[i].vsub_attached? &arbitrary_vsub : NULL;
+ osmo_strlcpy(fake_sms.text, fake_sms_db[i].msisdn, sizeof(fake_sms.text));
+ if (fake_sms_db[i].vsub_attached)
+ fake_sms_db[i].nr_of_sms --;
+ return &fake_sms;
+ }
+ return NULL;
+}
+
+void show_fake_sms_db()
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(fake_sms_db); i++) {
+ printf(" %s%s has %u SMS pending, %u failed attempts\n",
+ fake_sms_db[i].msisdn,
+ fake_sms_db[i].vsub_attached ? "" : " (NOT attached)",
+ fake_sms_db[i].nr_of_sms,
+ fake_sms_db[i].failed_attempts);
+ }
+ printf("-->\n");
+}
+
+static void test_next_sms()
+{
+ int i;
+ char last_msisdn[GSM_EXTENSION_LENGTH+1] = "";
+
+ printf("Testing smsq_take_next_sms()\n");
+
+ printf("\n- vsub 2, 3 and 5 each have 2 SMS pending, but 5 is not attached\n");
+ last_msisdn[0] = '\0';
+ show_fake_sms_db();
+ for (i = 0; i < 7; i++) {
+ struct gsm_sms *sms = smsq_take_next_sms(NULL, last_msisdn, sizeof(last_msisdn));
+ _test_take_next_sms_print(i, sms, last_msisdn);
+ OSMO_ASSERT(i >= 4 || sms);
+ }
+
+ printf("\n- SMS are pending at various nr failed attempts (cutoff at >= 10)\n");
+ last_msisdn[0] = '\0';
+ for (i = 0; i < ARRAY_SIZE(fake_sms_db); i++) {
+ fake_sms_db[i].vsub_attached = true;
+ fake_sms_db[i].nr_of_sms = 1 + i;
+ fake_sms_db[i].failed_attempts = i*5;
+
+ }
+ show_fake_sms_db();
+ for (i = 0; i < 7; i++) {
+ struct gsm_sms *sms = smsq_take_next_sms(NULL, last_msisdn, sizeof(last_msisdn));
+ _test_take_next_sms_print(i, sms, last_msisdn);
+ OSMO_ASSERT(i >= 2 || sms);
+ }
+
+ printf("\n- iterate the SMS DB at most once\n");
+ osmo_strlcpy(last_msisdn, "2345", sizeof(last_msisdn));
+ for (i = 0; i < ARRAY_SIZE(fake_sms_db); i++) {
+ fake_sms_db[i].vsub_attached = false;
+ fake_sms_db[i].nr_of_sms = 1;
+ fake_sms_db[i].failed_attempts = 0;
+ }
+ show_fake_sms_db();
+ for (i = 0; i < 3; i++) {
+ struct gsm_sms *sms = smsq_take_next_sms(NULL, last_msisdn, sizeof(last_msisdn));
+ _test_take_next_sms_print(i, sms, last_msisdn);
+ OSMO_ASSERT(!sms);
+ }
+
+ printf("\n- there are no SMS in the DB\n");
+ last_msisdn[0] = '\0';
+ for (i = 0; i < ARRAY_SIZE(fake_sms_db); i++) {
+ fake_sms_db[i].vsub_attached = true;
+ fake_sms_db[i].nr_of_sms = 0;
+ fake_sms_db[i].failed_attempts = 0;
+ }
+ show_fake_sms_db();
+ for (i = 0; i < 3; i++) {
+ struct gsm_sms *sms = smsq_take_next_sms(NULL, last_msisdn, sizeof(last_msisdn));
+ _test_take_next_sms_print(i, sms, last_msisdn);
+ OSMO_ASSERT(!sms);
+ }
+}
+
+
+static struct log_info_cat sms_queue_test_categories[] = {
+};
+
+static struct log_info info = {
+ .cat = sms_queue_test_categories,
+ .num_cat = ARRAY_SIZE(sms_queue_test_categories),
+};
+
+int main(int argc, char **argv)
+{
+ talloc_ctx = talloc_named_const(NULL, 1, "sms_queue_test");
+ msgb_talloc_ctx_init(talloc_ctx, 0);
+ osmo_init_logging(&info);
+
+ OSMO_ASSERT(osmo_stderr_target);
+ log_set_use_color(osmo_stderr_target, 0);
+ log_set_print_timestamp(osmo_stderr_target, 0);
+ log_set_print_filename(osmo_stderr_target, 0);
+ log_set_print_category(osmo_stderr_target, 1);
+ log_parse_category_mask(osmo_stderr_target, "DLOAP,1");
+
+ test_next_sms();
+ printf("Done\n");
+
+ return 0;
+}
diff --git a/tests/sms_queue/sms_queue_test.err b/tests/sms_queue/sms_queue_test.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/sms_queue/sms_queue_test.err
diff --git a/tests/sms_queue/sms_queue_test.ok b/tests/sms_queue/sms_queue_test.ok
new file mode 100644
index 000000000..146400d21
--- /dev/null
+++ b/tests/sms_queue/sms_queue_test.ok
@@ -0,0 +1,98 @@
+Testing smsq_take_next_sms()
+
+- vsub 2, 3 and 5 each have 2 SMS pending, but 5 is not attached
+ 1111 has 0 SMS pending, 0 failed attempts
+ 2222 has 2 SMS pending, 2 failed attempts
+ 3333 has 2 SMS pending, 3 failed attempts
+ 4444 has 0 SMS pending, 0 failed attempts
+ 5555 (NOT attached) has 2 SMS pending, 5 failed attempts
+-->
+ hitting database: looking for MSISDN > '', failed_attempts <= 9
+#0: sending SMS to 2222 (last_msisdn='2222')
+ hitting database: looking for MSISDN > '2222', failed_attempts <= 9
+#1: sending SMS to 3333 (last_msisdn='3333')
+ hitting database: looking for MSISDN > '3333', failed_attempts <= 9
+ hitting database: looking for MSISDN > '5555', failed_attempts <= 9
+ hitting database: looking for MSISDN > '', failed_attempts <= 9
+#2: sending SMS to 2222 (last_msisdn='2222')
+ hitting database: looking for MSISDN > '2222', failed_attempts <= 9
+#3: sending SMS to 3333 (last_msisdn='3333')
+ hitting database: looking for MSISDN > '3333', failed_attempts <= 9
+ hitting database: looking for MSISDN > '5555', failed_attempts <= 9
+ hitting database: looking for MSISDN > '', failed_attempts <= 9
+#4: no SMS to send (last_msisdn='5555')
+ hitting database: looking for MSISDN > '5555', failed_attempts <= 9
+ hitting database: looking for MSISDN > '', failed_attempts <= 9
+#5: no SMS to send (last_msisdn='5555')
+ hitting database: looking for MSISDN > '5555', failed_attempts <= 9
+ hitting database: looking for MSISDN > '', failed_attempts <= 9
+#6: no SMS to send (last_msisdn='5555')
+
+- SMS are pending at various nr failed attempts (cutoff at >= 10)
+ 1111 has 1 SMS pending, 0 failed attempts
+ 2222 has 2 SMS pending, 5 failed attempts
+ 3333 has 3 SMS pending, 10 failed attempts
+ 4444 has 4 SMS pending, 15 failed attempts
+ 5555 has 5 SMS pending, 20 failed attempts
+-->
+ hitting database: looking for MSISDN > '', failed_attempts <= 9
+#0: sending SMS to 1111 (last_msisdn='1111')
+ hitting database: looking for MSISDN > '1111', failed_attempts <= 9
+#1: sending SMS to 2222 (last_msisdn='2222')
+ hitting database: looking for MSISDN > '2222', failed_attempts <= 9
+ hitting database: looking for MSISDN > '', failed_attempts <= 9
+#2: sending SMS to 2222 (last_msisdn='2222')
+ hitting database: looking for MSISDN > '2222', failed_attempts <= 9
+ hitting database: looking for MSISDN > '', failed_attempts <= 9
+#3: no SMS to send (last_msisdn='')
+ hitting database: looking for MSISDN > '', failed_attempts <= 9
+#4: no SMS to send (last_msisdn='')
+ hitting database: looking for MSISDN > '', failed_attempts <= 9
+#5: no SMS to send (last_msisdn='')
+ hitting database: looking for MSISDN > '', failed_attempts <= 9
+#6: no SMS to send (last_msisdn='')
+
+- iterate the SMS DB at most once
+ 1111 (NOT attached) has 1 SMS pending, 0 failed attempts
+ 2222 (NOT attached) has 1 SMS pending, 0 failed attempts
+ 3333 (NOT attached) has 1 SMS pending, 0 failed attempts
+ 4444 (NOT attached) has 1 SMS pending, 0 failed attempts
+ 5555 (NOT attached) has 1 SMS pending, 0 failed attempts
+-->
+ hitting database: looking for MSISDN > '2345', failed_attempts <= 9
+ hitting database: looking for MSISDN > '3333', failed_attempts <= 9
+ hitting database: looking for MSISDN > '4444', failed_attempts <= 9
+ hitting database: looking for MSISDN > '5555', failed_attempts <= 9
+ hitting database: looking for MSISDN > '', failed_attempts <= 9
+ hitting database: looking for MSISDN > '1111', failed_attempts <= 9
+ hitting database: looking for MSISDN > '2222', failed_attempts <= 9
+#0: no SMS to send (last_msisdn='3333')
+ hitting database: looking for MSISDN > '3333', failed_attempts <= 9
+ hitting database: looking for MSISDN > '4444', failed_attempts <= 9
+ hitting database: looking for MSISDN > '5555', failed_attempts <= 9
+ hitting database: looking for MSISDN > '', failed_attempts <= 9
+ hitting database: looking for MSISDN > '1111', failed_attempts <= 9
+ hitting database: looking for MSISDN > '2222', failed_attempts <= 9
+#1: no SMS to send (last_msisdn='3333')
+ hitting database: looking for MSISDN > '3333', failed_attempts <= 9
+ hitting database: looking for MSISDN > '4444', failed_attempts <= 9
+ hitting database: looking for MSISDN > '5555', failed_attempts <= 9
+ hitting database: looking for MSISDN > '', failed_attempts <= 9
+ hitting database: looking for MSISDN > '1111', failed_attempts <= 9
+ hitting database: looking for MSISDN > '2222', failed_attempts <= 9
+#2: no SMS to send (last_msisdn='3333')
+
+- there are no SMS in the DB
+ 1111 has 0 SMS pending, 0 failed attempts
+ 2222 has 0 SMS pending, 0 failed attempts
+ 3333 has 0 SMS pending, 0 failed attempts
+ 4444 has 0 SMS pending, 0 failed attempts
+ 5555 has 0 SMS pending, 0 failed attempts
+-->
+ hitting database: looking for MSISDN > '', failed_attempts <= 9
+#0: no SMS to send (last_msisdn='')
+ hitting database: looking for MSISDN > '', failed_attempts <= 9
+#1: no SMS to send (last_msisdn='')
+ hitting database: looking for MSISDN > '', failed_attempts <= 9
+#2: no SMS to send (last_msisdn='')
+Done
diff --git a/tests/subscr/Makefile.am b/tests/subscr/Makefile.am
index 6342444ff..5b770bcb2 100644
--- a/tests/subscr/Makefile.am
+++ b/tests/subscr/Makefile.am
@@ -18,32 +18,14 @@ AM_LDFLAGS = \
$(NULL)
EXTRA_DIST = \
- subscr_test.ok \
bsc_subscr_test.ok \
bsc_subscr_test.err \
$(NULL)
noinst_PROGRAMS = \
- subscr_test \
bsc_subscr_test \
$(NULL)
-subscr_test_SOURCES = \
- subscr_test.c \
- $(NULL)
-
-subscr_test_LDADD = \
- $(top_builddir)/src/libbsc/libbsc.a \
- $(top_builddir)/src/libcommon-cs/libcommon-cs.a \
- $(top_builddir)/src/libtrau/libtrau.a \
- $(top_builddir)/src/libcommon/libcommon.a \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOABIS_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- $(LIBSMPP34_LIBS) \
- $(LIBOSMOVTY_LIBS) \
- $(NULL)
-
bsc_subscr_test_SOURCES = \
bsc_subscr_test.c \
$(NULL)
diff --git a/tests/subscr/subscr_test.c b/tests/subscr/subscr_test.c
deleted file mode 100644
index 2a5d0e1c2..000000000
--- a/tests/subscr/subscr_test.c
+++ /dev/null
@@ -1,117 +0,0 @@
-/* (C) 2008 by Jan Luebbe <jluebbe@debian.org>
- * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2014 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
- * All Rights Reserved
- *
- * 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 <openbsc/debug.h>
-#include <openbsc/db.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/gsm_04_11.h>
-
-#include <osmocom/core/application.h>
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <inttypes.h>
-
-static struct gsm_network dummy_net;
-static struct gsm_subscriber_group dummy_sgrp;
-
-static void test_subscr(void)
-{
- struct gsm_subscriber *subscr;
- const char *imsi = "1234567890";
-
- printf("Test subscriber allocation and deletion\n");
-
- /* Don't keep subscr */
-
- dummy_sgrp.keep_subscr = 0;
-
- OSMO_ASSERT(llist_empty(&active_subscribers));
-
- subscr = subscr_get_or_create(&dummy_sgrp, imsi);
-
- OSMO_ASSERT(!llist_empty(&active_subscribers));
- OSMO_ASSERT(subscr->use_count == 1);
-
- subscr_put(subscr);
-
- OSMO_ASSERT(llist_empty(&active_subscribers));
-
- /* Keep subscr */
-
- dummy_sgrp.keep_subscr = 1;
-
- subscr = subscr_get_or_create(&dummy_sgrp, imsi);
-
- OSMO_ASSERT(!llist_empty(&active_subscribers));
- OSMO_ASSERT(subscr->use_count == 1);
-
- subscr_put(subscr);
- OSMO_ASSERT(!llist_empty(&active_subscribers));
- OSMO_ASSERT(subscr->use_count == 0);
-
- subscr_get(subscr);
- OSMO_ASSERT(subscr->use_count == 1);
-
- subscr_purge_inactive(&dummy_sgrp);
-
- OSMO_ASSERT(!llist_empty(&active_subscribers));
- OSMO_ASSERT(subscr->use_count == 1);
-
- subscr_put(subscr);
- OSMO_ASSERT(!llist_empty(&active_subscribers));
- OSMO_ASSERT(subscr->use_count == 0);
-
- subscr_purge_inactive(&dummy_sgrp);
-
- OSMO_ASSERT(llist_empty(&active_subscribers));
-
- /* Test force_no_keep */
-
- dummy_sgrp.keep_subscr = 0;
-
- subscr = subscr_get_or_create(&dummy_sgrp, imsi);
- OSMO_ASSERT(subscr);
- subscr->keep_in_ram = 1;
-
- OSMO_ASSERT(!llist_empty(&active_subscribers));
- OSMO_ASSERT(subscr->use_count == 1);
-
- subscr->keep_in_ram = 0;
-
- subscr_put(subscr);
- OSMO_ASSERT(llist_empty(&active_subscribers));
-}
-
-int main()
-{
- printf("Testing subscriber core code.\n");
- osmo_init_logging(&log_info);
- log_set_print_filename(osmo_stderr_target, 0);
-
- dummy_net.subscr_group = &dummy_sgrp;
- dummy_sgrp.net = &dummy_net;
-
- test_subscr();
-
- printf("Done\n");
- return 0;
-}
diff --git a/tests/subscr/subscr_test.ok b/tests/subscr/subscr_test.ok
deleted file mode 100644
index 72a376944..000000000
--- a/tests/subscr/subscr_test.ok
+++ /dev/null
@@ -1,3 +0,0 @@
-Testing subscriber core code.
-Test subscriber allocation and deletion
-Done
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 280aeb2ed..84e83f1bf 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -7,12 +7,6 @@ cat $abs_srcdir/gsm0408/gsm0408_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/gsm0408/gsm0408_test], [], [expout], [ignore])
AT_CLEANUP
-AT_SETUP([subscr])
-AT_KEYWORDS([subscr])
-cat $abs_srcdir/subscr/subscr_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/subscr/subscr_test], [], [expout], [ignore])
-AT_CLEANUP
-
AT_SETUP([bsc_subscr])
AT_KEYWORDS([bsc_subscr])
cat $abs_srcdir/subscr/bsc_subscr_test.ok > expout
@@ -20,14 +14,6 @@ cat $abs_srcdir/subscr/bsc_subscr_test.err > experr
AT_CHECK([$abs_top_builddir/tests/subscr/bsc_subscr_test], [], [expout], [experr])
AT_CLEANUP
-AT_SETUP([db])
-AT_KEYWORDS([db])
-cat $abs_srcdir/db/db_test.ok > expout
-cat $abs_srcdir/db/db_test.err > experr
-cat $abs_srcdir/db/hlr.sqlite3 > hlr.sqlite3
-AT_CHECK([$abs_top_builddir/tests/db/db_test], [], [expout], [experr])
-AT_CLEANUP
-
AT_SETUP([channel])
AT_KEYWORDS([channel])
cat $abs_srcdir/channel/channel_test.ok > expout
@@ -126,12 +112,6 @@ cat $abs_srcdir/gtphub/gtphub_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/gtphub/gtphub_test], [], [expout], [ignore])
AT_CLEANUP
-AT_SETUP([mm_auth])
-AT_KEYWORDS([mm_auth])
-cat $abs_srcdir/mm_auth/mm_auth_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/mm_auth/mm_auth_test], [], [expout], [ignore])
-AT_CLEANUP
-
AT_SETUP([xid])
AT_KEYWORDS([xid])
AT_CHECK([test "$enable_sgsn_test" != no || exit 77])
@@ -165,3 +145,10 @@ AT_KEYWORDS([nanobts_omlattr])
cat $abs_srcdir/nanobts_omlattr/nanobts_omlattr_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/nanobts_omlattr/nanobts_omlattr_test], [], [expout], [ignore])
AT_CLEANUP
+
+AT_SETUP([sms_queue_test])
+AT_KEYWORDS([sms_queue_test])
+cat $abs_srcdir/sms_queue/sms_queue_test.ok > expout
+cat $abs_srcdir/sms_queue/sms_queue_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/sms_queue/sms_queue_test], [], [expout], [experr])
+AT_CLEANUP
diff --git a/tests/vty_test_runner.py b/tests/vty_test_runner.py
index 68a697d78..e19b12b14 100644
--- a/tests/vty_test_runner.py
+++ b/tests/vty_test_runner.py
@@ -250,8 +250,10 @@ class TestVTYNITB(TestVTYGenericBSC):
self.vty.command("end")
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 no-extension", ['']))
+ self.assertTrue(self.vty.verify('subscriber-create-on-demand',
+ ["% 'subscriber-create-on-demand' is no longer supported.", '% This is now up to osmo-hlr.']))
+ self.assertTrue(self.vty.verify("subscriber-create-on-demand no-extension",
+ ["% 'subscriber-create-on-demand' is no longer supported.", '% This is now up to osmo-hlr.']))
self.vty.command("end")
def testSi2Q(self):
@@ -367,133 +369,26 @@ class TestVTYNITB(TestVTYGenericBSC):
if classNum != 10:
self.assertEquals(res.find("rach access-control-class " + str(classNum) + " barred"), -1)
- def testSubscriberCreateDeleteTwice(self):
- """
- OS#1657 indicates that there might be an issue creating the
- same subscriber twice. This test will use the VTY command to
- create a subscriber and then issue a second create command
- with the same IMSI. The test passes if the VTY continues to
- respond to VTY commands.
- """
- self.vty.enable()
-
- imsi = "204300854013739"
-
- # Initially we don't have this subscriber
- self.vty.verify('show subscriber imsi '+imsi, ['% No subscriber found for imsi '+imsi])
-
- # Lets create one
- res = self.vty.command('subscriber create imsi '+imsi)
- self.assert_(res.find(" IMSI: "+imsi) > 0)
- # And now create one again.
- res2 = self.vty.command('subscriber create imsi '+imsi)
- self.assert_(res2.find(" IMSI: "+imsi) > 0)
- self.assertEqual(res, res2)
-
- # Verify it has been created
- res = self.vty.command('show subscriber imsi '+imsi)
- self.assert_(res.find(" IMSI: "+imsi) > 0)
-
- # Delete it
- res = self.vty.command('subscriber imsi ' + imsi + ' delete')
- self.assert_("" == res)
-
- # Now it should not be there anymore
- res = self.vty.command('show subscriber imsi '+imsi)
- self.assert_(('% No subscriber found for imsi ' + imsi) == res)
-
-
def testSubscriberCreateDelete(self):
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])
+ self.assertTrue(self.vty.verify('show subscriber imsi '+imsi, ['% No subscriber found for imsi '+imsi]))
- # 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)
- self.assert_(('% No subscriber found for imsi ' + imsi) == res)
+ # deprecated
+ self.assertTrue(self.vty.verify('subscriber create imsi '+imsi, ["% 'subscriber create' now needs to be done at osmo-hlr"]))
# range
self.vty.command("end")
self.vty.command("configure terminal")
self.vty.command("nitb")
- self.assertTrue(self.vty.verify("subscriber-create-on-demand random 9999999998 9999999999", ['']))
+ self.assertTrue(self.vty.verify('subscriber-create-on-demand', ["% 'subscriber-create-on-demand' is no longer supported.", '% This is now up to osmo-hlr.']))
res = self.vty.command("show running-config")
- self.assert_(res.find("subscriber-create-on-demand random 9999999998 9999999999"))
+ self.assert_(res.find("subscriber-create-on-demand") < 0)
self.vty.command("end")
- res = self.vty.command('subscriber create imsi ' + imsi)
- print(res)
- self.assert_(res.find(" IMSI: " + imsi) > 0)
- self.assert_(res.find("9999999998") > 0 or res.find("9999999999") > 0)
- self.assert_(res.find(" Extension: ") > 0)
-
- res = self.vty.command('subscriber imsi ' + imsi + ' delete')
- self.assert_("" == res)
-
res = self.vty.command('show subscriber imsi '+imsi)
self.assert_(('% No subscriber found for imsi ' + imsi) == res)
@@ -502,50 +397,15 @@ class TestVTYNITB(TestVTYGenericBSC):
self.vty.enable()
imsi = "204300854013739"
- imsi2 = "204301824913769"
- wrong_imsi = "204300999999999"
-
- # Lets create one
- res = self.vty.command('subscriber create imsi '+imsi)
- self.assert_(res.find(" IMSI: "+imsi) > 0)
- self.assert_(res.find("Extension") > 0)
- self.vty.verify('subscriber imsi '+wrong_imsi+' name wrong', ['% No subscriber found for imsi '+wrong_imsi])
- res = self.vty.command('subscriber imsi '+imsi+' name '+('X' * 160))
- self.assert_(res.find("NAME is too long") > 0)
-
- self.vty.verify('subscriber imsi '+imsi+' name '+('G' * 159), [''])
-
- self.vty.verify('subscriber imsi '+wrong_imsi+' extension 840', ['% No subscriber found for imsi '+wrong_imsi])
- res = self.vty.command('subscriber imsi '+imsi+' extension '+('9' * 15))
- self.assert_(res.find("EXTENSION is too long") > 0)
-
- self.vty.verify('subscriber imsi '+imsi+' extension '+('1' * 14), [''])
+ self.assertTrue(self.vty.verify('subscriber imsi '+imsi+' name foo', ["% 'subscriber name' is no longer supported.", '% This is now up to osmo-hlr.']))
+ self.assertTrue(self.vty.verify('subscriber imsi '+imsi+' extension 1234', ["% 'subscriber extension' is no longer supported.", '% This is now up to osmo-hlr.']))
+ self.assertTrue(self.vty.verify('subscriber imsi '+imsi+' delete', ["% 'subscriber delete' is no longer supported.", '% This is now up to osmo-hlr.']))
# 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")
- self.assert_(res.find("122") > 0)
- self.assert_(res.find("221") > 0)
- # correct interval
- self.assertTrue(self.vty.verify("subscriber-create-on-demand random 221 222", ['']))
- self.vty.command("end")
-
- # create subscriber with extension in a configured interval
- 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)
-
- # Delete it
- res = self.vty.command('subscriber imsi ' + imsi + ' delete')
- self.assert_(res != "")
- # imsi2 is inactive so deletion should succeed
- res = self.vty.command('subscriber imsi ' + imsi2 + ' delete')
- self.assert_("" == res)
+ self.assertTrue(self.vty.verify('subscriber-create-on-demand', ["% 'subscriber-create-on-demand' is no longer supported.", '% This is now up to osmo-hlr.']))
def testShowPagingGroup(self):
res = self.vty.command("show paging-group 255 1234567")