aboutsummaryrefslogtreecommitdiffstats
path: root/include/osmocom/msc/vlr.h
blob: 3b9bbc41cfd345a44976409d7e0154da98ab83a2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
#pragma once

#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/fsm.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/use_count.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/gsm/gsm23003.h>
#include <osmocom/gsm/gsm0808.h>
#include <osmocom/gsm/gsup.h>
#include <osmocom/gsm/ipa.h>
#include <osmocom/msc/ran_conn.h>
#include <osmocom/msc/msc_common.h>
#include <osmocom/gsupclient/gsup_client.h>
#include <osmocom/msc/vlr_sgs.h>

#define LOGGSUPP(level, gsup, fmt, args...)				\
	LOGP(DVLR, level, "GSUP(%s) " fmt, (gsup)->imsi, ## args)

#define LOGVSUBP(level, vsub, fmt, args...)				\
	LOGP(DVLR, level, "SUBSCR(%s) " fmt, vlr_subscr_name(vsub), ## args)

struct log_target;
struct osmo_mobile_identity;

#define VLR_SUBSCRIBER_NO_EXPIRATION	0
#define VLR_SUBSCRIBER_LU_EXPIRATION_INTERVAL	60	/* in seconds */

#define VSUB_USE_ATTACHED "attached"

/* from 3s to 10s */
#define GSM_29002_TIMER_S	10
/* from 15s to 30s */
#define GSM_29002_TIMER_M	30
/* from 1min to 10min */
#define GSM_29002_TIMER_ML	(10*60)
/* 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 authenticated */
	VLR_SUB_AS_NEEDS_AUTH,
	/* waiting for AuthInfo from HLR/AUC */
	VLR_SUB_AS_NEEDS_AUTH_WAIT_AI,
	/* waiting for response from subscriber */
	VLR_SUB_AS_WAIT_RESP,
	/* successfully authenticated */
	VLR_SUB_AS_AUTHENTICATED,
	/* subscriber needs re-sync */
	VLR_SUB_AS_NEEDS_RESYNC,
	/* waiting for AuthInfo with ReSync */
	VLR_SUB_AS_NEEDS_AUTH_WAIT_SAI_RESYNC,
	/* waiting for response from subscr, resync case */
	VLR_SUB_AS_WAIT_RESP_RESYNC,
	/* waiting for IMSI from subscriber */
	VLR_SUB_AS_WAIT_ID_IMSI,
	/* authentication has failed */
	VLR_SUB_AS_AUTH_FAILED,
};

enum vlr_lu_event {
	VLR_ULA_E_UPDATE_LA,	/* Initial trigger (LU from MS) */
	VLR_ULA_E_SEND_ID_ACK,	/* Result of Send-ID from PVLR */
	VLR_ULA_E_SEND_ID_NACK,	/* Result of Send-ID from PVLR */
	VLR_ULA_E_AUTH_RES,	/* Result of auth procedure */
	VLR_ULA_E_CIPH_RES,	/* Result of Ciphering Mode Command */
	VLR_ULA_E_ID_IMSI,	/* IMSI received from MS */
	VLR_ULA_E_ID_IMEI,	/* IMEI received from MS */
	VLR_ULA_E_ID_IMEISV,	/* IMEISV received from MS */
	VLR_ULA_E_HLR_IMEI_ACK,	/* Check_IMEI_VLR result from HLR */
	VLR_ULA_E_HLR_IMEI_NACK,/* Check_IMEI_VLR result from HLR */
	VLR_ULA_E_HLR_LU_RES,	/* HLR UpdateLocation result */
	VLR_ULA_E_UPD_HLR_COMPL,/* UpdatE_HLR_VLR result */
	VLR_ULA_E_LU_COMPL_SUCCESS,/* Location_Update_Completion_VLR result */
	VLR_ULA_E_LU_COMPL_FAILURE,/* Location_Update_Completion_VLR result */
	VLR_ULA_E_NEW_TMSI_ACK,	/* TMSI Reallocation Complete */
};

enum vlr_ciph_result_cause {
	VLR_CIPH_REJECT, /* ? */
	VLR_CIPH_COMPL,
};

struct vlr_auth_tuple {
	int use_count;
	int key_seq;
	struct osmo_auth_vector vec;
};
#define VLR_KEY_SEQ_INVAL	7	/* GSM 04.08 - 10.5.1.2 */


enum vlr_subscr_security_context {
	VLR_SEC_CTX_NONE,
	VLR_SEC_CTX_GSM,
	VLR_SEC_CTX_UMTS,
};

enum vlr_lu_type {
	VLR_LU_TYPE_PERIODIC,
	VLR_LU_TYPE_IMSI_ATTACH,
	VLR_LU_TYPE_REGULAR,
};

#define OSMO_LBUF_DECL(name, xlen)		\
	struct {				\
		uint8_t buf[xlen];		\
		size_t len;			\
	} name

struct sgsn_mm_ctx;
struct vlr_instance;

#define VLR_NAME_LENGTH 160

/* The VLR subscriber is the part of the GSM subscriber state in VLR (CS) or
 * SGSN (PS), particularly while interacting with the HLR via GSUP */
struct vlr_subscr {
	struct llist_head list;
	struct vlr_instance *vlr;

	/* TODO either populate from HLR or drop this completely? */
	long long unsigned int id;

	/* Data from HLR */				/* 3GPP TS 23.008 */
	/* Always use vlr_subscr_set_imsi() to write to imsi[] */
	char imsi[GSM23003_IMSI_MAX_DIGITS+1];		/* 2.1.1.1 */
	char msisdn[GSM23003_MSISDN_MAX_DIGITS+1];	/* 2.1.2 */
	char name[VLR_NAME_LENGTH+1];			/* proprietary */
	OSMO_LBUF_DECL(hlr, 16);			/* 2.4.7 */
	uint32_t age_indicator;				/* 2.17.1 */

	/* Authentication Data */
	struct vlr_auth_tuple auth_tuples[5];		/* 2.3.1-2.3.4 */
	struct vlr_auth_tuple *last_tuple;
	enum vlr_subscr_security_context sec_ctx;

	/* Data local to VLR is below */
	uint32_t tmsi;					/* 2.1.4 */
	/* Newly allocated TMSI that was not yet acked by MS */
	uint32_t tmsi_new;

	struct osmo_cell_global_id cgi;			/* 2.4.16 */

	char imeisv[GSM23003_IMEISV_NUM_DIGITS+1];	/* 2.2.3 */
	char imei[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];	/* 2.1.9 */
	bool imsi_detached_flag;			/* 2.7.1 */
	bool conf_by_radio_contact_ind;			/* 2.7.4.1 */
	bool sub_dataconf_by_hlr_ind;			/* 2.7.4.2 */
	bool loc_conf_in_hlr_ind;			/* 2.7.4.3 */
	bool dormant_ind;				/* 2.7.8 */
	bool cancel_loc_rx;				/* 2.7.8A */
	bool ms_not_reachable_flag;			/* 2.10.2 (MNRF) */
	bool la_allowed;

	struct osmo_use_count use_count;
	struct osmo_use_count_entry use_count_buf[8];
	int32_t max_total_use_count;

	struct osmo_fsm_inst *lu_fsm;
	struct osmo_fsm_inst *auth_fsm;
	struct osmo_fsm_inst *proc_arq_fsm;
	struct osmo_fsm_inst *sgs_fsm;

	bool lu_complete;
	time_t expire_lu;

	void *msc_conn_ref;

	/* PS (SGSN) specific parts */
	struct {
		struct llist_head pdp_list;
	} ps;
	/* CS (NITB/CSCN) specific parts */
	struct {
		/* pending requests */
		bool is_paging;
		struct osmo_timer_list paging_response_timer;
		/* list of struct subscr_request */
		struct llist_head requests;
		uint8_t lac;
		enum osmo_rat_type attached_via_ran;
	} cs;
	/* SGs (MME) specific parts */
	struct {
		struct vlr_sgs_cfg cfg;
		char mme_name[SGS_MME_NAME_LEN + 1];
		struct osmo_location_area_id lai;
		vlr_sgs_lu_response_cb_t response_cb;
		vlr_sgs_lu_paging_cb_t paging_cb;
		vlr_sgs_lu_mminfo_cb_t mminfo_cb;
		enum sgsap_service_ind paging_serv_ind;
		struct osmo_timer_list Ts5;
	} sgs;

	struct osmo_gsm48_classmark classmark;
};

enum vlr_ciph {
	VLR_CIPH_NONE = 0, /*< A5/0, no encryption */
	VLR_CIPH_A5_1 = 1, /*< A5/1, encryption */
	VLR_CIPH_A5_2 = 2, /*< A5/2, deprecated export-grade encryption */
	VLR_CIPH_A5_3 = 3, /*< A5/3, 'new secure' encryption */
};

static inline uint8_t vlr_ciph_to_gsm0808_alg_id(enum vlr_ciph ciph)
{
	switch (ciph) {
	case VLR_CIPH_NONE:
		return GSM0808_ALG_ID_A5_0;
	case VLR_CIPH_A5_1:
		return GSM0808_ALG_ID_A5_1;
	case VLR_CIPH_A5_2:
		return GSM0808_ALG_ID_A5_2;
	case VLR_CIPH_A5_3:
		return GSM0808_ALG_ID_A5_3;
	default:
		return GSM0808_ALG_ID_A5_7;
	}
}

struct vlr_ops {
	/* encode + transmit an AUTH REQ towards the MS.
	 * \param[in] at  auth tuple providing rand, key_seq and autn.
	 * \param[in] send_autn  True to send AUTN, for r99 UMTS auth.
	 */
	int (*tx_auth_req)(void *msc_conn_ref, struct vlr_auth_tuple *at,
			   bool send_autn);
	/* encode + transmit an AUTH REJECT towards the MS */
	int (*tx_auth_rej)(void *msc_conn_ref);

	/* encode + transmit an IDENTITY REQUEST towards the MS */
	int (*tx_id_req)(void *msc_conn_ref, uint8_t mi_type);

	int (*tx_lu_acc)(void *msc_conn_ref, uint32_t send_tmsi);
	int (*tx_lu_rej)(void *msc_conn_ref, enum gsm48_reject_value cause);
	int (*tx_cm_serv_acc)(void *msc_conn_ref, enum osmo_cm_service_type cm_service_type);
	int (*tx_cm_serv_rej)(void *msc_conn_ref, enum osmo_cm_service_type cm_service_type,
			      enum gsm48_reject_value cause);

	int (*set_ciph_mode)(void *msc_conn_ref, bool umts_aka, bool retrieve_imeisv);

	int (*tx_common_id)(void *msc_conn_ref);

	int (*tx_mm_info)(void *msc_conn_ref);

	/* notify MSC/SGSN that the subscriber data in VLR has been updated */
	void (*subscr_update)(struct vlr_subscr *vsub);
	/* notify MSC/SGSN that the given subscriber has been associated
	 * with this msc_conn_ref */
	int (*subscr_assoc)(void *msc_conn_ref, struct vlr_subscr *vsub);
};

/* An instance of the VLR codebase */
struct vlr_instance {
	struct llist_head subscribers;
	struct llist_head operations;
	struct gsup_client_mux *gcm;
	struct vlr_ops ops;
	struct osmo_timer_list lu_expire_timer;
	struct {
		bool retrieve_imeisv_early;
		bool retrieve_imeisv_ciphered;
		bool assign_tmsi;
		bool check_imei_rqd;
		int auth_tuple_max_reuse_count;
		bool auth_reuse_old_sets_on_error;
		bool parq_retrieve_imsi;
		bool is_ps;
		uint8_t nri_bitlen;
		struct osmo_nri_ranges *nri_ranges;
	} cfg;
	/* A free-form pointer for use by the caller */
	void *user_ctx;
};

extern const struct value_string vlr_ciph_names[];
static inline const char *vlr_ciph_name(enum vlr_ciph val)
{
	return get_value_string(vlr_ciph_names, val);
}

/* Location Updating request */
struct osmo_fsm_inst *
vlr_loc_update(struct osmo_fsm_inst *parent,
	       uint32_t parent_event_success,
	       uint32_t parent_event_failure,
	       void *parent_event_data,
	       struct vlr_instance *vlr, void *msc_conn_ref,
	       enum vlr_lu_type type, uint32_t tmsi, const char *imsi,
	       const struct osmo_location_area_id *old_lai,
	       const struct osmo_location_area_id *new_lai,
	       bool authentication_required,
	       bool ciphering_required,
	       uint8_t key_seq,
	       bool is_r99, bool is_utran,
	       bool assign_tmsi);

void vlr_loc_update_cancel(struct osmo_fsm_inst *fi,
			   enum osmo_fsm_term_cause fsm_cause,
			   uint8_t gsm48_cause);

/* tell the VLR that the RAN connection is gone */
int vlr_subscr_disconnected(struct vlr_subscr *vsub);
bool vlr_subscr_expire(struct vlr_subscr *vsub);
int vlr_subscr_rx_id_resp(struct vlr_subscr *vsub, const struct osmo_mobile_identity *mi);
int vlr_subscr_rx_auth_resp(struct vlr_subscr *vsub, bool is_r99, bool is_utran,
			    const uint8_t *res, uint8_t res_len);
int vlr_subscr_rx_auth_fail(struct vlr_subscr *vsub, const uint8_t *auts);
int vlr_subscr_tx_auth_fail_rep(const struct vlr_subscr *vsub) __attribute__((warn_unused_result));
void vlr_subscr_rx_ciph_res(struct vlr_subscr *vsub, enum vlr_ciph_result_cause result);
int vlr_subscr_rx_tmsi_reall_compl(struct vlr_subscr *vsub);
int vlr_subscr_rx_imsi_detach(struct vlr_subscr *vsub);

struct vlr_instance *vlr_alloc(void *ctx, const struct vlr_ops *ops);
int vlr_start(struct vlr_instance *vlr, struct gsup_client_mux *gcm);
int vlr_gsup_rx(struct gsup_client_mux *gcm, void *data, const struct osmo_gsup_message *gsup_msg);

/* internal use only */

void sub_pres_vlr_fsm_start(struct osmo_fsm_inst **fsm,
			    struct osmo_fsm_inst *parent,
			    struct vlr_subscr *vsub,
			    uint32_t term_event);
struct osmo_fsm_inst *
upd_hlr_vlr_proc_start(struct osmo_fsm_inst *parent,
		       struct vlr_subscr *vsub,
		       uint32_t parent_event);

struct osmo_fsm_inst *
lu_compl_vlr_proc_start(struct osmo_fsm_inst *parent,
			struct vlr_subscr *vsub,
			void *msc_conn_ref,
			uint32_t parent_event_success,
			uint32_t parent_event_failure);


const char *vlr_subscr_name(const struct vlr_subscr *vsub);
const char *vlr_subscr_short_name(const struct vlr_subscr *vsub, unsigned int maxlen);
const char *vlr_subscr_msisdn_or_name(const struct vlr_subscr *vsub);

#define vlr_subscr_find_by_imsi(vlr, imsi, USE) \
	_vlr_subscr_find_by_imsi(vlr, imsi, USE, __FILE__, __LINE__)
#define vlr_subscr_find_or_create_by_imsi(vlr, imsi, USE, created) \
	_vlr_subscr_find_or_create_by_imsi(vlr, imsi, USE, created, \
					   __FILE__, __LINE__)

#define vlr_subscr_find_by_tmsi(vlr, tmsi, USE) \
	_vlr_subscr_find_by_tmsi(vlr, tmsi, USE, __FILE__, __LINE__)
#define vlr_subscr_find_or_create_by_tmsi(vlr, tmsi, USE, created) \
	_vlr_subscr_find_or_create_by_tmsi(vlr, tmsi, USE, created, \
					   __FILE__, __LINE__)

#define vlr_subscr_find_by_msisdn(vlr, msisdn, USE) \
	_vlr_subscr_find_by_msisdn(vlr, msisdn, USE, __FILE__, __LINE__)

struct vlr_subscr *_vlr_subscr_find_by_imsi(struct vlr_instance *vlr,
					    const char *imsi,
					    const char *use,
					    const char *file, int line);
struct vlr_subscr *_vlr_subscr_find_or_create_by_imsi(struct vlr_instance *vlr,
						      const char *imsi,
						      const char *use,
						      bool *created,
						      const char *file,
						      int line);

struct vlr_subscr *_vlr_subscr_find_by_tmsi(struct vlr_instance *vlr,
					    uint32_t tmsi,
					    const char *use,
					    const char *file, int line);
struct vlr_subscr *_vlr_subscr_find_or_create_by_tmsi(struct vlr_instance *vlr,
						      uint32_t tmsi,
						      const char *use,
						      bool *created,
						      const char *file,
						      int line);

struct vlr_subscr *_vlr_subscr_find_by_msisdn(struct vlr_instance *vlr,
					      const char *msisdn,
					      const char *use,
					      const char *file, int line);

#define vlr_subscr_get(VSUB, USE) vlr_subscr_get_src(VSUB, USE, __FILE__, __LINE__)
#define vlr_subscr_put(VSUB, USE) vlr_subscr_put_src(VSUB, USE, __FILE__, __LINE__)

#define vlr_subscr_get_src(VSUB, USE, SRCFILE, SRCLINE) \
	OSMO_ASSERT((VSUB) && _osmo_use_count_get_put(&(VSUB)->use_count, USE, 1, SRCFILE, SRCLINE) == 0)
#define vlr_subscr_put_src(VSUB, USE, SRCFILE, SRCLINE) \
	OSMO_ASSERT((VSUB) && _osmo_use_count_get_put(&(VSUB)->use_count, USE, -1, SRCFILE, SRCLINE) == 0)

void vlr_subscr_free(struct vlr_subscr *vsub);
int vlr_subscr_alloc_tmsi(struct vlr_subscr *vsub);

void vlr_subscr_set_imsi(struct vlr_subscr *vsub, const char *imsi);
void vlr_subscr_set_imei(struct vlr_subscr *vsub, const char *imei);
void vlr_subscr_set_imeisv(struct vlr_subscr *vsub, const char *imeisv);
void vlr_subscr_set_msisdn(struct vlr_subscr *vsub, const char *msisdn);

bool vlr_subscr_matches_imsi(struct vlr_subscr *vsub, const char *imsi);
bool vlr_subscr_matches_tmsi(struct vlr_subscr *vsub, uint32_t tmsi);
bool vlr_subscr_matches_msisdn(struct vlr_subscr *vsub, const char *msisdn);
bool vlr_subscr_matches_imei(struct vlr_subscr *vsub, const char *imei);

uint32_t vlr_timer(struct vlr_instance *vlr, uint32_t timer);

int vlr_subscr_changed(struct vlr_subscr *vsub);
int vlr_subscr_purge(struct vlr_subscr *vsub) __attribute__((warn_unused_result));
void vlr_subscr_cancel_attach_fsm(struct vlr_subscr *vsub,
				  enum osmo_fsm_term_cause fsm_cause,
				  uint8_t gsm48_cause);

void vlr_subscr_enable_expire_lu(struct vlr_subscr *vsub);

/* Process Access Request FSM */

enum proc_arq_vlr_event {
	PR_ARQ_E_START,
	PR_ARQ_E_ID_IMSI,
	PR_ARQ_E_AUTH_RES,
	PR_ARQ_E_CIPH_RES,
	PR_ARQ_E_UPD_LOC_RES,
	PR_ARQ_E_TRACE_RES,
	PR_ARQ_E_IMEI_RES,
	PR_ARQ_E_PRES_RES,
	PR_ARQ_E_TMSI_ACK,
};

enum vlr_parq_type {
	VLR_PR_ARQ_T_INVALID = 0, /* to guard against unset vars */
	VLR_PR_ARQ_T_CM_SERV_REQ,
	VLR_PR_ARQ_T_PAGING_RESP,
	/* FIXME: differentiate between services of 24.008 10.5.3.3 */
};

/* Process Access Request (CM SERV REQ / PAGING RESP) */
void
vlr_proc_acc_req(struct osmo_fsm_inst *parent,
		 uint32_t parent_event_success,
		 uint32_t parent_event_failure,
		 void *parent_event_data,
		 struct vlr_instance *vlr, void *msc_conn_ref,
		 enum vlr_parq_type type, enum osmo_cm_service_type cm_service_type,
		 const struct osmo_mobile_identity *mi,
		 const struct osmo_location_area_id *lai,
		 bool authentication_required,
		 bool ciphering_required,
		 uint8_t key_seq,
		 bool is_r99, bool is_utran);

void vlr_parq_cancel(struct osmo_fsm_inst *fi,
		     enum osmo_fsm_term_cause fsm_cause,
		     enum gsm48_reject_value gsm48_cause);

void vlr_parq_fsm_init(void);

int vlr_set_ciph_mode(struct vlr_instance *vlr,
		      struct osmo_fsm_inst *fi,
		      void *msc_conn_ref,
		      bool ciph_required,
		      bool umts_aka,
		      bool retrieve_imeisv);

bool vlr_use_umts_aka(struct osmo_auth_vector *vec, bool is_r99);

void log_set_filter_vlr_subscr(struct log_target *target,
			       struct vlr_subscr *vlr_subscr);

void vlr_gmm_cause_to_mm_cause(enum gsm48_gmm_cause gmm_cause,
			       enum gsm48_reject_value *gsm48_rej_p);