blob: 90f0ba7ad2fc1ff58b2ae1eb12ac662ba4fed6c8 [file] [log] [blame]
Harald Weltef5a0fa32019-03-03 15:44:18 +01001#include <stdint.h>
2#include <string.h>
3#include <unistd.h>
4#include <pthread.h>
5
6#include <osmocom/core/linuxlist.h>
7#include <osmocom/core/select.h>
8#include <osmocom/core/fsm.h>
9#include <osmocom/core/logging.h>
10#include <osmocom/core/socket.h>
11#include <osmocom/gsm/protocol/ipaccess.h>
12#include <osmocom/abis/ipa.h>
13
14#include <osmocom/rspro/RsproPDU.h>
15
16#include "debug.h"
17#include "rspro_util.h"
18#include "rspro_server.h"
19
20#define S(x) (1 << (x))
21
Harald Weltef5a0fa32019-03-03 15:44:18 +010022static RsproPDU_t *slotmap2CreateMappingReq(const struct slot_mapping *slotmap)
23{
24 ClientSlot_t clslot;
25 BankSlot_t bslot;
26
27 client_slot2rspro(&clslot, &slotmap->client);
28 bank_slot2rspro(&bslot, &slotmap->bank);
29
30 return rspro_gen_CreateMappingReq(&clslot, &bslot);
31}
32
33static RsproPDU_t *slotmap2RemoveMappingReq(const struct slot_mapping *slotmap)
34{
35 ClientSlot_t clslot;
36 BankSlot_t bslot;
37
38 client_slot2rspro(&clslot, &slotmap->client);
39 bank_slot2rspro(&bslot, &slotmap->bank);
40
41 return rspro_gen_RemoveMappingReq(&clslot, &bslot);
42}
43
44
45static void client_conn_send(struct rspro_client_conn *conn, RsproPDU_t *pdu)
46{
47 struct msgb *msg_tx = rspro_enc_msg(pdu);
48 if (!msg_tx) {
49 ASN_STRUCT_FREE(asn_DEF_RsproPDU, pdu);
50 return;
51 }
Harald Welteba781c02019-03-30 17:32:53 +010052 LOGPFSM(conn->fi, "Tx RSPRO %s\n", rspro_msgt_name(pdu));
Harald Weltef5a0fa32019-03-03 15:44:18 +010053 ipa_prepend_header_ext(msg_tx, IPAC_PROTO_EXT_RSPRO);
54 ipa_msg_push_header(msg_tx, IPAC_PROTO_OSMO);
55 ipa_server_conn_send(conn->peer, msg_tx);
56}
57
58
59/***********************************************************************
60 * per-client connection FSM
61 ***********************************************************************/
62
63static void rspro_client_conn_destroy(struct rspro_client_conn *conn);
64
65enum remsim_server_client_fsm_state {
66 CLNTC_ST_INIT,
67 CLNTC_ST_ESTABLISHED,
Harald Weltee5c77732019-03-07 23:58:24 +010068 CLNTC_ST_WAIT_CONF_RES, /* waiting for ConfigClientRes */
Harald Weltef5a0fa32019-03-03 15:44:18 +010069 CLNTC_ST_CONNECTED,
70};
71
72enum remsim_server_client_event {
73 CLNTC_E_TCP_UP,
74 CLNTC_E_CLIENT_CONN, /* Connect{Client,Bank}Req received */
75 CLNTC_E_BANK_CONN,
76 CLNTC_E_TCP_DOWN,
Harald Welte15b75e12019-03-08 16:55:46 +010077 CLNTC_E_KA_TIMEOUT,
Harald Weltef5a0fa32019-03-03 15:44:18 +010078 CLNTC_E_CREATE_MAP_RES, /* CreateMappingRes received */
79 CLNTC_E_REMOVE_MAP_RES, /* RemoveMappingRes received */
Harald Weltee5c77732019-03-07 23:58:24 +010080 CLNTC_E_CONFIG_CL_RES, /* ConfigClientRes received */
Harald Weltef5a0fa32019-03-03 15:44:18 +010081 CLNTC_E_PUSH, /* drain maps_new or maps_delreq */
82};
83
84static const struct value_string server_client_event_names[] = {
85 OSMO_VALUE_STRING(CLNTC_E_TCP_UP),
86 OSMO_VALUE_STRING(CLNTC_E_CLIENT_CONN),
87 OSMO_VALUE_STRING(CLNTC_E_BANK_CONN),
88 OSMO_VALUE_STRING(CLNTC_E_TCP_DOWN),
Harald Welte15b75e12019-03-08 16:55:46 +010089 OSMO_VALUE_STRING(CLNTC_E_KA_TIMEOUT),
Harald Weltef5a0fa32019-03-03 15:44:18 +010090 OSMO_VALUE_STRING(CLNTC_E_CREATE_MAP_RES),
91 OSMO_VALUE_STRING(CLNTC_E_REMOVE_MAP_RES),
Harald Weltee5c77732019-03-07 23:58:24 +010092 OSMO_VALUE_STRING(CLNTC_E_CONFIG_CL_RES),
Harald Weltef5a0fa32019-03-03 15:44:18 +010093 OSMO_VALUE_STRING(CLNTC_E_PUSH),
94 { 0, NULL }
95};
96
97static void clnt_st_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
98{
99 switch (event) {
100 case CLNTC_E_TCP_UP:
101 osmo_fsm_inst_state_chg(fi, CLNTC_ST_ESTABLISHED, 10, 1);
102 break;
103 default:
104 OSMO_ASSERT(0);
105 }
106}
107
108static void clnt_st_established(struct osmo_fsm_inst *fi, uint32_t event, void *data)
109{
110 struct rspro_client_conn *conn = fi->priv;
111 const RsproPDU_t *pdu = data;
112 const ConnectClientReq_t *cclreq = NULL;
113 const ConnectBankReq_t *cbreq = NULL;
114 RsproPDU_t *resp = NULL;
115
116 switch (event) {
117 case CLNTC_E_CLIENT_CONN:
118 cclreq = &pdu->msg.choice.connectClientReq;
119 /* save the [remote] component identity in 'conn' */
120 rspro_comp_id_retrieve(&conn->comp_id, &cclreq->identity);
121 if (conn->comp_id.type != ComponentType_remsimClient) {
122 LOGPFSM(fi, "ConnectClientReq from identity != Client ?!?\n");
123 osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
124 }
Harald Weltee5c77732019-03-07 23:58:24 +0100125 if (!cclreq->clientSlot) {
126#if 0
127 /* FIXME: determine ClientID */
128 resp = rspro_gen_ConnectClientRes(&conn->srv->comp_id, ResultCode_ok);
129 client_conn_send(conn, resp);
130 osmo_fsm_inst_state_chg(fi, CLNTC_ST_WAIT_CL_CONF_RES, 3, 30);
131#else
132 /* FIXME: the original plan was to dynamically assign a ClientID
133 * from server to client here. Send ConfigReq and transition to
134 * CLNTC_ST_WAIT_CONF_RES */
135 LOGPFSM(fi, "ConnectClientReq without ClientId not supported yet!\n");
136 osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
137#endif
138 } else {
139 /* FIXME: check for unique-ness */
140 rspro2client_slot(&conn->client.slot, cclreq->clientSlot);
141 osmo_fsm_inst_update_id_f(fi, "C%u:%u", conn->client.slot.client_id,
142 conn->client.slot.slot_nr);
143 resp = rspro_gen_ConnectClientRes(&conn->srv->comp_id, ResultCode_ok);
144 client_conn_send(conn, resp);
145 osmo_fsm_inst_state_chg(fi, CLNTC_ST_CONNECTED, 0, 0);
146 }
Harald Weltef5a0fa32019-03-03 15:44:18 +0100147
148 /* reparent us from srv->connections to srv->clients */
149 pthread_rwlock_wrlock(&conn->srv->rwlock);
150 llist_del(&conn->list);
151 llist_add_tail(&conn->list, &conn->srv->clients);
152 pthread_rwlock_unlock(&conn->srv->rwlock);
Harald Weltef5a0fa32019-03-03 15:44:18 +0100153 break;
154 case CLNTC_E_BANK_CONN:
155 cbreq = &pdu->msg.choice.connectBankReq;
156 /* save the [remote] component identity in 'conn' */
157 rspro_comp_id_retrieve(&conn->comp_id, &cbreq->identity);
158 if (conn->comp_id.type != ComponentType_remsimBankd) {
159 LOGPFSM(fi, "ConnectBankReq from identity != Bank ?!?\n");
160 osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
161 }
Harald Weltee5c77732019-03-07 23:58:24 +0100162 /* FIXME: check for unique-ness */
Harald Weltef5a0fa32019-03-03 15:44:18 +0100163 conn->bank.bank_id = cbreq->bankId;
164 conn->bank.num_slots = cbreq->numberOfSlots;
165 osmo_fsm_inst_update_id_f(fi, "B%u", conn->bank.bank_id);
166
167 /* reparent us from srv->connections to srv->banks */
168 pthread_rwlock_wrlock(&conn->srv->rwlock);
169 llist_del(&conn->list);
170 llist_add_tail(&conn->list, &conn->srv->banks);
171 pthread_rwlock_unlock(&conn->srv->rwlock);
172
173 /* send response to bank first */
174 resp = rspro_gen_ConnectBankRes(&conn->srv->comp_id, ResultCode_ok);
175 client_conn_send(conn, resp);
176
177 /* the state change will associate any pre-existing slotmaps */
178 osmo_fsm_inst_state_chg(fi, CLNTC_ST_CONNECTED, 0, 0);
179
180 osmo_fsm_inst_dispatch(fi, CLNTC_E_PUSH, NULL);
181 break;
182 default:
183 OSMO_ASSERT(0);
184 }
185}
186
Harald Weltee5c77732019-03-07 23:58:24 +0100187static void clnt_st_wait_cl_conf_res(struct osmo_fsm_inst *fi, uint32_t event, void *data)
188{
189 switch (event) {
190 case CLNTC_E_CONFIG_CL_RES:
191 osmo_fsm_inst_state_chg(fi, CLNTC_ST_CONNECTED, 0, 0);
192 break;
193 default:
194 OSMO_ASSERT(0);
195 }
196}
Harald Weltef5a0fa32019-03-03 15:44:18 +0100197
198static void clnt_st_connected_cl_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
199{
200#if 0
201 struct rspro_client_conn *conn = fi->priv;
202 ClientSlot_t clslot;
203 RsproPDU_t *pdu;
204
205 /* send configuration to this new client */
206 client_slot2rspro(&clslot, FIXME);
207 pdu = rspro_gen_ConfigClientReq(&clslot, bankd_ip, bankd_port);
208 client_conn_send(conn, pdu);
209#endif
210}
211
212static void clnt_st_connected_bk_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
213{
214 struct rspro_client_conn *conn = fi->priv;
215 struct slotmaps *slotmaps = conn->srv->slotmaps;
216 struct slot_mapping *map;
217
218 LOGPFSM(fi, "Associating pre-existing slotmaps (if any)\n");
219 /* Link all known mappings to this new bank */
220 slotmaps_wrlock(slotmaps);
221 llist_for_each_entry(map, &slotmaps->mappings, list) {
222 if (map->bank.bank_id == conn->bank.bank_id)
223 _slotmap_state_change(map, SLMAP_S_NEW, &conn->bank.maps_new);
224 }
225 slotmaps_unlock(slotmaps);
226}
227
228static void clnt_st_connected_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
229{
230 struct rspro_client_conn *conn = fi->priv;
231 switch (conn->comp_id.type) {
232 case ComponentType_remsimClient:
233 clnt_st_connected_cl_onenter(fi, prev_state);
234 break;
235 case ComponentType_remsimBankd:
236 clnt_st_connected_bk_onenter(fi, prev_state);
237 break;
238 default:
239 OSMO_ASSERT(0);
240 }
241}
242
243static void clnt_st_connected(struct osmo_fsm_inst *fi, uint32_t event, void *data)
244{
245 struct rspro_client_conn *conn = fi->priv;
246 struct slotmaps *slotmaps = conn->srv->slotmaps;
247 const struct RsproPDU_t *rx = NULL;
248 struct slot_mapping *map, *map2;
249
250 switch (event) {
Harald Welte1b07f7f2019-03-30 17:33:07 +0100251 case CLNTC_E_CREATE_MAP_RES: /* Bankd acknowledges mapping was created */
Harald Weltef5a0fa32019-03-03 15:44:18 +0100252 rx = data;
253 slotmaps_wrlock(slotmaps);
254 /* FIXME: resolve map by pdu->tag */
255 /* as hack use first element of conn->maps_unack */
256 map = llist_first_entry(&conn->bank.maps_unack, struct slot_mapping, bank_list);
257 if (!map) {
258 slotmaps_unlock(slotmaps);
259 LOGPFSM(fi, "CreateMapRes but no unacknowledged map");
260 break;
261 }
262 _slotmap_state_change(map, SLMAP_S_ACTIVE, &conn->bank.maps_active);
263 slotmaps_unlock(slotmaps);
264 break;
Harald Welte1b07f7f2019-03-30 17:33:07 +0100265 case CLNTC_E_REMOVE_MAP_RES: /* Bankd acknowledges mapping was removed */
Harald Weltef5a0fa32019-03-03 15:44:18 +0100266 rx = data;
267 slotmaps_wrlock(slotmaps);
268 /* FIXME: resolve map by pdu->tag */
269 /* as hack use first element of conn->maps_deleting */
270 map = llist_first_entry(&conn->bank.maps_deleting, struct slot_mapping, bank_list);
271 if (!map) {
272 slotmaps_unlock(slotmaps);
273 LOGPFSM(fi, "RemoveMapRes but no unacknowledged map");
274 break;
275 }
276 slotmaps_unlock(slotmaps);
277 /* slotmap_del() will remove it from both global and bank list */
278 slotmap_del(map->maps, map);
279 break;
Harald Welte1b07f7f2019-03-30 17:33:07 +0100280 case CLNTC_E_PUSH: /* check if any create or delete requests are pending */
Harald Weltef5a0fa32019-03-03 15:44:18 +0100281 slotmaps_wrlock(slotmaps);
282 /* send any pending create requests */
283 llist_for_each_entry_safe(map, map2, &conn->bank.maps_new, bank_list) {
284 RsproPDU_t *pdu = slotmap2CreateMappingReq(map);
285 client_conn_send(conn, pdu);
286 _slotmap_state_change(map, SLMAP_S_UNACKNOWLEDGED, &conn->bank.maps_unack);
287 }
288 /* send any pending delete requests */
289 llist_for_each_entry_safe(map, map2, &conn->bank.maps_delreq, bank_list) {
290 RsproPDU_t *pdu = slotmap2RemoveMappingReq(map);
291 client_conn_send(conn, pdu);
292 _slotmap_state_change(map, SLMAP_S_DELETING, &conn->bank.maps_deleting);
293 }
294 slotmaps_unlock(slotmaps);
295 break;
296 default:
297 OSMO_ASSERT(0);
298 }
299}
300
301static void clnt_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
302{
303 struct rspro_client_conn *conn = fi->priv;
304
305 switch (event) {
306 case CLNTC_E_TCP_DOWN:
307 osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
308 break;
Harald Welte15b75e12019-03-08 16:55:46 +0100309 case CLNTC_E_KA_TIMEOUT:
310 osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
311 break;
Harald Weltef5a0fa32019-03-03 15:44:18 +0100312 default:
313 OSMO_ASSERT(0);
314 }
315}
316
317static int server_client_fsm_timer_cb(struct osmo_fsm_inst *fi)
318{
319 struct rspro_client_conn *conn = fi->priv;
320
321 switch (fi->T) {
322 case 1:
323 /* No ClientConnectReq received:disconnect */
324 return 1; /* ask core to terminate FSM */
325 default:
326 OSMO_ASSERT(0);
327 }
328 return 0;
329}
330
331static void server_client_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
332{
333 struct rspro_client_conn *conn = fi->priv;
334 /* this call will destroy the IPA connection, which will in turn call closed_cb()
335 * which will try to deliver a E_TCP_DOWN event. Clear conn->fi to avoid that loop */
336 conn->fi = NULL;
337 rspro_client_conn_destroy(conn);
338}
339
340static const struct osmo_fsm_state server_client_fsm_states[] = {
341 [CLNTC_ST_INIT] = {
342 .name = "INIT",
343 .in_event_mask = S(CLNTC_E_TCP_UP),
344 .out_state_mask = S(CLNTC_ST_ESTABLISHED),
345 .action = clnt_st_init,
346 },
347 [CLNTC_ST_ESTABLISHED] = {
348 .name = "ESTABLISHED",
349 .in_event_mask = S(CLNTC_E_CLIENT_CONN) | S(CLNTC_E_BANK_CONN),
Harald Weltee5c77732019-03-07 23:58:24 +0100350 .out_state_mask = S(CLNTC_ST_CONNECTED) | S(CLNTC_ST_WAIT_CONF_RES),
Harald Weltef5a0fa32019-03-03 15:44:18 +0100351 .action = clnt_st_established,
352 },
Harald Weltee5c77732019-03-07 23:58:24 +0100353 [CLNTC_ST_WAIT_CONF_RES] = {
354 .name = "WAIT_CONFIG_RES",
355 .in_event_mask = S(CLNTC_E_CONFIG_CL_RES),
356 .out_state_mask = S(CLNTC_ST_CONNECTED),
357 .action = clnt_st_wait_cl_conf_res,
358 },
Harald Weltef5a0fa32019-03-03 15:44:18 +0100359 [CLNTC_ST_CONNECTED] = {
360 .name = "CONNECTED",
361 .in_event_mask = S(CLNTC_E_CREATE_MAP_RES) | S(CLNTC_E_REMOVE_MAP_RES) |
362 S(CLNTC_E_PUSH),
363 .action = clnt_st_connected,
364 .onenter = clnt_st_connected_onenter,
365 },
366};
367
368static struct osmo_fsm remsim_server_client_fsm = {
369 .name = "SERVER_CONN",
370 .states = server_client_fsm_states,
371 .num_states = ARRAY_SIZE(server_client_fsm_states),
372 .allstate_event_mask = S(CLNTC_E_TCP_DOWN),
373 .allstate_action = clnt_allstate_action,
374 .cleanup = server_client_cleanup,
375 .timer_cb = server_client_fsm_timer_cb,
376 .log_subsys = DMAIN,
377 .event_names = server_client_event_names,
378};
379
380struct osmo_fsm_inst *server_client_fsm_alloc(void *ctx, struct rspro_client_conn *conn)
381{
382 //const char *id = osmo_sock_get_name2(conn->peer->ofd.fd);
383 return osmo_fsm_inst_alloc(&remsim_server_client_fsm, ctx, conn, LOGL_DEBUG, NULL);
384}
385
386
387static __attribute__((constructor)) void on_dso_load(void)
388{
389 osmo_fsm_register(&remsim_server_client_fsm);
390}
391
392
393/***********************************************************************
394 * IPA RSPRO Server
395 ***********************************************************************/
396
397struct rspro_client_conn *_bankd_conn_by_id(struct rspro_server *srv, uint16_t bank_id)
398{
399 struct rspro_client_conn *conn;
400 llist_for_each_entry(conn, &srv->banks, list) {
401 if (conn->bank.bank_id == bank_id)
402 return conn;
403 }
404 return NULL;
405}
406struct rspro_client_conn *bankd_conn_by_id(struct rspro_server *srv, uint16_t bank_id)
407{
408 struct rspro_client_conn *conn;
409 pthread_rwlock_rdlock(&srv->rwlock);
410 conn = _bankd_conn_by_id(srv, bank_id);
411 pthread_rwlock_unlock(&srv->rwlock);
412 return conn;
413}
414
415static int handle_rx_rspro(struct rspro_client_conn *conn, const RsproPDU_t *pdu)
416{
Harald Welteb49ac9c2019-03-09 20:36:07 +0100417 LOGPFSM(conn->fi, "Rx RSPRO %s\n", rspro_msgt_name(pdu));
418
Harald Weltef5a0fa32019-03-03 15:44:18 +0100419 switch (pdu->msg.present) {
420 case RsproPDUchoice_PR_connectClientReq:
421 osmo_fsm_inst_dispatch(conn->fi, CLNTC_E_CLIENT_CONN, (void *)pdu);
422 break;
423 case RsproPDUchoice_PR_connectBankReq:
424 osmo_fsm_inst_dispatch(conn->fi, CLNTC_E_BANK_CONN, (void *)pdu);
425 break;
426 case RsproPDUchoice_PR_createMappingRes:
427 osmo_fsm_inst_dispatch(conn->fi, CLNTC_E_CREATE_MAP_RES, (void *)pdu);
428 break;
429 case RsproPDUchoice_PR_removeMappingRes:
430 osmo_fsm_inst_dispatch(conn->fi, CLNTC_E_REMOVE_MAP_RES, (void *)pdu);
431 break;
Harald Welted571a3e2019-03-11 22:09:50 +0100432 case RsproPDUchoice_PR_configClientIdRes:
Harald Weltee5c77732019-03-07 23:58:24 +0100433 osmo_fsm_inst_dispatch(conn->fi, CLNTC_E_CONFIG_CL_RES, (void *)pdu);
434 break;
Harald Weltef5a0fa32019-03-03 15:44:18 +0100435 default:
436 LOGPFSML(conn->fi, LOGL_ERROR, "Received unknown/unimplemented RSPRO msg_type %d\n",
437 pdu->msg.present);
438 return -1;
439 }
440 return 0;
441}
442
443/* data was received from one of the client connections to the RSPRO socket */
444static int sock_read_cb(struct ipa_server_conn *peer, struct msgb *msg)
445{
446 struct ipaccess_head *hh = (struct ipaccess_head *) msg->data;
447 struct ipaccess_head_ext *he = (struct ipaccess_head_ext *) msgb_l2(msg);
448 struct rspro_client_conn *conn = peer->data;
449 RsproPDU_t *pdu;
450 int rc;
451
452 if (msgb_length(msg) < sizeof(*hh))
453 goto invalid;
454 msg->l2h = &hh->data[0];
Harald Welte7bfcc652019-03-08 16:55:01 +0100455 switch (hh->proto) {
456 case IPAC_PROTO_IPACCESS:
457 rc = ipa_server_conn_ccm(peer, msg);
Harald Welte15b75e12019-03-08 16:55:46 +0100458 if (rc < 0)
459 break;
460 switch (hh->data[0]) {
461 case IPAC_MSGT_PONG:
462 ipa_keepalive_fsm_pong_received(conn->keepalive_fi);
463 rc = 0;
464 break;
465 default:
466 break;
467 }
Harald Welte7bfcc652019-03-08 16:55:01 +0100468 break;
469 case IPAC_PROTO_OSMO:
470 if (!he || msgb_l2len(msg)< sizeof(*he))
471 goto invalid;
472 msg->l2h = &he->data[0];
Harald Weltef5a0fa32019-03-03 15:44:18 +0100473
Harald Welte7bfcc652019-03-08 16:55:01 +0100474 switch (he->proto) {
475 case IPAC_PROTO_EXT_RSPRO:
476 pdu = rspro_dec_msg(msg);
477 if (!pdu)
478 goto invalid;
Harald Weltef5a0fa32019-03-03 15:44:18 +0100479
Harald Welte7bfcc652019-03-08 16:55:01 +0100480 rc = handle_rx_rspro(conn, pdu);
481 ASN_STRUCT_FREE(asn_DEF_RsproPDU, pdu);
482 break;
483 default:
484 goto invalid;
485 }
486 break;
487 default:
Harald Weltef5a0fa32019-03-03 15:44:18 +0100488 goto invalid;
Harald Welte7bfcc652019-03-08 16:55:01 +0100489 }
Harald Weltef5a0fa32019-03-03 15:44:18 +0100490 return rc;
491
492invalid:
493 msgb_free(msg);
494 return -1;
495}
496
497static int sock_closed_cb(struct ipa_server_conn *peer)
498{
499 struct rspro_client_conn *conn = peer->data;
500 if (conn->fi)
501 osmo_fsm_inst_dispatch(conn->fi, CLNTC_E_TCP_DOWN, NULL);
502 /* FIXME: who cleans up conn? */
503 /* ipa server code relases 'peer' just after this */
504 return 0;
505}
506
Harald Welte15b75e12019-03-08 16:55:46 +0100507static const struct ipa_keepalive_params ka_params = {
508 .interval = 30,
509 .wait_for_resp = 10,
510};
511
Harald Weltef5a0fa32019-03-03 15:44:18 +0100512/* a new TCP connection was accepted on the RSPRO server socket */
513static int accept_cb(struct ipa_server_link *link, int fd)
514{
515 struct rspro_server *srv = link->data;
516 struct rspro_client_conn *conn;
517
518 conn = talloc_zero(srv, struct rspro_client_conn);
519 OSMO_ASSERT(conn);
520
521 conn->srv = srv;
522 /* don't allocate peer under 'conn', as it must survive 'conn' during teardown */
523 conn->peer = ipa_server_conn_create(link, link, fd, sock_read_cb, sock_closed_cb, conn);
524 if (!conn->peer)
525 goto out_err;
526
527 /* don't allocate 'fi' as slave from 'conn', as 'fi' needs to survive 'conn' during
528 * teardown */
529 conn->fi = server_client_fsm_alloc(srv, conn);
530 if (!conn->fi)
531 goto out_err_conn;
532
Harald Welte15b75e12019-03-08 16:55:46 +0100533 /* use ipa_keepalive_fsm to periodically send an IPA_PING and expect a PONG in response */
534 conn->keepalive_fi = ipa_server_conn_alloc_keepalive_fsm(conn->peer, &ka_params, NULL);
535 if (!conn->keepalive_fi)
536 goto out_err_fi;
537 /* ensure parent is notified once keepalive FSM instance is dying */
538 osmo_fsm_inst_change_parent(conn->keepalive_fi, conn->fi, CLNTC_E_KA_TIMEOUT);
539 ipa_keepalive_fsm_start(conn->keepalive_fi);
540
Harald Weltef5a0fa32019-03-03 15:44:18 +0100541 INIT_LLIST_HEAD(&conn->bank.maps_new);
542 INIT_LLIST_HEAD(&conn->bank.maps_unack);
543 INIT_LLIST_HEAD(&conn->bank.maps_active);
544 INIT_LLIST_HEAD(&conn->bank.maps_delreq);
545 INIT_LLIST_HEAD(&conn->bank.maps_deleting);
546
547 pthread_rwlock_wrlock(&conn->srv->rwlock);
548 llist_add_tail(&conn->list, &srv->connections);
549 pthread_rwlock_unlock(&conn->srv->rwlock);
550
551 osmo_fsm_inst_dispatch(conn->fi, CLNTC_E_TCP_UP, NULL);
552 return 0;
553
Harald Welte15b75e12019-03-08 16:55:46 +0100554out_err_fi:
555 osmo_fsm_inst_term(conn->fi, OSMO_FSM_TERM_ERROR, NULL);
Harald Weltef5a0fa32019-03-03 15:44:18 +0100556out_err_conn:
557 ipa_server_conn_destroy(conn->peer);
558 /* the above will free 'conn' down the chain */
559 return -1;
560out_err:
561 talloc_free(conn);
562 return -1;
563}
564
565/* call-back if we were triggered by a rest_api thread */
566int event_fd_cb(struct osmo_fd *ofd, unsigned int what)
567{
568 struct rspro_server *srv = ofd->data;
569 struct rspro_client_conn *conn;
570 bool non_empty_new, non_empty_del;
571 uint64_t value;
572 int rc;
573
574 /* read from the socket to "confirm" the event and make it non-readable again */
575 rc = read(ofd->fd, &value, 8);
576 if (rc < 8) {
577 fprintf(stderr, "Error reading eventfd: %d\n", rc);
578 return rc;
579 }
580
581 printf("rspro_server: Event FD arrived, checking for any pending work\n");
582
583 pthread_rwlock_rdlock(&srv->rwlock);
584 llist_for_each_entry(conn, &srv->banks, list) {
585 slotmaps_rdlock(srv->slotmaps);
586 non_empty_new = llist_empty(&conn->bank.maps_new);
587 non_empty_del = llist_empty(&conn->bank.maps_delreq);
588 slotmaps_unlock(srv->slotmaps);
589
590 /* trigger FSM to send any pending new/deleted maps */
591 if (non_empty_new || non_empty_del)
592 osmo_fsm_inst_dispatch(conn->fi, CLNTC_E_PUSH, NULL);
593 }
594 pthread_rwlock_unlock(&srv->rwlock);
595
596 return 0;
597}
598
599/* unlink all slotmaps from any of the lists of this conn->bank.maps_* */
600static void _unlink_all_slotmaps(struct rspro_client_conn *conn)
601{
602 struct slot_mapping *smap, *smap2;
603
604 llist_for_each_entry_safe(smap, smap2, &conn->bank.maps_new, bank_list) {
605 /* unlink from list and keep in state NEW */
606 _slotmap_state_change(smap, SLMAP_S_NEW, NULL);
607 }
608 llist_for_each_entry_safe(smap, smap2, &conn->bank.maps_unack, bank_list) {
609 /* unlink from list and change to state NEW */
610 _slotmap_state_change(smap, SLMAP_S_NEW, NULL);
611 }
612 llist_for_each_entry_safe(smap, smap2, &conn->bank.maps_active, bank_list) {
613 /* unlink from list and change to state NEW */
614 _slotmap_state_change(smap, SLMAP_S_NEW, NULL);
615 }
616 llist_for_each_entry_safe(smap, smap2, &conn->bank.maps_delreq, bank_list) {
617 /* unlink from list and delete */
618 _slotmap_del(smap->maps, smap);
619 }
620 llist_for_each_entry_safe(smap, smap2, &conn->bank.maps_deleting, bank_list) {
621 /* unlink from list and delete */
622 _slotmap_del(smap->maps, smap);
623 }
624}
625
626/* only to be used by the FSM cleanup. */
627static void rspro_client_conn_destroy(struct rspro_client_conn *conn)
628{
629 /* this will internally call closed_cb() which will dispatch a TCP_DOWN event */
630 ipa_server_conn_destroy(conn->peer);
631 conn->peer = NULL;
632
633 /* ensure all slotmaps are unlinked + returned to NEW or deleted */
634 slotmaps_wrlock(conn->srv->slotmaps);
635 _unlink_all_slotmaps(conn);
636 slotmaps_unlock(conn->srv->slotmaps);
637
638 pthread_rwlock_wrlock(&conn->srv->rwlock);
639 llist_del(&conn->list);
640 pthread_rwlock_unlock(&conn->srv->rwlock);
641
642 talloc_free(conn);
643}
644
645
646struct rspro_server *rspro_server_create(void *ctx, const char *host, uint16_t port)
647
648{
649 struct rspro_server *srv = talloc_zero(ctx, struct rspro_server);
650 OSMO_ASSERT(srv);
651
652 pthread_rwlock_init(&srv->rwlock, NULL);
653 pthread_rwlock_wrlock(&srv->rwlock);
654 INIT_LLIST_HEAD(&srv->connections);
655 INIT_LLIST_HEAD(&srv->clients);
656 INIT_LLIST_HEAD(&srv->banks);
657 pthread_rwlock_unlock(&srv->rwlock);
658
659 srv->link = ipa_server_link_create(ctx, NULL, host, port, accept_cb, srv);
660 ipa_server_link_open(srv->link);
661
662 return srv;
663}
664
665void rspro_server_destroy(struct rspro_server *srv)
666{
667 /* FIXME: clear all lists */
668
669 ipa_server_link_destroy(srv->link);
670 srv->link = NULL;
671 talloc_free(srv);
672}