blob: 321dcf23a3d65ce0373c4506a1c32ba230d72d6e [file] [log] [blame]
Harald Welte3dcdd202019-03-09 13:06:46 +01001/* (C) 2018-2019 by Harald Welte <laforge@gnumonks.org>
2 *
3 * All Rights Reserved
4 *
5 * SPDX-License-Identifier: GPL-2.0+
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
Harald Welte3dcdd202019-03-09 13:06:46 +010017 */
18
19
Harald Welte24173fb2018-08-24 20:37:28 +020020#include <stdint.h>
21#include <string.h>
22#include <errno.h>
James Tavaresb8900182022-11-14 11:04:11 -050023#include <unistd.h>
Harald Welte24173fb2018-08-24 20:37:28 +020024
25#include <talloc.h>
26
27#include <osmocom/core/logging.h>
28#include <osmocom/core/utils.h>
29#include <osmocom/core/msgb.h>
30#include <osmocom/core/fsm.h>
31
32#include <osmocom/abis/ipa.h>
33#include <osmocom/gsm/protocol/ipaccess.h>
34
Harald Weltef7598fe2019-12-16 16:52:45 +010035#include "debug.h"
Harald Weltef5f58052022-05-05 19:19:33 +020036#include "asn1c_helpers.h"
Harald Weltef7598fe2019-12-16 16:52:45 +010037#include "rspro_client_fsm.h"
Harald Welte24173fb2018-08-24 20:37:28 +020038
39#define S(x) (1 << (x))
40
Harald Welte707c85a2019-03-09 12:56:35 +010041#define T1_WAIT_CLIENT_CONN_RES 10
42#define T2_RECONNECT 10
43
James Tavaresb8900182022-11-14 11:04:11 -050044static const int k_reestablish_delay_s[] = {
45 0, 0, 0, // 3 immediate retries
46 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
47 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
48 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 Hz for 30 seconds
49 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
50 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
51 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 1/2 hz for 1 minute
52 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
53 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
54 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 1/4 Hz for 2 minutes
55 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
56 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
57 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 1/8 Hz for 4 minutes
58 16, // 1/16 Hz thereafter
59};
60
61#define REESTABLISH_DELAY_COUNT ARRAY_SIZE(k_reestablish_delay_s)
62
Harald Welte707c85a2019-03-09 12:56:35 +010063/***********************************************************************
Harald Welte0a684972019-12-02 20:10:59 +010064 * client-side FSM for a RSPRO connection to remsim-server
65 *
66 * This implements the TCP/IPA client side of an RSPRO connection between
67 * a logical (TCP-level) client implementation and a remote RSPRO server.
68 *
69 * 'client' and 'server' here strictly refer to RSPRO protocol roles, not
70 * to be confused with the remsim-client or remsim-server!
71 *
72 * Specifically, this RSPRO client FSM is used by both remsim-client and
73 * remsim-bankd for their RSPRO control connection to remsim-server.
Harald Welte707c85a2019-03-09 12:56:35 +010074 ***********************************************************************/
75
Harald Welte7f0e82f2018-10-14 20:07:59 +020076static void push_and_send(struct ipa_client_conn *ipa, struct msgb *msg_tx)
77{
78 ipa_prepend_header_ext(msg_tx, IPAC_PROTO_EXT_RSPRO);
79 ipa_msg_push_header(msg_tx, IPAC_PROTO_OSMO);
80 ipa_client_conn_send(ipa, msg_tx);
81 /* msg_tx is now queued and will be freed. */
82}
83
Harald Welte6d61b162019-12-02 22:47:35 +010084static int ipa_client_conn_send_rspro(struct ipa_client_conn *ipa, RsproPDU_t *rspro)
Harald Welte7f0e82f2018-10-14 20:07:59 +020085{
86 struct msgb *msg = rspro_enc_msg(rspro);
Harald Welte2eee4502019-03-30 08:50:35 +010087 if (!msg) {
Harald Welte46122ab2021-12-08 20:57:16 +010088 LOGP(DRSPRO, LOGL_ERROR, "Error encoding RSPRO: %s\n", rspro_msgt_name(rspro));
89 osmo_log_backtrace(DRSPRO, LOGL_ERROR);
Harald Welte2eee4502019-03-30 08:50:35 +010090 ASN_STRUCT_FREE(asn_DEF_RsproPDU, rspro);
Harald Weltea844bb02019-03-09 13:38:50 +010091 return -1;
Harald Welte2eee4502019-03-30 08:50:35 +010092 }
Harald Welte7f0e82f2018-10-14 20:07:59 +020093 push_and_send(ipa, msg);
Harald Weltea844bb02019-03-09 13:38:50 +010094 return 0;
95}
96
Harald Weltea3b14d12019-12-02 22:58:34 +010097static int _server_conn_send_rspro(struct rspro_server_conn *srvc, RsproPDU_t *rspro)
98{
Harald Welte89344122021-12-08 15:55:06 +010099 LOGPFSML(srvc->fi, LOGL_DEBUG, "Tx RSPRO %s\n", rspro_msgt_name(rspro));
Harald Weltea3b14d12019-12-02 22:58:34 +0100100 return ipa_client_conn_send_rspro(srvc->conn, rspro);
101}
102
Harald Weltea844bb02019-03-09 13:38:50 +0100103int server_conn_send_rspro(struct rspro_server_conn *srvc, RsproPDU_t *rspro)
104{
Harald Weltea8b86ce2019-03-31 14:59:20 +0200105 if (!rspro) {
106 LOGPFSML(srvc->fi, LOGL_ERROR, "Attempt to transmit NULL\n");
Harald Welte46122ab2021-12-08 20:57:16 +0100107 osmo_log_backtrace(DRSPRO, LOGL_ERROR);
Harald Weltea8b86ce2019-03-31 14:59:20 +0200108 return -EINVAL;
109 }
Harald Weltea3b14d12019-12-02 22:58:34 +0100110 if (osmo_fsm_inst_dispatch(srvc->fi, SRVC_E_RSPRO_TX, rspro) < 0) {
111 ASN_STRUCT_FREE(asn_DEF_RsproPDU, rspro);
112 return -EPERM;
113 }
114 return 0;
Harald Welte7f0e82f2018-10-14 20:07:59 +0200115}
116
Harald Welte24173fb2018-08-24 20:37:28 +0200117enum server_conn_fsm_state {
Harald Weltea9bb9a92022-05-03 15:28:02 +0200118 /* waiting for initial connection to remsim-server */
Harald Welte24173fb2018-08-24 20:37:28 +0200119 SRVC_ST_INIT,
120 /* server connection established, waiting for ClientConnectRes */
121 SRVC_ST_ESTABLISHED,
Harald Weltea9bb9a92022-05-03 15:28:02 +0200122 /* server connection established, ClientConnect succeeded */
Harald Welte24173fb2018-08-24 20:37:28 +0200123 SRVC_ST_CONNECTED,
James Tavaresb8900182022-11-14 11:04:11 -0500124 /* connection lost, 1st step: delaying until we re-establish */
125 SRVC_ST_REESTABLISH_DELAY,
126 /* connection lost, 2nd step: wait for a re-establish */
Harald Welte24173fb2018-08-24 20:37:28 +0200127 SRVC_ST_REESTABLISH,
128};
129
130static const struct value_string server_conn_fsm_event_names[] = {
Harald Welted2192e22019-11-07 18:10:57 +0100131 OSMO_VALUE_STRING(SRVC_E_ESTABLISH),
Harald Welte16c81ea2020-02-16 14:44:00 +0100132 OSMO_VALUE_STRING(SRVC_E_DISCONNECT),
Harald Welte24173fb2018-08-24 20:37:28 +0200133 OSMO_VALUE_STRING(SRVC_E_TCP_UP),
134 OSMO_VALUE_STRING(SRVC_E_TCP_DOWN),
Harald Welte03b24112019-03-08 19:43:02 +0100135 OSMO_VALUE_STRING(SRVC_E_KA_TIMEOUT),
Harald Welteb8ec65a2019-12-14 17:16:12 +0100136 OSMO_VALUE_STRING(SRVC_E_KA_TERMINATED),
Harald Welte24173fb2018-08-24 20:37:28 +0200137 OSMO_VALUE_STRING(SRVC_E_CLIENT_CONN_RES),
Harald Weltea3b14d12019-12-02 22:58:34 +0100138 OSMO_VALUE_STRING(SRVC_E_RSPRO_TX),
Harald Welte24173fb2018-08-24 20:37:28 +0200139 { 0, NULL }
140};
141
Harald Welte84ba2342018-08-24 22:20:20 +0200142static void srvc_updown_cb(struct ipa_client_conn *conn, int up)
143{
144 struct rspro_server_conn *srvc = conn->data;
145
Harald Welte89344122021-12-08 15:55:06 +0100146 LOGPFSML(srvc->fi, LOGL_NOTICE, "RSPRO link to %s:%d %s\n",
147 conn->addr, conn->port, up ? "UP" : "DOWN");
Harald Welte84ba2342018-08-24 22:20:20 +0200148
149 osmo_fsm_inst_dispatch(srvc->fi, up ? SRVC_E_TCP_UP: SRVC_E_TCP_DOWN, 0);
150}
151
152static int srvc_read_cb(struct ipa_client_conn *conn, struct msgb *msg)
153{
154 struct ipaccess_head *hh = (struct ipaccess_head *) msg->data;
155 struct ipaccess_head_ext *he = (struct ipaccess_head_ext *) msgb_l2(msg);
156 struct rspro_server_conn *srvc = conn->data;
Harald Weltef9995a32018-10-14 20:40:05 +0200157 RsproPDU_t *pdu;
Harald Welte84ba2342018-08-24 22:20:20 +0200158 int rc;
159
160 if (msgb_length(msg) < sizeof(*hh))
161 goto invalid;
162 msg->l2h = &hh->data[0];
Harald Welte1a171042019-03-08 19:18:52 +0100163 switch (hh->proto) {
Harald Welte03b24112019-03-08 19:43:02 +0100164 case IPAC_PROTO_IPACCESS:
165 rc = ipaccess_bts_handle_ccm(srvc->conn, &srvc->ipa_dev, msg);
Harald Weltecacbc2b2019-07-24 18:42:02 +0200166 if (rc < 0) {
167 msgb_free(msg);
Harald Welte03b24112019-03-08 19:43:02 +0100168 break;
Harald Weltecacbc2b2019-07-24 18:42:02 +0200169 }
Harald Welte03b24112019-03-08 19:43:02 +0100170 switch (hh->data[0]) {
171 case IPAC_MSGT_PONG:
172 ipa_keepalive_fsm_pong_received(srvc->keepalive_fi);
173 rc = 0;
174 break;
175 default:
176 break;
177 }
178 break;
Harald Welte1a171042019-03-08 19:18:52 +0100179 case IPAC_PROTO_OSMO:
180 if (!he || msgb_l2len(msg) < sizeof(*he))
181 goto invalid;
182 msg->l2h = &he->data[0];
183 switch (he->proto) {
184 case IPAC_PROTO_EXT_RSPRO:
Harald Welte89344122021-12-08 15:55:06 +0100185 LOGPFSML(srvc->fi, LOGL_DEBUG, "Received RSPRO %s\n", msgb_hexdump(msg));
Harald Welte1a171042019-03-08 19:18:52 +0100186 pdu = rspro_dec_msg(msg);
Harald Welte9cdd0942019-12-15 00:10:57 +0100187 if (!pdu) {
188 rc = -EIO;
Harald Welte573a5b92019-09-12 20:27:58 +0200189 break;
Harald Welte9cdd0942019-12-15 00:10:57 +0100190 }
Harald Welte1a171042019-03-08 19:18:52 +0100191 rc = srvc->handle_rx(srvc, pdu);
192 ASN_STRUCT_FREE(asn_DEF_RsproPDU, pdu);
193 break;
194 default:
195 goto invalid;
196 }
197 break;
198 default:
Harald Welte84ba2342018-08-24 22:20:20 +0200199 goto invalid;
Harald Welte1a171042019-03-08 19:18:52 +0100200 }
Harald Welte573a5b92019-09-12 20:27:58 +0200201
202 msgb_free(msg);
Harald Welte84ba2342018-08-24 22:20:20 +0200203 return rc;
Harald Welte84ba2342018-08-24 22:20:20 +0200204
205invalid:
Harald Weltee57334e2019-03-27 22:58:10 +0100206 LOGPFSML(srvc->fi, LOGL_ERROR, "Error decoding PDU\n");
Harald Welte84ba2342018-08-24 22:20:20 +0200207 msgb_free(msg);
208 return -1;
209}
210
Harald Welte03b24112019-03-08 19:43:02 +0100211static const struct ipa_keepalive_params ka_params = {
212 .interval = 30,
213 .wait_for_resp = 10,
214};
Harald Welte84ba2342018-08-24 22:20:20 +0200215
James Tavaresb8900182022-11-14 11:04:11 -0500216static int64_t get_monotonic_ms(void)
217{
218 struct timespec t;
219 clock_gettime(CLOCK_BOOTTIME, &t);
220 return ((1000LL * t.tv_sec) + (t.tv_nsec / 1000000));
221}
222
223static void srvc_do_reestablish(struct osmo_fsm_inst *fi)
224{
225 struct rspro_server_conn *srvc = (struct rspro_server_conn *) fi->priv;
226
227 const int64_t since_last_ms = get_monotonic_ms() - srvc->reestablish_last_ms;
228
229 /* reset delay loop if it has been > 2x the longest timeout since our last attempt;
230 * this lets us revert to rapid reconnect behavior for a good connection */
231 const int64_t reset_ms = 2*1000*(OSMO_MAX(OSMO_MAX(T1_WAIT_CLIENT_CONN_RES, T2_RECONNECT),
232 k_reestablish_delay_s[REESTABLISH_DELAY_COUNT-1]));
233
234 if (since_last_ms > reset_ms) {
235 srvc->reestablish_delay_idx = 0;
236 LOGPFSML(fi, LOGL_DEBUG, "->REESTABLISH_DELAY reset; %" PRId64 "ms since last attempt\n",
237 since_last_ms);
238 }
239
240 /* determine if we need to delay reestablishment */
241 const int64_t need_ms = k_reestablish_delay_s[srvc->reestablish_delay_idx] * 1000;
242 int64_t delay_ms = need_ms - since_last_ms;
243
244 if (delay_ms > 0) {
245 LOGPFSML(fi, LOGL_DEBUG, "->REESTABLISH_DELAY delay %" PRId64 "ms; %" PRId64 "ms since last attempt [step %zu/%zu@%ds]\n",
246 delay_ms, since_last_ms, srvc->reestablish_delay_idx, (REESTABLISH_DELAY_COUNT-1),
247 k_reestablish_delay_s[srvc->reestablish_delay_idx]);
248 } else {
249 /* cheat and always use a minimum delay of 1ms to ensure a fsm timeout is triggered */
250 delay_ms = 1;
251 }
252
253 osmo_fsm_inst_state_chg_ms(fi, SRVC_ST_REESTABLISH_DELAY, delay_ms, 3);
254}
255
Harald Welte24173fb2018-08-24 20:37:28 +0200256static void srvc_st_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
257{
Harald Welte84ba2342018-08-24 22:20:20 +0200258 switch (event) {
Harald Welted2192e22019-11-07 18:10:57 +0100259 case SRVC_E_ESTABLISH:
Harald Welte84ba2342018-08-24 22:20:20 +0200260 default:
261 OSMO_ASSERT(0);
262 }
263}
264
265static void srvc_st_established_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
266{
267 struct rspro_server_conn *srvc = (struct rspro_server_conn *) fi->priv;
268 RsproPDU_t *pdu;
269
Harald Welte03b24112019-03-08 19:43:02 +0100270 ipa_keepalive_fsm_start(srvc->keepalive_fi);
271
Harald Welte707c85a2019-03-09 12:56:35 +0100272 if (srvc->own_comp_id.type == ComponentType_remsimClient)
273 pdu = rspro_gen_ConnectClientReq(&srvc->own_comp_id, srvc->clslot);
274 else
Harald Welte4fa407a2019-04-01 21:05:06 +0200275 pdu = rspro_gen_ConnectBankReq(&srvc->own_comp_id, srvc->bankd.bank_id,
276 srvc->bankd.num_slots);
Harald Weltea3b14d12019-12-02 22:58:34 +0100277 _server_conn_send_rspro(srvc, pdu);
Harald Welte24173fb2018-08-24 20:37:28 +0200278}
279
280static void srvc_st_established(struct osmo_fsm_inst *fi, uint32_t event, void *data)
281{
Harald Welte4e7a2852019-03-17 21:01:50 +0100282 struct rspro_server_conn *srvc = (struct rspro_server_conn *) fi->priv;
283 RsproPDU_t *pdu = NULL;
284 e_ResultCode res;
285
Harald Welte84ba2342018-08-24 22:20:20 +0200286 switch (event) {
287 case SRVC_E_TCP_DOWN:
Harald Welte03b24112019-03-08 19:43:02 +0100288 case SRVC_E_KA_TIMEOUT:
James Tavaresb8900182022-11-14 11:04:11 -0500289 srvc_do_reestablish(fi);
Harald Welte84ba2342018-08-24 22:20:20 +0200290 break;
291 case SRVC_E_CLIENT_CONN_RES:
Harald Welte4e7a2852019-03-17 21:01:50 +0100292 pdu = data;
293 res = rspro_get_result(pdu);
294 if (res != ResultCode_ok) {
Harald Weltef5f58052022-05-05 19:19:33 +0200295 LOGPFSML(fi, LOGL_ERROR, "Rx RSPRO connectClientRes(result=%s), closing\n",
296 asn_enum_name(&asn_DEF_ResultCode, res));
Harald Welte4e7a2852019-03-17 21:01:50 +0100297 ipa_client_conn_close(srvc->conn);
298 osmo_fsm_inst_dispatch(fi, SRVC_E_TCP_DOWN, NULL);
299 } else {
300 /* somehow notify the main code? */
301 osmo_fsm_inst_state_chg(fi, SRVC_ST_CONNECTED, 0, 0);
302 }
Harald Welte84ba2342018-08-24 22:20:20 +0200303 break;
304 default:
305 OSMO_ASSERT(0);
306 }
Harald Welte24173fb2018-08-24 20:37:28 +0200307}
308
Harald Welteb266d502020-02-22 20:53:19 +0100309static void srvc_st_connected_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
310{
311 struct rspro_server_conn *srvc = (struct rspro_server_conn *) fi->priv;
312
313 if (fi->proc.parent)
314 osmo_fsm_inst_dispatch(fi->proc.parent, srvc->parent_conn_evt, NULL);
315}
316
Harald Welte24173fb2018-08-24 20:37:28 +0200317static void srvc_st_connected(struct osmo_fsm_inst *fi, uint32_t event, void *data)
318{
Harald Weltea3b14d12019-12-02 22:58:34 +0100319 struct rspro_server_conn *srvc = (struct rspro_server_conn *) fi->priv;
320 RsproPDU_t *pdu = NULL;
321
Harald Welte84ba2342018-08-24 22:20:20 +0200322 switch (event) {
323 case SRVC_E_TCP_DOWN:
Harald Welte03b24112019-03-08 19:43:02 +0100324 case SRVC_E_KA_TIMEOUT:
James Tavaresb8900182022-11-14 11:04:11 -0500325 srvc_do_reestablish(fi);
Harald Welte84ba2342018-08-24 22:20:20 +0200326 break;
Harald Weltea3b14d12019-12-02 22:58:34 +0100327 case SRVC_E_RSPRO_TX:
328 pdu = data;
329 _server_conn_send_rspro(srvc, pdu);
330 break;
Harald Welte84ba2342018-08-24 22:20:20 +0200331 default:
332 OSMO_ASSERT(0);
333 }
334}
335
Harald Welteb266d502020-02-22 20:53:19 +0100336static void srvc_st_connected_onleave(struct osmo_fsm_inst *fi, uint32_t next_state)
337{
338 struct rspro_server_conn *srvc = (struct rspro_server_conn *) fi->priv;
339
340 if (fi->proc.parent)
341 osmo_fsm_inst_dispatch(fi->proc.parent, srvc->parent_disc_evt, NULL);
342}
343
Harald Welteb8ec65a2019-12-14 17:16:12 +0100344static int ipa_kaepalive_timeout_cb(struct osmo_fsm_inst *ka_fi, void *conn)
345{
346 struct osmo_fsm_inst *fi = ka_fi->proc.parent;
347 osmo_fsm_inst_dispatch(fi, SRVC_E_KA_TIMEOUT, NULL);
348 return 0; /* we will explicitly terminate it */
349}
350
James Tavaresb8900182022-11-14 11:04:11 -0500351static void srvc_st_reestablish_delay_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
Harald Welte84ba2342018-08-24 22:20:20 +0200352{
353 struct rspro_server_conn *srvc = (struct rspro_server_conn *) fi->priv;
Harald Welte84ba2342018-08-24 22:20:20 +0200354
Harald Welted2192e22019-11-07 18:10:57 +0100355 if (srvc->keepalive_fi) {
356 ipa_keepalive_fsm_stop(srvc->keepalive_fi);
357 osmo_fsm_inst_term(srvc->keepalive_fi, OSMO_FSM_TERM_REGULAR, NULL);
358 srvc->keepalive_fi = NULL;
359 }
360
361 if (srvc->conn) {
362 LOGPFSML(fi, LOGL_INFO, "Destroying existing connection to server\n");
363 ipa_client_conn_close(srvc->conn);
364 ipa_client_conn_destroy(srvc->conn);
365 srvc->conn = NULL;
366 }
James Tavaresb8900182022-11-14 11:04:11 -0500367
368 /* saturate timeout at last (longest) entry */
369 if (srvc->reestablish_delay_idx < REESTABLISH_DELAY_COUNT-1) {
370 srvc->reestablish_delay_idx++;
371 }
372}
373
374static void srvc_st_reestablish_delay(struct osmo_fsm_inst *fi, uint32_t event, void *data)
375{
376 switch (event) {
377 default:
378 OSMO_ASSERT(0);
379 }
380}
381static void srvc_st_reestablish_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
382{
383 struct rspro_server_conn *srvc = (struct rspro_server_conn *) fi->priv;
384 int rc;
385
386 srvc->reestablish_last_ms = get_monotonic_ms();
387
Harald Welted2192e22019-11-07 18:10:57 +0100388 LOGPFSML(fi, LOGL_INFO, "Creating TCP connection to server at %s:%u\n",
389 srvc->server_host, srvc->server_port);
Harald Welte8da22092020-02-16 15:59:32 +0100390 srvc->conn = ipa_client_conn_create2(fi, NULL, 0, NULL, 0, srvc->server_host, srvc->server_port,
Harald Welted2192e22019-11-07 18:10:57 +0100391 srvc_updown_cb, srvc_read_cb, NULL, srvc);
392 if (!srvc->conn) {
393 LOGPFSML(fi, LOGL_FATAL, "Unable to create socket: %s\n", strerror(errno));
394 exit(1);
395 }
396
397 srvc->keepalive_fi = ipa_client_conn_alloc_keepalive_fsm(srvc->conn, &ka_params, fi->id);
398 if (!srvc->keepalive_fi) {
399 LOGPFSM(fi, "Unable to create keepalive FSM\n");
400 exit(1);
401 }
Harald Welteb8ec65a2019-12-14 17:16:12 +0100402 ipa_keepalive_fsm_set_timeout_cb(srvc->keepalive_fi, ipa_kaepalive_timeout_cb);
Harald Welted2192e22019-11-07 18:10:57 +0100403 /* ensure parent is notified once keepalive FSM instance is dying */
Harald Welteb8ec65a2019-12-14 17:16:12 +0100404 osmo_fsm_inst_change_parent(srvc->keepalive_fi, srvc->fi, SRVC_E_KA_TERMINATED);
Harald Welte03b24112019-03-08 19:43:02 +0100405
Harald Welte84ba2342018-08-24 22:20:20 +0200406 /* Attempt to connect TCP socket */
407 rc = ipa_client_conn_open(srvc->conn);
408 if (rc < 0) {
Harald Welted2192e22019-11-07 18:10:57 +0100409 LOGPFSML(fi, LOGL_FATAL, "Unable to connect RSPRO to %s:%u - %s\n",
Harald Welte84ba2342018-08-24 22:20:20 +0200410 srvc->server_host, srvc->server_port, strerror(errno));
411 /* FIXME: retry? Timer? Abort? */
412 OSMO_ASSERT(0);
413 }
Harald Welte24173fb2018-08-24 20:37:28 +0200414}
415
416static void srvc_st_reestablish(struct osmo_fsm_inst *fi, uint32_t event, void *data)
417{
Harald Welte84ba2342018-08-24 22:20:20 +0200418 switch (event) {
Harald Welte52cc7db2018-09-24 11:51:51 +0200419 case SRVC_E_TCP_UP:
Harald Welte84ba2342018-08-24 22:20:20 +0200420 osmo_fsm_inst_state_chg(fi, SRVC_ST_ESTABLISHED, T1_WAIT_CLIENT_CONN_RES, 1);
421 break;
Harald Welte52cc7db2018-09-24 11:51:51 +0200422 case SRVC_E_TCP_DOWN:
423 /* wait for normal T2 call-back */
424 break;
Harald Welte84ba2342018-08-24 22:20:20 +0200425 default:
426 OSMO_ASSERT(0);
427 }
Harald Welte24173fb2018-08-24 20:37:28 +0200428}
429
Harald Welted2192e22019-11-07 18:10:57 +0100430static void srvc_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
431{
Harald Welte16c81ea2020-02-16 14:44:00 +0100432 struct rspro_server_conn *srvc = (struct rspro_server_conn *) fi->priv;
433
Harald Welted2192e22019-11-07 18:10:57 +0100434 switch (event) {
435 case SRVC_E_ESTABLISH:
James Tavaresb8900182022-11-14 11:04:11 -0500436 /* reset delay connect immediately on our first connection */
437 srvc->reestablish_delay_idx = 0;
438 srvc->reestablish_last_ms = 0;
439 srvc_do_reestablish(fi);
Harald Welted2192e22019-11-07 18:10:57 +0100440 break;
Harald Welte16c81ea2020-02-16 14:44:00 +0100441 case SRVC_E_DISCONNECT:
442 if (srvc->keepalive_fi) {
443 ipa_keepalive_fsm_stop(srvc->keepalive_fi);
444 osmo_fsm_inst_term(srvc->keepalive_fi, OSMO_FSM_TERM_REGULAR, NULL);
445 srvc->keepalive_fi = NULL;
446 }
447 if (srvc->conn) {
448 LOGPFSML(fi, LOGL_INFO, "Destroying existing connection to server\n");
449 ipa_client_conn_close(srvc->conn);
450 ipa_client_conn_destroy(srvc->conn);
451 srvc->conn = NULL;
452 }
453 osmo_fsm_inst_state_chg(fi, SRVC_ST_INIT, 0, 0);
454 break;
Harald Welted2192e22019-11-07 18:10:57 +0100455 default:
456 OSMO_ASSERT(0);
457 }
458}
459
Harald Welte24173fb2018-08-24 20:37:28 +0200460static int server_conn_fsm_timer_cb(struct osmo_fsm_inst *fi)
461{
Harald Weltea025e702019-03-09 21:39:09 +0100462 struct rspro_server_conn *srvc = (struct rspro_server_conn *) fi->priv;
463
Harald Welte52cc7db2018-09-24 11:51:51 +0200464 switch (fi->T) {
James Tavaresb8900182022-11-14 11:04:11 -0500465 case 3:
466 /* delay has expired; let's re-establish */
Harald Welte52cc7db2018-09-24 11:51:51 +0200467 osmo_fsm_inst_state_chg(fi, SRVC_ST_REESTABLISH, T2_RECONNECT, 2);
468 break;
James Tavaresb8900182022-11-14 11:04:11 -0500469 case 2:
470 /* TCP reconnect failed: retry after wait */
471 srvc_do_reestablish(fi);
472 break;
Harald Welte52cc7db2018-09-24 11:51:51 +0200473 case 1:
Harald Welted2192e22019-11-07 18:10:57 +0100474 /* no ClientConnectRes received: disconnect + reconnect */
Harald Weltea025e702019-03-09 21:39:09 +0100475 ipa_client_conn_close(srvc->conn);
476 osmo_fsm_inst_dispatch(fi, SRVC_E_TCP_DOWN, NULL);
Harald Welte52cc7db2018-09-24 11:51:51 +0200477 break;
478 default:
479 OSMO_ASSERT(0);
480 }
481
Harald Welte24173fb2018-08-24 20:37:28 +0200482 return 0;
483}
484
485static const struct osmo_fsm_state server_conn_fsm_states[] = {
486 [SRVC_ST_INIT] = {
487 .name = "INIT",
Harald Welted2192e22019-11-07 18:10:57 +0100488 .in_event_mask = 0, /* S(SRVC_E_ESTABLISH) via allstate */
James Tavaresb8900182022-11-14 11:04:11 -0500489 .out_state_mask = S(SRVC_ST_INIT) | S(SRVC_ST_REESTABLISH_DELAY),
Harald Welte24173fb2018-08-24 20:37:28 +0200490 .action = srvc_st_init,
491 },
492 [SRVC_ST_ESTABLISHED] = {
493 .name = "ESTABLISHED",
Harald Welte03b24112019-03-08 19:43:02 +0100494 .in_event_mask = S(SRVC_E_TCP_DOWN) | S(SRVC_E_KA_TIMEOUT) | S(SRVC_E_CLIENT_CONN_RES),
James Tavaresb8900182022-11-14 11:04:11 -0500495 .out_state_mask = S(SRVC_ST_CONNECTED) | S(SRVC_ST_REESTABLISH_DELAY) | S(SRVC_ST_INIT),
Harald Welte24173fb2018-08-24 20:37:28 +0200496 .action = srvc_st_established,
Harald Welte84ba2342018-08-24 22:20:20 +0200497 .onenter = srvc_st_established_onenter,
Harald Welte24173fb2018-08-24 20:37:28 +0200498 },
499 [SRVC_ST_CONNECTED] = {
500 .name = "CONNECTED",
Harald Weltea3b14d12019-12-02 22:58:34 +0100501 .in_event_mask = S(SRVC_E_TCP_DOWN) | S(SRVC_E_KA_TIMEOUT) | S(SRVC_E_RSPRO_TX),
James Tavaresb8900182022-11-14 11:04:11 -0500502 .out_state_mask = S(SRVC_ST_REESTABLISH_DELAY) | S(SRVC_ST_INIT),
Harald Welte24173fb2018-08-24 20:37:28 +0200503 .action = srvc_st_connected,
Harald Welteb266d502020-02-22 20:53:19 +0100504 .onenter = srvc_st_connected_onenter,
505 .onleave = srvc_st_connected_onleave,
Harald Welte24173fb2018-08-24 20:37:28 +0200506 },
James Tavaresb8900182022-11-14 11:04:11 -0500507 [SRVC_ST_REESTABLISH_DELAY] = {
508 .name = "REESTABLISH_DELAY",
509 .in_event_mask = 0,
510 .out_state_mask = S(SRVC_ST_REESTABLISH) | S(SRVC_ST_INIT),
511 .action = srvc_st_reestablish_delay,
512 .onenter = srvc_st_reestablish_delay_onenter,
513 },
Harald Welte24173fb2018-08-24 20:37:28 +0200514 [SRVC_ST_REESTABLISH] = {
515 .name = "REESTABLISH",
Harald Welte52cc7db2018-09-24 11:51:51 +0200516 .in_event_mask = S(SRVC_E_TCP_UP) | S(SRVC_E_TCP_DOWN),
James Tavaresb8900182022-11-14 11:04:11 -0500517 .out_state_mask = S(SRVC_ST_ESTABLISHED) | S(SRVC_ST_REESTABLISH_DELAY) | S(SRVC_ST_INIT),
Harald Welte24173fb2018-08-24 20:37:28 +0200518 .action = srvc_st_reestablish,
Harald Welte84ba2342018-08-24 22:20:20 +0200519 .onenter = srvc_st_reestablish_onenter,
Harald Welte24173fb2018-08-24 20:37:28 +0200520 },
521};
522
Harald Welte3cded632019-03-09 12:59:41 +0100523struct osmo_fsm rspro_client_server_fsm = {
524 .name = "RSPRO_CLIENT",
Harald Welte24173fb2018-08-24 20:37:28 +0200525 .states = server_conn_fsm_states,
526 .num_states = ARRAY_SIZE(server_conn_fsm_states),
Harald Welte16c81ea2020-02-16 14:44:00 +0100527 .allstate_event_mask = S(SRVC_E_ESTABLISH) | S(SRVC_E_DISCONNECT),
Harald Welted2192e22019-11-07 18:10:57 +0100528 .allstate_action = srvc_allstate_action,
Harald Welte24173fb2018-08-24 20:37:28 +0200529 .timer_cb = server_conn_fsm_timer_cb,
Harald Welte46122ab2021-12-08 20:57:16 +0100530 .log_subsys = DRSPRO,
Harald Welte24173fb2018-08-24 20:37:28 +0200531 .event_names = server_conn_fsm_event_names,
532};
533
Harald Welte098ef872018-10-14 20:08:50 +0200534int server_conn_fsm_alloc(void *ctx, struct rspro_server_conn *srvc)
Harald Welte24173fb2018-08-24 20:37:28 +0200535{
536 struct osmo_fsm_inst *fi;
537
Harald Welte3cded632019-03-09 12:59:41 +0100538 fi = osmo_fsm_inst_alloc(&rspro_client_server_fsm, ctx, srvc, LOGL_DEBUG, "server");
Harald Welte24173fb2018-08-24 20:37:28 +0200539 if (!fi)
540 return -1;
541
Harald Welte84ba2342018-08-24 22:20:20 +0200542 srvc->fi = fi;
James Tavaresb8900182022-11-14 11:04:11 -0500543 srvc->reestablish_delay_idx = 0;
544 srvc->reestablish_last_ms = 0;
545
Harald Welte24173fb2018-08-24 20:37:28 +0200546 return 0;
547}
Harald Welte7fc64bc2019-03-02 12:55:59 +0100548
549static __attribute__((constructor)) void on_dso_load(void)
550{
Harald Welte4ccd2fc2019-12-01 13:32:54 +0100551 OSMO_ASSERT(osmo_fsm_register(&rspro_client_server_fsm) == 0);
Harald Welte7fc64bc2019-03-02 12:55:59 +0100552}