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