Completed implementation of v52_le_pp_fsm.c

This commit is contained in:
Andreas Eversberg 2023-01-05 18:12:38 +01:00
parent f7117cdeae
commit fe66653df6
2 changed files with 851 additions and 0 deletions

845
src/v52_le_pp_fsm.c Normal file
View File

@ -0,0 +1,845 @@
/* ITu-T G.965 Section 18 V5.2-interface Protection protocol FSM - LE side */
/* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
*
* 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.
*
*/
/***********************************************************************/
/* internal data structures */
/***********************************************************************/
#include <unistd.h>
#include <stdint.h>
#include <errno.h>
#include <arpa/inet.h>
#include <osmocom/core/utils.h>
#include "v5x_internal.h"
#include "v5x_protocol.h"
#include "v5x_le_management.h"
#include "v52_le_pp_fsm.h"
#include "logging.h"
/* Table 64/G.965 Timers in the LE */
#define TIMEOUT_TSO1 1500
#define TIMEOUT_TSO2 1500
#define TIMEOUT_TSO4 20000
#define TIMEOUT_TSO5 10000
#define S(x) (1 << (x))
/***********************************************************************/
/* state names, event names, primitives, ... */
/***********************************************************************/
/* 18.3.1.2 */
enum v52_le_pp_fsm_state {
V52_PPFSM_S_SOLE0_NULL,
V52_PPFSM_S_SOLE1_SWITCH_OVER_INIT_BY_LE,
V52_PPFSM_S_SOLE2_SWITCH_OVER_REQ_BY_AN,
};
enum v52_le_pp_fsm_event {
V52_PPFSM_E_MDU_Protection_switch_over_com,
V52_PPFSM_E_MDU_Protection_OS_switch_over_com,
V52_PPFSM_E_MDU_Protection_switch_over_reject,
V52_PPFSM_E_MDU_Protection_reset_SN_req,
V52_PPFSM_E_VP_S_VP_R_misalignment_detected,
V52_PPFSM_E_SWITCH_OVER_REQ,
V52_PPFSM_E_SWITCH_OVER_ACK,
V52_PPFSM_E_SWITCH_OVER_REJECT,
V52_PPFSM_E_PROTOCOL_ERROR,
V52_PPFSM_E_RESET_SN_COM,
V52_PPFSM_E_RESET_SN_ACK,
V52_PPFSM_E_TSO1,
V52_PPFSM_E_TSO2,
V52_PPFSM_E_TSO4,
V52_PPFSM_E_TSO5,
};
static const struct value_string v52_le_pp_fsm_event_names[] = {
{ V52_PPFSM_E_MDU_Protection_switch_over_com, "MDU-Protection (switch_over com)" },
{ V52_PPFSM_E_MDU_Protection_OS_switch_over_com, "MDU-Protection (OS_switch-over com)" },
{ V52_PPFSM_E_MDU_Protection_switch_over_reject, "MDU-Protection (switch-over reject)" },
{ V52_PPFSM_E_MDU_Protection_reset_SN_req, "MDU-Protection (reset SN req)" },
{ V52_PPFSM_E_VP_S_VP_R_misalignment_detected, "VP(S) VP(R) misalignment detected" },
{ V52_PPFSM_E_SWITCH_OVER_REQ, "SWITCH-OVER REQ" },
{ V52_PPFSM_E_SWITCH_OVER_ACK, "SWITCH-OVER ACK" },
{ V52_PPFSM_E_SWITCH_OVER_REJECT, "SWITCH-OVER REJECT" },
{ V52_PPFSM_E_PROTOCOL_ERROR, "PROTOCOL ERROR" },
{ V52_PPFSM_E_RESET_SN_COM, "RESET SN COM" },
{ V52_PPFSM_E_RESET_SN_ACK, "RESET SN ACK" },
{ V52_PPFSM_E_TSO1, "expiry TSO1" },
{ V52_PPFSM_E_TSO2, "expiry TSO2" },
{ V52_PPFSM_E_TSO4, "expiry TSO4" },
{ V52_PPFSM_E_TSO5, "expiry TSO5" },
{ 0, NULL }
};
/***********************************************************************/
/* Messages to other layers */
/***********************************************************************/
static inline void v52_pp_pcc_dec(uint16_t ie, uint8_t *link_id, uint8_t *ts);
/* send message to upper (management) layer */
static void mdu_rcv(struct osmo_fsm_inst *fi, enum v5x_mgmt_prim prim, const struct tlv_parsed *tp)
{
struct v52_pp_proto *pp = fi->priv;
struct v5x_interface *v5if = pp->interface;
const uint8_t *ie, *cause = NULL;
uint8_t ie_len, link_id = 0, ts = 0, cause_len = 0;
if (tp) {
if (TLVP_PRESENT(tp, V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID)) {
ie = TLVP_VAL(tp, V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID);
ie_len = TLVP_LEN(tp, V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID);
if (ie_len == 2)
v52_pp_pcc_dec(ntohs(*((uint16_t *)ie)), &link_id, &ts);
}
if (TLVP_PRESENT(tp, V52_CTRL_IEI_PP_REJECTION_CAUSE)) {
cause = TLVP_VAL(tp, V52_CTRL_IEI_PP_REJECTION_CAUSE);
cause_len = TLVP_LEN(tp, V52_CTRL_IEI_PP_REJECTION_CAUSE);
}
if (TLVP_PRESENT(tp, V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE)) {
cause = TLVP_VAL(tp, V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE);
cause_len = TLVP_LEN(tp, V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE);
}
}
v52_le_pp_mdu_rcv(v5if, prim, link_id, ts, cause, cause_len);
}
/* send message to lower (DL) layer(s) */
static void dl_send(struct osmo_fsm_inst *fi, struct msgb *msg)
{
struct v52_pp_proto *pp = fi->priv;
v5x_dl_snd(pp->interface, V52_DLADDR_PROTECTION, msg);
}
/***********************************************************************
* V5 Message encoding / sending
***********************************************************************/
static inline uint16_t v52_pp_pcc_enc(uint8_t link_id, uint8_t ts)
{
return (link_id << 8) | ts;
}
static inline void v52_pp_pcc_dec(uint16_t ie, uint8_t *link_id, uint8_t *ts)
{
*link_id = ie >> 8;
*ts = ie & 0x1f;
}
/* G.965 Section 18.4.2/18.4.3 / Table 53/54 */
static struct msgb *v52_enc_switch_over_com(struct v52_pp_proto *pp, uint16_t cc_id, uint8_t link_id, uint8_t ts,
uint8_t msg_type)
{
uint8_t seq_ie = pp->vp_s | 0x80;
uint16_t v5_cc_ie;
struct v5x_l3_hdr *l3h;
struct msgb *msg = msgb_alloc_v5x();
if (!msg)
return NULL;
l3h = (struct v5x_l3_hdr *) msgb_put(msg, sizeof(*l3h));
l3h->pdisc = V5X_CTRL_PDISC;
l3h->l3_addr = htons(cc_id);
l3h->msg_type = msg_type;
/* Sequence Number */
msgb_tlv_put(msg, V52_CTRL_IEI_PP_SEQUENCE_NR, 1, &seq_ie);
pp->vp_s = (pp->vp_s + 1) & 0x7f;
/* Physical C-channel identification */
v5_cc_ie = htons(v52_pp_pcc_enc(link_id, ts));
msgb_tlv_put(msg, V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID, 2, (uint8_t *)&v5_cc_ie);
return msg;
}
/* G.965 Section 18.4.5 / Table 56 */
static struct msgb *v52_enc_switch_over_rej(struct v52_pp_proto *pp, uint16_t cc_id, uint8_t link_id, uint8_t ts,
uint8_t cause)
{
uint8_t seq_ie = pp->vp_s | 0x80;
uint16_t v5_cc_ie;
uint8_t cause_ie = cause | 0x80;
struct v5x_l3_hdr *l3h;
struct msgb *msg = msgb_alloc_v5x();
if (!msg)
return NULL;
l3h = (struct v5x_l3_hdr *) msgb_put(msg, sizeof(*l3h));
l3h->pdisc = V5X_CTRL_PDISC;
l3h->l3_addr = htons(cc_id);
l3h->msg_type = V52_CTRL_MSGT_PP_SWITCH_OVER_REJECT;
/* Sequence Number */
msgb_tlv_put(msg, V52_CTRL_IEI_PP_SEQUENCE_NR, 1, &seq_ie);
pp->vp_s = (pp->vp_s + 1) & 0x7f;
/* Physical C-channel identification */
v5_cc_ie = htons(v52_pp_pcc_enc(link_id, ts));
msgb_tlv_put(msg, V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID, 2, (uint8_t *)&v5_cc_ie);
/* Rejection Cause */
msgb_tlv_put(msg, V52_CTRL_IEI_PP_REJECTION_CAUSE, 1, &cause_ie);
return msg;
}
/* G.965 Section 18.4.6 / Table 57 */
static struct msgb *v52_enc_protocol_error(struct v52_pp_proto *pp, uint16_t cc_id, uint8_t cause, uint8_t *diagnostic,
int diagnostic_len)
{
uint8_t seq_ie = pp->vp_s | 0x80;
uint8_t cause_ie[3] = { cause | 0x80 };
struct v5x_l3_hdr *l3h;
struct msgb *msg = msgb_alloc_v5x();
if (!msg)
return NULL;
l3h = (struct v5x_l3_hdr *) msgb_put(msg, sizeof(*l3h));
l3h->pdisc = V5X_CTRL_PDISC;
l3h->l3_addr = htons(cc_id);
l3h->msg_type = V52_CTRL_MSGT_PP_PROTOCOL_ERROR;
/* Sequence Number */
msgb_tlv_put(msg, V52_CTRL_IEI_PP_SEQUENCE_NR, 1, &seq_ie);
pp->vp_s = (pp->vp_s + 1) & 0x7f;
/* Protocol Error Cause */
if (diagnostic && diagnostic_len)
memcpy(cause_ie + 1, diagnostic, diagnostic_len);
msgb_tlv_put(msg, V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE, 1 + diagnostic_len, (uint8_t *)&cause_ie);
return msg;
}
/* G.965 Section 18.4.7/18.4.8 / Table 58/59 */
static struct msgb *v52_enc_reset_sn_xxx(uint16_t cc_id, uint8_t msg_type)
{
struct v5x_l3_hdr *l3h;
struct msgb *msg = msgb_alloc_v5x();
if (!msg)
return NULL;
l3h = (struct v5x_l3_hdr *) msgb_put(msg, sizeof(*l3h));
l3h->pdisc = V5X_CTRL_PDISC;
l3h->l3_addr = htons(cc_id);
l3h->msg_type = msg_type;
return msg;
}
/***********************************************************************/
/* Protection Protocol state FSM */
/***********************************************************************/
static void stop_timer(struct osmo_fsm_inst *fi)
{
LOGP(DV5PP, LOGL_DEBUG, "Stop all timers\n");
osmo_timer_del(&fi->timer);
}
static void start_timer(struct osmo_fsm_inst *fi, enum v52_le_pp_fsm_event event, int count, struct v52_pp_mgmt_info *info)
{
struct v52_pp_proto *pp = fi->priv;
int timeout = 0;
const char *timer_name = NULL;
switch (event) {
case V52_PPFSM_E_TSO1:
timer_name = "TSO1";
timeout = TIMEOUT_TSO1;
fi->T = 1;
break;
case V52_PPFSM_E_TSO2:
timer_name = "TSO2";
timeout = TIMEOUT_TSO2;
fi->T = 2;
break;
case V52_PPFSM_E_TSO4:
timer_name = "TSO4";
timeout = TIMEOUT_TSO4;
fi->T = 3;
break;
case V52_PPFSM_E_TSO5:
timer_name = "TSO5";
timeout = TIMEOUT_TSO5;
fi->T = 4;
break;
default:
OSMO_ASSERT(0);
}
if (info)
memcpy(&pp->info, info, sizeof(*info));
LOGP(DV5PP, LOGL_DEBUG, "Start timer %s (count = %d)\n", timer_name, count);
pp->timeout_event = event;
pp->timeout_count = count;
osmo_timer_schedule(&fi->timer, timeout / 1000, timeout % 1000);
}
static bool timer_pending(struct osmo_fsm_inst *fi, enum v52_le_pp_fsm_event event)
{
struct v52_pp_proto *pp = fi->priv;
if (pp->timeout_event != (int)event)
return false;
return osmo_timer_pending(&fi->timer);
}
static int v52_le_pp_fsm_timer_cb(struct osmo_fsm_inst *fi)
{
struct v52_pp_proto *pp = fi->priv;
osmo_fsm_inst_dispatch(fi, pp->timeout_event, &pp->info);
return 0;
}
static void pp_sole0_null(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct v52_pp_proto *pp = fi->priv;
struct v52_pp_mgmt_info *info = data;
switch (event) {
case V52_PPFSM_E_MDU_Protection_switch_over_com:
if (!timer_pending(fi, V52_PPFSM_E_TSO4)) {
/* change state to SOLE1 */
osmo_fsm_inst_state_chg_ms(fi, V52_PPFSM_S_SOLE1_SWITCH_OVER_INIT_BY_LE, 0, 0);
/* start TSO1 */
start_timer(fi, V52_PPFSM_E_TSO1, 1, info);
/* send SWITCH-OVER COM */
dl_send(fi, v52_enc_switch_over_com(pp, pp->interface->protection.cc_id, info->link_id, info->ts, V52_CTRL_MSGT_PP_SWITCH_OVER_COM));
} else {
/* send MDU-Prot. (reset SN error ind) */
mdu_rcv(fi, MDU_Protection_reset_SN_error_ind, NULL);
}
break;
case V52_PPFSM_E_MDU_Protection_OS_switch_over_com:
if (!timer_pending(fi, V52_PPFSM_E_TSO4)) {
/* change state to SOLE1 */
osmo_fsm_inst_state_chg_ms(fi, V52_PPFSM_S_SOLE1_SWITCH_OVER_INIT_BY_LE, 0, 0);
/* start TSO2 */
start_timer(fi, V52_PPFSM_E_TSO2, 1, info);
/* send OS SWITCH-OVER COM */
dl_send(fi, v52_enc_switch_over_com(pp, pp->interface->protection.cc_id, info->link_id, info->ts, V52_CTRL_MSGT_PP_OS_SWITCH_OVER_COM));
} else {
/* send MDU-Prot. (reset SN error ind) */
mdu_rcv(fi, MDU_Protection_reset_SN_error_ind, NULL);
}
break;
case V52_PPFSM_E_SWITCH_OVER_ACK:
if (!timer_pending(fi, V52_PPFSM_E_TSO4)) {
/* send MDU-Prot. (switch-over ack) */
mdu_rcv(fi, MDU_Protection_switch_over_ack, data);
} else {
/* ignore */
}
break;
case V52_PPFSM_E_SWITCH_OVER_REQ:
if (!timer_pending(fi, V52_PPFSM_E_TSO4)) {
/* change state to SOLE2 */
osmo_fsm_inst_state_chg(fi, V52_PPFSM_S_SOLE2_SWITCH_OVER_REQ_BY_AN, 0, 0);
/* send MDU-Prot. (switch-over req) */
mdu_rcv(fi, MDU_Protection_switch_over_req, data);
} else {
/* ignore */
}
break;
case V52_PPFSM_E_SWITCH_OVER_REJECT:
if (!timer_pending(fi, V52_PPFSM_E_TSO4)) {
/* send MDU-Prot. (switch-over reject ind) */
mdu_rcv(fi, MDU_Protection_switch_over_reject_ind, data);
} else {
/* ignore */
}
break;
case V52_PPFSM_E_VP_S_VP_R_misalignment_detected:
case V52_PPFSM_E_MDU_Protection_reset_SN_req:
/* start TSO4 */
start_timer(fi, V52_PPFSM_E_TSO4, 1, NULL);
/* send RESET SN COM */
dl_send(fi, v52_enc_reset_sn_xxx(pp->interface->protection.cc_id, V52_CTRL_MSGT_PP_RESET_SN_COM));
/* set VP(S) = VP(R) = 0 */
pp->vp_s = pp->vp_r = 0;
/* send MDU-Prot. (reset SN com) */
mdu_rcv(fi, MDU_Protection_reset_SN_com, NULL);
break;
case V52_PPFSM_E_RESET_SN_COM:
if (!timer_pending(fi, V52_PPFSM_E_TSO5)) {
/* start TSO5 */
start_timer(fi, V52_PPFSM_E_TSO5, 1, NULL);
/* set VP(S) = VP(R) = 0 */
pp->vp_s = pp->vp_r = 0;
/* send RESET SN ACK */
dl_send(fi, v52_enc_reset_sn_xxx(pp->interface->protection.cc_id, V52_CTRL_MSGT_PP_RESET_SN_ACK));
/* send MDU-Prot. (reset SN ind) */
mdu_rcv(fi, MDU_Protection_reset_SN_ind, NULL);
} else {
/* ignore */
}
break;
case V52_PPFSM_E_RESET_SN_ACK:
if (!timer_pending(fi, V52_PPFSM_E_TSO4)) {
/* ignore */
} else {
/* stop TSO4 */
stop_timer(fi);
/* send MDU-Prot. (reset SN ack) */
mdu_rcv(fi, MDU_Protection_reset_SN_ack, NULL);
}
break;
case V52_PPFSM_E_TSO4:
if (pp->timeout_count < 2) {
/* restart TSO4 */
start_timer(fi, V52_PPFSM_E_TSO4, 2, NULL);
/* send RESET SN COM */
dl_send(fi, v52_enc_reset_sn_xxx(pp->interface->protection.cc_id, V52_CTRL_MSGT_PP_RESET_SN_COM));
/* set VP(S) = VP(R) = 0 */
pp->vp_s = pp->vp_r = 0;
/* send MDU-Prot. (reset SN com) */
mdu_rcv(fi, MDU_Protection_reset_SN_com, NULL);
} else {
/* send MDU-Prot. (reset SN error ind) */
mdu_rcv(fi, MDU_Protection_reset_SN_error_ind, NULL);
}
break;
case V52_PPFSM_E_TSO5:
/* ignore */
break;
case V52_PPFSM_E_PROTOCOL_ERROR:
if (!timer_pending(fi, V52_PPFSM_E_TSO4)) {
/* send MDU-Prot. (Protocol error ind) */
mdu_rcv(fi, MDU_Protection_protocol_error_ind, data);
} else {
/* ignore */
}
break;
default:
OSMO_ASSERT(0);
}
}
static void pp_sole1_switch_over_init_by_le(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct v52_pp_proto *pp = fi->priv;
struct v52_pp_mgmt_info *info = data;
switch (event) {
case V52_PPFSM_E_SWITCH_OVER_ACK:
/* stop timer */
stop_timer(fi);
/* change state to SOLE0, stop timer */
osmo_fsm_inst_state_chg(fi, V52_PPFSM_S_SOLE0_NULL, 0, 0);
/* send MDU-Prot. (switch-over ack) */
mdu_rcv(fi, MDU_Protection_switch_over_ack, data);
break;
case V52_PPFSM_E_SWITCH_OVER_REQ:
/* ignore */
break;
case V52_PPFSM_E_SWITCH_OVER_REJECT:
/* stop timer */
stop_timer(fi);
/* change state to SOLE0 */
osmo_fsm_inst_state_chg(fi, V52_PPFSM_S_SOLE0_NULL, 0, 0);
/* send MDU-Prot. (switch-over reject ind) */
mdu_rcv(fi, MDU_Protection_switch_over_reject_ind, data);
break;
case V52_PPFSM_E_TSO1:
/* first timeout ? */
if (pp->timeout_count < 2) {
/* restart TSO1 */
start_timer(fi, V52_PPFSM_E_TSO1, 2, info);
/* send SWITCH-OVER COM */
dl_send(fi, v52_enc_switch_over_com(pp, pp->interface->protection.cc_id, info->link_id, info->ts, V52_CTRL_MSGT_PP_SWITCH_OVER_COM));
} else {
/* change state to SOLE0 */
osmo_fsm_inst_state_chg(fi, V52_PPFSM_S_SOLE0_NULL, 0, 0);
/* send MDU-Prot. (switch-over error ind) */
mdu_rcv(fi, MDU_Protection_switch_over_error_ind, NULL);
}
break;
case V52_PPFSM_E_TSO2:
if (pp->timeout_count < 2) {
/* restart TSO2 */
start_timer(fi, V52_PPFSM_E_TSO2, 2, info);
/* send OS SWITCH-OVER COM */
dl_send(fi, v52_enc_switch_over_com(pp, pp->interface->protection.cc_id, info->link_id, info->ts, V52_CTRL_MSGT_PP_OS_SWITCH_OVER_COM));
} else {
/* change state to SOLE0 */
osmo_fsm_inst_state_chg(fi, V52_PPFSM_S_SOLE0_NULL, 0, 0);
/* send MDU-Prot. (switch-over error ind) */
mdu_rcv(fi, MDU_Protection_switch_over_error_ind, NULL);
}
break;
case V52_PPFSM_E_VP_S_VP_R_misalignment_detected:
case V52_PPFSM_E_MDU_Protection_reset_SN_req:
/* stop timer */
stop_timer(fi);
/* change state to SOLE0, stop timer */
osmo_fsm_inst_state_chg(fi, V52_PPFSM_S_SOLE0_NULL, 0, 4);
/* start TSO4 */
start_timer(fi, V52_PPFSM_E_TSO4, 1, NULL);
/* send RESET SN COM */
dl_send(fi, v52_enc_reset_sn_xxx(pp->interface->protection.cc_id, V52_CTRL_MSGT_PP_RESET_SN_COM));
/* set VP(S) = VP(R) = 0 */
pp->vp_s = pp->vp_r = 0;
/* send MDU-Prot. (reset SN com) */
mdu_rcv(fi, MDU_Protection_reset_SN_com, NULL);
break;
case V52_PPFSM_E_RESET_SN_COM:
/* first timeout ? */
if (pp->timeout_count < 2) {
/* stop timer */
stop_timer(fi);
/* change state to SOLE0 */
osmo_fsm_inst_state_chg(fi, V52_PPFSM_S_SOLE0_NULL, 0, 0);
/* start TSO5 */
start_timer(fi, V52_PPFSM_E_TSO5, 1, NULL);
/* set VP(S) = VP(R) = 0 */
pp->vp_s = pp->vp_r = 0;
/* send RESET SN ACK */
dl_send(fi, v52_enc_reset_sn_xxx(pp->interface->protection.cc_id, V52_CTRL_MSGT_PP_RESET_SN_ACK));
/* send MDU-Prot. (reset SN ind) */
mdu_rcv(fi, MDU_Protection_reset_SN_ind, NULL);
} else {
/* ignore */
}
break;
case V52_PPFSM_E_RESET_SN_ACK:
/* ignore */
break;
case V52_PPFSM_E_TSO5:
/* ignore */
break;
case V52_PPFSM_E_PROTOCOL_ERROR:
/* send MDU-Prot. (Protocol error ind) */
mdu_rcv(fi, MDU_Protection_protocol_error_ind, data);
break;
default:
OSMO_ASSERT(0);
}
}
static void pp_sole2_switch_over_req_by_an(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct v52_pp_proto *pp = fi->priv;
struct v52_pp_mgmt_info *info = data;
switch (event) {
case V52_PPFSM_E_MDU_Protection_switch_over_com:
/* change state to SOLE1 */
osmo_fsm_inst_state_chg_ms(fi, V52_PPFSM_S_SOLE1_SWITCH_OVER_INIT_BY_LE, 0, 0);
/* start TSO1 */
start_timer(fi, V52_PPFSM_E_TSO1, 1, info);
/* send SWITCH-OVER COM */
dl_send(fi, v52_enc_switch_over_com(pp, pp->interface->protection.cc_id, info->link_id, info->ts, V52_CTRL_MSGT_PP_SWITCH_OVER_COM));
break;
case V52_PPFSM_E_MDU_Protection_OS_switch_over_com:
/* change state to SOLE1 */
osmo_fsm_inst_state_chg_ms(fi, V52_PPFSM_S_SOLE1_SWITCH_OVER_INIT_BY_LE, 0, 0);
/* start TSO2 */
start_timer(fi, V52_PPFSM_E_TSO2, 1, info);
/* send OS SWITCH-OVER COM */
dl_send(fi, v52_enc_switch_over_com(pp, pp->interface->protection.cc_id, info->link_id, info->ts, V52_CTRL_MSGT_PP_OS_SWITCH_OVER_COM));
break;
case V52_PPFSM_E_MDU_Protection_switch_over_reject:
/* change state to SOLE0 */
osmo_fsm_inst_state_chg(fi, V52_PPFSM_S_SOLE0_NULL, 0, 0);
/* send OS SWITCH-OVER REJ */
dl_send(fi, v52_enc_switch_over_rej(pp, pp->interface->protection.cc_id, info->link_id, info->ts, info->cause));
break;
case V52_PPFSM_E_VP_S_VP_R_misalignment_detected:
case V52_PPFSM_E_MDU_Protection_reset_SN_req:
/* change state to SOLE0 */
osmo_fsm_inst_state_chg(fi, V52_PPFSM_S_SOLE0_NULL, TIMEOUT_TSO4, 0);
/* start TSO4 */
start_timer(fi, V52_PPFSM_E_TSO4, 1, NULL);
/* send RESET SN COM */
dl_send(fi, v52_enc_reset_sn_xxx(pp->interface->protection.cc_id, V52_CTRL_MSGT_PP_RESET_SN_COM));
/* set VP(S) = VP(R) = 0 */
pp->vp_s = pp->vp_r = 0;
/* send MDU-Prot. (reset SN com) */
mdu_rcv(fi, MDU_Protection_reset_SN_com, NULL);
break;
case V52_PPFSM_E_RESET_SN_COM:
/* first timeout ? */
if (pp->timeout_count < 2) {
/* change state to SOLE0, start TSO5 */
osmo_fsm_inst_state_chg(fi, V52_PPFSM_S_SOLE0_NULL, 0, 0);
/* start TSO5 */
start_timer(fi, V52_PPFSM_E_TSO5, 1, NULL);
/* set VP(S) = VP(R) = 0 */
pp->vp_s = pp->vp_r = 0;
/* send RESET SN ACK */
dl_send(fi, v52_enc_reset_sn_xxx(pp->interface->protection.cc_id, V52_CTRL_MSGT_PP_RESET_SN_ACK));
/* send MDU-Prot. (reset SN ind) */
mdu_rcv(fi, MDU_Protection_reset_SN_ind, NULL);
} else {
/* ignore */
}
break;
case V52_PPFSM_E_RESET_SN_ACK:
/* ignore */
break;
case V52_PPFSM_E_TSO5:
/* ignore */
break;
case V52_PPFSM_E_PROTOCOL_ERROR:
/* send MDU-Prot. (Protocol error ind) */
mdu_rcv(fi, MDU_Protection_protocol_error_ind, data);
break;
default:
OSMO_ASSERT(0);
}
}
/* Table 66/G.965 LE protection protocol FSM */
static const struct osmo_fsm_state v52_le_pp_fsm_states[] = {
[V52_PPFSM_S_SOLE0_NULL] = {
.name = "NULL (SOLE0)",
.in_event_mask = S(V52_PPFSM_E_MDU_Protection_switch_over_com) |
S(V52_PPFSM_E_MDU_Protection_OS_switch_over_com) |
S(V52_PPFSM_E_SWITCH_OVER_ACK) |
S(V52_PPFSM_E_SWITCH_OVER_REQ) |
S(V52_PPFSM_E_SWITCH_OVER_REJECT) |
S(V52_PPFSM_E_VP_S_VP_R_misalignment_detected) |
S(V52_PPFSM_E_MDU_Protection_reset_SN_req) |
S(V52_PPFSM_E_RESET_SN_COM) |
S(V52_PPFSM_E_RESET_SN_ACK) |
S(V52_PPFSM_E_TSO4) |
S(V52_PPFSM_E_TSO5) |
S(V52_PPFSM_E_PROTOCOL_ERROR),
.out_state_mask = S(V52_PPFSM_S_SOLE1_SWITCH_OVER_INIT_BY_LE) |
S(V52_PPFSM_S_SOLE2_SWITCH_OVER_REQ_BY_AN),
.action = pp_sole0_null,
},
[V52_PPFSM_S_SOLE1_SWITCH_OVER_INIT_BY_LE] = {
.name = "Switch over init by LE (SOLE1)",
.in_event_mask = S(V52_PPFSM_E_SWITCH_OVER_ACK) |
S(V52_PPFSM_E_SWITCH_OVER_REQ) |
S(V52_PPFSM_E_SWITCH_OVER_REJECT) |
S(V52_PPFSM_E_TSO1) |
S(V52_PPFSM_E_TSO2) |
S(V52_PPFSM_E_VP_S_VP_R_misalignment_detected) |
S(V52_PPFSM_E_MDU_Protection_reset_SN_req) |
S(V52_PPFSM_E_RESET_SN_COM) |
S(V52_PPFSM_E_RESET_SN_ACK) |
S(V52_PPFSM_E_TSO5) |
S(V52_PPFSM_E_PROTOCOL_ERROR),
.out_state_mask = S(V52_PPFSM_S_SOLE0_NULL),
.action = pp_sole1_switch_over_init_by_le,
},
[V52_PPFSM_S_SOLE2_SWITCH_OVER_REQ_BY_AN] = {
.name = "Switch over req by AN (SOLE2)",
.in_event_mask = S(V52_PPFSM_E_MDU_Protection_switch_over_com) |
S(V52_PPFSM_E_MDU_Protection_OS_switch_over_com) |
S(V52_PPFSM_E_MDU_Protection_switch_over_reject) |
S(V52_PPFSM_E_VP_S_VP_R_misalignment_detected) |
S(V52_PPFSM_E_MDU_Protection_reset_SN_req) |
S(V52_PPFSM_E_RESET_SN_COM) |
S(V52_PPFSM_E_RESET_SN_ACK) |
S(V52_PPFSM_E_TSO5) |
S(V52_PPFSM_E_PROTOCOL_ERROR),
.out_state_mask = S(V52_PPFSM_S_SOLE0_NULL) |
S(V52_PPFSM_S_SOLE1_SWITCH_OVER_INIT_BY_LE),
.action = pp_sole2_switch_over_req_by_an,
},
};
struct osmo_fsm v52_le_pp_fsm = {
.name = "V52_LE_PP",
.states = v52_le_pp_fsm_states,
.num_states = ARRAY_SIZE(v52_le_pp_fsm_states),
.allstate_event_mask = 0,
.allstate_action = NULL,
.cleanup = NULL,
.timer_cb = v52_le_pp_fsm_timer_cb,
.log_subsys = DV5PP,
.event_names = v52_le_pp_fsm_event_names,
};
struct v52_pp_proto *v52_le_pp_create(struct v5x_interface *v5if)
{
struct v52_pp_proto *pp;
OSMO_ASSERT(v5if);
pp = talloc_zero(v5if, struct v52_pp_proto);
if (!pp)
return NULL;
pp->interface = v5if;
pp->fi = osmo_fsm_inst_alloc(&v52_le_pp_fsm, pp, pp, LOGL_DEBUG, NULL);
if (!pp->fi)
goto error;
return pp;
error:
v52_le_pp_destroy(pp);
return NULL;
}
void v52_le_pp_destroy(struct v52_pp_proto *pp)
{
OSMO_ASSERT(pp);
if (pp->fi) {
osmo_fsm_inst_free(pp->fi);
}
talloc_free(pp);
}
void v52_le_pp_init(void)
{
int rc;
rc = osmo_fsm_register(&v52_le_pp_fsm);
OSMO_ASSERT(!rc);
LOGP(DV5PP, LOGL_NOTICE, "Using V52 Protection protocol\n");
}
/***********************************************************************
* V5 Message receiving / decoding
***********************************************************************/
/* receive message from lower (DL) layer */
int v52_le_pp_dl_rcv(struct v5x_link *v5l, uint16_t cc_id, uint8_t msg_type, const struct tlv_parsed *tp)
{
struct v52_pp_proto *pp = v5l->interface->protection.pp;
enum v52_le_pp_fsm_event event;
int check_sn = 0;
if (!pp) {
LOGP(DV5PP, LOGL_NOTICE, "Received message from DL, but there is no protection on this interface.\n");
return -EIO;
}
switch (msg_type) {
case V52_CTRL_MSGT_PP_SWITCH_OVER_REQ:
event = V52_PPFSM_E_SWITCH_OVER_REQ;
check_sn = 1;
break;
case V52_CTRL_MSGT_PP_SWITCH_OVER_ACK:
event = V52_PPFSM_E_SWITCH_OVER_ACK;
check_sn = 1;
break;
case V52_CTRL_MSGT_PP_SWITCH_OVER_REJECT:
event = V52_PPFSM_E_SWITCH_OVER_REJECT;
check_sn = 1;
break;
case V52_CTRL_MSGT_PP_PROTOCOL_ERROR:
event = V52_PPFSM_E_PROTOCOL_ERROR;
check_sn = 1;
break;
case V52_CTRL_MSGT_PP_RESET_SN_COM:
event = V52_PPFSM_E_RESET_SN_COM;
break;
case V52_CTRL_MSGT_PP_RESET_SN_ACK:
event = V52_PPFSM_E_RESET_SN_ACK;
break;
default:
LOGP(DV5PP, LOGL_NOTICE, "Invalid Protection protocol message %d receied from AN.\n", msg_type);
return -EINVAL;
}
if (cc_id != pp->interface->protection.cc_id) {
LOGP(DV5PP, LOGL_NOTICE, "Protection protocol of AN uses CC-ID %d, but we use CC-ID %d. Please check provisioning.\n", cc_id, pp->interface->protection.cc_id);
dl_send(pp->fi, v52_enc_protocol_error(pp, pp->interface->protection.cc_id, V5X_CAUSE_T_REF_NR_CODING_ERROR, NULL, 0));
return -EINVAL;
}
/* check sequence number (see 18.6.2.2 G.965) */
/* NOTE: If TSO4 is running, there is an ongoing sequence reset, so no check is performed and all received
* message with sequence number are ignored by state machine. */
if (check_sn && !timer_pending(pp->fi, V52_PPFSM_E_TSO4)) {
uint8_t seq_nr = *TLVP_VAL(tp, V52_CTRL_IEI_PP_SEQUENCE_NR) & 0x7f, diff;
/* if we have our first message, we take the serial number */
if (!pp->vp_r_set) {
pp->vp_r = seq_nr;
pp->vp_r_set = true;
}
/* how much is the SN ahead of what we expect */
diff = (seq_nr - pp->vp_r) & 0x7f;
/* if VP(R)-5 <= SN <= VP(R)-1 (SN is 1..5 values lower than we want) */
if (diff >= 128 - 5) {
LOGP(DV5PP, LOGL_DEBUG, "Receiveds message with sequence %d, while we expect %d. Ignore, because it was already received earlier.\n", seq_nr, pp->vp_r);
return 0;
}
/* if VP(R) <= SN <= VP(R) + 4 (SN is 0..4 higher than we want) */
if (diff <= 4) {
/* set received sequence number to the next one expected and deliver message */
pp->vp_r = (seq_nr + 1) & 0x7f;
} else {
LOGP(DV5PP, LOGL_NOTICE, "Receiveds message with sequence %d, while we expect %d. This a a misalignment, perform an SN reset!\n", seq_nr, pp->vp_r);
event = V52_PPFSM_E_VP_S_VP_R_misalignment_detected;
}
}
osmo_fsm_inst_dispatch(pp->fi, event, (void *)tp);
return 0;
}
int v52_le_pp_mdu_snd(struct v5x_interface *v5if, enum v5x_mgmt_prim prim, uint8_t link_id, uint8_t ts, uint8_t cause)
{
enum v52_le_pp_fsm_event event;
struct v52_pp_mgmt_info info = { link_id, ts, cause };
if (!v5if->protection.pp) {
LOGP(DV5PP, LOGL_NOTICE, "Cannot perform action, because there is no protection on this interface.\n");
return -EINVAL;
}
OSMO_ASSERT(v5if->protection.pp);
switch (prim) {
case MDU_Protection_switch_over_com:
event = V52_PPFSM_E_MDU_Protection_switch_over_com;
break;
case MDU_Protection_OS_switch_over_com:
event = V52_PPFSM_E_MDU_Protection_OS_switch_over_com;
break;
case MDU_Protection_switch_over_rej:
event = V52_PPFSM_E_MDU_Protection_switch_over_reject;
break;
case MDU_Protection_reset_SN_req:
event = V52_PPFSM_E_MDU_Protection_reset_SN_req;
break;
default:
LOGP(DV5PP, LOGL_NOTICE, "Invalid MDU primitive %d receied from management.\n", prim);
return -EINVAL;
}
osmo_fsm_inst_dispatch(v5if->protection.pp->fi, event, &info);
return 0;
}

6
src/v52_le_pp_fsm.h Normal file
View File

@ -0,0 +1,6 @@
struct v52_pp_proto *v52_le_pp_create(struct v5x_interface *v5if);
void v52_le_pp_destroy(struct v52_pp_proto *pp);
void v52_le_pp_init(void);
int v52_le_pp_dl_rcv(struct v5x_link *v5l, uint16_t cc_id, uint8_t msg_type, const struct tlv_parsed *tp);
int v52_le_pp_mdu_snd(struct v5x_interface *v5if, enum v5x_mgmt_prim prim, uint8_t link_id, uint8_t ts, uint8_t cause);