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
This commit is contained in:
Harald Welte 2016-06-19 18:06:02 +02:00 committed by Neels Hofmeyr
parent b8b85a1b2e
commit 2483f1b050
74 changed files with 2215 additions and 4257 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 \

View File

@ -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)

View File

@ -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 */

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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]
@ -69,20 +71,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
*/
@ -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;
/* libbsc subscriber information */
/* 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 (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 */

View File

@ -95,7 +95,6 @@ enum bts_gprs_mode {
};
struct gsm_lchan;
struct gsm_subscriber;
struct gsm_mncc;
struct osmo_rtp_socket;
struct rtp_socket;

View File

@ -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 */

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -1,7 +0,0 @@
#ifndef _TOKEN_AUTH_H
#define _TOKEN_AUTH_H
void on_dso_load_token(void);
#endif /* _TOKEN_AUTH_H */

View File

@ -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

View File

@ -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 */

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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);
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 (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);
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)) {

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
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;
}
struct gsm_subscriber *subscr_alloc(void)
const char *vlr_subscr_msisdn_or_name(struct vlr_subscr *vsub)
{
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;
}
int subscr_purge_inactive(struct gsm_subscriber_group *sgrp)
{
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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

@ -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;
subscr = subscr_get_by_imsi(net->subscr_group, cmd->value);
if (!subscr) {
cmd->reply = "Failed to find subscriber";
cmd->reply = "Command moved to osmo-hlr, no longer available here";
return CTRL_CMD_ERROR;
}
if (subscr->use_count != 1) {
LOGP(DCTRL, LOGL_NOTICE, "Going to remove active subscriber.\n");
was_used = 1;
}
rc = db_subscriber_delete(subscr);
subscr_put(subscr);
if (rc != 0) {
cmd->reply = "Failed to remove subscriber";
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)
{
struct vlr_subscr *vsub;
if (!msc_ctrl_net) {
cmd->reply = "MSC CTRL commands not initialized";
return CTRL_CMD_ERROR;
}
if (!msc_ctrl_net->vlr) {
cmd->reply = "VLR not initialized";
return CTRL_CMD_ERROR;
}
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);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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,7 +312,7 @@ try_local:
#endif
/* determine gsms->receiver based on dialled number */
gsms->receiver = subscr_get_by_extension(conn->network->subscr_group,
gsms->receiver = vlr_subscr_find_by_msisdn(conn->network->vlr,
gsms->dst.addr);
if (!gsms->receiver) {
#ifdef BUILD_SMPP
@ -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,17 +1006,17 @@ 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,
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);
@ -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;

View File

@ -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,
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_connection *connection_for_subscr(struct vlr_subscr *vsub)
{
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)
{
/* 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;
}

View File

@ -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 */

View File

@ -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;
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 };
DEBUGP(DRR, "CIPHERING MODE COMPLETE\n");
/* Safety check */
if (!conn->sec_operation) {
DEBUGP(DRR, "No authentication/cipher operation in progress !!!\n");
if (!gh) {
LOGP(DRR, LOGL_ERROR, "invalid: msgb without l3 header\n");
return;
}
/* FIXME: check for MI (if any) */
/* 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);
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;
}
/* Complete the operation */
release_security_operation(conn);
DEBUGP(DRR, "%s: CIPHERING MODE COMPLETE\n",
vlr_subscr_name(conn->vsub));
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;
}
}
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;
/* skip releasing of silent calls as they have no transaction */
if (conn->silent_call)
return;
/* If we're closing in a middle of a trans, we need to clean up */
trans_conn_closed(conn);
/* check if there is a pending operation */
if (conn->loc_operation || conn->sec_operation || conn->anch_operation)
return;
if (trans_has_conn(conn))
return;
/* no more connections, asking to release the channel */
/*
* 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);
conn->in_release = 1;
gsm0808_clear(conn);
msc_subscr_con_free(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;
}
}
/* 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;
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 (!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);
}
/* 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);
if (conn->in_release)
return NULL;
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);
}
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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)
{
switch (event) {
case SUBSCR_CONN_E_COMMUNICATING:
/* no-op */
return;
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;
/* 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));
return;
}
conn->conn_fsm = NULL;
msc_subscr_conn_close(conn, cause);
msc_subscr_conn_put(conn);
}
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);
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;
}
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) |
.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);

View File

@ -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);
}

View File

@ -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;
}
}
}

View File

@ -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);

View File

@ -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;
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,
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,
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);
vty_out(vty, "%% 'subscriber create' now needs to be done at osmo-hlr%s",
VTY_NEWLINE);
return CMD_WARNING;
}
}
/* Show info about the created subscriber. */
subscr_dump_full_vty(vty, subscr);
subscr_put(subscr);
return CMD_SUCCESS;
}
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);
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;
}
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;
}
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);
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;
}
subscr->authorized = atoi(argv[2]);
db_sync_subscriber(subscr);
subscr_put(subscr);
return CMD_SUCCESS;
}
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);
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;
}
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;
}
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);
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;
}
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;
}
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);
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;
}
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;
}
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);
vty_out(vty, "%% 'subscriber update' is no longer supported.%s",
VTY_NEWLINE);
return CMD_WARNING;
}
subscr_update_from_db(subscr);
subscr_put(subscr);
return CMD_SUCCESS;
}
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);
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;
}
gsmnet->ext_min = mi;
gsmnet->ext_max = ma;
return CMD_SUCCESS;
}
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);

View File

@ -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"

View File

@ -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;

View File

@ -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

View File

@ -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) \

View File

@ -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;

View File

@ -1,3 +1 @@
Testing the gsm_subscriber chan logic
Reached, didn't crash, test passed
Testing subslot numbers for pchan types

View File

@ -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

View File

@ -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() {}

View File

@ -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)

View File

@ -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;
}

View File

@ -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

View File

@ -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)

View File

@ -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;
}

View File

View File

@ -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

View File

@ -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)

View File

@ -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;
}

View File

@ -1,3 +0,0 @@
Testing subscriber core code.
Test subscriber allocation and deletion
Done

View File

@ -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

View File

@ -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")