blob: 12d3e5c2665d1fef600d6aa27f41c19524e43688 [file] [log] [blame]
Neels Hofmeyr1971b672020-01-23 04:40:51 +01001#include <osmocom/core/linuxlist.h>
2#include <osmocom/core/utils.h>
3#include <osmocom/core/fsm.h>
4#include <osmocom/core/tdef.h>
5#include <osmocom/gsm/apn.h>
6#include <osmocom/hlr/hlr.h>
7#include <osmocom/hlr/proxy_mm.h>
8#include <osmocom/hlr/proxy_db.h>
9
10enum proxy_mm_fsm_state {
11 PROXY_MM_ST_READY,
12 PROXY_MM_ST_WAIT_SUBSCR_DATA,
13 PROXY_MM_ST_WAIT_GSUP_ISD_RESULT,
14 PROXY_MM_ST_WAIT_AUTH_TUPLES,
15};
16
17static const struct value_string proxy_mm_fsm_event_names[] = {
18 OSMO_VALUE_STRING(PROXY_MM_EV_SUBSCR_INVALID),
19 OSMO_VALUE_STRING(PROXY_MM_EV_RX_GSUP_LU),
20 OSMO_VALUE_STRING(PROXY_MM_EV_RX_GSUP_SAI),
21 OSMO_VALUE_STRING(PROXY_MM_EV_RX_SUBSCR_DATA),
22 OSMO_VALUE_STRING(PROXY_MM_EV_RX_GSUP_ISD_RESULT),
23 OSMO_VALUE_STRING(PROXY_MM_EV_RX_AUTH_TUPLES),
24 {}
25};
26
27static struct osmo_fsm proxy_mm_fsm;
28static struct osmo_fsm proxy_to_home_fsm;
29
30struct osmo_tdef proxy_mm_tdefs[] = {
31// FIXME
32 { .T=-1, .default_val=5, .desc="proxy_mm ready timeout" },
33 { .T=-2, .default_val=5, .desc="proxy_mm wait_subscr_data timeout" },
34 { .T=-3, .default_val=5, .desc="proxy_mm wait_gsup_isd_result timeout" },
35 { .T=-4, .default_val=5, .desc="proxy_mm wait_auth_tuples timeout" },
36 {}
37};
38
39static const struct osmo_tdef_state_timeout proxy_mm_fsm_timeouts[32] = {
40// FIXME
41 [PROXY_MM_ST_READY] = { .T=-1 },
42 [PROXY_MM_ST_WAIT_SUBSCR_DATA] = { .T=-2 },
43 [PROXY_MM_ST_WAIT_GSUP_ISD_RESULT] = { .T=-3 },
44 [PROXY_MM_ST_WAIT_AUTH_TUPLES] = { .T=-4 },
45};
46
47#define proxy_mm_fsm_state_chg(state) \
48 osmo_tdef_fsm_inst_state_chg(mm_fi, state, \
49 proxy_mm_fsm_timeouts, \
50 proxy_mm_tdefs, \
51 5)
52
53LLIST_HEAD(proxy_mm_list);
54
55struct proxy_mm *proxy_mm_alloc(const struct osmo_gsup_peer_id *vlr_name,
56 bool is_ps,
57 const char *imsi)
58{
59 struct proxy_mm *proxy_mm;
60
61 struct osmo_fsm_inst *mm_fi = osmo_fsm_inst_alloc(&proxy_mm_fsm, g_hlr, NULL, LOGL_DEBUG, imsi);
62 OSMO_ASSERT(mm_fi);
63
64 proxy_mm = talloc(mm_fi, struct proxy_mm);
65 OSMO_ASSERT(proxy_mm);
66 mm_fi->priv = proxy_mm;
67 *proxy_mm = (struct proxy_mm){
68 .mm_fi = mm_fi,
69 .is_ps = is_ps,
70 };
71 OSMO_STRLCPY_ARRAY(proxy_mm->imsi, imsi);
72 INIT_LLIST_HEAD(&proxy_mm->auth_cache);
73
74 llist_add(&proxy_mm->entry, &proxy_mm_list);
75
76 proxy_mm->to_home_fi = osmo_fsm_inst_alloc_child(&proxy_to_home_fsm, mm_fi, PROXY_MM_EV_SUBSCR_INVALID);
77 proxy_mm->to_home_fi->priv = proxy_mm;
78
79 /* Do a state change to activate timeout */
80 proxy_mm_fsm_state_chg(PROXY_MM_ST_READY);
81
82 return proxy_mm;
83}
84
85void proxy_mm_add_auth_vectors(struct proxy_mm *proxy_mm,
86 const struct osmo_auth_vector *auth_vectors, size_t num_auth_vectors)
87{
88 struct proxy_mm_auth_cache *ac = talloc_zero(proxy_mm, struct proxy_mm_auth_cache);
89 int i;
90 OSMO_ASSERT(ac);
91 ac->num_auth_vectors = num_auth_vectors;
92 for (i = 0; i < num_auth_vectors; i++)
93 ac->auth_vectors[i] = auth_vectors[i];
94 if (proxy_db_add_auth_vectors(&proxy_mm->vlr_name, ac)) {
95 talloc_free(ac);
96 return;
97 }
98 llist_add(&ac->entry, &proxy_mm->auth_cache);
99}
100
101struct proxy_mm_auth_cache *proxy_mm_get_auth_vectors(struct proxy_mm *proxy_mm)
102{
103 struct proxy_mm_auth_cache *i;
104 struct proxy_mm_auth_cache *ac = NULL;
105
106 llist_for_each_entry(i, &proxy_mm->auth_cache, entry) {
107 if (!ac || i->sent_to_vlr_count < ac->sent_to_vlr_count) {
108 ac = i;
109 }
110 }
111
112 /* ac now points to (one of) the least used auth cache entries (or NULL if none). */
113 return ac;
114}
115
116void proxy_mm_discard_auth_vectors(struct proxy_mm *proxy_mm, struct proxy_mm_auth_cache *ac)
117{
118 proxy_db_drop_auth_vectors(ac->db_id);
119 llist_del(&ac->entry);
120 talloc_free(ac);
121}
122
123/* Mark given auth cache entries as sent to the VLR and clean up if necessary. */
124void proxy_mm_use_auth_vectors(struct proxy_mm *proxy_mm, struct proxy_mm_auth_cache *ac)
125{
126 struct proxy_mm_auth_cache *i, *i_next;
127 bool found_fresh_ac = false;
128
129 /* The aim is to keep at least one set of already used auth tuples in the cache. If there are still fresh ones,
130 * all used auth vectors can be discarded. If there are no fresh ones left, keep only this last set. */
131
132 llist_for_each_entry_safe(i, i_next, &proxy_mm->auth_cache, entry) {
133 if (i == ac)
134 continue;
135 if (i->sent_to_vlr_count) {
136 /* An auth entry other than this freshly used one, which has been used before.
137 * No need to keep it. */
138 proxy_mm_discard_auth_vectors(proxy_mm, i);
139 continue;
140 }
141 if (!i->sent_to_vlr_count)
142 found_fresh_ac = true;
143 }
144
145 if (found_fresh_ac) {
146 /* There are still other, fresh auth vectors. */
147 proxy_mm_discard_auth_vectors(proxy_mm, ac);
148 } else {
149 /* else, only this ac remains in the list */
150 ac->sent_to_vlr_count++;
151 proxy_db_auth_vectors_update_sent_count(ac);
152 }
153}
154
155static void proxy_mm_ready_action(struct osmo_fsm_inst *mm_fi, uint32_t event, void *data)
156{
157 struct proxy *proxy = g_hlr->gs->proxy;
158 struct proxy_mm *proxy_mm = mm_fi->priv;
159
160 switch (event) {
161
162 case PROXY_MM_EV_SUBSCR_INVALID:
163 /* Home HLR has responded and rejected a Location Updating, or no home HLR could be found. The
164 * subscriber is invalid, remove it from the cache. */
165 proxy_subscr_del(proxy, proxy_mm->imsi);
166 osmo_fsm_inst_term(mm_fi, OSMO_FSM_TERM_REGULAR, NULL);
167 break;
168
169 case PROXY_MM_EV_RX_GSUP_LU:
170 /* The MSC asks for a LU. If we don't know details about this subscriber, then we'll have to wait for the
171 * home HLR to respond. If we already know details about the subscriber, we respond immediately (with
172 * Insert Subscriber Data and accept the LU), but also ask the home HLR to confirm the LU later. */
173 osmo_fsm_inst_dispatch(proxy_mm->to_home_fi, PROXY_TO_HOME_EV_CONFIRM_LU, NULL);
174
175 if (proxy_mm_subscriber_data_known(proxy_mm))
176 proxy_mm_fsm_state_chg(PROXY_MM_ST_WAIT_GSUP_ISD_RESULT);
177 else
178 proxy_mm_fsm_state_chg(PROXY_MM_ST_WAIT_SUBSCR_DATA);
179 break;
180
181 case PROXY_MM_EV_RX_GSUP_SAI:
182 // FIXME
183 break;
184
185 default:
186 OSMO_ASSERT(false);
187 }
188}
189
190static int proxy_mm_ready_timeout(struct osmo_fsm_inst *mm_fi)
191{
192 /* Return 1 to terminate FSM instance, 0 to keep running */
193 return 1;
194}
195
196void proxy_mm_wait_subscr_data_onenter(struct osmo_fsm_inst *mm_fi, uint32_t prev_state)
197{
198 //struct proxy_mm *proxy_mm = mm_fi->priv;
199 // FIXME
200}
201
202static void proxy_mm_wait_subscr_data_action(struct osmo_fsm_inst *mm_fi, uint32_t event, void *data)
203{
204 //struct proxy_mm *proxy_mm = mm_fi->priv;
205
206 switch (event) {
207
208 case PROXY_MM_EV_RX_SUBSCR_DATA:
209 // FIXME
210 break;
211
212 default:
213 OSMO_ASSERT(false);
214 }
215}
216
217static int proxy_mm_wait_subscr_data_timeout(struct osmo_fsm_inst *mm_fi)
218{
219 /* Return 1 to terminate FSM instance, 0 to keep running */
220 return 1;
221}
222
223static void proxy_mm_lu_error(struct osmo_fsm_inst *mm_fi)
224{
225 osmo_gsup_req_respond_err(req, GMM_CAUSE_ROAMING_NOTALLOWED,
226 "LU does not accept GSUP rx");
227
228}
229
230void proxy_mm_wait_gsup_isd_result_onenter(struct osmo_fsm_inst *mm_fi, uint32_t prev_state)
231{
232 struct proxy_mm *proxy_mm = mm_fi->priv;
233 struct proxy_subscr proxy_subscr;
234 struct osmo_gsup_message isd_req;
235
236 uint8_t msisdn_enc[OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN];
237 uint8_t apn[APN_MAXLEN];
238
239 isd_req.message_type = OSMO_GSUP_MSGT_INSERT_DATA_REQUEST;
240
241 if (proxy_subscr_get_by_imsi(&proxy_subscr, g_hlr->gs->proxy, proxy_mm->imsi)) {
242 LOGPFSML(mm_fi, LOGL_ERROR,
243 "Proxy: trying to send cached Subscriber Data, but there is no proxy entry\n");
244 proxy_mm_lu_error(mm_fi);
245 return;
246 }
247
248 if (proxy_subscr.msisdn[0] == '\0') {
249 LOGPFSML(mm_fi, LOGL_ERROR,
250 "Proxy: trying to send cached Subscriber Data, but subscriber has no MSISDN in proxy cache\n");
251 proxy_mm_lu_error(mm_fi);
252 return;
253 }
254
255 if (osmo_gsup_create_insert_subscriber_data_msg(&isd_req, proxy_mm->imsi,
256 proxy_subscr->msisdn, msisdn_enc, sizeof(msisdn_enc),
257 apn, sizeof(apn),
258 proxy_mm->is_ps? OSMO_GSUP_CN_DOMAIN_PS : OSMO_GSUP_CN_DOMAIN_CS)) {
259 LOGPFSML(mm_fi, LOGL_ERROR, "Proxy: failed to send cached Subscriber Data\n");
260 proxy_mm_lu_error(mm_fi);
261 return;
262 }
263}
264
265static void proxy_mm_wait_gsup_isd_result_action(struct osmo_fsm_inst *mm_fi, uint32_t event, void *data)
266{
267 //struct proxy_mm *proxy_mm = mm_fi->priv;
268
269 switch (event) {
270
271 case PROXY_MM_EV_RX_GSUP_ISD_RESULT:
272 // FIXME
273 break;
274
275 default:
276 OSMO_ASSERT(false);
277 }
278}
279
280static int proxy_mm_wait_gsup_isd_result_timeout(struct osmo_fsm_inst *mm_fi)
281{
282 /* Return 1 to terminate FSM instance, 0 to keep running */
283 return 1;
284}
285
286void proxy_mm_wait_auth_tuples_onenter(struct osmo_fsm_inst *mm_fi, uint32_t prev_state)
287{
288 //struct proxy_mm *proxy_mm = mm_fi->priv;
289 // FIXME
290}
291
292static void proxy_mm_wait_auth_tuples_action(struct osmo_fsm_inst *mm_fi, uint32_t event, void *data)
293{
294 //struct proxy_mm *proxy_mm = mm_fi->priv;
295
296 switch (event) {
297
298 case PROXY_MM_EV_RX_AUTH_TUPLES:
299 // FIXME
300 break;
301
302 default:
303 OSMO_ASSERT(false);
304 }
305}
306
307static int proxy_mm_wait_auth_tuples_timeout(struct osmo_fsm_inst *mm_fi)
308{
309 /* Return 1 to terminate FSM instance, 0 to keep running */
310 return 1;
311}
312
313#define S(x) (1 << (x))
314
315static const struct osmo_fsm_state proxy_mm_fsm_states[] = {
316 [PROXY_MM_ST_READY] = {
317 .name = "ready",
318 .in_event_mask = 0
319 | S(PROXY_MM_EV_SUBSCR_INVALID)
320 | S(PROXY_MM_EV_RX_GSUP_LU)
321 | S(PROXY_MM_EV_RX_GSUP_SAI)
322 ,
323 .out_state_mask = 0
324 | S(PROXY_MM_ST_READY)
325 | S(PROXY_MM_ST_WAIT_SUBSCR_DATA)
326 | S(PROXY_MM_ST_WAIT_GSUP_ISD_RESULT)
327 | S(PROXY_MM_ST_WAIT_AUTH_TUPLES)
328 ,
329 .action = proxy_mm_ready_action,
330 },
331 [PROXY_MM_ST_WAIT_SUBSCR_DATA] = {
332 .name = "wait_subscr_data",
333 .in_event_mask = 0
334 | S(PROXY_MM_EV_RX_SUBSCR_DATA)
335 ,
336 .out_state_mask = 0
337 | S(PROXY_MM_ST_WAIT_GSUP_ISD_RESULT)
338 | S(PROXY_MM_ST_READY)
339 ,
340 .onenter = proxy_mm_wait_subscr_data_onenter,
341 .action = proxy_mm_wait_subscr_data_action,
342 },
343 [PROXY_MM_ST_WAIT_GSUP_ISD_RESULT] = {
344 .name = "wait_gsup_isd_result",
345 .in_event_mask = 0
346 | S(PROXY_MM_EV_RX_GSUP_ISD_RESULT)
347 ,
348 .out_state_mask = 0
349 | S(PROXY_MM_ST_READY)
350 ,
351 .onenter = proxy_mm_wait_gsup_isd_result_onenter,
352 .action = proxy_mm_wait_gsup_isd_result_action,
353 },
354 [PROXY_MM_ST_WAIT_AUTH_TUPLES] = {
355 .name = "wait_auth_tuples",
356 .in_event_mask = 0
357 | S(PROXY_MM_EV_RX_AUTH_TUPLES)
358 ,
359 .out_state_mask = 0
360 | S(PROXY_MM_ST_READY)
361 ,
362 .onenter = proxy_mm_wait_auth_tuples_onenter,
363 .action = proxy_mm_wait_auth_tuples_action,
364 },
365};
366
367static int proxy_mm_fsm_timer_cb(struct osmo_fsm_inst *mm_fi)
368{
369 //struct proxy_mm *proxy_mm = mm_fi->priv;
370 switch (mm_fi->state) {
371
372 case PROXY_MM_ST_READY:
373 return proxy_mm_ready_timeout(mm_fi);
374
375 case PROXY_MM_ST_WAIT_SUBSCR_DATA:
376 return proxy_mm_wait_subscr_data_timeout(mm_fi);
377
378 case PROXY_MM_ST_WAIT_GSUP_ISD_RESULT:
379 return proxy_mm_wait_gsup_isd_result_timeout(mm_fi);
380
381 case PROXY_MM_ST_WAIT_AUTH_TUPLES:
382 return proxy_mm_wait_auth_tuples_timeout(mm_fi);
383
384 default:
385 /* Return 1 to terminate FSM instance, 0 to keep running */
386 return 1;
387 }
388}
389
390void proxy_mm_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
391{
392 struct proxy_mm *proxy_mm = fi->priv;
393 llist_del(&proxy_mm->entry);
394}
395
396static struct osmo_fsm proxy_mm_fsm = {
397 .name = "proxy_mm",
398 .states = proxy_mm_fsm_states,
399 .num_states = ARRAY_SIZE(proxy_mm_fsm_states),
400 .log_subsys = DLGLOBAL, // FIXME
401 .event_names = proxy_mm_fsm_event_names,
402 .timer_cb = proxy_mm_fsm_timer_cb,
403 .cleanup = proxy_mm_fsm_cleanup,
404};
405
406static __attribute__((constructor)) void proxy_mm_fsm_register(void)
407{
408 OSMO_ASSERT(osmo_fsm_register(&proxy_mm_fsm) == 0);
409}
410
411bool proxy_mm_subscriber_data_known(const struct proxy_mm *proxy_mm)
412{
413 struct proxy_subscr proxy_subscr;
414 if (proxy_subscr_get_by_imsi(&proxy_subscr, g_hlr->gs->proxy, proxy_mm->imsi))
415 return false;
416 return proxy_subscr.msisdn[0] != '\0';
417}