2023-01-05 17:12:38 +00:00
/* 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 )
{
2023-08-13 13:02:01 +00:00
LOGPFSML ( fi , LOGL_DEBUG , " Stop all timers \n " ) ;
2023-01-05 17:12:38 +00:00
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 ) ) ;
2023-08-13 13:02:01 +00:00
LOGPFSML ( fi , LOGL_DEBUG , " Start timer %s (count = %d) \n " , timer_name , count ) ;
2023-01-05 17:12:38 +00:00
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 ;
2023-08-13 12:17:40 +00:00
osmo_fsm_inst_update_id_f ( pp - > fi , " %s " , v5x_interface_name ( v5if ) ) ;
2023-01-05 17:12:38 +00:00
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 :
2023-08-13 13:02:01 +00:00
LOGPFSML ( pp - > fi , LOGL_NOTICE , " Invalid Protection protocol message %d receied from AN. \n " , msg_type ) ;
2023-01-05 17:12:38 +00:00
return - EINVAL ;
}
if ( cc_id ! = pp - > interface - > protection . cc_id ) {
2023-08-13 13:02:01 +00:00
LOGPFSML ( pp - > fi , 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 ) ;
2023-01-05 17:12:38 +00:00
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 ) {
2023-08-13 13:02:01 +00:00
LOGPFSML ( pp - > fi , LOGL_DEBUG , " Receiveds message with sequence %d, while we expect %d. Ignore, because it was already received earlier. \n " , seq_nr , pp - > vp_r ) ;
2023-01-05 17:12:38 +00:00
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 {
2023-08-13 13:02:01 +00:00
LOGPFSML ( pp - > fi , 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 ) ;
2023-01-05 17:12:38 +00:00
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 :
2023-08-13 13:02:01 +00:00
LOGPFSML ( v5if - > protection . pp - > fi , LOGL_NOTICE , " Invalid MDU primitive %d receied from management. \n " , prim ) ;
2023-01-05 17:12:38 +00:00
return - EINVAL ;
}
osmo_fsm_inst_dispatch ( v5if - > protection . pp - > fi , event , & info ) ;
return 0 ;
}