Add IPA keep-alive FSM implementation
The IPA keep-alive FSM code takes care of periodically transmitting and IPA CCM PING and expecting an IPA CCM PONG in return. Change-Id: I2763da49a74de85046ac07d53592c89973314ca6
This commit is contained in:
parent
bab7ae7e88
commit
3d60dbd021
|
@ -5,6 +5,7 @@
|
|||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/gsm/ipa.h>
|
||||
|
||||
struct e1inp_line;
|
||||
|
@ -99,4 +100,39 @@ int ipaccess_bts_handle_ccm(struct ipa_client_conn *link,
|
|||
|
||||
void ipa_msg_push_header(struct msgb *msg, uint8_t proto);
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* IPA Keep-Alive FSM
|
||||
***********************************************************************/
|
||||
|
||||
/*! parameters describing the keep-alive FSM (timeouts). */
|
||||
struct ipa_keepalive_params {
|
||||
/*! interval in which to send IPA CCM PING requests to the peer. */
|
||||
unsigned int interval;
|
||||
/*! time to wait for an IPA CCM PONG in response to a IPA CCM PING before giving up. */
|
||||
unsigned int wait_for_resp;
|
||||
};
|
||||
|
||||
typedef void ipa_keepalive_timeout_cb_t(struct osmo_fsm_inst *fi, void *conn);
|
||||
|
||||
struct osmo_fsm_inst *ipa_client_conn_alloc_keepalive_fsm(struct ipa_client_conn *client,
|
||||
const struct ipa_keepalive_params *params,
|
||||
const char *id);
|
||||
|
||||
struct osmo_fsm_inst *ipa_server_conn_alloc_keepalive_fsm(struct ipa_server_conn *server,
|
||||
const struct ipa_keepalive_params *params,
|
||||
const char *id);
|
||||
|
||||
struct osmo_fsm_inst *ipa_keepalive_alloc_server(struct ipa_server_conn *server,
|
||||
const struct ipa_keepalive_params *params,
|
||||
const char *id);
|
||||
|
||||
void ipa_keepalive_fsm_set_timeout_cb(struct osmo_fsm_inst *fi, ipa_keepalive_timeout_cb_t *cb);
|
||||
|
||||
void ipa_keepalive_fsm_start(struct osmo_fsm_inst *fi);
|
||||
|
||||
void ipa_keepalive_fsm_stop(struct osmo_fsm_inst *fi);
|
||||
|
||||
void ipa_keepalive_fsm_pong_received(struct osmo_fsm_inst *fi);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -21,6 +21,7 @@ libosmoabis_la_SOURCES = init.c \
|
|||
trau_frame.c \
|
||||
input/dahdi.c \
|
||||
input/ipa.c \
|
||||
input/ipa_keepalive.c \
|
||||
input/ipaccess.c \
|
||||
input/lapd.c \
|
||||
input/lapd_pcap.c \
|
||||
|
|
|
@ -0,0 +1,290 @@
|
|||
/* IPA keep-alive FSM; Periodically transmit IPA_PING and expect IPA_PONG in return.
|
||||
*
|
||||
* (C) 2019 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include <osmocom/gsm/protocol/ipaccess.h>
|
||||
|
||||
#include <osmocom/abis/ipa.h>
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
|
||||
/* generate a msgb containing an IPA CCM PING message */
|
||||
static struct msgb *gen_ipa_ping(void)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc_headroom(64, 32, "IPA PING");
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
msgb_put_u8(msg, IPAC_MSGT_PING);
|
||||
ipa_msg_push_header(msg, IPAC_PROTO_IPACCESS);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
enum osmo_ipa_keepalive_state {
|
||||
OSMO_IPA_KA_S_INIT,
|
||||
OSMO_IPA_KA_S_IDLE, /* waiting for next interval */
|
||||
OSMO_IPA_KA_S_WAIT_RESP, /* waiting for response to keepalive */
|
||||
};
|
||||
|
||||
enum osmo_ipa_keepalive_event {
|
||||
OSMO_IPA_KA_E_START,
|
||||
OSMO_IPA_KA_E_STOP,
|
||||
OSMO_IPA_KA_E_PONG,
|
||||
};
|
||||
|
||||
static const struct value_string ipa_keepalive_event_names[] = {
|
||||
OSMO_VALUE_STRING(OSMO_IPA_KA_E_START),
|
||||
OSMO_VALUE_STRING(OSMO_IPA_KA_E_STOP),
|
||||
OSMO_VALUE_STRING(OSMO_IPA_KA_E_PONG),
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
enum ipa_fsm_timer {
|
||||
T_SEND_NEXT_PING = 1,
|
||||
T_PONG_NOT_RECEIVED = 2,
|
||||
};
|
||||
|
||||
struct ipa_fsm_priv {
|
||||
struct ipa_keepalive_params params;
|
||||
|
||||
struct ipa_server_conn *srv_conn;
|
||||
struct ipa_client_conn *client_conn;
|
||||
ipa_keepalive_timeout_cb_t *timeout_cb;
|
||||
};
|
||||
|
||||
static void ipa_ka_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct ipa_fsm_priv *ifp = fi->priv;
|
||||
|
||||
switch (event) {
|
||||
case OSMO_IPA_KA_E_START:
|
||||
osmo_fsm_inst_state_chg(fi, OSMO_IPA_KA_S_WAIT_RESP,
|
||||
ifp->params.wait_for_resp, T_PONG_NOT_RECEIVED);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ipa_ka_wait_resp_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct ipa_fsm_priv *ifp = fi->priv;
|
||||
struct msgb *msg;
|
||||
|
||||
/* Send an IPA PING to the peer */
|
||||
msg = gen_ipa_ping();
|
||||
OSMO_ASSERT(msg);
|
||||
|
||||
if (ifp->srv_conn)
|
||||
ipa_server_conn_send(ifp->srv_conn, msg);
|
||||
else {
|
||||
OSMO_ASSERT(ifp->client_conn);
|
||||
ipa_client_conn_send(ifp->client_conn, msg);
|
||||
}
|
||||
}
|
||||
|
||||
static void ipa_ka_wait_resp(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct ipa_fsm_priv *ifp = fi->priv;
|
||||
|
||||
switch (event) {
|
||||
case OSMO_IPA_KA_E_PONG:
|
||||
osmo_fsm_inst_state_chg(fi, OSMO_IPA_KA_S_IDLE,
|
||||
ifp->params.interval, T_SEND_NEXT_PING);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static int ipa_ka_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct ipa_fsm_priv *ifp = fi->priv;
|
||||
void *conn;
|
||||
|
||||
switch (fi->T) {
|
||||
case T_SEND_NEXT_PING:
|
||||
/* send another PING */
|
||||
osmo_fsm_inst_state_chg(fi, OSMO_IPA_KA_S_WAIT_RESP,
|
||||
ifp->params.wait_for_resp, T_PONG_NOT_RECEIVED);
|
||||
return 0;
|
||||
case T_PONG_NOT_RECEIVED:
|
||||
/* PONG not received within time */
|
||||
if (ifp->srv_conn)
|
||||
conn = ifp->srv_conn;
|
||||
else
|
||||
conn = ifp->client_conn;
|
||||
if (ifp->timeout_cb)
|
||||
ifp->timeout_cb(fi, conn);
|
||||
/* ask fsm core to terminate us */
|
||||
return 1;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void ipa_ka_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case OSMO_IPA_KA_E_STOP:
|
||||
osmo_fsm_inst_state_chg(fi, OSMO_IPA_KA_S_INIT, 0, 0);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct osmo_fsm_state ipa_keepalive_states[] = {
|
||||
[OSMO_IPA_KA_S_INIT] = {
|
||||
.name = "INIT",
|
||||
.in_event_mask = S(OSMO_IPA_KA_E_START),
|
||||
.out_state_mask = S(OSMO_IPA_KA_S_WAIT_RESP),
|
||||
.action = ipa_ka_init,
|
||||
},
|
||||
[OSMO_IPA_KA_S_IDLE] = {
|
||||
.name = "IDLE",
|
||||
.out_state_mask = S(OSMO_IPA_KA_S_WAIT_RESP) | S(OSMO_IPA_KA_S_INIT),
|
||||
/* no permitted events aside from E_START, which is handled in allstate_events */
|
||||
},
|
||||
[OSMO_IPA_KA_S_WAIT_RESP] = {
|
||||
.name = "WAIT_RESP",
|
||||
.in_event_mask = S(OSMO_IPA_KA_E_PONG),
|
||||
.out_state_mask = S(OSMO_IPA_KA_S_IDLE) | S(OSMO_IPA_KA_S_INIT),
|
||||
.action = ipa_ka_wait_resp,
|
||||
.onenter = ipa_ka_wait_resp_onenter,
|
||||
},
|
||||
};
|
||||
|
||||
static struct osmo_fsm ipa_keepalive_fsm = {
|
||||
.name = "IPA-KEEPALIVE",
|
||||
.states = ipa_keepalive_states,
|
||||
.num_states = ARRAY_SIZE(ipa_keepalive_states),
|
||||
.log_subsys = DLINP,
|
||||
.allstate_action = ipa_ka_allstate_action,
|
||||
.event_names = ipa_keepalive_event_names,
|
||||
.timer_cb = ipa_ka_fsm_timer_cb,
|
||||
};
|
||||
|
||||
static __attribute__((constructor)) void on_dso_load(void)
|
||||
{
|
||||
osmo_fsm_register(&ipa_keepalive_fsm);
|
||||
}
|
||||
|
||||
|
||||
static struct osmo_fsm_inst *
|
||||
__ipa_conn_alloc_keepalive_fsm(void *ctx, const struct ipa_keepalive_params *params, const char *id)
|
||||
{
|
||||
struct osmo_fsm_inst *fi;
|
||||
struct ipa_fsm_priv *ifp;
|
||||
|
||||
fi = osmo_fsm_inst_alloc(&ipa_keepalive_fsm, ctx, NULL, LOGL_DEBUG, id);
|
||||
if (!fi)
|
||||
return NULL;
|
||||
ifp = talloc_zero(fi, struct ipa_fsm_priv);
|
||||
if (!ifp) {
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(&ifp->params, params, sizeof(ifp->params));
|
||||
fi->priv = ifp;
|
||||
|
||||
return fi;
|
||||
}
|
||||
|
||||
/*! Create a new instance of an IPA keepalive FSM: Periodically transmit PING and expect PONG.
|
||||
* \param[in] client The client connection for which to crate the FSM. Used as talloc context.
|
||||
* \param[in] params Parameters describing the keepalive FSM time-outs.
|
||||
* \param[in] id String used as identifier for the FSM.
|
||||
* \returns pointer to the newly-created FSM instance; NULL in case of error. */
|
||||
struct osmo_fsm_inst *ipa_client_conn_alloc_keepalive_fsm(struct ipa_client_conn *client,
|
||||
const struct ipa_keepalive_params *params,
|
||||
const char *id)
|
||||
{
|
||||
struct osmo_fsm_inst *fi;
|
||||
struct ipa_fsm_priv *ifp;
|
||||
|
||||
fi = __ipa_conn_alloc_keepalive_fsm(client, params, id);
|
||||
if (!fi)
|
||||
return NULL;
|
||||
ifp = fi->priv;
|
||||
ifp->client_conn = client;
|
||||
return fi;
|
||||
}
|
||||
|
||||
/*! Create a new instance of an IPA keepalive FSM: Periodically transmit PING and expect PONG.
|
||||
* \param[in] server The server connection for which to crate the FSM. Used as talloc context.
|
||||
* \param[in] params Parameters describing the keepalive FSM time-outs.
|
||||
* \param[in] id String used as identifier for the FSM.
|
||||
* \returns pointer to the newly-created FSM instance; NULL in case of error. */
|
||||
struct osmo_fsm_inst *ipa_server_conn_alloc_keepalive_fsm(struct ipa_server_conn *server,
|
||||
const struct ipa_keepalive_params *params,
|
||||
const char *id)
|
||||
{
|
||||
struct osmo_fsm_inst *fi;
|
||||
struct ipa_fsm_priv *ifp;
|
||||
|
||||
fi = __ipa_conn_alloc_keepalive_fsm(server, params, id);
|
||||
if (!fi)
|
||||
return NULL;
|
||||
ifp = fi->priv;
|
||||
ifp->srv_conn = server;
|
||||
return fi;
|
||||
}
|
||||
|
||||
/*! Set a timeout call-back which is to be called once the peer doesn't respond anymore */
|
||||
void ipa_keepalive_fsm_set_timeout_cb(struct osmo_fsm_inst *fi, ipa_keepalive_timeout_cb_t *cb)
|
||||
{
|
||||
struct ipa_fsm_priv *ifp = fi->priv;
|
||||
OSMO_ASSERT(fi->fsm == &ipa_keepalive_fsm);
|
||||
ifp->timeout_cb = cb;
|
||||
}
|
||||
|
||||
/*! Inform IPA Keepalive FSM that a PONG has been received. */
|
||||
void ipa_keepalive_fsm_pong_received(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
OSMO_ASSERT(fi->fsm == &ipa_keepalive_fsm);
|
||||
osmo_fsm_inst_dispatch(fi, OSMO_IPA_KA_E_PONG, NULL);
|
||||
}
|
||||
|
||||
/*! Start the ping/pong procedure of the IPA Keepalive FSM. */
|
||||
void ipa_keepalive_fsm_start(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
OSMO_ASSERT(fi->fsm == &ipa_keepalive_fsm);
|
||||
osmo_fsm_inst_dispatch(fi, OSMO_IPA_KA_E_START, NULL);
|
||||
}
|
||||
|
||||
/*! Stop the ping/pong procedure of the IPA Keepalive FSM. */
|
||||
void ipa_keepalive_fsm_stop(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
OSMO_ASSERT(fi->fsm == &ipa_keepalive_fsm);
|
||||
osmo_fsm_inst_dispatch(fi, OSMO_IPA_KA_E_STOP, NULL);
|
||||
}
|
Loading…
Reference in New Issue