forked from retronetworking/osmo-v5
846 lines
26 KiB
C
846 lines
26 KiB
C
/* 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;
|
|
}
|